[app/xdpyinfo,v3] Use XRANDR 1.2 extension for reporting dimensions and resolution per output

Submitted by Pali Rohár on April 12, 2018, 6:47 p.m.

Details

Message ID 20180412184734.31710-1-pali.rohar@gmail.com
State New
Series "Use XRANDR 1.2 extension for reporting dimensions and resolution per output"
Headers show

Commit Message

Pali Rohár April 12, 2018, 6:47 p.m.
XServer with enabled XRANDR 1.2 extension does not provide correct
dimensions from DisplayWidthMM() and DisplayHeightMM() calls anymore.
Values are calculated from fixed DPI 96.

Therefore when XRANDR 1.2 extension is enabled, present and user requested
for it, instead use XRRGetScreenResources() and XRRGetOutputInfo() calls to
get correct dimensions and resolution information.

Otherwise when XRANDR 1.2 extension is enabled, present and user did not
request for it (which is default), show note that printed dimension does
not have to be correct and instruct user to run xdpyinfo with -ext RANDR.

Also update manual page and add information that xdpyinfo does not provide
correct information about DPI.

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
---
Changes since v2:
* Fixed dimensions calculation when rotation is active
* Show XRANDR output only when explicitly requested
* Update manpage

Changes since v1:
* Fixed detection of presence of XRANDR 1.2
* Fixed resolution calculation when dimensions are zero
---
 Makefile.am      |   2 +
 configure.ac     |  12 ++++++
 man/xdpyinfo.man |   7 ++++
 xdpyinfo.c       | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 128 insertions(+), 10 deletions(-)

Patch hide | download patch | download mbox

diff --git a/Makefile.am b/Makefile.am
index 2f21dda..496094e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,7 @@  AM_CFLAGS = \
 	$(DPY_XCOMPOSITE_CFLAGS) \
 	$(DPY_XINERAMA_CFLAGS) \
 	$(DPY_DMX_CFLAGS) \
+	$(DPY_XRANDR_CFLAGS) \
 	$(DPY_XTST_CFLAGS)
 
 xdpyinfo_LDADD = \
@@ -49,6 +50,7 @@  xdpyinfo_LDADD = \
 	$(DPY_XCOMPOSITE_LIBS) \
 	$(DPY_XINERAMA_LIBS) \
 	$(DPY_DMX_LIBS) \
+	$(DPY_XRANDR_LIBS) \
 	$(DPY_XTST_LIBS)
 
 xdpyinfo_SOURCES =	\
diff --git a/configure.ac b/configure.ac
index 73dce26..4473faa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -132,6 +132,18 @@  else
 	echo "without dmx"
 fi
 
