* src/xterm.c: Explain frame resize synchronization.
This commit is contained in:
parent
d150eb438b
commit
e05478050a
1 changed files with 113 additions and 65 deletions
178
src/xterm.c
178
src/xterm.c
|
@ -340,34 +340,35 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
|
||||
FRAME RESIZING
|
||||
|
||||
In the following explanations "frame size" refers to the "native size"
|
||||
of a frame as reported by the (frame.h) macros FRAME_PIXEL_WIDTH and
|
||||
FRAME_PIXEL_HEIGHT. These specify the size of a frame as the values
|
||||
passed to/received from a toolkit and the window manager. The "text
|
||||
size" Emacs Lisp code uses in functions like 'set-frame-size' or sees
|
||||
in the ‘width’ and 'height' frame parameters is only loosely related
|
||||
to the native size. The necessary translations are provided by the
|
||||
macros FRAME_TEXT_TO_PIXEL_WIDTH and FRAME_TEXT_TO_PIXEL_HEIGHT as
|
||||
well as FRAME_PIXEL_TO_TEXT_WIDTH and FRAME_PIXEL_TO_TEXT_HEIGHT (in
|
||||
In the following explanations "frame size" refers to the "native
|
||||
size" of a frame as reported by the (frame.h) macros
|
||||
FRAME_PIXEL_WIDTH and FRAME_PIXEL_HEIGHT. These specify the size of
|
||||
a frame as the values passed to/received from a toolkit and the
|
||||
window manager. The "text size" Emacs Lisp code uses in functions
|
||||
like 'set-frame-size' or sees in the ‘width’ and 'height' frame
|
||||
parameters is only loosely related to the native size. The
|
||||
necessary translations are provided by the macros
|
||||
FRAME_TEXT_TO_PIXEL_WIDTH and FRAME_TEXT_TO_PIXEL_HEIGHT as well as
|
||||
FRAME_PIXEL_TO_TEXT_WIDTH and FRAME_PIXEL_TO_TEXT_HEIGHT (in
|
||||
frame.h).
|
||||
|
||||
Lisp functions may ask for resizing a frame either explicitly, using
|
||||
one of the interfaces provided for that purpose like, for example,
|
||||
'set-frame-size' or changing the 'height' or 'width' parameter of that
|
||||
frame, or implicitly, for example, by turning off/on or changing the
|
||||
width of fringes or scroll bars for that frame. Any such request
|
||||
passes through the routine 'adjust_frame_size' (in frame.c) which
|
||||
decides, among others, whether the native frame size would really
|
||||
change and whether it is allowed to change it at that moment. Only if
|
||||
'adjust_frame_size' decides that the corresponding terminal's
|
||||
'set_window_size_hook' may be run, it will dispatch execution to the
|
||||
appropriate function which, for X builds, is 'x_set_window_size' in
|
||||
this file.
|
||||
'set-frame-size' or changing the 'height' or 'width' parameter of
|
||||
that frame, or implicitly, for example, by turning off/on or
|
||||
changing the width of fringes or scroll bars for that frame. Any
|
||||
such request passes through the routine 'adjust_frame_size' (in
|
||||
frame.c) which decides, among others, whether the native frame size
|
||||
would really change and whether it is allowed to change it at that
|
||||
moment. Only if 'adjust_frame_size' decides that the corresponding
|
||||
terminal's 'set_window_size_hook' may be run, it will dispatch
|
||||
execution to the appropriate function which, for X builds, is
|
||||
'x_set_window_size' in this file.
|
||||
|
||||
For GTK builds, 'x_set_window_size' calls 'xg_frame_set_char_size' in
|
||||
gtkutil.c if the frame has an edit widget and 'x_set_window_size_1' in
|
||||
this file otherwise. For non-GTK builds, 'x_set_window_size' always
|
||||
calls 'x_set_window_size_1' directly.
|
||||
For GTK builds, 'x_set_window_size' calls 'xg_frame_set_char_size'
|
||||
in gtkutil.c if the frame has an edit widget and
|
||||
'x_set_window_size_1' in this file otherwise. For non-GTK builds,
|
||||
'x_set_window_size' always calls 'x_set_window_size_1' directly.
|
||||
|
||||
'xg_frame_set_char_size' calls the GTK function 'gtk_window_resize'
|
||||
for the frame's outer widget; x_set_window_size_1 calls the Xlib
|
||||
|
@ -378,28 +379,30 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
visible, it calls 'adjust_frame_size' to run 'resize_frame_windows'
|
||||
(see below) and hopes for the best.
|
||||
|
||||
Note that if Emacs receives a ConfigureEvent in response to an earlier
|
||||
resize request, the sizes specified by that event are not necessarily
|
||||
the sizes Emacs requested. Window manager and toolkit may override
|
||||
any of the requested sizes for their own reasons.
|
||||
Note that if Emacs receives a ConfigureEvent in response to an
|
||||
earlier resize request, the sizes specified by that event are not
|
||||
necessarily the sizes Emacs requested. Window manager and toolkit
|
||||
may override any of the requested sizes for their own reasons.
|
||||
|
||||
On X, size notifications are received as ConfigureNotify events. The
|
||||
expected reaction to such an event on the Emacs side is to resize all
|
||||
Emacs windows that are on the frame referred to by the event. Since
|
||||
resizing Emacs windows and redisplaying their buffers is a costly
|
||||
operation, Emacs may collapse several subsequent ConfigureNotify
|
||||
events into one to avoid that Emacs falls behind in user interactions
|
||||
like resizing a frame by dragging one of its borders with the mouse.
|
||||
On X, size notifications are received as ConfigureNotify events.
|
||||
The expected reaction to such an event on the Emacs side is to
|
||||
resize all Emacs windows that are on the frame referred to by the
|
||||
event. Since resizing Emacs windows and redisplaying their buffers
|
||||
is a costly operation, Emacs may collapse several subsequent
|
||||
ConfigureNotify events into one to avoid that Emacs falls behind in
|
||||
user interactions like resizing a frame by dragging one of its
|
||||
borders with the mouse.
|
||||
|
||||
Each ConfigureEvent event specifies a window, a width and a height.
|
||||
The event loop uses 'x_top_window_to_frame' to associate the window
|
||||
with its frame. Once the frame has been identified, on GTK the event
|
||||
is dispatched to 'xg_frame_resized'. On Motif/Lucid 'x_window' has
|
||||
installed 'EmacsFrameResize' as the routine that handles resize
|
||||
events. In either case, these routines end up calling the function
|
||||
'change_frame_size' in dispnew.c. On non-toolkit builds the effect is
|
||||
to call 'change_frame_size' directly from the event loop. In either
|
||||
case, the value true is passed as the DELAY argument.
|
||||
with its frame. Once the frame has been identified, on GTK the
|
||||
event is dispatched to 'xg_frame_resized'. On Motif/Lucid
|
||||
'x_window' has installed 'EmacsFrameResize' as the routine that
|
||||
handles resize events. In either case, these routines end up
|
||||
calling the function 'change_frame_size' in dispnew.c. On
|
||||
non-toolkit builds the effect is to call 'change_frame_size'
|
||||
directly from the event loop. In either case, the value true is
|
||||
passed as the DELAY argument.
|
||||
|
||||
'change_frame_size' is the central function to decide whether it is
|
||||
safe to process a resize request immediately or it has to be delayed
|
||||
|
@ -412,28 +415,30 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
possibly replacing ones that have been stored there upon the receipt
|
||||
of a preceding ConfigureEvent.
|
||||
|
||||
Delayed size changes are applied eventually upon calls of the function
|
||||
'do_pending_window_change' (in dispnew.c) which is called by the
|
||||
redisplay code at suitable spots where it's safe to change sizes.
|
||||
'do_pending_window_change' calls 'change_frame_size' with its DELAY
|
||||
argument false in the hope that it is now safe to call the function
|
||||
'resize_frame_windows' (in window.c) which is in charge of adjusting
|
||||
the sizes of all Emacs windows on the frame accordingly. Note that if
|
||||
'resize_frame_windows' decides that the windows of a frame do not fit
|
||||
into the constraints set up by the new frame sizes, it will resize the
|
||||
windows to some minimum sizes with the effect that parts of the frame
|
||||
at the right and bottom will appear clipped off.
|
||||
Delayed size changes are applied eventually upon calls of the
|
||||
function 'do_pending_window_change' (in dispnew.c) which is called
|
||||
by the redisplay code at suitable spots where it's safe to change
|
||||
sizes. 'do_pending_window_change' calls 'change_frame_size' with
|
||||
its DELAY argument false in the hope that it is now safe to call the
|
||||
function 'resize_frame_windows' (in window.c) which is in charge of
|
||||
adjusting the sizes of all Emacs windows on the frame accordingly.
|
||||
Note that if 'resize_frame_windows' decides that the windows of a
|
||||
frame do not fit into the constraints set up by the new frame sizes,
|
||||
it will resize the windows to some minimum sizes with the effect
|
||||
that parts of the frame at the right and bottom will appear clipped
|
||||
off.
|
||||
|
||||
In addition to explicitly passing width and height values in functions
|
||||
like 'gtk_window_resize' or 'XResizeWindow', Emacs also sets window
|
||||
manager size hints - a more implicit form of asking for the size Emacs
|
||||
would like its frames to assume. Some of these hints only restate the
|
||||
size and the position explicitly requested for a frame. Another hint
|
||||
specifies the increments in which the window manager should resize a
|
||||
frame to - either set to the default character size of a frame or to
|
||||
one pixel for a non-nil value of 'frame-resize-pixelwise'. See the
|
||||
function 'x_wm_set_size_hint' - in gtkutil.c for GTK and in this file
|
||||
for other builds - for the details.
|
||||
In addition to explicitly passing width and height values in
|
||||
functions like 'gtk_window_resize' or 'XResizeWindow', Emacs also
|
||||
sets window manager size hints - a more implicit form of asking for
|
||||
the size Emacs would like its frames to assume. Some of these hints
|
||||
only restate the size and the position explicitly requested for a
|
||||
frame. Another hint specifies the increments in which the window
|
||||
manager should resize a frame to - either set to the default
|
||||
character size of a frame or to one pixel for a non-nil value of
|
||||
'frame-resize-pixelwise'. See the function 'x_wm_set_size_hint' -
|
||||
in gtkutil.c for GTK and in this file for other builds - for the
|
||||
details.
|
||||
|
||||
We have not discussed here a number of special issues like, for
|
||||
example, how to handle size requests and notifications for maximized
|
||||
|
@ -442,13 +447,56 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||
used.
|
||||
|
||||
One thing that might come handy when investigating problems wrt
|
||||
resizing frames is the variable 'frame-size-history'. Setting this to
|
||||
a non-nil value, will cause Emacs to start recording frame size
|
||||
resizing frames is the variable 'frame-size-history'. Setting this
|
||||
to a non-nil value, will cause Emacs to start recording frame size
|
||||
adjustments, usually specified by the function that asked for an
|
||||
adjustment, a sizes part that records the old and new values of the
|
||||
frame's width and height and maybe some additional information. The
|
||||
internal function `frame--size-history' can then be used to display
|
||||
the value of this variable in a more readable form. */
|
||||
the value of this variable in a more readable form.
|
||||
|
||||
FRAME RESIZE SYNCHRONIZATION
|
||||
|
||||
The X window system operates asynchronously. That is to say, the
|
||||
window manager and X server might think a window has been resized
|
||||
before Emacs has a chance to process the ConfigureNotify event that
|
||||
was sent.
|
||||
|
||||
When a compositing manager is present, and the X server and Emacs
|
||||
both support the X synchronization extension, the semi-standard
|
||||
frame synchronization protocol can be used to notify the compositing
|
||||
manager of when Emacs has actually finished redisplaying the
|
||||
contents of a frame after a resize. The compositing manager will
|
||||
customarily then postpone displaying the contents of the frame until
|
||||
the redisplay is complete.
|
||||
|
||||
Emacs announces support for this protocol by creating an X
|
||||
server-side counter object, and setting it as the
|
||||
`_NET_WM_SYNC_REQUEST_COUNTER' property of the frame's top-level
|
||||
window. The window manager then initiates the synchronized resize
|
||||
process by sending Emacs a ClientMessage event before the
|
||||
ConfigureNotify event where:
|
||||
|
||||
type = ClientMessage
|
||||
window = the respective client window
|
||||
message_type = WM_PROTOCOLS
|
||||
format = 32
|
||||
data.l[0] = _NET_WM_SYNC_REQUEST
|
||||
data.l[1] = timestamp
|
||||
data.l[2] = low 32 bits of a provided frame counter value
|
||||
data.l[3] = high 32 bits of a provided frame counter value
|
||||
data.l[4] = 1 if the the extended frame counter should be updated,
|
||||
otherwise 0
|
||||
|
||||
Upon receiving such an event, Emacs constructs and saves a counter
|
||||
value from the provided low and high 32 bits. Then, when the
|
||||
display engine tells us that a frame has been completely updated
|
||||
(presumably because of a redisplay caused by a ConfigureNotify
|
||||
event), we set the counter to the saved value, telling the
|
||||
compositing manager that the contents of the window now accurately
|
||||
reflect the new size. The compositing manager will then display the
|
||||
contents of the window, and the window manager might also postpone
|
||||
updating the window decorations until this moment. */
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
|
|
Loading…
Add table
Reference in a new issue