Subject: Re: bug#25380: 25.1; save-window-excursion problem in batch mode

From: Eli Zaretskii <[email-masked]>
Date: Mon, 09 Jan 2017 17:16:43 +0200

> Date: Mon, 09 Jan 2017 10:39:36 +0100
> From: martin rudalics <[email-masked]>
> Cc: [email-masked]
> 
> > It works in 24.5 and earlier. Something to do with window-edges?
> >
> > emacs -Q -batch --eval '(progn (setq c (current-window-configuration))
> > (print (window-edges))
> > (set-window-configuration c)
> > (print (window-edges)))'
> >
> > 24.5:
> > (0 1 10 9)
> > (0 1 10 9)
> >
> > 25.1:
> > (0 0 10 9)
> > (0 1 10 10)
> 
> IIUC the pixel comes from a menubar line which gets spuriously added.

I believe you are right, see the backtrace below from the place which
changes the value from 0 to 1 (it's not pixel units, btw, it's
character units, AFAIU).

More accurately, the initial current-window-configuration call happens
so early that the basic geometry of the "windows" is not yet set, and
in particular the menu bar is not yet computed and accounted for.
This is indeed where Emacs 25 behaves differently from previous
versions, and for a very good reason.

> If someone told me how to debug this, I might be able to tell more.

I just ran Emacs under a debugger with a breakpoint in
Fcurrent_window_configuration, then put a watchpoint on every top_line
member of every window that got saved there, and waited for it to
break.

> I have no idea how the frame seen by ‘current-window-configuration’
> gets created in batch mode.

It comes from temacs, AFAIR.

Turning back to the original report:

> $ emacs -Q -batch -eval '(print (equal (save-window-excursion 
> (current-window-configuration)) (current-window-configuration)))'
> 
> nil
> 
> This is unexpected, especially in unit tests that want to check window
> comparisons.  In interactive mode, this works (i.e. (equal
> (save-window-excursion (current-window-configuration))
> (current-window-configuration)) is t).

(This works in an interactive session because by the time you evaluate
the form, the frame geometry has been completely computed and set up.)

I'm not actually certain we should try fixing this, unless Martin can
do that in some easy and safe way.  The code involved in this is quite
fragile, because Emacs creates its first frame without knowing
anything about its geometry and the window-system.  That code took a
long time to get right on all systems; I'd hate to break it to cater
to (IMO) much less important use cases.  I'd rather people wouldn't
count on anything related to "frames" and "windows" in the batch
session, except that they exist.  (They must exist because some
functions will simply not work without a frame or a window.)

IOW, I think unit tests that must compare windows cannot be naïvely
run in batch mode; you need to use tricks.  For example:

  emacs -batch -eval "(progn (save-window-excursion (current-window-configuration)) (print (equal (save-window-excursion (current-window-configuration)) (current-window-configuration))))" => t

Here's the backtrace which shows where we change the top pixel from
zero to 1:

Thread 1 hit Hardware watchpoint 4: -location w->pixel_top

Old value = 0
New value = 1
resize_frame_windows (f=0x17752c8 <dumped_data+95208>, size=10,
    horflag=false, pixelwise=true) at window.c:4125
4125            r->total_lines = new_size;
(gdb) bt
#0  resize_frame_windows (f=0x17752c8 <dumped_data+95208>, size=10,
    horflag=false, pixelwise=true) at window.c:4125
#1  0x01014819 in adjust_frame_size (f=0x17752c8 <dumped_data+95208>,
    new_width=-1, new_height=-1, inhibit=1, pretend=false, parameter=52528)
    at frame.c:550
#2  0x010cf523 in Fset_window_configuration (
    configuration=-6917529027611094376) at window.c:6404
#3  0x012278e1 in eval_sub (form=-4611686018401701024) at eval.c:2211
#4  0x01220476 in Fprogn (body=-4611686018401701056) at eval.c:450
#5  0x0122052a in unwind_body (body=-4611686018401701056) at eval.c:463
#6  0x0122c92a in do_one_unbind (this_binding=0x88dc10, unwinding=true)
    at eval.c:3379
#7  0x0122cd0c in unbind_to (count=18, value=-6917529027611093664)
    at eval.c:3499
