emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Patch to fix frame positioning bug on Windows with (make-frame '((le


From: Francis Litterio
Subject: Re: Patch to fix frame positioning bug on Windows with (make-frame '((left . -1)))
Date: Wed, 12 Jan 2005 21:59:18 -0500
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/21.3.50 (windows-nt)

Francis Litterio wrote:

> Jan D. wrote:

>> Can you verify if your change has any impact on this bug:
>>
>> http://lists.gnu.org/archive/html/emacs-pretest-bug/2004-11/msg00519.html
>>
>> This was the reason a change was made.  It may be impossible to get
>> Emacs to work correctly on W32.  Just [subtracting] 7 is no good, as
>> you self pointed out, a more general solution must be found.
>
> My change breaks the fix for that bug, so I'm going to investigate
> further.

OK, I think I've gotten to the bottom of the issue.  The above bug
report by Drew Adams contains an incorrect assumption in step #2 of the
steps to reproduce:

> 1. Position a new frame at the left border (the position isn't important,
> but keep track of the position you use - using 0 makes it easier). Evaluate
> (assq 'left (frame-parameters nil)), getting, for example, (left . 0).
> 
> 2. Measure the frame width in pixels: (frame-pixel-width nil), getting, for
> example, 600.

600 is not the width of the frame in pixels.  It is the width of the
frame's client area in pixels.  The window manager (i.e., Windows) draws
left and right borders on either side of the client area, but
frame-pixel-width does not include those borders in the value it
returns..

So the value computed in step #2 above should be:

        (+ (frame-pixel-width nil) 6 6)
        => 612

Assuming his window manager borders are 6 pixels wide.

> 3. Measure the display width in pixels: (display-pixel-width nil), getting,
> for example, 1280.
> 
> 4. Calculate the distance of the frame's right edge from the display's right
> edge, getting, for example, 1280 - 600 = 680.

In step #4, the calculation of the frame's right edge from the display's
right edit should be:

        1280 - 612 = 668

Actually, since a 'left parameter of 0 means "flush against the left
edge of the screen" but -1 means "flush against the right edge of the
screen", that calculation should be:

        1280 - 612 + 1 = 669

> 5. Change the frame's left parameter to the negation of the value in #4, for
> example: (modify-frame-parameters nil '((left . -680))). According to the
> doc, this positions the right edge of the frame 680 pixels from the right
> edge of the display.
> 
> However the frame has in fact shifted 12 pixels to the left.

The reason for this movement is that setting the 'left parameter to -680
was wrong.  It should have been set to -669.

To demonstrate that the window borders must be taken into account when
computing the negative 'left frame parameter, evaluate the below form in
a frame that is completely visible (no parts of the frame are
off-screen).  Evaluating this form should not move the frame at all,
assuming your window manager draws left and right borders that are 4
pixels each (change the two 4's if your borders are different):

(let ((negative-left (- (+ 1 (- (display-pixel-width)
                                (+ 4 4
                                   (frame-pixel-width nil)
                                   (frame-parameter nil 'left)))))))
  (modify-frame-parameters nil `((left . ,negative-left))))

On Windows, Emacs does not have to take the window manager borders into
account when positioning frames using positive 'left and 'top
parameters.  Positive 'left and 'top parameters are simply passed
directly to Windows, which is why this form:

        (make-frame '((top . 0) (left . 0)))

positions the frame with the Windows borders flush against the left and
top of the screen.  This is desirable behavior.

The only time that Emacs needs to take the window manager borders into
account is when it converts a negative 'left or 'top parameter to an
equivalent positive value, which is done in x_calc_absolute_position():

    void
    x_calc_absolute_position (f)
         struct frame *f;
    {
      int flags = f->size_hint_flags;

      /* Treat negative positions as relative to the leftmost bottommost
         position that fits on the screen.  */
      if (flags & XNegative)
        f->left_pos = (FRAME_W32_DISPLAY_INFO (f)->width
                       - FRAME_PIXEL_WIDTH (f)
                       + f->left_pos);

      if (flags & YNegative)
        f->top_pos = (FRAME_W32_DISPLAY_INFO (f)->height
                      - FRAME_PIXEL_HEIGHT (f)
                      + f->top_pos);
      /* The left_pos and top_pos
         are now relative to the top and left screen edges,
         so the flags should correspond.  */
      f->size_hint_flags &= ~ (XNegative | YNegative);
    }

and nowhere else (that I can find).  Thus, to fix both the bug reported in:

  http://lists.gnu.org/archive/html/emacs-pretest-bug/2004-11/msg00519.html

and my bug, the above function needs to compute f->left_pos like this:

        f->left_pos = (FRAME_W32_DISPLAY_INFO (f)->width
                       - FRAME_PIXEL_WIDTH (f)
                       + f->left_pos
                       - (FRAME_W32_VERTICAL_BORDER_WIDTH * 2) - 1);

where FRAME_W32_VERTICAL_BORDER_WIDTH is a proposed macro that evaluates
to the pixel width of one vertical border drawn by Windows.  On my
system, it would expand to 4.  On other Windows system, it may be
different.  Worse, under XP, the user can change it interactively using
the Appearance tab on the desktop properties dialog.

Similarly, f->top_pos should be computed like this:

        f->top_pos = (FRAME_W32_DISPLAY_INFO (f)->height
                      - FRAME_PIXEL_HEIGHT (f)
                      + f->top_pos
                      - (FRAME_W32_TOP_BORDER_WIDTH +
                         FRAME_W32_BOTTOM_BORDER_WIDTH) - 1);

This needs two new macros, because the top and bottom borders have
different sizes.

The question is: What is the correct implementation of these new macros?

On my system (Windows XP, with ), these values work:

        #define FRAME_W32_VERTICAL_BORDER_WIDTH 4
        #define FRAME_W32_TOP_BORDER_WIDTH 25
        #define FRAME_W32_BOTTOM_BORDER_WIDTH 4

I have build Emacs with the above change (and the above macro values),
and when I evaluate:

        (make-frame '((top . -1) (left . -1)))

the new frame is perfectly positioned flush against the right and bottom
edges of the display.

I hope this helps.
--
Francis Litterio
franl <at> world . std . com





reply via email to

[Prev in Thread] Current Thread [Next in Thread]