+#ifndef USE_GTK
+
+/* 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,
+ const char *source)
+{
+ 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;
+
+#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)