#8  0x01223b37 in Funwind_protect (args=-4611686018401701584) at eval.c:1198
#9  0x01227368 in eval_sub (form=-4611686018401701600) at eval.c:2164
#10 0x01220476 in Fprogn (body=-4611686018401701648) at eval.c:450
#11 0x01223005 in Flet (args=-4611686018401701680) at eval.c:973
#12 0x01227368 in eval_sub (form=-4611686018401702176) at eval.c:2164
#13 0x012280ff in eval_sub (form=-4611686018401978320) at eval.c:2282
#14 0x012277fd in eval_sub (form=-4611686018401978192) at eval.c:2199
#15 0x012277fd in eval_sub (form=-4611686018401978160) at eval.c:2199
#16 0x01226b8e in Feval (form=-4611686018401978160, lexical=0) at eval.c:2033
#17 0x01229fc9 in funcall_subr (subr=0x167d720 <Seval>, numargs=1,
    args=0x88e738) at eval.c:2812
#18 0x01229a14 in Ffuncall (nargs=2, args=0x88e730) at eval.c:2735
#19 0x0128e4e0 in exec_byte_code (bytestr=-9223372036833358424,
    vector=-6917529027619664456, maxdepth=4611686018427387927,
    args_template=4611686018427388161, nargs=1, args=0x88ec38)
    at bytecode.c:726
#20 0x0122ac8c in funcall_lambda (fun=-6917529027619664520, nargs=1,
    arg_vector=0x88ec30) at eval.c:2935
#21 0x01229a58 in Ffuncall (nargs=2, args=0x88ec28) at eval.c:2737
#22 0x0128e4e0 in exec_byte_code (bytestr=-9223372036833377352,
    vector=-6917529027619683384, maxdepth=4611686018427387925,
    args_template=4611686018427387904, nargs=0, args=0x88f1a8)
    at bytecode.c:726
#23 0x0122ac8c in funcall_lambda (fun=-6917529027619683448, nargs=0,
    arg_vector=0x88f1a8) at eval.c:2935
#24 0x01229a58 in Ffuncall (nargs=1, args=0x88f1a0) at eval.c:2737
#25 0x0128e4e0 in exec_byte_code (bytestr=-9223372036833380592,
    vector=-6917529027619686624, maxdepth=4611686018427387916,
    args_template=4611686018427387904, nargs=0, args=0x88f650)
    at bytecode.c:726
#26 0x0122ac8c in funcall_lambda (fun=-6917529027619686688, nargs=0,
    arg_vector=0x88f650) at eval.c:2935
#27 0x0122a755 in apply_lambda (fun=-6917529027619686688, args=0, count=4)
    at eval.c:2872
#28 0x01227e87 in eval_sub (form=-4611686018401371376) at eval.c:2256
#29 0x01226b8e in Feval (form=-4611686018401371376, lexical=0) at eval.c:2033
#30 0x01156473 in top_level_2 () at keyboard.c:1124
#31 0x0122464a in internal_condition_case (bfun=0x115643d <top_level_2>,
    handlers=21728, hfun=0x1155bf4 <cmd_error>) at eval.c:1336
#32 0x011564d9 in top_level_1 (ignore=0) at keyboard.c:1132
#33 0x01223786 in internal_catch (tag=58016, func=0x1156479 <top_level_1>,
    arg=0) at eval.c:1101
#34 0x01156355 in command_loop () at keyboard.c:1093
#35 0x01155620 in recursive_edit_1 () at keyboard.c:700
#36 0x011558be in Frecursive_edit () at keyboard.c:771
#37 0x01153068 in main (argc=4, argv=0xc21478) at emacs.c:1686

Lisp Backtrace:
"set-window-configuration" (0x88da40)
"unwind-protect" (0x88dd70)
"let" (0x88e000)
"save-window-excursion" (0x88e170)
"equal" (0x88e2e0)
"print" (0x88e450)
"eval" (0x88e738)
"command-line-1" (0x88ec30)
"command-line" (0x88f1a8)
"normal-top-level" (0x88f650)
(gdb) p FRAME_MENU_BAR_HEIGHT(f)
$8 = 1
(gdb) p FRAME_TOOL_BAR_HEIGHT(f)
$9 = 0
(gdb) p FRAME_TOP_MARGIN_HEIGHT(f)
$10 = 1
(gdb)