unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: "Jan D." <jan.h.d@swipnet.se>
To: YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
Cc: emacs-devel@gnu.org
Subject: Re: x-display-pixel-width/height inconsistency
Date: Sun, 28 Apr 2013 19:16:25 +0200	[thread overview]
Message-ID: <8BD4B041-5A3F-4D7C-AFD3-E997E194AA9D@swipnet.se> (raw)
In-Reply-To: <wla9oj1lh9.wl%mituharu@math.s.chiba-u.ac.jp>

[-- Attachment #1: Type: text/plain, Size: 1587 bytes --]

Hello.

28 apr 2013 kl. 03:40 skrev YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>:

>>>>>> On Sat, 27 Apr 2013 10:04:46 +0200, Jan Djärv <jan.h.d@swipnet.se> said:
> 
>> A general objection is that XRandr/Xinerama is not used, so
>> the non-Gtk+ code relies on a freedesktop compliant window manager,
>> rather than taking the general approach.  If XRandr/Xinerama is
>> used, no split between Gtk+ and non-Gtk+ code is needed.
>> Xrandr/Xinerama is what Gtk+ use anyway so we are not adding any new
>> dependencies.
> 
> Of course, patches welcome.  (And also for implementations for other
> platforms.)  I guess XRandR 1.2 covers most of the current uses.

Patch attached. The patch includes your code.
Actually 1.3 is better, it has XRRGetOutputPrimary.  Without it we have to guess.

> 
> Because XRandR or Xinerama doesn't help us get information about the
> workarea (that's the task of window managers), we need some function
> like x_get_net_workarea regardless of the use of XRandR or Xinerama.

Right.

> 
> Even with XRandR or Xinerama code, leaving the GDK code would be
> meaningful because we may have yet another framework for multiple
> monitor support (GDK already covers 3 of them on X11) in future.

Gdk covers the "Xinerama on Solaris" case which is a bit different.  I don't have any Solaris available, so I can't do that.

Another comment:
Your defun is x-display-monitor-attributes-list.  Aren't we supposed to try to avoid the use of x-prefixed functions?  display-monitor-attributes-list sounds fine to me.

	Jan D.



[-- Attachment #2: randr.diff --]
[-- Type: application/octet-stream, Size: 24614 bytes --]

=== modified file 'configure.ac'
--- configure.ac	2013-04-26 19:31:09 +0000
+++ configure.ac	2013-04-28 17:03:38 +0000
@@ -2814,6 +2814,57 @@
 fi
 AC_SUBST(LIBXSM)
 
+### Use XRandr (-lXrandr) if available
+HAVE_XRANDR=no
+if test "${HAVE_X11}" = "yes"; then
+  XRANDR_REQUIRED=1.2.2
+  XRANDR_MODULES="xrandr >= $XRANDR_REQUIRED"
+  PKG_CHECK_MODULES(XRANDR, $XRANDR_MODULES, HAVE_XRANDR=yes, HAVE_XRANDR=no)
+  if test $HAVE_XRANDR = no; then
+    # Test old way in case pkg-config doesn't have it (older machines).
+    AC_CHECK_HEADER(X11/extensions/Xrandr.h,
+      [AC_CHECK_LIB(XRANDR, XRRQueryExtension, HAVE_XRANDR=yes, , -lXrandr)])
+    if test $HAVE_XRANDR = yes; then
+      XRANDR_LIBS=-lXrandr
+      AC_SUBST(XRANDR_LIBS)
+    fi
+  fi
+  if test $HAVE_XRANDR = yes; then
+    SAVE_CFLAGS="$CFLAGS"
+    SAVE_LIBS="$LIBS"
+    CFLAGS="$XRANDR_CFLAGS $CFLAGS"
+    LIBS="$XRANDR_LIBS $LIBS"
+    AC_CHECK_FUNCS(XRRGetOutputPrimary)
+    CFLAGS="$SAVE_CFLAGS"
+    LIBS="$SAVE_LIBS"
+
+    AC_DEFINE(HAVE_XRANDR, 1, [Define to 1 if you have the XRandr extension.])
+  fi
+fi
+
+### Use Xinerama (-lXinerama) if available
+HAVE_XINERAMA=no
+if test "${HAVE_X11}" = "yes"; then
+  XINERAMA_REQUIRED=1.0.2
+  XINERAMA_MODULES="xinerama >= $XINERAMA_REQUIRED"
+  PKG_CHECK_MODULES(XINERAMA, $XINERAMA_MODULES, HAVE_XINERAMA=yes, 
+                    HAVE_XINERAMA=no)
+  if test $HAVE_XINERAMA = no; then
+    # Test old way in case pkg-config doesn't have it (older machines).
+    AC_CHECK_HEADER(X11/extensions/Xrandr.h,
+      [AC_CHECK_LIB(XINERAMA, XineramaQueryExtension,
+                    HAVE_XINERAMA=yes, , -lXinerama)])
+    if test $HAVE_XINERAMA = yes; then
+      XINERAMA_LIBS=-lXinerama
+      AC_SUBST(XINERAMA_LIBS)
+    fi
+  fi
+  if test $HAVE_XINERAMA = yes; then
+    AC_DEFINE(HAVE_XINERAMA, 1, [Define to 1 if you have the Xinerama extension.])
+  fi
+fi
+
+
 ### Use libxml (-lxml2) if available
 HAVE_LIBXML2=no
 if test "${with_xml2}" != "no"; then

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2013-04-22 05:18:30 +0000
+++ src/Makefile.in	2013-04-28 16:31:43 +0000
@@ -232,6 +232,12 @@
 LIBXML2_LIBS = @LIBXML2_LIBS@
 LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
 
+XRANDR_LIBS = @XRANDR_LIBS@
+XRANDR_CFLAGS = @XRANDR_CFLAGS@
+
+XINERAMA_LIBS = @XINERAMA_LIBS@
+XINERAMA_CFLAGS = @XINERAMA_CFLAGS@
+
 ## widget.o if USE_X_TOOLKIT, otherwise empty.
 WIDGET_OBJ=@WIDGET_OBJ@
 
@@ -315,7 +321,7 @@
   -I$(lib) -I$(srcdir)/../lib \
   $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \
   $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
-  $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \
+  $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) \
   $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
   $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
   $(LIBGNUTLS_CFLAGS) \
@@ -393,7 +399,7 @@
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
    $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
-   $(LIB_EXECINFO) \
+   $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \

=== modified file 'src/xfns.c'
--- src/xfns.c	2013-04-07 04:41:19 +0000
+++ src/xfns.c	2013-04-28 17:04:07 +0000
@@ -59,6 +59,13 @@
 
 #include "xsettings.h"
 
+#ifdef HAVE_XRANDR
+#include <X11/extensions/Xrandr.h>
+#endif
+#ifdef HAVE_XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
+
 #ifdef USE_GTK
 #include "gtkutil.h"
 #endif
@@ -126,6 +133,7 @@
 static Lisp_Object Qsuppress_icon;
 static Lisp_Object Qundefined_color;
 static Lisp_Object Qcompound_text, Qcancel_timer;
+static Lisp_Object Qgeometry, Qworkarea, Qmm_size, Qframes;
 Lisp_Object Qfont_param;
 
 #ifdef GLYPH_DEBUG
@@ -3791,6 +3799,548 @@
   else
     return Qnil;
 }
+
+/* Return an alist of the form
+   ((left . (+ XOFFSET)) (top . (+ YOFFSET))
+    (width . WIDTH) (height . HEIGHT))
+   with converting XOFFSET, YOFFSET, WIDTH and HEIGHT to Lisp
+   integers.  */
+
+static Lisp_Object
+x_create_geometry_alist (EMACS_INT xoffset, EMACS_INT yoffset,
+			 EMACS_INT width, EMACS_INT height)
+{
+  return list4 (Fcons (Qleft, list2 (Qplus, make_number (xoffset))),
+		Fcons (Qtop, list2 (Qplus, make_number (yoffset))),
+		Fcons (Qwidth, make_number (width)),
+		Fcons (Qheight, make_number (height)));
+}
+
+/* Return an alist of the form ((width . WIDTH) (height . HEIGHT)).
+   with converting WIDTH and HEIGHT to Lisp integers.  */
+
+static Lisp_Object
+x_create_size_alist (EMACS_INT width, EMACS_INT height)
+{
+  return list2 (Fcons (Qwidth, make_number (width)),
+		Fcons (Qheight, make_number (height)));
+}
+
+/* Store the geometry of the workarea on display DPYINFO into *RECT.
+   Return false if and only if the workarea information cannot be
+   obtained via the _NET_WORKAREA root window property.  */
+
+static bool
+x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect)
+{
+  Display *dpy = dpyinfo->display;
+  long offset, max_len;
+  Atom target_type, actual_type;
+  unsigned long actual_size, bytes_remaining;
+  int rc, actual_format;
+  unsigned char *tmp_data = NULL;
+  bool result = false;
+
+  x_catch_errors (dpy);
+  offset = 0;
+  max_len = 1;
+  target_type = XA_CARDINAL;
+  rc = XGetWindowProperty (dpy, dpyinfo->root_window,
+			   dpyinfo->Xatom_net_current_desktop,
+			   offset, max_len, False, target_type,
+			   &actual_type, &actual_format, &actual_size,
+			   &bytes_remaining, &tmp_data);
+  if (rc == Success && actual_type == target_type && !x_had_errors_p (dpy)
+      && actual_format == 32 && actual_size == max_len)
+    {
+      long current_desktop = ((long *) tmp_data)[0];
+
+      XFree (tmp_data);
+      tmp_data = NULL;
+
+      offset = 4 * current_desktop;
+      max_len = 4;
+      rc = XGetWindowProperty (dpy, dpyinfo->root_window,
+			       dpyinfo->Xatom_net_workarea,
+			       offset, max_len, False, target_type,
+			       &actual_type, &actual_format, &actual_size,
+			       &bytes_remaining, &tmp_data);
+      if (rc == Success && actual_type == target_type && !x_had_errors_p (dpy)
+	  && actual_format == 32 && actual_size == max_len)
+	{
+	  long *workareas = (long *) tmp_data;
+
+	  rect->x = workareas[0];
+	  rect->y = workareas[1];
+	  rect->width = workareas[2];
+	  rect->height = workareas[3];
+
+	  XFree (tmp_data);
+	  tmp_data = NULL;
+
+	  result = true;
+	}
+    }
+  if (tmp_data)
+    XFree (tmp_data);
+  x_uncatch_errors ();
+
+  return result;
+}
+
+struct MonitorInfo {
+  XRectangle geom, work;
+  int mm_width, mm_height;
+};
+
+/* Return monitor number where F is "most" or closest to.  */
+static int
+x_get_monitor_for_frame (struct frame *f,
+                         struct MonitorInfo *monitors,
+                         int n_monitors)
+{
+  XRectangle frect;
+  int area = 0, dist = -1;
+  int best_area = -1, best_dist = -1;
+  int i;
+
+  if (n_monitors == 1) return 0;
+  frect.x = f->left_pos;
+  frect.y = f->top_pos;
+  frect.width = FRAME_PIXEL_WIDTH (f);
+  frect.height = FRAME_PIXEL_HEIGHT (f);
+
+  for (i = 0; i < n_monitors; ++i)
+    {
+      struct MonitorInfo *mi = &monitors[i];
+      XRectangle res;
+      int a = 0;
+
+      if (mi->geom.width == 0) continue;
+
+      if (x_intersect_rectangles (&mi->geom, &frect, &res))
+        {
+          a = res.width * res.height;
+          if (a > area) {
+            area = a;
+            best_area = i;
+          }
+        }
+
+      if (a == 0 && area == 0)
+        {
+          int dx, dy, d;
+          if (frect.x + frect.width < mi->geom.x)
+            dx = mi->geom.x - frect.x + frect.width;
+          else if (frect.x > mi->geom.x + mi->geom.width)
+            dx = frect.x - mi->geom.x + mi->geom.width;
+          else
+            dx = 0;
+          if (frect.y + frect.height < mi->geom.y)
+            dy = mi->geom.y - frect.y + frect.height;
+          else if (frect.y > mi->geom.y + mi->geom.height)
+            dy = frect.y - mi->geom.y + mi->geom.height;
+          else
+            dy = 0;
+
+          d = dx*dx + dy*dy;
+          if (dist == -1 || dist > d)
+            {
+              dist = d;
+              best_dist = i;
+            }
+        }
+    }
+
+  return best_area != -1 ? best_area : (best_dist != -1 ? best_dist : 0);
+}
+
+static Lisp_Object
+x_make_monitor_attribute_list (struct MonitorInfo *monitors,
+                               int n_monitors,
+                               int primary_monitor,
+                               struct x_display_info *dpyinfo)
+{
+  Lisp_Object monitor_frames = Fmake_vector (make_number (n_monitors), Qnil);
+  Lisp_Object frame, rest, attributes_list = Qnil;
+  Lisp_Object primary_monitor_attributes = Qnil;
+  int i;
+
+  FOR_EACH_FRAME (rest, frame)
+    {
+      struct frame *f = XFRAME (frame);
+
+      if (FRAME_X_P (f) && FRAME_X_DISPLAY_INFO (f) == dpyinfo
+	  && !EQ (frame, tip_frame))
+	{
+	  i = x_get_monitor_for_frame (f, monitors, n_monitors);
+	  ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i)));
+	}
+    }
+  
+  for (i = 0; i < n_monitors; ++i) 
+    {
+      Lisp_Object geometry, workarea, attributes = Qnil;
+      struct MonitorInfo *mi = &monitors[i];
+
+      if (mi->geom.width == 0) continue;
+
+      workarea = x_create_geometry_alist (mi->work.x, mi->work.y,
+                                          mi->work.width, mi->work.height);
+      geometry = x_create_geometry_alist (mi->geom.x, mi->geom.y,
+                                          mi->geom.width, mi->geom.height);
+      attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
+			  attributes);
+      attributes = Fcons (Fcons (Qmm_size,
+                                 x_create_size_alist (mi->mm_width,
+                                                      mi->mm_height)),
+                          attributes);
+      attributes = Fcons (Fcons (Qworkarea, workarea), attributes);
+      attributes = Fcons (Fcons (Qgeometry, geometry), attributes);
+
+      if (i == primary_monitor)
+        primary_monitor_attributes = attributes;
+      else
+        attributes_list = Fcons (attributes, attributes_list);
+    }
+
+  if (!NILP (primary_monitor_attributes))
+    attributes_list = Fcons (primary_monitor_attributes, attributes_list);
+
+}
+
+static Lisp_Object
+x_get_monitor_attributes_fallback (struct x_display_info *dpyinfo)
+{
+  struct MonitorInfo monitor;
+  int width_mm, height_mm;
+  XRectangle workarea_r;
+
+  /* Fallback: treat (possibly) multiple physical monitors as if they
+     formed a single monitor as a whole.  This should provide a
+     consistent result at least on single monitor environments.  */
+  monitor.geom.x = monitor.geom.y = 0;
+  monitor.geom.width = x_display_pixel_width (dpyinfo);
+  monitor.geom.height = x_display_pixel_height (dpyinfo);
+  monitor.mm_width = WidthMMOfScreen (dpyinfo->screen);
+  monitor.mm_height = HeightMMOfScreen (dpyinfo->screen);
+  if (x_get_net_workarea (dpyinfo, &workarea_r))
+    monitor.work = workarea_r;
+  else
+    monitor.work = monitor.geom;
+  return x_make_monitor_attribute_list (&monitor, 1, 0, dpyinfo);
+}
+
+
+#ifdef HAVE_XINERAMA
+static Lisp_Object
+x_get_monitor_attributes_xinerama (struct x_display_info *dpyinfo)
+{
+  int n_monitors, i;
+  Lisp_Object attributes_list = Qnil;
+  Display *dpy = dpyinfo->display;
+  XineramaScreenInfo *info = XineramaQueryScreens (dpy, &n_monitors);
+  struct MonitorInfo *monitors;
+  float mm_width_per_pixel, mm_height_per_pixel;
+
+  if (! info) return attributes_list;
+
+  mm_width_per_pixel = ((float) WidthMMOfScreen (dpyinfo->screen)
+			/ x_display_pixel_width (dpyinfo));
+  mm_height_per_pixel = ((float) HeightMMOfScreen (dpyinfo->screen)
+			 / x_display_pixel_height (dpyinfo));
+  monitors = (struct MonitorInfo *) xzalloc (n_monitors * sizeof (*monitors));
+  for (i = 0; i < n_monitors; ++i) 
+    {
+      struct MonitorInfo *mi = &monitors[i];
+      XRectangle workarea_r;
+
+      mi->geom.x = info[i].x_org;
+      mi->geom.y = info[i].y_org;
+      mi->geom.width = info[i].width;
+      mi->geom.height = info[i].height;
+      mi->mm_width = mi->geom.width * mm_width_per_pixel + 0.5;
+      mi->mm_height = mi->geom.height * mm_height_per_pixel + 0.5;
+
+      /* Xinerama usually have primary monitor first, just use that.  */
+      if (i == 0 && x_get_net_workarea (dpyinfo, &workarea_r))
+            {
+              mi->work = workarea_r;
+              if (! x_intersect_rectangles (&mi->geom, &mi->work, &mi->work))
+                mi->work = mi->geom;
+            }
+          else
+            mi->work = mi->geom;
+    }
+  XFree (info);
+
+  attributes_list = x_make_monitor_attribute_list (monitors,
+                                                   n_monitors,
+                                                   0,
+                                                   dpyinfo);
+  xfree (monitors);
+  return attributes_list;
+  
+}
+#endif /* HAVE_XINERAMA */
+
+
+#ifdef HAVE_XRANDR
+static Lisp_Object
+x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo)
+{
+  Lisp_Object attributes_list = Qnil;
+  XRRScreenResources *resources;
+  Display *dpy = dpyinfo->display;
+  int i, n_monitors, primary = -1;
+  RROutput pxid = None;
+  struct MonitorInfo *monitors;
+
+  resources = XRRGetScreenResourcesCurrent (dpy, dpyinfo->root_window);
+  if (! resources) return Qnil;
+  n_monitors = resources->noutput;
+  monitors = (struct MonitorInfo *) xzalloc (n_monitors * sizeof (*monitors));
+
+#ifdef HAVE_XRRGETOUTPUTPRIMARY
+  pxid = XRRGetOutputPrimary (dpy, dpyinfo->root_window);
+#endif
+
+  for (i = 0; i < n_monitors; ++i) 
+    {
+      XRROutputInfo *info = XRRGetOutputInfo (dpy, resources,
+                                              resources->outputs[i]);
+      Connection conn = info ? info->connection : RR_Disconnected;
+      RRCrtc id = info ? info->crtc : None;
+      
+      if (conn != RR_Disconnected && id != None)
+        {
+          XRRCrtcInfo *crtc = XRRGetCrtcInfo (dpy, resources, id);
+          struct MonitorInfo *mi = &monitors[i];
+          XRectangle workarea_r;
+
+          if (! crtc) continue;
+
+          mi->geom.x = crtc->x;
+          mi->geom.y = crtc->y;
+          mi->geom.width = crtc->width;
+          mi->geom.height = crtc->height;
+          mi->mm_width = info->mm_width;
+          mi->mm_height = info->mm_height;
+
+          if (pxid != None && pxid == resources->outputs[i])
+            primary = i;
+          else if (primary == -1 && strcmp (info->name, "LVDS") == 0)
+            primary = i;
+                
+          if (i == primary && x_get_net_workarea (dpyinfo, &workarea_r))
+            {
+              mi->work= workarea_r;
+              if (! x_intersect_rectangles (&mi->geom, &mi->work, &mi->work))
+                mi->work = mi->geom;
+            }
+          else
+            mi->work = mi->geom;
+
+          XRRFreeCrtcInfo (crtc);
+        }
+      XRRFreeOutputInfo (info);
+    }
+  XRRFreeScreenResources (resources);
+
+  attributes_list = x_make_monitor_attribute_list (monitors,
+                                                   n_monitors,
+                                                   primary,
+                                                   dpyinfo);
+  xfree (monitors);
+  return attributes_list;
+}
+#endif /* HAVE_XRANDR */
+
+static Lisp_Object
+x_get_monitor_attributes (struct x_display_info *dpyinfo)
+{
+  Lisp_Object attributes_list = Qnil;
+  Display *dpy = dpyinfo->display;
+
+#ifdef HAVE_XRANDR
+  int xrr_event_base, xrr_error_base;
+  bool xrr_ok = false;
+  xrr_ok = XRenderQueryExtension (dpy, &xrr_event_base, &xrr_error_base);
+  if (xrr_ok)
+    {
+      int xrr_major, xrr_minor;
+      XRRQueryVersion (dpy, &xrr_major, &xrr_minor);
+      xrr_ok = (xrr_major == 1 && xrr_minor >= 2) || xrr_major > 1;
+    }
+
+  if (xrr_ok)
+    attributes_list = x_get_monitor_attributes_xrandr (dpyinfo);
+#endif /* HAVE_XRANDR */
+
+#ifdef HAVE_XINERAMA
+  if (NILP (attributes_list))
+    {
+      int xin_event_base, xin_error_base;
+      bool xin_ok = false;
+      xin_ok = XineramaQueryExtension (dpy, &xin_event_base, &xin_error_base);
+      if (xin_ok && XineramaIsActive (dpy))
+        attributes_list = x_get_monitor_attributes_xinerama (dpyinfo);
+    }
+#endif /* HAVE_XINERAMA */
+
+  if (NILP (attributes_list))
+    attributes_list = x_get_monitor_attributes_fallback (dpyinfo);
+
+  return attributes_list;
+}
+
+DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list,
+       Sx_display_monitor_attributes_list,
+       0, 1, 0,
+       doc: /* Return a list of physical monitor attributes on X display.
+Each element of the list represents the attributes of each physical
+monitor.  The first element corresponds to the primary monitor.
+
+Attributes for a physical monitor is represented as an alist of
+attribute names and values as follows:
+
+    Name     | Value
+    ---------+--------------------------------------------------
+    geometry | Position and size in the form of
+	     | ((left . (+ XOFFSET)) (top . (+ YOFFSET))
+	     |  (width . WIDTH) (height . HEIGHT)).
+	     |
+    workarea | Position and size of the workarea in the form of
+	     | ((left . (+ XOFFSET)) (top . (+ YOFFSET))
+	     |  (width . WIDTH) (height . HEIGHT)).
+	     |
+    mm-size  | Width and height in millimeters in the form of
+	     | ((width . WIDTH) (height . HEIGHT)).
+	     |
+    frames   | List of frames belonging to the physical monitor.
+
+where XOFFSET, YOFFSET, WIDTH, and HEIGHT are integers.  A frame
+belongs to a monitor when either the largest area of the frame resides
+in the monitor, or the monitor is the closest to the frame if the
+frame does not intersect any monitors.  Every non-tip frame (including
+invisible one) in a graphical display belongs to exactly one monitor.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  struct x_display_info *dpyinfo = check_x_display_info (terminal);
+  Lisp_Object attributes_list = Qnil;
+
+#ifdef USE_GTK
+  float mm_width_per_pixel, mm_height_per_pixel;
+  GdkDisplay *gdpy;
+  GdkScreen *gscreen;
+  gint primary_monitor = 0, n_monitors, i;
+  Lisp_Object primary_monitor_attributes = Qnil;
+  Lisp_Object monitor_frames, rest, frame;
+
+  block_input ();
+  mm_width_per_pixel = ((float) WidthMMOfScreen (dpyinfo->screen)
+			/ x_display_pixel_width (dpyinfo));
+  mm_height_per_pixel = ((float) HeightMMOfScreen (dpyinfo->screen)
+			 / x_display_pixel_height (dpyinfo));
+  gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display);
+  gscreen = gdk_display_get_default_screen (gdpy);
+#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 20
+  primary_monitor = gdk_screen_get_primary_monitor (gscreen);
+#endif
+  n_monitors = gdk_screen_get_n_monitors (gscreen);
+  monitor_frames = Fmake_vector (make_number (n_monitors), Qnil);
+  FOR_EACH_FRAME (rest, frame)
+    {
+      struct frame *f = XFRAME (frame);
+
+      if (FRAME_X_P (f) && FRAME_X_DISPLAY_INFO (f) == dpyinfo
+	  && !EQ (frame, tip_frame))
+	{
+	  GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_WIDGET (f));
+
+	  i = gdk_screen_get_monitor_at_window (gscreen, gwin);
+	  ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i)));
+	}
+    }
+
+  i = n_monitors;
+  while (i-- > 0)
+    {
+      Lisp_Object geometry, workarea, attributes = Qnil;
+      gint width_mm = -1, height_mm = -1;
+      GdkRectangle rec;
+
+      attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
+			  attributes);
+
+      gdk_screen_get_monitor_geometry (gscreen, i, &rec);
+      geometry = x_create_geometry_alist (rec.x, rec.y, rec.width, rec.height);
+
+#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 14
+      width_mm = gdk_screen_get_monitor_width_mm (gscreen, i);
+      height_mm = gdk_screen_get_monitor_height_mm (gscreen, i);
+#endif
+      if (width_mm < 0)
+	width_mm = rec.width * mm_width_per_pixel + 0.5;
+      if (height_mm < 0)
+	height_mm = rec.height * mm_height_per_pixel + 0.5;
+      attributes = Fcons (Fcons (Qmm_size,
+				 x_create_size_alist (width_mm, height_mm)),
+			  attributes);
+
+#if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4)
+      gdk_screen_get_monitor_workarea (gscreen, i, &rec);
+      workarea = x_create_geometry_alist (rec.x, rec.y, rec.width, rec.height);
+#else
+      /* Emulate the behavior of GTK+ 3.4.  */
+      {
+	XRectangle workarea_r;
+
+	workarea = Qnil;
+	if (i == primary_monitor && x_get_net_workarea (dpyinfo, &workarea_r))
+	  {
+	    GdkRectangle work;
+
+	    work.x = workarea_r.x;
+	    work.y = workarea_r.y;
+	    work.width = workarea_r.width;
+	    work.height = workarea_r.height;
+	    if (gdk_rectangle_intersect (&rec, &work, &work))
+	      workarea = x_create_geometry_alist (work.x, work.y,
+						  work.width, work.height);
+	  }
+	if (NILP (workarea))
+	  workarea = geometry;
+      }
+#endif
+      attributes = Fcons (Fcons (Qworkarea, workarea), attributes);
+
+      attributes = Fcons (Fcons (Qgeometry, geometry), attributes);
+
+      if (i == primary_monitor)
+	primary_monitor_attributes = attributes;
+      else
+	attributes_list = Fcons (attributes, attributes_list);
+    }
+
+  if (!NILP (primary_monitor_attributes))
+    attributes_list = Fcons (primary_monitor_attributes, attributes_list);
+  unblock_input ();
+#else  /* not USE_GTK */
+
+  block_input ();
+  attributes_list = x_get_monitor_attributes (dpyinfo);
+  unblock_input ();
+
+#endif	/* not USE_GTK */
+
+  return attributes_list;
+}
+
 \f
 int
 x_pixel_width (register struct frame *f)
