From edf9700c3ca65d92bdfca59306845ffc0717d690 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 21 May 2022 11:17:34 +0800 Subject: [PATCH] Add a hook run upon monitor configuration changes * doc/lispref/frames.texi (Multiple Terminals): Document new hook `display-monitors-changed-functions'. * etc/NEWS: Announce new abnormal hook. * src/keyboard.c (kbd_buffer_get_event): Handle MONITORS_CHANGED_EVENT. (syms_of_keyboard): New hook and defsyms. * src/termhooks.h (enum event_kind): Add new event `MONITORS_CHANGED_EVENT'. * src/xterm.c (handle_one_xevent): Handle RRNotify and RRScreenChangeNotify events. (x_term_init): Select for RRScreenChange, RRCrtcChange and RROutputChange. * src/xterm.h (struct x_display_info): Improve RandR version detection. --- doc/lispref/frames.texi | 10 ++++++++ etc/NEWS | 5 ++++ src/keyboard.c | 23 ++++++++++++++++++ src/termhooks.h | 11 +++++++-- src/xterm.c | 52 +++++++++++++++++++++++++++++++++++++---- src/xterm.h | 3 +++ 6 files changed, 98 insertions(+), 6 deletions(-) diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 5ea060871f4..7d600b5a0c5 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -458,6 +458,16 @@ monitor, the same string as returned by the function @var{display} should be the name of an X display (a string). @end deffn +@cindex monitor change functions +@defvar display-monitors-changed-functions +This variable is an abnormal hook run when the monitor configuration +changes, which can happen if a monitor is rotated, moved, added or +removed from a multiple-monitor setup, if the primary monitor changes, +or if the resolution of a monitor changes. It is called with a single +argument consisting of the terminal on which the monitor configuration +changed. +@end defvar + @node Frame Geometry @section Frame Geometry @cindex frame geometry diff --git a/etc/NEWS b/etc/NEWS index 70f2c0e6daf..5eab8e23bb2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1850,6 +1850,11 @@ functions. * Lisp Changes in Emacs 29.1 ++++ +** New hook 'display-monitors-changed-functions'. +It is called whenever the configuration of different monitors on a +display changes. + +++ ** 'prin1' and 'prin1-to-string' now take an optional OVERRIDES parameter. This parameter can be used to override values of print-related settings. diff --git a/src/keyboard.c b/src/keyboard.c index 481633f92fe..a2322f1b49d 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4058,6 +4058,18 @@ kbd_buffer_get_event (KBOARD **kbp, } #endif + case MONITORS_CHANGED_EVENT: + { + kbd_fetch_ptr = next_kbd_event (event); + input_pending = readable_events (0); + + CALLN (Frun_hook_with_args, + Qdisplay_monitors_changed_functions, + event->ie.arg); + + break; + } + #ifdef HAVE_EXT_MENU_BAR case MENU_BAR_ACTIVATE_EVENT: { @@ -12609,6 +12621,8 @@ See also `pre-command-hook'. */); DEFSYM (Qtouchscreen_end, "touchscreen-end"); DEFSYM (Qtouchscreen_update, "touchscreen-update"); DEFSYM (Qpinch, "pinch"); + DEFSYM (Qdisplay_monitors_changed_functions, + "display-monitors-changed-functions"); DEFSYM (Qcoding, "coding"); @@ -12953,6 +12967,15 @@ Otherwise, a wheel event will be sent every time the mouse wheel is moved. */); mwheel_coalesce_scroll_events = true; + DEFVAR_LISP ("display-monitors-changed-functions", Vdisplay_monitors_changed_functions, + doc: /* Abnormal hook run when the monitor configuration changes. +This can happen if a monitor is rotated, moved, plugged in or removed +from a multi-monitor setup, if the primary monitor changes, or if the +resolution of a monitor changes. The hook should accept a single +argument, which is the terminal on which the monitor configuration +changed. */); + Vdisplay_monitors_changed_functions = Qnil; + pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper); } diff --git a/src/termhooks.h b/src/termhooks.h index 08bde0aec0d..d7190e77362 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -31,7 +31,8 @@ struct glyph; INLINE_HEADER_BEGIN -enum scroll_bar_part { +enum scroll_bar_part +{ scroll_bar_nowhere, scroll_bar_above_handle, scroll_bar_handle, @@ -301,8 +302,9 @@ enum event_kind #endif #ifdef HAVE_XWIDGETS - /* events generated by xwidgets*/ + /* An event generated by an xwidget to tell us something. */ , XWIDGET_EVENT + /* Event generated when WebKit asks us to display another widget. */ , XWIDGET_DISPLAY_EVENT #endif @@ -349,6 +351,11 @@ enum event_kind positive delta represents a change clockwise, and a negative delta represents a change counter-clockwise. */ , PINCH_EVENT + + /* In a MONITORS_CHANGED_EVENT, .arg gives the terminal on which the + monitor configuration changed. .timestamp gives the time on + which the monitors changed. */ + , MONITORS_CHANGED_EVENT }; /* Bit width of an enum event_kind tag at the start of structs and unions. */ diff --git a/src/xterm.c b/src/xterm.c index cbe6426447e..b321f43da16 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -20103,6 +20103,38 @@ handle_one_xevent (struct x_display_info *dpyinfo, } } } +#endif +#ifdef HAVE_XRANDR + if (dpyinfo->xrandr_supported_p + && (event->type == (dpyinfo->xrandr_event_base + + RRScreenChangeNotify) + || event->type == (dpyinfo->xrandr_event_base + + RRNotify))) + { + union buffered_input_event *ev; + Time timestamp; + + if (event->type == (dpyinfo->xrandr_event_base + + RRScreenChangeNotify)) + timestamp = ((XRRScreenChangeNotifyEvent *) event)->timestamp; + else + timestamp = 0; + + ev = (kbd_store_ptr == kbd_buffer + ? kbd_buffer + KBD_BUFFER_SIZE - 1 + : kbd_store_ptr - 1); + + if (kbd_store_ptr != kbd_fetch_ptr + && ev->ie.kind == MONITORS_CHANGED_EVENT + && XTERMINAL (ev->ie.arg) == dpyinfo->terminal) + /* Don't store a MONITORS_CHANGED_EVENT if there is + already an undelivered event on the queue. */ + goto OTHER; + + inev.ie.kind = MONITORS_CHANGED_EVENT; + inev.ie.timestamp = timestamp; + XSETTERMINAL (inev.ie.arg, dpyinfo->terminal); + } #endif OTHER: #ifdef USE_X_TOOLKIT @@ -24405,13 +24437,25 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) #endif #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) + dpyinfo->xrandr_supported_p + = XRRQueryExtension (dpy, &dpyinfo->xrandr_event_base, + &dpyinfo->xrandr_error_base); + if (dpyinfo->xrandr_supported_p) { XRRQueryVersion (dpy, &dpyinfo->xrandr_major_version, &dpyinfo->xrandr_minor_version); + + if (dpyinfo->xrandr_major_version == 1 + && dpyinfo->xrandr_minor_version >= 2) + XRRSelectInput (dpyinfo->display, + dpyinfo->root_window, + (RRScreenChangeNotifyMask + | RRCrtcChangeNotifyMask + | RROutputChangeNotifyMask + /* Emacs doesn't actually need this, but GTK + selects for it when the display is + initialized. */ + | RROutputPropertyNotifyMask)); } #endif diff --git a/src/xterm.h b/src/xterm.h index a05bc404f69..8571bd9d39c 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -602,6 +602,9 @@ struct x_display_info XModifierKeymap *modmap; #ifdef HAVE_XRANDR + bool xrandr_supported_p; + int xrandr_event_base; + int xrandr_error_base; int xrandr_major_version; int xrandr_minor_version; #endif -- 2.39.2