Add multi-monitor support on X11.
This commit is contained in:
parent
af69a478c0
commit
4e3f92301d
11 changed files with 743 additions and 3 deletions
|
@ -1,3 +1,8 @@
|
|||
2013-05-07 Jan Djärv <jan.h.d@swipnet.se>
|
||||
|
||||
* configure.ac (HAVE_XRANDR, HAVE_XINERAMA): Define if available.
|
||||
(XRANDR_LIBS, XINERAMA_LIBS): New AC_SUBSTs.
|
||||
|
||||
2013-05-06 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
Merge from gnulib, incorporating:
|
||||
|
|
50
configure.ac
50
configure.ac
|
@ -2814,6 +2814,56 @@ if test "${HAVE_X11}" = "yes"; then
|
|||
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)])
|
||||
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 XRRGetScreenResourcesCurrent)
|
||||
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/Xinerama.h,
|
||||
[AC_CHECK_LIB(Xinerama, XineramaQueryExtension, HAVE_XINERAMA=yes)])
|
||||
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
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
2013-05-07 YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
|
||||
|
||||
* NEWS: Mention multi-monitor support.
|
||||
|
||||
2013-05-05 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
`write-region-inhibit-fsync' defaults to noninteractive (Bug#14273).
|
||||
|
|
6
etc/NEWS
6
etc/NEWS
|
@ -79,6 +79,12 @@ of the buffer is visible).
|
|||
|
||||
** In compiled Lisp files, the header no longer includes a timestamp.
|
||||
|
||||
** Multi-monitor support has been added.
|
||||
|
||||
*** New functions `display-monitor-attributes-list' and
|
||||
`frame-monitor-attributes' can be used to obtain information about
|
||||
each physical monitor on multi-monitor setups.
|
||||
|
||||
|
||||
* Editing Changes in Emacs 24.4
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2013-05-07 YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
|
||||
|
||||
* frame.el (display-monitor-attributes-list)
|
||||
(frame-monitor-attributes): New functions.
|
||||
|
||||
2013-05-06 Leo Liu <sdl.web@gmail.com>
|
||||
|
||||
* progmodes/octave.el (octave-syntax-propertize-function): Change
|
||||
|
|
|
@ -1256,6 +1256,23 @@ bars (top, bottom, or nil)."
|
|||
(unless (memq vert '(left right nil))
|
||||
(setq vert default-frame-scroll-bars))
|
||||
(cons vert hor)))
|
||||
|
||||
(defun frame-monitor-attributes (&optional frame)
|
||||
"Return the attributes of the physical monitor dominating FRAME.
|
||||
If FRAME is omitted, describe the currently selected frame.
|
||||
|
||||
A frame is dominated by a physical 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
|
||||
physical monitors.
|
||||
|
||||
See `display-monitor-attributes-list' for the list of attribute
|
||||
keys and their meanings."
|
||||
(or frame (setq frame (selected-frame)))
|
||||
(cl-loop for attributes in (display-monitor-attributes-list frame)
|
||||
for frames = (cdr (assq 'frames attributes))
|
||||
if (memq frame frames) return attributes))
|
||||
|
||||
|
||||
;;;; Frame/display capabilities.
|
||||
(defun selected-terminal ()
|
||||
|
@ -1476,6 +1493,50 @@ The value is one of the symbols `static-gray', `gray-scale',
|
|||
(t
|
||||
'static-gray))))
|
||||
|
||||
(declare-function x-display-monitor-attributes-list "xfns.c"
|
||||
(&optional terminal))
|
||||
|
||||
(defun display-monitor-attributes-list (&optional display)
|
||||
"Return a list of physical monitor attributes on 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 keys and values as follows:
|
||||
|
||||
geometry -- Position and size in pixels in the form of
|
||||
(X Y WIDTH HEIGHT)
|
||||
workarea -- Position and size of the workarea in pixels in the
|
||||
form of (X Y WIDTH HEIGHT)
|
||||
mm-size -- Width and height in millimeters in the form of
|
||||
(WIDTH HEIGHT)
|
||||
frames -- List of frames dominated by the physical monitor
|
||||
name (*) -- Name of the physical monitor as a string
|
||||
|
||||
where X, Y, WIDTH, and HEIGHT are integers. Keys labeled
|
||||
with (*) are optional.
|
||||
|
||||
A frame is dominated by a physical 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
|
||||
physical monitors. Every non-tip frame (including invisible one)
|
||||
in a graphical display is dominated by exactly one physical
|
||||
monitor at a time, though it can span multiple (or no) physical
|
||||
monitors."
|
||||
(let ((frame-type (framep-on-display display)))
|
||||
(cond
|
||||
((eq frame-type 'x)
|
||||
(x-display-monitor-attributes-list display))
|
||||
(t
|
||||
(let ((geometry (list 0 0 (display-pixel-width display)
|
||||
(display-pixel-height display))))
|
||||
`(((geometry . ,geometry)
|
||||
(workarea . ,geometry)
|
||||
(mm-size . (,(display-mm-width display)
|
||||
,(display-mm-height display)))
|
||||
(frames . ,(frames-on-display-list display)))))))))
|
||||
|
||||
|
||||
;;;; Frame geometry values
|
||||
|
||||
|
|
|
@ -1,3 +1,28 @@
|
|||
2013-05-07 YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp>
|
||||
Jan Djärv <jan.h.d@swipnet.se>
|
||||
|
||||
* Makefile.in (XRANDR_LIBS, XRANDR_CFLAGS, XINERAMA_LIBS)
|
||||
(XINERAMA_CFLAGS): New macros.
|
||||
(ALL_CFLAGS, LIBES): Use them.
|
||||
|
||||
* xfns.c: Include <X11/extensions/Xrandr.h> if HAVE_XRANDR, and
|
||||
include <X11/extensions/Xinerama.h> if HAVE_XINERAMA.
|
||||
(Qgeometry, Qworkarea, Qmm_size, Qframes, Qsource): New variables.
|
||||
(syms_of_xfns): DEFSYM them.
|
||||
(struct MonitorInfo): New struct.
|
||||
(x_get_net_workarea, free_monitors, x_get_monitor_for_frame)
|
||||
(x_make_monitor_attribute_list, x_get_monitor_attributes_fallback)
|
||||
(x_get_monitor_attributes_xrandr, x_get_monitor_attributes)
|
||||
(x_get_monitor_attributes_xinerama): New functions.
|
||||
(Fx_display_monitor_attributes_list): New primitive.
|
||||
(syms_of_xfns): Defsubr it.
|
||||
|
||||
* xterm.h (x_display_info): Add Xatom_net_workarea and
|
||||
Xatom_net_current_desktop.
|
||||
|
||||
* xterm.c (x_term_init): Initialize dpyinfo->Xatom_net_workarea
|
||||
and dpyinfo->Xatom_net_current_desktop.
|
||||
|
||||
2013-05-06 Eli Zaretskii <eliz@gnu.org>
|
||||
|
||||
* xdisp.c (pos_visible_p): Use the special code for finding the
|
||||
|
|
|
@ -232,6 +232,12 @@ IMAGEMAGICK_CFLAGS= @IMAGEMAGICK_CFLAGS@
|
|||
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 @@ ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
|
|||
-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 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
|
|||
$(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) \
|
||||
|
|
575
src/xfns.c
575
src/xfns.c
|
@ -59,6 +59,13 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
|||
|
||||
#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 @@ extern LWLIB_ID widget_id_tick;
|
|||
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, Qsource;
|
||||
Lisp_Object Qfont_param;
|
||||
|
||||
#ifdef GLYPH_DEBUG
|
||||
|
@ -3791,6 +3799,567 @@ If omitted or nil, that stands for the selected frame's display. */)
|
|||
else
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/* 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 *values = (long *) tmp_data;
|
||||
|
||||
rect->x = values[0];
|
||||
rect->y = values[1];
|
||||
rect->width = values[2];
|
||||
rect->height = values[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;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static void
|
||||
free_monitors (struct MonitorInfo *monitors, int n_monitors)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n_monitors; ++i)
|
||||
xfree (monitors[i].name);
|
||||
xfree (monitors);
|
||||
}
|
||||
|
||||
|
||||
/* 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, 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 = list4i (mi->work.x, mi->work.y,
|
||||
mi->work.width, mi->work.height);
|
||||
geometry = list4i (mi->geom.x, mi->geom.y,
|
||||
mi->geom.width, mi->geom.height);
|
||||
attributes = Fcons (Fcons (Qsource,
|
||||
make_string (source, strlen (source))),
|
||||
attributes);
|
||||
attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
|
||||
attributes);
|
||||
attributes = Fcons (Fcons (Qmm_size,
|
||||
list2i (mi->mm_width, mi->mm_height)),
|
||||
attributes);
|
||||
attributes = Fcons (Fcons (Qworkarea, workarea), attributes);
|
||||
attributes = Fcons (Fcons (Qgeometry, geometry), attributes);
|
||||
if (mi->name)
|
||||
attributes = Fcons (Fcons (Qname, make_string (mi->name,
|
||||
strlen (mi->name))),
|
||||
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);
|
||||
return 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);
|
||||
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;
|
||||
float mm_width_per_pixel, mm_height_per_pixel;
|
||||
|
||||
if (! info || n_monitors == 0)
|
||||
{
|
||||
if (info)
|
||||
XFree (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;
|
||||
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 = (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 (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;
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
static const char *source = "Gdk";
|
||||
|
||||
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 (Qsource,
|
||||
make_string (source, strlen (source))),
|
||||
attributes);
|
||||
attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
|
||||
attributes);
|
||||
|
||||
gdk_screen_get_monitor_geometry (gscreen, i, &rec);
|
||||
geometry = list4i (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,
|
||||
list2i (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 = list4i (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 = list4i (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 GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 14)
|
||||
{
|
||||
char *name = gdk_screen_get_monitor_plug_name (gscreen, i);
|
||||
if (name)
|
||||
attributes = Fcons (Fcons (Qname, make_string (name, strlen (name))),
|
||||
attributes);
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
x_pixel_width (register struct frame *f)
|
||||
|
@ -5701,6 +6270,11 @@ syms_of_xfns (void)
|
|||
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 (Qsource, "source");
|
||||
DEFSYM (Qfont_param, "font-parameter");
|
||||
/* This is the end of symbol initialization. */
|
||||
|
||||
|
@ -5864,6 +6438,7 @@ When using Gtk+ tooltips, the tooltip face is not used. */);
|
|||
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);
|
||||
|
|
|
@ -10251,6 +10251,8 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
|
|||
{ "_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 },
|
||||
|
|
|
@ -346,7 +346,8 @@ struct x_display_info
|
|||
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;
|
||||
|
|
Loading…
Add table
Reference in a new issue