@@ -5701,6 +6251,10 @@
   DEFSYM (Qundefined_color, "undefined-color");
   DEFSYM (Qcompound_text, "compound-text");
   DEFSYM (Qcancel_timer, "cancel-timer");
+  DEFSYM (Qgeometry, "geometry");
+  DEFSYM (Qworkarea, "workarea");
+  DEFSYM (Qmm_size, "mm-size");
+  DEFSYM (Qframes, "frames");
   DEFSYM (Qfont_param, "font-parameter");
   /* This is the end of symbol initialization.  */
 
@@ -5864,6 +6418,7 @@
   defsubr (&Sx_display_visual_class);
   defsubr (&Sx_display_backing_store);
   defsubr (&Sx_display_save_under);
+  defsubr (&Sx_display_monitor_attributes_list);
   defsubr (&Sx_wm_set_size_hint);
   defsubr (&Sx_create_frame);
   defsubr (&Sx_open_connection);

=== modified file 'src/xterm.c'
--- src/xterm.c	2013-03-25 17:58:35 +0000
+++ src/xterm.c	2013-04-28 12:00:47 +0000
@@ -10251,6 +10251,8 @@
       { "_NET_WM_WINDOW_OPACITY", &dpyinfo->Xatom_net_wm_window_opacity },
       { "_NET_ACTIVE_WINDOW", &dpyinfo->Xatom_net_active_window },
       { "_NET_FRAME_EXTENTS", &dpyinfo->Xatom_net_frame_extents },
