Each buffer has a point, and in addition each window also has a point. The manual explains the relationship:
- The window point is established when a window is first created; it is initialized from the buffer's point, or from the window point of another window opened on the buffer if such a window exists.
- Selecting a window sets the value of point in its buffer from the window's value of point. Conversely, deselecting a window sets the window's value of point from that of the buffer. Thus, when you switch between windows that display a given buffer, the point value for the selected window is in effect in the buffer, while the point values for the other windows are stored in those windows.
- As long as the selected window displays the current buffer, the window's point and the buffer's point always move together; they remain equal.
Here's a little experiment to illustrate this. Create a buffer and display it in another window (it doesn't matter whether that window is in the same frame or not). Stick to a window that doesn't show the buffer. Insert some text in the buffer, without selecting the other window, and notice that the buffer's point is updated to be after the inserted text. Then select the window displaying the buffer. Notice that now the buffer's point has now changed to the window's point.
(with-selected-window (selected-window) (find-file-other-window "foo"))
#<buffer foo>
(with-current-buffer (set-buffer "foo") (insert "foo") (cons (point) (point-max)))
(4 . 4)
(with-selected-window (selected-window) (other-window 1))
nil
(with-current-buffer (set-buffer "foo") (cons (point) (point-max)))
(1 . 4)
To avoid this effect where the point is reset to another location when the user switches to a window that displays the buffer, you can use insert-before-markers
instead of plain insert
. This works because the window's point is implemented as a marker internally. (The buffer's point is not a marker.) This is what accept-process-output
does internally, for example, which is why output from a subprocess doesn't get mixed up when the destination buffer is displayed in a non-selected window.
Another approach is to set window-point-insertion-type
to t
. By default, a window's point stays behind insertions. By setting it to a non-nil value, the window's point moves with insertions at that location. This is the approach used by Comint, for example: it declares window-point-insertion-type
as buffer-local and sets it to t
.
In your experiment, you never explicitly switch to the window that's displaying the test buffer. But that buffer is in the selected window of another GUI frame. Since the insertion code is running in a timer, with user interactions between each insertion, the redisplay code runs between each insertion. One of the effects of the redisplay code is to recalculate the frame's title. Deep in the bowels of x_consider_frame_title
, the code temporarily selects the frame's selected window, causing the cursor update. This analysis is confirmed by running the experiment with the buffer displayed in a terminal frame instead of a GUI frame: the insertions are in the expected order. You'd see the same effect from any kind of asynchronous operation: timer, process sentinel, etc.