+AC_ARG_WITH(xrandr, AS_HELP_STRING([--without-xrandr],[Disable xrandr 1.2 support.]),
+		[USE_XRANDR="$withval"], [USE_XRANDR="yes"])
+if test "x$USE_XRANDR" != "xno" ; then
+	PKG_CHECK_MODULES(DPY_XRANDR, xrandr >= 1.2,
+		[SAVE_CPPFLAGS="$CPPFLAGS"
+		CPPFLAGS="$CPPFLAGS $DPY_XRANDR_CFLAGS $DPY_X11_CFLAGS"
+		AC_CHECK_HEADERS([X11/extensions/Xrandr.h],,,[#include <X11/Xlib.h>])
+		CPPFLAGS="$SAVE_CPPFLAGS"],[echo "not found"])
+else
+	echo "without xrandr 1.2"
+fi
+
 PKG_CHECK_MODULES(DPY_XTST, xtst,
 	[SAVE_CPPFLAGS="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $DPY_XTST_CFLAGS $DPY_X11_CFLAGS"
diff --git a/man/xdpyinfo.man b/man/xdpyinfo.man
index c3d5c6d..5df44ea 100644
--- a/man/xdpyinfo.man
+++ b/man/xdpyinfo.man
@@ -51,6 +51,13 @@  Detailed information about a particular extension is displayed with the
 \fBall\fP, information about all extensions supported by both \fIxdpyinfo\fP
 and the server is displayed.
 .PP
+Do not use this utility for determining dimensions of monitor when XRANDR 1.2+
+extension is enabled for X screen, because it does not provide them. For
+determining physical dimensions or DPI of particular monitor use either
+.IR xrandr (__appmansuffix__)
+utility or call xdpyinfo with parameter \fB\-ext RANDR\fP (supported since
+xdpyinfo version 1.3.3).
+.PP
 If \fB-version\fP is specified, xdpyinfo prints its version and exits, without
 contacting the X server.
 .SH ENVIRONMENT
diff --git a/xdpyinfo.c b/xdpyinfo.c
index 152e32c..ac2526f 100644
--- a/xdpyinfo.c
+++ b/xdpyinfo.c
@@ -76,6 +76,10 @@  in this Software without prior written authorization from The Open Group.
 #  define DMX
 # endif
 
+# if HAVE_X11_EXTENSIONS_XRANDR_H
+#  define XRANDR
+# endif
+
 #endif
 
 #ifdef WIN32
@@ -137,6 +141,9 @@  in this Software without prior written authorization from The Open Group.
 #ifdef DMX
 #include <X11/extensions/dmxext.h>
 #endif
+#ifdef XRANDR
+#include <X11/extensions/Xrandr.h>
+#endif
 #include <X11/Xos.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -442,6 +449,10 @@  print_visual_info(XVisualInfo *vip)
 	    vip->bits_per_rgb);
 }
 
+#ifdef XRANDR
+static Bool print_xrandr = False;
+#endif
+
 static void
 print_screen_info(Display *dpy, int scr)
 {
@@ -455,6 +466,14 @@  print_screen_info(Display *dpy, int scr)
     double xres, yres;
     int ndepths = 0, *depths = NULL;
     unsigned int width, height;
+    Bool has_xrandr = False;
+#ifdef XRANDR
+    int event_base, error_base;
+    int major, minor;
+    XRRScreenResources *res;
+    XRROutputInfo *output;
+    XRRCrtcInfo *crtc;
+#endif
 
     /*
      * there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
@@ -464,18 +483,81 @@  print_screen_info(Display *dpy, int scr)
      *         = N * 25.4 pixels / M inch
      */
 
-    xres = ((((double) DisplayWidth(dpy,scr)) * 25.4) /
-	    ((double) DisplayWidthMM(dpy,scr)));
-    yres = ((((double) DisplayHeight(dpy,scr)) * 25.4) /
-	    ((double) DisplayHeightMM(dpy,scr)));
-
     printf ("\n");
     printf ("screen #%d:\n", scr);
-    printf ("  dimensions:    %dx%d pixels (%dx%d millimeters)\n",
-	    XDisplayWidth (dpy, scr),  XDisplayHeight (dpy, scr),
-	    XDisplayWidthMM(dpy, scr), XDisplayHeightMM (dpy, scr));
-    printf ("  resolution:    %dx%d dots per inch\n",
-	    (int) (xres + 0.5), (int) (yres + 0.5));
+
+#ifdef XRANDR
+    if (XRRQueryExtension (dpy, &event_base, &error_base) &&
+        XRRQueryVersion (dpy, &major, &minor) &&
+        (major > 1 || (major == 1 && minor >= 2)))
+        has_xrandr = True;
+#endif
+
+#ifdef XRANDR
+    if (has_xrandr && print_xrandr)
+    {
+        res = XRRGetScreenResources (dpy, RootWindow (dpy, scr));
+        if (res) {
+            for (i = 0; i < res->noutput; ++i) {
+                output = XRRGetOutputInfo (dpy, res, res->outputs[i]);
+                if (!output || !output->crtc || output->connection != RR_Connected)
+                    continue;
+
+                crtc = XRRGetCrtcInfo (dpy, res, output->crtc);
+                if (!crtc) {
+                    XRRFreeOutputInfo (output);
+                    continue;
+                }
+
+                /* width and height is reported according to rotation, but mm_width and mm_height not */
+                if (crtc->rotation == RR_Rotate_0 || crtc->rotation == RR_Rotate_180) {
+                    width = crtc->width;
+                    height = crtc->height;
+                } else {
+                    width = crtc->height;
+                    height = crtc->width;
+                }
+
+                printf ("  output: %s\n", output->name);
+                printf ("    dimensions:    %ux%u pixels (%lux%lu millimeters)\n",
+                        width, height, output->mm_width, output->mm_height);
+
+                if (output->mm_width && output->mm_height) {
+                    xres = ((((double) width) * 25.4) / ((double) output->mm_width));
+                    yres = ((((double) height) * 25.4) / ((double) output->mm_height));
+                } else {
+                    xres = 0;
+                    yres = 0;
+                }
+                printf ("    resolution:    %dx%d dots per inch\n",
+                        (int) (xres + 0.5), (int) (yres + 0.5));
+
+                XRRFreeCrtcInfo (crtc);
+                XRRFreeOutputInfo (output);
+            }
+            XRRFreeScreenResources (res);
+        }
+    }
+    else
+#endif
+    {
+        printf ("  dimensions:    %dx%d pixels (%dx%d millimeters)\n",
+                DisplayWidth (dpy, scr),  DisplayHeight (dpy, scr),
+                DisplayWidthMM(dpy, scr), DisplayHeightMM (dpy, scr));
+
+        xres = ((((double) DisplayWidth(dpy,scr)) * 25.4) /
+                ((double) DisplayWidthMM(dpy,scr)));
+        yres = ((((double) DisplayHeight(dpy,scr)) * 25.4) /
+                ((double) DisplayHeightMM(dpy,scr)));
+        printf ("  resolution:    %dx%d dots per inch\n",
+                (int) (xres + 0.5), (int) (yres + 0.5));
+
+        if (has_xrandr)
+            printf ("    NOTE: above information is obsoleted and may be incorrect\n"
+                    "          instead run `%s -ext RANDR' for correct output\n\n",
+                    ProgramName);
+    }
+
     depths = XListDepths (dpy, scr, &ndepths);
     if (!depths) ndepths = 0;
     printf ("  depths (%d):    ", ndepths);
@@ -1316,6 +1398,10 @@  static int print_dmx_info(Display *dpy, const char *extname)
 
 #endif /* DMX */
 
+#ifdef XRANDR
+static int print_none_info(Display *dpy, const char *extname) { return 1; }
+#endif
+
 /* utilities to manage the list of recognized extensions */
 
 
@@ -1369,6 +1455,9 @@  static ExtensionPrintInfo known_extensions[] =
 #ifdef DMX
     {"DMX", print_dmx_info, False},
 #endif
+#ifdef XRANDR
+    {RANDR_NAME, print_none_info, False},
+#endif
     /* add new extensions here */
 };
 
@@ -1397,8 +1486,16 @@  mark_extension_for_printing(const char *extname)
 {
     int i;
 
+#ifdef XRANDR
+    if (strcmp(extname, RANDR_NAME) == 0)
+	print_xrandr = True;
+#endif
+
     if (strcmp(extname, "all") == 0)
     {
+#ifdef XRANDR
+	print_xrandr = True;
+#endif
 	for (i = 0; i < num_known_extensions; i++)
 	    known_extensions[i].printit = True;
     }

Comments

Adam Jackson April 12, 2018, 8:34 p.m.
On Thu, 2018-04-12 at 20:47 +0200, Pali Rohár wrote:
> XServer with enabled XRANDR 1.2 extension does not provide correct
> dimensions from DisplayWidthMM() and DisplayHeightMM() calls anymore.
> Values are calculated from fixed DPI 96.
> 
> Therefore when XRANDR 1.2 extension is enabled, present and user requested
> for it, instead use XRRGetScreenResources() and XRRGetOutputInfo() calls to
> get correct dimensions and resolution information.
> 
> Otherwise when XRANDR 1.2 extension is enabled, present and user did not
> request for it (which is default), show note that printed dimension does
> not have to be correct and instruct user to run xdpyinfo with -ext RANDR.

This should print the RANDR data in a separate stanza after the main
output, like the other extensions do. Again: the purpose of the core of
xdpyinfo is to tell you what the connection block says. Don't make it
print something else.

- ajax
Pali Rohár April 18, 2018, 1:33 p.m.
On Thursday 12 April 2018 16:34:15 Adam Jackson wrote:
> This should print the RANDR data in a separate stanza after the main
> output, like the other extensions do. Again: the purpose of the core of
> xdpyinfo is to tell you what the connection block says. Don't make it
> print something else.

This patch does not change anything in the output when command line
option for RANDR is not used. Therefore you would get same output as
before (without applying patch).

And when RANDR is explicitly requested then it outputs correct dimension
information. Yes, it hides what is reported by connection block, but the
main problem is that this tools is not already used like you said. Users
and also scripts expects that they would get correct monitor/output
dimension from xdpyinfo and not something which do not match with their
physical monitor device.

As Giuseppe said, this seems like a good compromise. When no parameter
is specified then xdpyinfo reports exactly same data as without this
patch. And with this patch which adds support for optional RANDR
parameter, then it reports dimensions for each monitor/output correctly.
So users would see what they are already expecting and want.
Giuseppe Bilotta April 18, 2018, 1:45 p.m.
On Wed, Apr 18, 2018 at 3:33 PM, Pali Rohár <pali.rohar@gmail.com> wrote:
> On Thursday 12 April 2018 16:34:15 Adam Jackson wrote:
>> This should print the RANDR data in a separate stanza after the main
>> output, like the other extensions do. Again: the purpose of the core of
>> xdpyinfo is to tell you what the connection block says. Don't make it
>> print something else.
>
> This patch does not change anything in the output when command line
> option for RANDR is not used. Therefore you would get same output as
> before (without applying patch).
>
> And when RANDR is explicitly requested then it outputs correct dimension
> information. Yes, it hides what is reported by connection block, but the
> main problem is that this tools is not already used like you said. Users
> and also scripts expects that they would get correct monitor/output
> dimension from xdpyinfo and not something which do not match with their
> physical monitor device.
>
> As Giuseppe said, this seems like a good compromise. When no parameter
> is specified then xdpyinfo reports exactly same data as without this
> patch. And with this patch which adds support for optional RANDR
> parameter, then it reports dimensions for each monitor/output correctly.
> So users would see what they are already expecting and want.


No, in the RANDR case you are still replacing the core output, which
is not what I suggested. Instead, the RANDR information should be
provided _separately_ and _in addition to_ the core output. So instead
of defining a useless print_none_info, put the RANDR code in
print_randr_info and add _that_ to the known_extensions array.
Pali Rohár May 7, 2018, 9:33 p.m.
On Wednesday 18 April 2018 15:45:20 Giuseppe Bilotta wrote:
> On Wed, Apr 18, 2018 at 3:33 PM, Pali Rohár <pali.rohar@gmail.com> wrote:
> > On Thursday 12 April 2018 16:34:15 Adam Jackson wrote:
> >> This should print the RANDR data in a separate stanza after the main
> >> output, like the other extensions do. Again: the purpose of the core of
> >> xdpyinfo is to tell you what the connection block says. Don't make it
> >> print something else.
> >
> > This patch does not change anything in the output when command line
> > option for RANDR is not used. Therefore you would get same output as
> > before (without applying patch).
> >
> > And when RANDR is explicitly requested then it outputs correct dimension
> > information. Yes, it hides what is reported by connection block, but the
> > main problem is that this tools is not already used like you said. Users
> > and also scripts expects that they would get correct monitor/output
> > dimension from xdpyinfo and not something which do not match with their
> > physical monitor device.
> >
> > As Giuseppe said, this seems like a good compromise. When no parameter
> > is specified then xdpyinfo reports exactly same data as without this
> > patch. And with this patch which adds support for optional RANDR
> > parameter, then it reports dimensions for each monitor/output correctly.
> > So users would see what they are already expecting and want.
> 
> 
> No, in the RANDR case you are still replacing the core output, which
> is not what I suggested. Instead, the RANDR information should be
> provided _separately_ and _in addition to_ the core output. So instead
> of defining a useless print_none_info, put the RANDR code in
> print_randr_info and add _that_ to the known_extensions array.

Ok. I will put both core and randr information into output. But still I
think that those dimension information should be at one place and not
separated to different parts. It is also hard to find them when reading
output or parse.