+      { "_NET_CURRENT_DESKTOP", &dpyinfo->Xatom_net_current_desktop },
+      { "_NET_WORKAREA", &dpyinfo->Xatom_net_workarea },
       /* Session management */
       { "SM_CLIENT_ID", &dpyinfo->Xatom_SM_CLIENT_ID },
       { "_XSETTINGS_SETTINGS", &dpyinfo->Xatom_xsettings_prop },

=== modified file 'src/xterm.h'
--- src/xterm.h	2013-04-07 04:41:19 +0000
+++ src/xterm.h	2013-04-28 12:00:47 +0000
@@ -346,7 +346,8 @@
   Atom Xatom_net_wm_state, Xatom_net_wm_state_fullscreen,
     Xatom_net_wm_state_maximized_horz, Xatom_net_wm_state_maximized_vert,
     Xatom_net_wm_state_sticky, Xatom_net_wm_state_hidden,
-    Xatom_net_frame_extents;
+    Xatom_net_frame_extents,
+    Xatom_net_current_desktop, Xatom_net_workarea;
 
   /* XSettings atoms and windows.  */
   Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr;


[-- Attachment #3: Type: text/plain, Size: 4 bytes --]






  reply	other threads:[~2013-04-28 17:16 UTC|newest]

Thread overview: 97+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-03-21  0:58 x-display-pixel-width/height inconsistency grischka
2013-03-21  1:05 ` YAMAMOTO Mitsuharu
2013-03-21  1:09   ` grischka
2013-03-21  1:44     ` YAMAMOTO Mitsuharu
2013-03-21 23:29       ` YAMAMOTO Mitsuharu
2013-03-22 10:33         ` Eli Zaretskii
2013-03-23  0:32           ` YAMAMOTO Mitsuharu
2013-03-23  6:15             ` Eli Zaretskii
2013-03-23 13:35               ` Jan Djärv
2013-03-23 23:58               ` YAMAMOTO Mitsuharu
2013-03-24  3:53                 ` Eli Zaretskii
2013-03-24  4:36                   ` YAMAMOTO Mitsuharu
2013-03-24 16:19                     ` Eli Zaretskii
2013-04-27  5:13                     ` YAMAMOTO Mitsuharu
2013-04-27  8:04                       ` Jan Djärv
2013-04-28  1:40                         ` YAMAMOTO Mitsuharu
2013-04-28 17:16                           ` Jan D. [this message]
2013-04-29  2:27                             ` YAMAMOTO Mitsuharu
2013-04-29  2:42                               ` YAMAMOTO Mitsuharu
2013-05-01  9:58                               ` Jan Djärv
2013-05-02  4:09                                 ` YAMAMOTO Mitsuharu
2013-05-06  1:04                                   ` YAMAMOTO Mitsuharu
2013-05-06  1:55                                     ` Stefan Monnier
2013-05-06  6:15                                       ` YAMAMOTO Mitsuharu
2013-05-06 13:37                                         ` Stefan Monnier
2013-05-08 10:46                                         ` YAMAMOTO Mitsuharu
2013-05-08 11:24                                           ` YAMAMOTO Mitsuharu
2013-05-08 17:41                                           ` Eli Zaretskii
2013-05-09  0:09                                             ` YAMAMOTO Mitsuharu
2013-05-09  1:52                                               ` Glenn Morris
2013-05-09  3:19                                                 ` YAMAMOTO Mitsuharu
2013-05-09  6:27                                                   ` Glenn Morris
2013-05-09  2:53                                               ` Eli Zaretskii
2013-05-09  8:14                                               ` Jan Djärv
2013-05-09  8:43                                                 ` YAMAMOTO Mitsuharu
2013-05-09 15:18                                                   ` Jan Djärv
2013-05-09 20:03                                               ` Eli Zaretskii
2013-05-09 21:28                                                 ` Stefan Monnier
2013-05-10  6:00                                                 ` YAMAMOTO Mitsuharu
2013-05-10  6:05                                                   ` YAMAMOTO Mitsuharu
2013-05-10  7:06                                                   ` Eli Zaretskii
2013-05-10  7:47                                                     ` YAMAMOTO Mitsuharu
2013-05-10  8:41                                                       ` Eli Zaretskii
2013-05-10  8:55                                                         ` YAMAMOTO Mitsuharu
2013-05-10  9:15                                                           ` Eli Zaretskii
2013-05-10  9:27                                                             ` YAMAMOTO Mitsuharu
2013-05-14 10:39                                                               ` YAMAMOTO Mitsuharu
2013-07-01  6:49                                                                 ` martin rudalics
2013-07-02  1:30                                                                   ` YAMAMOTO Mitsuharu
2013-07-02 10:38                                                                     ` martin rudalics
2013-07-02 10:53                                                                       ` Juanma Barranquero
2013-07-02 13:11                                                                         ` martin rudalics
2013-07-02 14:05                                                                           ` Juanma Barranquero
2013-07-03  9:27                                                                             ` martin rudalics
2013-07-03 10:49                                                                               ` Juanma Barranquero
2013-07-03 12:44                                                                                 ` martin rudalics
2013-07-03 13:43                                                                                   ` Juanma Barranquero
2013-07-04  9:34                                                                                     ` martin rudalics
     [not found]                                                                                       ` <5987E3>
2013-07-04 22:32                                                                                       ` Juanma Barranquero
2013-07-05  7:44                                                                                         ` martin rudalics
2013-07-05  9:32                                                                                           ` Juanma Barranquero
2013-07-05  9:34                                                                                         ` Jan Djärv
2013-07-05  9:41                                                                                           ` Juanma Barranquero
2013-07-05 11:25                                                                                             ` Jan Djärv
2013-07-05 11:56                                                                                               ` Juanma Barranquero
2013-07-05 12:12                                                                                                 ` Jan Djärv
2013-07-05 12:16                                                                                                   ` Juanma Barranquero
2013-07-05 15:30                                                                                                     ` Drew Adams
2013-07-05 15:53                                                                                                       ` Juanma Barranquero
2013-07-05 16:58                                                                                                         ` Drew Adams
2013-07-06 14:48                                                                                                           ` Juanma Barranquero
2013-07-06 19:25                                                                                                             ` Drew Adams
2013-07-05 15:27                                                                                                   ` Drew Adams
2013-07-04 10:28                                                                                     ` YAMAMOTO Mitsuharu
2013-05-10  7:44                                                   ` Jan Djärv
2013-04-28  1:48                       ` YAMAMOTO Mitsuharu
  -- strict thread matches above, loose matches on Subject: below --
2013-03-19  0:39 YAMAMOTO Mitsuharu
2013-03-19  1:34 ` Leo Liu
2013-03-19  4:54   ` Xue Fuqiao
2013-03-19 15:41     ` Drew Adams
2013-03-19 15:51       ` Leo Liu
2013-03-19 15:58         ` Drew Adams
2013-03-20  0:55           ` Leo Liu
2013-03-19 22:25 ` YAMAMOTO Mitsuharu
2013-03-19 23:15   ` Dmitry Gutov
2013-03-19 23:52     ` YAMAMOTO Mitsuharu
2013-03-20  0:12       ` Dmitry Gutov
2013-03-20  0:20         ` YAMAMOTO Mitsuharu
2013-03-20  1:41           ` Dmitry Gutov
2013-03-20  3:58             ` YAMAMOTO Mitsuharu
2013-03-20 14:05               ` Dmitry Gutov
2013-03-20 23:28                 ` YAMAMOTO Mitsuharu
2013-03-21  1:27                   ` Dmitry Gutov
2013-03-21  1:51                     ` YAMAMOTO Mitsuharu
2013-03-21  2:43                       ` Dmitry Gutov
2013-03-21  3:47                         ` YAMAMOTO Mitsuharu
2013-03-21  4:22                           ` YAMAMOTO Mitsuharu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8BD4B041-5A3F-4D7C-AFD3-E997E194AA9D@swipnet.se \
    --to=jan.h.d@swipnet.se \
    --cc=emacs-devel@gnu.org \
    --cc=mituharu@math.s.chiba-u.ac.jp \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).