aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYAMAMOTO Mitsuharu2013-05-07 10:12:22 +0900
committerYAMAMOTO Mitsuharu2013-05-07 10:12:22 +0900
commit4e3f92301d062dcfa29128e7df5058e6e21dcb07 (patch)
tree62e27bb22b1a48cbd52973548ef16e8649eb4f02
parentaf69a478c07658772615402f021601c5a3c118d9 (diff)
downloademacs-4e3f92301d062dcfa29128e7df5058e6e21dcb07.tar.gz
emacs-4e3f92301d062dcfa29128e7df5058e6e21dcb07.zip
Add multi-monitor support on X11.
-rw-r--r--ChangeLog5
-rw-r--r--configure.ac50
-rw-r--r--etc/ChangeLog4
-rw-r--r--etc/NEWS6
-rw-r--r--lisp/ChangeLog5
-rw-r--r--lisp/frame.el61
-rw-r--r--src/ChangeLog25
-rw-r--r--src/Makefile.in10
-rw-r--r--src/xfns.c575
-rw-r--r--src/xterm.c2
-rw-r--r--src/xterm.h3
11 files changed, 743 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index 1d17e1a86c6..4636d43eee7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
12013-05-07 Jan Djärv <jan.h.d@swipnet.se>
2
3 * configure.ac (HAVE_XRANDR, HAVE_XINERAMA): Define if available.
4 (XRANDR_LIBS, XINERAMA_LIBS): New AC_SUBSTs.
5
12013-05-06 Paul Eggert <eggert@cs.ucla.edu> 62013-05-06 Paul Eggert <eggert@cs.ucla.edu>
2 7
3 Merge from gnulib, incorporating: 8 Merge from gnulib, incorporating:
diff --git a/configure.ac b/configure.ac
index 6c59dcaa7e3..e3a10275df4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2814,6 +2814,56 @@ if test "${HAVE_X11}" = "yes"; then
2814fi 2814fi
2815AC_SUBST(LIBXSM) 2815AC_SUBST(LIBXSM)
2816 2816
2817### Use XRandr (-lXrandr) if available
2818HAVE_XRANDR=no
2819if test "${HAVE_X11}" = "yes"; then
2820 XRANDR_REQUIRED=1.2.2
2821 XRANDR_MODULES="xrandr >= $XRANDR_REQUIRED"
2822 PKG_CHECK_MODULES(XRANDR, $XRANDR_MODULES, HAVE_XRANDR=yes, HAVE_XRANDR=no)
2823 if test $HAVE_XRANDR = no; then
2824 # Test old way in case pkg-config doesn't have it (older machines).
2825 AC_CHECK_HEADER(X11/extensions/Xrandr.h,
2826 [AC_CHECK_LIB(Xrandr, XRRQueryExtension, HAVE_XRANDR=yes)])
2827 if test $HAVE_XRANDR = yes; then
2828 XRANDR_LIBS=-lXrandr
2829 AC_SUBST(XRANDR_LIBS)
2830 fi
2831 fi
2832 if test $HAVE_XRANDR = yes; then
2833 SAVE_CFLAGS="$CFLAGS"
2834 SAVE_LIBS="$LIBS"
2835 CFLAGS="$XRANDR_CFLAGS $CFLAGS"
2836 LIBS="$XRANDR_LIBS $LIBS"
2837 AC_CHECK_FUNCS(XRRGetOutputPrimary XRRGetScreenResourcesCurrent)
2838 CFLAGS="$SAVE_CFLAGS"
2839 LIBS="$SAVE_LIBS"
2840
2841 AC_DEFINE(HAVE_XRANDR, 1, [Define to 1 if you have the XRandr extension.])
2842 fi
2843fi
2844
2845### Use Xinerama (-lXinerama) if available
2846HAVE_XINERAMA=no
2847if test "${HAVE_X11}" = "yes"; then
2848 XINERAMA_REQUIRED=1.0.2
2849 XINERAMA_MODULES="xinerama >= $XINERAMA_REQUIRED"
2850 PKG_CHECK_MODULES(XINERAMA, $XINERAMA_MODULES, HAVE_XINERAMA=yes,
2851 HAVE_XINERAMA=no)
2852 if test $HAVE_XINERAMA = no; then
2853 # Test old way in case pkg-config doesn't have it (older machines).
2854 AC_CHECK_HEADER(X11/extensions/Xinerama.h,
2855 [AC_CHECK_LIB(Xinerama, XineramaQueryExtension, HAVE_XINERAMA=yes)])
2856 if test $HAVE_XINERAMA = yes; then
2857 XINERAMA_LIBS=-lXinerama
2858 AC_SUBST(XINERAMA_LIBS)
2859 fi
2860 fi
2861 if test $HAVE_XINERAMA = yes; then
2862 AC_DEFINE(HAVE_XINERAMA, 1, [Define to 1 if you have the Xinerama extension.])
2863 fi
2864fi
2865
2866
2817### Use libxml (-lxml2) if available 2867### Use libxml (-lxml2) if available
2818HAVE_LIBXML2=no 2868HAVE_LIBXML2=no
2819if test "${with_xml2}" != "no"; then 2869if test "${with_xml2}" != "no"; then
diff --git a/etc/ChangeLog b/etc/ChangeLog
index 9cbd67c209f..4e9ba154cfe 100644
--- a/etc/ChangeLog
+++ b/etc/ChangeLog
@@ -1,3 +1,7 @@
12013-05-07 YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
2
3 * NEWS: Mention multi-monitor support.
4
12013-05-05 Paul Eggert <eggert@cs.ucla.edu> 52013-05-05 Paul Eggert <eggert@cs.ucla.edu>
2 6
3 `write-region-inhibit-fsync' defaults to noninteractive (Bug#14273). 7 `write-region-inhibit-fsync' defaults to noninteractive (Bug#14273).
diff --git a/etc/NEWS b/etc/NEWS
index d56c02960fc..f278317b08d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -79,6 +79,12 @@ of the buffer is visible).
79 79
80** In compiled Lisp files, the header no longer includes a timestamp. 80** In compiled Lisp files, the header no longer includes a timestamp.
81 81
82** Multi-monitor support has been added.
83
84*** New functions `display-monitor-attributes-list' and
85`frame-monitor-attributes' can be used to obtain information about
86each physical monitor on multi-monitor setups.
87
82 88
83* Editing Changes in Emacs 24.4 89* Editing Changes in Emacs 24.4
84 90
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index c4dbc803fcc..676e4286395 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,8 @@
12013-05-07 YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
2
3 * frame.el (display-monitor-attributes-list)
4 (frame-monitor-attributes): New functions.
5
12013-05-06 Leo Liu <sdl.web@gmail.com> 62013-05-06 Leo Liu <sdl.web@gmail.com>
2 7
3 * progmodes/octave.el (octave-syntax-propertize-function): Change 8 * progmodes/octave.el (octave-syntax-propertize-function): Change
diff --git a/lisp/frame.el b/lisp/frame.el
index 454b229d59e..94a4842fc4e 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1256,6 +1256,23 @@ bars (top, bottom, or nil)."
1256 (unless (memq vert '(left right nil)) 1256 (unless (memq vert '(left right nil))
1257 (setq vert default-frame-scroll-bars)) 1257 (setq vert default-frame-scroll-bars))
1258 (cons vert hor))) 1258 (cons vert hor)))
1259
1260(defun frame-monitor-attributes (&optional frame)
1261 "Return the attributes of the physical monitor dominating FRAME.
1262If FRAME is omitted, describe the currently selected frame.
1263
1264A frame is dominated by a physical monitor when either the
1265largest area of the frame resides in the monitor, or the monitor
1266is the closest to the frame if the frame does not intersect any
1267physical monitors.
1268
1269See `display-monitor-attributes-list' for the list of attribute
1270keys and their meanings."
1271 (or frame (setq frame (selected-frame)))
1272 (cl-loop for attributes in (display-monitor-attributes-list frame)
1273 for frames = (cdr (assq 'frames attributes))
1274 if (memq frame frames) return attributes))
1275
1259 1276
1260;;;; Frame/display capabilities. 1277;;;; Frame/display capabilities.
1261(defun selected-terminal () 1278(defun selected-terminal ()
@@ -1476,6 +1493,50 @@ The value is one of the symbols `static-gray', `gray-scale',
1476 (t 1493 (t
1477 'static-gray)))) 1494 'static-gray))))
1478 1495
1496(declare-function x-display-monitor-attributes-list "xfns.c"
1497 (&optional terminal))
1498
1499(defun display-monitor-attributes-list (&optional display)
1500 "Return a list of physical monitor attributes on DISPLAY.
1501Each element of the list represents the attributes of each
1502physical monitor. The first element corresponds to the primary
1503monitor.
1504
1505Attributes for a physical monitor is represented as an alist of
1506attribute keys and values as follows:
1507
1508 geometry -- Position and size in pixels in the form of
1509 (X Y WIDTH HEIGHT)
1510 workarea -- Position and size of the workarea in pixels in the
1511 form of (X Y WIDTH HEIGHT)
1512 mm-size -- Width and height in millimeters in the form of
1513 (WIDTH HEIGHT)
1514 frames -- List of frames dominated by the physical monitor
1515 name (*) -- Name of the physical monitor as a string
1516
1517where X, Y, WIDTH, and HEIGHT are integers. Keys labeled
1518with (*) are optional.
1519
1520A frame is dominated by a physical monitor when either the
1521largest area of the frame resides in the monitor, or the monitor
1522is the closest to the frame if the frame does not intersect any
1523physical monitors. Every non-tip frame (including invisible one)
1524in a graphical display is dominated by exactly one physical
1525monitor at a time, though it can span multiple (or no) physical
1526monitors."
1527 (let ((frame-type (framep-on-display display)))
1528 (cond
1529 ((eq frame-type 'x)
1530 (x-display-monitor-attributes-list display))
1531 (t
1532 (let ((geometry (list 0 0 (display-pixel-width display)
1533 (display-pixel-height display))))
1534 `(((geometry . ,geometry)
1535 (workarea . ,geometry)
1536 (mm-size . (,(display-mm-width display)
1537 ,(display-mm-height display)))
1538 (frames . ,(frames-on-display-list display)))))))))
1539
1479 1540
1480;;;; Frame geometry values 1541;;;; Frame geometry values
1481 1542
diff --git a/src/ChangeLog b/src/ChangeLog
index 5ac0f885d8e..0d06f4e291d 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,28 @@
12013-05-07 YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
2 Jan Djärv <jan.h.d@swipnet.se>
3
4 * Makefile.in (XRANDR_LIBS, XRANDR_CFLAGS, XINERAMA_LIBS)
5 (XINERAMA_CFLAGS): New macros.
6 (ALL_CFLAGS, LIBES): Use them.
7
8 * xfns.c: Include <X11/extensions/Xrandr.h> if HAVE_XRANDR, and
9 include <X11/extensions/Xinerama.h> if HAVE_XINERAMA.
10 (Qgeometry, Qworkarea, Qmm_size, Qframes, Qsource): New variables.
11 (syms_of_xfns): DEFSYM them.
12 (struct MonitorInfo): New struct.
13 (x_get_net_workarea, free_monitors, x_get_monitor_for_frame)
14 (x_make_monitor_attribute_list, x_get_monitor_attributes_fallback)
15 (x_get_monitor_attributes_xrandr, x_get_monitor_attributes)
16 (x_get_monitor_attributes_xinerama): New functions.
17 (Fx_display_monitor_attributes_list): New primitive.
18 (syms_of_xfns): Defsubr it.
19
20 * xterm.h (x_display_info): Add Xatom_net_workarea and
21 Xatom_net_current_desktop.
22
23 * xterm.c (x_term_init): Initialize dpyinfo->Xatom_net_workarea
24 and dpyinfo->Xatom_net_current_desktop.
25
12013-05-06 Eli Zaretskii <eliz@gnu.org> 262013-05-06 Eli Zaretskii <eliz@gnu.org>
2 27
3 * xdisp.c (pos_visible_p): Use the special code for finding the 28 * xdisp.c (pos_visible_p): Use the special code for finding the
diff --git a/src/Makefile.in b/src/Makefile.in
index cef58c55e68..1222b5a5f98 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -232,6 +232,12 @@ IMAGEMAGICK_CFLAGS= @IMAGEMAGICK_CFLAGS@
232LIBXML2_LIBS = @LIBXML2_LIBS@ 232LIBXML2_LIBS = @LIBXML2_LIBS@
233LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ 233LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
234 234
235XRANDR_LIBS = @XRANDR_LIBS@
236XRANDR_CFLAGS = @XRANDR_CFLAGS@
237
238XINERAMA_LIBS = @XINERAMA_LIBS@
239XINERAMA_CFLAGS = @XINERAMA_CFLAGS@
240
235## widget.o if USE_X_TOOLKIT, otherwise empty. 241## widget.o if USE_X_TOOLKIT, otherwise empty.
236WIDGET_OBJ=@WIDGET_OBJ@ 242WIDGET_OBJ=@WIDGET_OBJ@
237 243
@@ -315,7 +321,7 @@ ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
315 -I$(lib) -I$(srcdir)/../lib \ 321 -I$(lib) -I$(srcdir)/../lib \
316 $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \ 322 $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \
317 $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ 323 $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
318 $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \ 324 $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) \
319 $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ 325 $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
320 $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ 326 $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
321 $(LIBGNUTLS_CFLAGS) \ 327 $(LIBGNUTLS_CFLAGS) \
@@ -393,7 +399,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
393 $(LIBX_OTHER) $(LIBSOUND) \ 399 $(LIBX_OTHER) $(LIBSOUND) \
394 $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \ 400 $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
395 $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \ 401 $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
396 $(LIB_EXECINFO) \ 402 $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) \
397 $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \ 403 $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
398 $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ 404 $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
399 $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ 405 $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
diff --git a/src/xfns.c b/src/xfns.c
index f4c24cb09a0..d3e3479be2d 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -59,6 +59,13 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
59 59
60#include "xsettings.h" 60#include "xsettings.h"
61 61
62#ifdef HAVE_XRANDR
63#include <X11/extensions/Xrandr.h>
64#endif
65#ifdef HAVE_XINERAMA
66#include <X11/extensions/Xinerama.h>
67#endif
68
62#ifdef USE_GTK 69#ifdef USE_GTK
63#include "gtkutil.h" 70#include "gtkutil.h"
64#endif 71#endif
@@ -126,6 +133,7 @@ extern LWLIB_ID widget_id_tick;
126static Lisp_Object Qsuppress_icon; 133static Lisp_Object Qsuppress_icon;
127static Lisp_Object Qundefined_color; 134static Lisp_Object Qundefined_color;
128static Lisp_Object Qcompound_text, Qcancel_timer; 135static Lisp_Object Qcompound_text, Qcancel_timer;
136static Lisp_Object Qgeometry, Qworkarea, Qmm_size, Qframes, Qsource;
129Lisp_Object Qfont_param; 137Lisp_Object Qfont_param;
130 138
131#ifdef GLYPH_DEBUG 139#ifdef GLYPH_DEBUG
@@ -3791,6 +3799,567 @@ If omitted or nil, that stands for the selected frame's display. */)
3791 else 3799 else
3792 return Qnil; 3800 return Qnil;
3793} 3801}
3802
3803/* Store the geometry of the workarea on display DPYINFO into *RECT.
3804 Return false if and only if the workarea information cannot be
3805 obtained via the _NET_WORKAREA root window property. */
3806
3807static bool
3808x_get_net_workarea (struct x_display_info *dpyinfo, XRectangle *rect)
3809{
3810 Display *dpy = dpyinfo->display;
3811 long offset, max_len;
3812 Atom target_type, actual_type;
3813 unsigned long actual_size, bytes_remaining;
3814 int rc, actual_format;
3815 unsigned char *tmp_data = NULL;
3816 bool result = false;
3817
3818 x_catch_errors (dpy);
3819 offset = 0;
3820 max_len = 1;
3821 target_type = XA_CARDINAL;
3822 rc = XGetWindowProperty (dpy, dpyinfo->root_window,
3823 dpyinfo->Xatom_net_current_desktop,
3824 offset, max_len, False, target_type,
3825 &actual_type, &actual_format, &actual_size,
3826 &bytes_remaining, &tmp_data);
3827 if (rc == Success && actual_type == target_type && !x_had_errors_p (dpy)
3828 && actual_format == 32 && actual_size == max_len)
3829 {
3830 long current_desktop = ((long *) tmp_data)[0];
3831
3832 XFree (tmp_data);
3833 tmp_data = NULL;
3834
3835 offset = 4 * current_desktop;
3836 max_len = 4;
3837 rc = XGetWindowProperty (dpy, dpyinfo->root_window,
3838 dpyinfo->Xatom_net_workarea,
3839 offset, max_len, False, target_type,
3840 &actual_type, &actual_format, &actual_size,
3841 &bytes_remaining, &tmp_data);
3842 if (rc == Success && actual_type == target_type && !x_had_errors_p (dpy)
3843 && actual_format == 32 && actual_size == max_len)
3844 {
3845 long *values = (long *) tmp_data;
3846
3847 rect->x = values[0];
3848 rect->y = values[1];
3849 rect->width = values[2];
3850 rect->height = values[3];
3851
3852 XFree (tmp_data);
3853 tmp_data = NULL;
3854
3855 result = true;
3856 }
3857 }
3858 if (tmp_data)
3859 XFree (tmp_data);
3860 x_uncatch_errors ();
3861
3862 return result;
3863}
3864
3865struct MonitorInfo {
3866 XRectangle geom, work;
3867 int mm_width, mm_height;
3868 char *name;
3869};
3870
3871static void
3872free_monitors (struct MonitorInfo *monitors, int n_monitors)
3873{
3874 int i;
3875 for (i = 0; i < n_monitors; ++i)
3876 xfree (monitors[i].name);
3877 xfree (monitors);
3878}
3879
3880
3881/* Return monitor number where F is "most" or closest to. */
3882static int
3883x_get_monitor_for_frame (struct frame *f,
3884 struct MonitorInfo *monitors,
3885 int n_monitors)
3886{
3887 XRectangle frect;
3888 int area = 0, dist = -1;
3889 int best_area = -1, best_dist = -1;
3890 int i;
3891
3892 if (n_monitors == 1) return 0;
3893 frect.x = f->left_pos;
3894 frect.y = f->top_pos;
3895 frect.width = FRAME_PIXEL_WIDTH (f);
3896 frect.height = FRAME_PIXEL_HEIGHT (f);
3897
3898 for (i = 0; i < n_monitors; ++i)
3899 {
3900 struct MonitorInfo *mi = &monitors[i];
3901 XRectangle res;
3902 int a = 0;
3903
3904 if (mi->geom.width == 0) continue;
3905
3906 if (x_intersect_rectangles (&mi->geom, &frect, &res))
3907 {
3908 a = res.width * res.height;
3909 if (a > area) {
3910 area = a;
3911 best_area = i;
3912 }
3913 }
3914
3915 if (a == 0 && area == 0)
3916 {
3917 int dx, dy, d;
3918 if (frect.x + frect.width < mi->geom.x)
3919 dx = mi->geom.x - frect.x + frect.width;
3920 else if (frect.x > mi->geom.x + mi->geom.width)
3921 dx = frect.x - mi->geom.x + mi->geom.width;
3922 else
3923 dx = 0;
3924 if (frect.y + frect.height < mi->geom.y)
3925 dy = mi->geom.y - frect.y + frect.height;
3926 else if (frect.y > mi->geom.y + mi->geom.height)
3927 dy = frect.y - mi->geom.y + mi->geom.height;
3928 else
3929 dy = 0;
3930
3931 d = dx*dx + dy*dy;
3932 if (dist == -1 || dist > d)
3933 {
3934 dist = d;
3935 best_dist = i;
3936 }
3937 }
3938 }
3939
3940 return best_area != -1 ? best_area : (best_dist != -1 ? best_dist : 0);
3941}
3942
3943static Lisp_Object
3944x_make_monitor_attribute_list (struct MonitorInfo *monitors,
3945 int n_monitors,
3946 int primary_monitor,
3947 struct x_display_info *dpyinfo,
3948 const char *source)
3949{
3950 Lisp_Object monitor_frames = Fmake_vector (make_number (n_monitors), Qnil);
3951 Lisp_Object frame, rest, attributes_list = Qnil;
3952 Lisp_Object primary_monitor_attributes = Qnil;
3953 int i;
3954
3955 FOR_EACH_FRAME (rest, frame)
3956 {
3957 struct frame *f = XFRAME (frame);
3958
3959 if (FRAME_X_P (f) && FRAME_X_DISPLAY_INFO (f) == dpyinfo
3960 && !EQ (frame, tip_frame))
3961 {
3962 i = x_get_monitor_for_frame (f, monitors, n_monitors);
3963 ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i)));
3964 }
3965 }
3966
3967 for (i = 0; i < n_monitors; ++i)
3968 {
3969 Lisp_Object geometry, workarea, attributes = Qnil;
3970 struct MonitorInfo *mi = &monitors[i];
3971
3972 if (mi->geom.width == 0) continue;
3973
3974 workarea = list4i (mi->work.x, mi->work.y,
3975 mi->work.width, mi->work.height);
3976 geometry = list4i (mi->geom.x, mi->geom.y,
3977 mi->geom.width, mi->geom.height);
3978 attributes = Fcons (Fcons (Qsource,
3979 make_string (source, strlen (source))),
3980 attributes);
3981 attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
3982 attributes);
3983 attributes = Fcons (Fcons (Qmm_size,
3984 list2i (mi->mm_width, mi->mm_height)),
3985 attributes);
3986 attributes = Fcons (Fcons (Qworkarea, workarea), attributes);
3987 attributes = Fcons (Fcons (Qgeometry, geometry), attributes);
3988 if (mi->name)
3989 attributes = Fcons (Fcons (Qname, make_string (mi->name,
3990 strlen (mi->name))),
3991 attributes);
3992
3993 if (i == primary_monitor)
3994 primary_monitor_attributes = attributes;
3995 else
3996 attributes_list = Fcons (attributes, attributes_list);
3997 }
3998
3999 if (!NILP (primary_monitor_attributes))
4000 attributes_list = Fcons (primary_monitor_attributes, attributes_list);
4001 return attributes_list;
4002}
4003
4004static Lisp_Object
4005x_get_monitor_attributes_fallback (struct x_display_info *dpyinfo)
4006{
4007 struct MonitorInfo monitor;
4008 int width_mm, height_mm;
4009 XRectangle workarea_r;
4010
4011 /* Fallback: treat (possibly) multiple physical monitors as if they
4012 formed a single monitor as a whole. This should provide a
4013 consistent result at least on single monitor environments. */
4014 monitor.geom.x = monitor.geom.y = 0;
4015 monitor.geom.width = x_display_pixel_width (dpyinfo);
4016 monitor.geom.height = x_display_pixel_height (dpyinfo);
4017 monitor.mm_width = WidthMMOfScreen (dpyinfo->screen);
4018 monitor.mm_height = HeightMMOfScreen (dpyinfo->screen);
4019 monitor.name = xstrdup ("combined screen");
4020
4021 if (x_get_net_workarea (dpyinfo, &workarea_r))
4022 monitor.work = workarea_r;
4023 else
4024 monitor.work = monitor.geom;
4025 return x_make_monitor_attribute_list (&monitor, 1, 0, dpyinfo, "fallback");
4026}
4027
4028
4029#ifdef HAVE_XINERAMA
4030static Lisp_Object
4031x_get_monitor_attributes_xinerama (struct x_display_info *dpyinfo)
4032{
4033 int n_monitors, i;
4034 Lisp_Object attributes_list = Qnil;
4035 Display *dpy = dpyinfo->display;
4036 XineramaScreenInfo *info = XineramaQueryScreens (dpy, &n_monitors);
4037 struct MonitorInfo *monitors;
4038 float mm_width_per_pixel, mm_height_per_pixel;
4039
4040 if (! info || n_monitors == 0)
4041 {
4042 if (info)
4043 XFree (info);
4044 return attributes_list;
4045 }
4046
4047 mm_width_per_pixel = ((float) WidthMMOfScreen (dpyinfo->screen)
4048 / x_display_pixel_width (dpyinfo));
4049 mm_height_per_pixel = ((float) HeightMMOfScreen (dpyinfo->screen)
4050 / x_display_pixel_height (dpyinfo));
4051 monitors = (struct MonitorInfo *) xzalloc (n_monitors * sizeof (*monitors));
4052 for (i = 0; i < n_monitors; ++i)
4053 {
4054 struct MonitorInfo *mi = &monitors[i];
4055 XRectangle workarea_r;
4056
4057 mi->geom.x = info[i].x_org;
4058 mi->geom.y = info[i].y_org;
4059 mi->geom.width = info[i].width;
4060 mi->geom.height = info[i].height;
4061 mi->mm_width = mi->geom.width * mm_width_per_pixel + 0.5;
4062 mi->mm_height = mi->geom.height * mm_height_per_pixel + 0.5;
4063 mi->name = 0;
4064
4065 /* Xinerama usually have primary monitor first, just use that. */
4066 if (i == 0 && x_get_net_workarea (dpyinfo, &workarea_r))
4067 {
4068 mi->work = workarea_r;
4069 if (! x_intersect_rectangles (&mi->geom, &mi->work, &mi->work))
4070 mi->work = mi->geom;
4071 }
4072 else
4073 mi->work = mi->geom;
4074 }
4075 XFree (info);
4076
4077 attributes_list = x_make_monitor_attribute_list (monitors,
4078 n_monitors,
4079 0,
4080 dpyinfo,
4081 "Xinerama");
4082 free_monitors (monitors, n_monitors);
4083 return attributes_list;
4084}
4085#endif /* HAVE_XINERAMA */
4086
4087
4088#ifdef HAVE_XRANDR
4089static Lisp_Object
4090x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo)
4091{
4092 Lisp_Object attributes_list = Qnil;
4093 XRRScreenResources *resources;
4094 Display *dpy = dpyinfo->display;
4095 int i, n_monitors, primary = -1;
4096 RROutput pxid = None;
4097 struct MonitorInfo *monitors;
4098
4099#ifdef HAVE_XRRGETSCREENRESOURCESCURRENT
4100 resources = XRRGetScreenResourcesCurrent (dpy, dpyinfo->root_window);
4101#else
4102 resources = XRRGetScreenResources (dpy, dpyinfo->root_window);
4103#endif
4104 if (! resources || resources->noutput == 0)
4105 {
4106 if (resources)
4107 XRRFreeScreenResources (resources);
4108 return Qnil;
4109 }
4110 n_monitors = resources->noutput;
4111 monitors = (struct MonitorInfo *) xzalloc (n_monitors * sizeof (*monitors));
4112
4113#ifdef HAVE_XRRGETOUTPUTPRIMARY
4114 pxid = XRRGetOutputPrimary (dpy, dpyinfo->root_window);
4115#endif
4116
4117 for (i = 0; i < n_monitors; ++i)
4118 {
4119 XRROutputInfo *info = XRRGetOutputInfo (dpy, resources,
4120 resources->outputs[i]);
4121 Connection conn = info ? info->connection : RR_Disconnected;
4122 RRCrtc id = info ? info->crtc : None;
4123
4124 if (strcmp (info->name, "default") == 0)
4125 {
4126 /* Non XRandr 1.2 driver, does not give useful data. */
4127 XRRFreeOutputInfo (info);
4128 XRRFreeScreenResources (resources);
4129 free_monitors (monitors, n_monitors);
4130 return Qnil;
4131 }
4132
4133 if (conn != RR_Disconnected && id != None)
4134 {
4135 XRRCrtcInfo *crtc = XRRGetCrtcInfo (dpy, resources, id);
4136 struct MonitorInfo *mi = &monitors[i];
4137 XRectangle workarea_r;
4138
4139 if (! crtc)
4140 {
4141 XRRFreeOutputInfo (info);
4142 continue;
4143 }
4144
4145 mi->geom.x = crtc->x;
4146 mi->geom.y = crtc->y;
4147 mi->geom.width = crtc->width;
4148 mi->geom.height = crtc->height;
4149 mi->mm_width = info->mm_width;
4150 mi->mm_height = info->mm_height;
4151 mi->name = xstrdup (info->name);
4152
4153 if (pxid != None && pxid == resources->outputs[i])
4154 primary = i;
4155 else if (primary == -1 && strcmp (info->name, "LVDS") == 0)
4156 primary = i;
4157
4158 if (i == primary && x_get_net_workarea (dpyinfo, &workarea_r))
4159 {
4160 mi->work= workarea_r;
4161 if (! x_intersect_rectangles (&mi->geom, &mi->work, &mi->work))
4162 mi->work = mi->geom;
4163 }
4164 else
4165 mi->work = mi->geom;
4166
4167 XRRFreeCrtcInfo (crtc);
4168 }
4169 XRRFreeOutputInfo (info);
4170 }
4171 XRRFreeScreenResources (resources);
4172
4173 attributes_list = x_make_monitor_attribute_list (monitors,
4174 n_monitors,
4175 primary,
4176 dpyinfo,
4177 "XRandr");
4178 free_monitors (monitors, n_monitors);
4179 return attributes_list;
4180}
4181#endif /* HAVE_XRANDR */
4182
4183static Lisp_Object
4184x_get_monitor_attributes (struct x_display_info *dpyinfo)
4185{
4186 Lisp_Object attributes_list = Qnil;
4187 Display *dpy = dpyinfo->display;
4188
4189#ifdef HAVE_XRANDR
4190 int xrr_event_base, xrr_error_base;
4191 bool xrr_ok = false;
4192 xrr_ok = XRRQueryExtension (dpy, &xrr_event_base, &xrr_error_base);
4193 if (xrr_ok)
4194 {
4195 int xrr_major, xrr_minor;
4196 XRRQueryVersion (dpy, &xrr_major, &xrr_minor);
4197 xrr_ok = (xrr_major == 1 && xrr_minor >= 2) || xrr_major > 1;
4198 }
4199
4200 if (xrr_ok)
4201 attributes_list = x_get_monitor_attributes_xrandr (dpyinfo);
4202#endif /* HAVE_XRANDR */
4203
4204#ifdef HAVE_XINERAMA
4205 if (NILP (attributes_list))
4206 {
4207 int xin_event_base, xin_error_base;
4208 bool xin_ok = false;
4209 xin_ok = XineramaQueryExtension (dpy, &xin_event_base, &xin_error_base);
4210 if (xin_ok && XineramaIsActive (dpy))
4211 attributes_list = x_get_monitor_attributes_xinerama (dpyinfo);
4212 }
4213#endif /* HAVE_XINERAMA */
4214
4215 if (NILP (attributes_list))
4216 attributes_list = x_get_monitor_attributes_fallback (dpyinfo);
4217
4218 return attributes_list;
4219}
4220
4221DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list,
4222 Sx_display_monitor_attributes_list,
4223 0, 1, 0,
4224 doc: /* Return a list of physical monitor attributes on the X display TERMINAL.
4225
4226The optional argument TERMINAL specifies which display to ask about.
4227TERMINAL should be a terminal object, a frame or a display name (a string).
4228If omitted or nil, that stands for the selected frame's display.
4229
4230In addition to the standard attribute keys listed in
4231`display-monitor-attributes-list', the following keys are contained in
4232the attributes:
4233
4234 source -- String describing the source from which multi-monitor
4235 information is obtained, one of \"Gdk\", \"XRandr\",
4236 \"Xinerama\", or \"fallback\"
4237
4238Internal use only, use `display-monitor-attributes-list' instead. */)
4239 (Lisp_Object terminal)
4240{
4241 struct x_display_info *dpyinfo = check_x_display_info (terminal);
4242 Lisp_Object attributes_list = Qnil;
4243
4244#ifdef USE_GTK
4245 float mm_width_per_pixel, mm_height_per_pixel;
4246 GdkDisplay *gdpy;
4247 GdkScreen *gscreen;
4248 gint primary_monitor = 0, n_monitors, i;
4249 Lisp_Object primary_monitor_attributes = Qnil;
4250 Lisp_Object monitor_frames, rest, frame;
4251 static const char *source = "Gdk";
4252
4253 block_input ();
4254 mm_width_per_pixel = ((float) WidthMMOfScreen (dpyinfo->screen)
4255 / x_display_pixel_width (dpyinfo));
4256 mm_height_per_pixel = ((float) HeightMMOfScreen (dpyinfo->screen)
4257 / x_display_pixel_height (dpyinfo));
4258 gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display);
4259 gscreen = gdk_display_get_default_screen (gdpy);
4260#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 20
4261 primary_monitor = gdk_screen_get_primary_monitor (gscreen);
4262#endif
4263 n_monitors = gdk_screen_get_n_monitors (gscreen);
4264 monitor_frames = Fmake_vector (make_number (n_monitors), Qnil);
4265 FOR_EACH_FRAME (rest, frame)
4266 {
4267 struct frame *f = XFRAME (frame);
4268
4269 if (FRAME_X_P (f) && FRAME_X_DISPLAY_INFO (f) == dpyinfo
4270 && !EQ (frame, tip_frame))
4271 {
4272 GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_WIDGET (f));
4273
4274 i = gdk_screen_get_monitor_at_window (gscreen, gwin);
4275 ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i)));
4276 }
4277 }
4278
4279 i = n_monitors;
4280 while (i-- > 0)
4281 {
4282 Lisp_Object geometry, workarea, attributes = Qnil;
4283 gint width_mm = -1, height_mm = -1;
4284 GdkRectangle rec;
4285
4286 attributes = Fcons (Fcons (Qsource,
4287 make_string (source, strlen (source))),
4288 attributes);
4289 attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
4290 attributes);
4291
4292 gdk_screen_get_monitor_geometry (gscreen, i, &rec);
4293 geometry = list4i (rec.x, rec.y, rec.width, rec.height);
4294
4295#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION >= 14
4296 width_mm = gdk_screen_get_monitor_width_mm (gscreen, i);
4297 height_mm = gdk_screen_get_monitor_height_mm (gscreen, i);
4298#endif
4299 if (width_mm < 0)
4300 width_mm = rec.width * mm_width_per_pixel + 0.5;
4301 if (height_mm < 0)
4302 height_mm = rec.height * mm_height_per_pixel + 0.5;
4303 attributes = Fcons (Fcons (Qmm_size,
4304 list2i (width_mm, height_mm)),
4305 attributes);
4306
4307#if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4)
4308 gdk_screen_get_monitor_workarea (gscreen, i, &rec);
4309 workarea = list4i (rec.x, rec.y, rec.width, rec.height);
4310#else
4311 /* Emulate the behavior of GTK+ 3.4. */
4312 {
4313 XRectangle workarea_r;
4314
4315 workarea = Qnil;
4316 if (i == primary_monitor && x_get_net_workarea (dpyinfo, &workarea_r))
4317 {
4318 GdkRectangle work;
4319
4320 work.x = workarea_r.x;
4321 work.y = workarea_r.y;
4322 work.width = workarea_r.width;
4323 work.height = workarea_r.height;
4324 if (gdk_rectangle_intersect (&rec, &work, &work))
4325 workarea = list4i (work.x, work.y, work.width, work.height);
4326 }
4327 if (NILP (workarea))
4328 workarea = geometry;
4329 }
4330#endif
4331 attributes = Fcons (Fcons (Qworkarea, workarea), attributes);
4332
4333 attributes = Fcons (Fcons (Qgeometry, geometry), attributes);
4334#if GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 14)
4335 {
4336 char *name = gdk_screen_get_monitor_plug_name (gscreen, i);
4337 if (name)
4338 attributes = Fcons (Fcons (Qname, make_string (name, strlen (name))),
4339 attributes);
4340 }
4341#endif
4342
4343 if (i == primary_monitor)
4344 primary_monitor_attributes = attributes;
4345 else
4346 attributes_list = Fcons (attributes, attributes_list);
4347 }
4348
4349 if (!NILP (primary_monitor_attributes))
4350 attributes_list = Fcons (primary_monitor_attributes, attributes_list);
4351 unblock_input ();
4352#else /* not USE_GTK */
4353
4354 block_input ();
4355 attributes_list = x_get_monitor_attributes (dpyinfo);
4356 unblock_input ();
4357
4358#endif /* not USE_GTK */
4359
4360 return attributes_list;
4361}
4362
3794 4363
3795int 4364int
3796x_pixel_width (register struct frame *f) 4365x_pixel_width (register struct frame *f)
@@ -5701,6 +6270,11 @@ syms_of_xfns (void)
5701 DEFSYM (Qundefined_color, "undefined-color"); 6270 DEFSYM (Qundefined_color, "undefined-color");
5702 DEFSYM (Qcompound_text, "compound-text"); 6271 DEFSYM (Qcompound_text, "compound-text");
5703 DEFSYM (Qcancel_timer, "cancel-timer"); 6272 DEFSYM (Qcancel_timer, "cancel-timer");
6273 DEFSYM (Qgeometry, "geometry");
6274 DEFSYM (Qworkarea, "workarea");
6275 DEFSYM (Qmm_size, "mm-size");
6276 DEFSYM (Qframes, "frames");
6277 DEFSYM (Qsource, "source");
5704 DEFSYM (Qfont_param, "font-parameter"); 6278 DEFSYM (Qfont_param, "font-parameter");
5705 /* This is the end of symbol initialization. */ 6279 /* This is the end of symbol initialization. */
5706 6280
@@ -5864,6 +6438,7 @@ When using Gtk+ tooltips, the tooltip face is not used. */);
5864 defsubr (&Sx_display_visual_class); 6438 defsubr (&Sx_display_visual_class);
5865 defsubr (&Sx_display_backing_store); 6439 defsubr (&Sx_display_backing_store);
5866 defsubr (&Sx_display_save_under); 6440 defsubr (&Sx_display_save_under);
6441 defsubr (&Sx_display_monitor_attributes_list);
5867 defsubr (&Sx_wm_set_size_hint); 6442 defsubr (&Sx_wm_set_size_hint);
5868 defsubr (&Sx_create_frame); 6443 defsubr (&Sx_create_frame);
5869 defsubr (&Sx_open_connection); 6444 defsubr (&Sx_open_connection);
diff --git a/src/xterm.c b/src/xterm.c
index e4a681031ef..93473986ca5 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -10251,6 +10251,8 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
10251 { "_NET_WM_WINDOW_OPACITY", &dpyinfo->Xatom_net_wm_window_opacity }, 10251 { "_NET_WM_WINDOW_OPACITY", &dpyinfo->Xatom_net_wm_window_opacity },
10252 { "_NET_ACTIVE_WINDOW", &dpyinfo->Xatom_net_active_window }, 10252 { "_NET_ACTIVE_WINDOW", &dpyinfo->Xatom_net_active_window },
10253 { "_NET_FRAME_EXTENTS", &dpyinfo->Xatom_net_frame_extents }, 10253 { "_NET_FRAME_EXTENTS", &dpyinfo->Xatom_net_frame_extents },
10254 { "_NET_CURRENT_DESKTOP", &dpyinfo->Xatom_net_current_desktop },
10255 { "_NET_WORKAREA", &dpyinfo->Xatom_net_workarea },
10254 /* Session management */ 10256 /* Session management */
10255 { "SM_CLIENT_ID", &dpyinfo->Xatom_SM_CLIENT_ID }, 10257 { "SM_CLIENT_ID", &dpyinfo->Xatom_SM_CLIENT_ID },
10256 { "_XSETTINGS_SETTINGS", &dpyinfo->Xatom_xsettings_prop }, 10258 { "_XSETTINGS_SETTINGS", &dpyinfo->Xatom_xsettings_prop },
diff --git a/src/xterm.h b/src/xterm.h
index 16effc5c9ea..dc060fbbcff 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -346,7 +346,8 @@ struct x_display_info
346 Atom Xatom_net_wm_state, Xatom_net_wm_state_fullscreen, 346 Atom Xatom_net_wm_state, Xatom_net_wm_state_fullscreen,
347 Xatom_net_wm_state_maximized_horz, Xatom_net_wm_state_maximized_vert, 347 Xatom_net_wm_state_maximized_horz, Xatom_net_wm_state_maximized_vert,
348 Xatom_net_wm_state_sticky, Xatom_net_wm_state_hidden, 348 Xatom_net_wm_state_sticky, Xatom_net_wm_state_hidden,
349 Xatom_net_frame_extents; 349 Xatom_net_frame_extents,
350 Xatom_net_current_desktop, Xatom_net_workarea;
350 351
351 /* XSettings atoms and windows. */ 352 /* XSettings atoms and windows. */
352 Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr; 353 Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr;