aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYAMAMOTO Mitsuharu2013-05-07 10:12:22 +0900
committerYAMAMOTO Mitsuharu2013-05-07 10:12:22 +0900
commit4e3f92301d062dcfa29128e7df5058e6e21dcb07 (patch)
tree62e27bb22b1a48cbd52973548ef16e8649eb4f02 /src
parentaf69a478c07658772615402f021601c5a3c118d9 (diff)
downloademacs-4e3f92301d062dcfa29128e7df5058e6e21dcb07.tar.gz
emacs-4e3f92301d062dcfa29128e7df5058e6e21dcb07.zip
Add multi-monitor support on X11.
Diffstat (limited to 'src')
-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
5 files changed, 612 insertions, 3 deletions
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;