+ Lisp_Object monitor_frames = Fmake_vector (make_number (n_monitors), Qnil);
+ Lisp_Object frame, rest;
+
+ FOR_EACH_FRAME (rest, frame)
+ {
+ struct frame *f = XFRAME (frame);
+
+ if (FRAME_X_P (f) && FRAME_DISPLAY_INFO (f) == dpyinfo
+ && !EQ (frame, tip_frame))
+ {
+ int i = x_get_monitor_for_frame (f, monitors, n_monitors);
+ ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i)));
+ }
+ }
+
+ return make_monitor_attribute_list (monitors, n_monitors, primary_monitor,
+ monitor_frames, source);
+}
+
+static Lisp_Object
+x_get_monitor_attributes_fallback (struct x_display_info *dpyinfo)
+{
+ struct MonitorInfo monitor;
+ 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);
+ monitor.name = xstrdup ("combined 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, "fallback");
+}
+
+
+#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;
+ double mm_width_per_pixel, mm_height_per_pixel;
+
+ if (! info || n_monitors == 0)
+ {
+ if (info)
+ XFree (info);
+ return attributes_list;
+ }
+
+ mm_width_per_pixel = ((double) WidthMMOfScreen (dpyinfo->screen)
+ / x_display_pixel_width (dpyinfo));
+ mm_height_per_pixel = ((double) HeightMMOfScreen (dpyinfo->screen)
+ / x_display_pixel_height (dpyinfo));
+ monitors = 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;
+ mi->name = 0;
+
+ /* 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,
+ "Xinerama");
+ free_monitors (monitors, n_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;
+
+#ifdef HAVE_XRRGETSCREENRESOURCESCURRENT
+ resources = XRRGetScreenResourcesCurrent (dpy, dpyinfo->root_window);
+#else
+ resources = XRRGetScreenResources (dpy, dpyinfo->root_window);
+#endif
+ if (! resources || resources->noutput == 0)
+ {
+ if (resources)
+ XRRFreeScreenResources (resources);
+ return Qnil;
+ }
+ n_monitors = resources->noutput;
+ monitors = 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 (strcmp (info->name, "default") == 0)
+ {
+ /* Non XRandr 1.2 driver, does not give useful data. */
+ XRRFreeOutputInfo (info);
+ XRRFreeScreenResources (resources);
+ free_monitors (monitors, n_monitors);
+ return Qnil;
+ }
+
+ if (conn != RR_Disconnected && id != None)
+ {
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo (dpy, resources, id);
+ struct MonitorInfo *mi = &monitors[i];
+ XRectangle workarea_r;
+
+ if (! crtc)
+ {
+ XRRFreeOutputInfo (info);
+ 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;
+ mi->name = xstrdup (info->name);
+
+ 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,
+ "XRandr");
+ free_monitors (monitors, n_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;
+
+ (void) dpy; /* Suppress unused variable warning. */
+
+#ifdef HAVE_XRANDR
+ int xrr_event_base, xrr_error_base;
+ bool xrr_ok = false;
+ xrr_ok = XRRQueryExtension (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;
+}
+
+#endif /* !USE_GTK */
+
+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 the X display TERMINAL.
+
+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.
+
+In addition to the standard attribute keys listed in
+`display-monitor-attributes-list', the following keys are contained in
+the attributes:
+
+ source -- String describing the source from which multi-monitor
+ information is obtained, one of \"Gdk\", \"XRandr\",
+ \"Xinerama\", or \"fallback\"
+
+Internal use only, use `display-monitor-attributes-list' instead. */)
+ (Lisp_Object terminal)
+{
+ struct x_display_info *dpyinfo = check_x_display_info (terminal);
+ Lisp_Object attributes_list = Qnil;
+
+#ifdef USE_GTK
+ double mm_width_per_pixel, mm_height_per_pixel;
+ GdkDisplay *gdpy;
+ GdkScreen *gscreen;
+ gint primary_monitor = 0, n_monitors, i;
+ Lisp_Object monitor_frames, rest, frame;
+ static const char *source = "Gdk";
+ struct MonitorInfo *monitors;
+
+ block_input ();
+ mm_width_per_pixel = ((double) WidthMMOfScreen (dpyinfo->screen)
+ / x_display_pixel_width (dpyinfo));
+ mm_height_per_pixel = ((double) HeightMMOfScreen (dpyinfo->screen)
+ / x_display_pixel_height (dpyinfo));
+ gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display);
+ gscreen = gdk_display_get_default_screen (gdpy);
+#if GTK_CHECK_VERSION (2, 20, 0)
+ 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);
+ monitors = xzalloc (n_monitors * sizeof *monitors);
+
+ FOR_EACH_FRAME (rest, frame)
+ {
+ struct frame *f = XFRAME (frame);
+
+ if (FRAME_X_P (f) && FRAME_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)));
+ }
+ }
+
+ for (i = 0; i < n_monitors; ++i)
+ {
+ gint width_mm = -1, height_mm = -1;
+ GdkRectangle rec, work;
+ struct MonitorInfo *mi = &monitors[i];
+
+ gdk_screen_get_monitor_geometry (gscreen, i, &rec);
+
+#if GTK_CHECK_VERSION (2, 14, 0)
+ 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;
+
+#if GTK_CHECK_VERSION (3, 4, 0)
+ gdk_screen_get_monitor_workarea (gscreen, i, &work);
+#else
+ /* Emulate the behavior of GTK+ 3.4. */
+ {
+ XRectangle workarea_r;
+
+ if (i == primary_monitor && x_get_net_workarea (dpyinfo, &workarea_r))
+ {
+ 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))
+ work = rec;
+ }
+ else
+ work = rec;
+ }
+#endif
+
+
+ mi->geom.x = rec.x;
+ mi->geom.y = rec.y;
+ mi->geom.width = rec.width;
+ mi->geom.height = rec.height;
+ mi->work.x = work.x;
+ mi->work.y = work.y;
+ mi->work.width = work.width;
+ mi->work.height = work.height;
+ mi->mm_width = width_mm;
+ mi->mm_height = height_mm;
+
+#if GTK_CHECK_VERSION (2, 14, 0)
+ mi->name = gdk_screen_get_monitor_plug_name (gscreen, i);
+#endif
+ }
+
+ attributes_list = make_monitor_attribute_list (monitors,
+ n_monitors,
+ primary_monitor,
+ monitor_frames,
+ source);
+ unblock_input ();
+#else /* not USE_GTK */
+
+ block_input ();
+ attributes_list = x_get_monitor_attributes (dpyinfo);
+ unblock_input ();
+
+#endif /* not USE_GTK */
+
+ return attributes_list;