Fl_Window::decorated_h on X11 changes before actual height

45 views
Skip to first unread message

Evan Laforge

unread,
Jan 23, 2022, 9:58:56 PM1/23/22
to fltkc...@googlegroups.com
This doc: https://www.fltk.org/doc-1.4/classFl__Window.html#acaf7d4846b4454911f7a2b4186d6c912

says decorated_h() "Returns the window height including any window
title bar and any frame added by the window manager." However, if you
call it in a Fl_Window overridden resize() method, it's actually the
*requested* height (presumably plus decorations), while this->h() is
the current height, which may become the requested height if the
superclass resize() is called.

This led to a bug for me because I use `int titlebar =
this->decorated_h() - this->h();` along with Fl::screen_work_area to
detect the size of the titlebar, and restrict the actual height it
passes on to Fl_Window::resize so it won't exceed the screen height -
menubar - titlebar. However on X11, since decorated_h() has already
changed to the requested height, the subtraction no longer works!
This doesn't seem to happen on OSX.

So... I think it would be proper for X11 to do like OSX and for
decorated_h() to be the current height plus decoration. Alternately,
if there's a better way to detect the window titlebar height I'm all
ears! For the moment I'm going to put the titlebar height in a static
variable, assuming it never changes. Interestingly, this is probably
only reliable for X11, since it seems to send a bunch of resize
requests when the window is displayed, while OSX doesn't seem to send
any, but of course I only need the workaround on X11, so it works out
:)

The reason I have to do this is that the app can receive resize
requests from inside itself, simpler apps that only get resizes from
the OS can rely on the OS enforcing the screen fit rules.

I assume decorated_w() has the same problem though I don't check it
since OSX doesn't have decoration on the side.

Oh and this is a totally different thing, but I recently fixed a bug
where I accidentally called Fl_Window::resize for the superclass
instead of Fl_Double_Window. That was fine on OS X where they are the
same, but led to messed up drawing on X11. It would be nice to remove
the two kinds of window, assuming non-double-buffered windows are
truly obsolete.

Here's a little program to demonstrate the problem. Run it, then have
your window manager maximize the window vertically:

#include <iostream>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/fl_draw.H>
#include <FL/Fl.H>

class Bug : public Fl_Window {
public:
Bug(int x, int y, int w, int h)
: Fl_Window(x, y, w, h), box(0, 0, w, h)
{
this->resizable(this);
box.box(FL_FLAT_BOX);
box.color(FL_WHITE);
}

void resize(int x, int y, int w, int h) override {
std::cout << "BEFORE h:" << this->h() << " decorated_h:"
<< this->decorated_h() << '\n';
Fl_Window::resize(x, y, w, h);
std::cout << "AFTER h:" << this->h() << " decorated_h:"
<< this->decorated_h() << '\n';
}

Fl_Box box;
};

int main()
{
Bug win(200, 200, 200, 200);
win.show();
Fl::run();
return 0;
}

Manolo

unread,
Jan 24, 2022, 7:00:50 AM1/24/22
to fltk.coredev
I would suggest you compute once the titlebar height, as shown below,
and then use it in your resize() override.

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>

class myWindow : public Fl_Window {
public:
  myWindow(int w, int h) : Fl_Window(w,h) {}
  void resize(int X, int Y, int W, int H) {
    static int titlebar_height = 0;
    if (shown() && !titlebar_height) {
      titlebar_height = decorated_h() - h();
      printf("titlebar_height=%d\n",titlebar_height);
    }
    Fl_Window::resize(X,Y,W,H);
  }
};

int main(int argc, char **argv) {
myWindow *window = new myWindow(340, 180);
Fl_Box *box = new Fl_Box(20, 40, 300, 100, "Hello, World!");
box->box(FL_UP_BOX);
box->labelfont(FL_BOLD + FL_ITALIC);
box->labelsize(36);
box->labeltype(FL_SHADOW_LABEL);
window->end();
window->resizable(window);
window->show(argc, argv);
return Fl::run();
}

Evan Laforge

unread,
Feb 7, 2022, 3:10:28 AM2/7/22
to fltkc...@googlegroups.com
Oops, I had this in my drafts and forgot about it!

On Mon, Jan 24, 2022 at 4:00 AM Manolo wrote:
> I would suggest you compute once the titlebar height, as shown below,
> and then use it in your resize() override.

Yes, that's what I'm doing, similar to what you wrote, except I can't
use 0 because that in fact is the usual titlebar height for my window
manager. It seems like it should be reliable on linux for now,
because new windows seem to get a resize call when they are shown and
we can assume that the requested height and current height are the
same. Though it's not specified so I don't know if it's meant to be
reliable. Since it doesn't happen on OS X, that implies it's a
coincidence. It's also assuming window decoration doesn't change,
which while I know it can, my setup doesn't do that, so it works for
me. It also seems unintuitive that while h() changes after the
resize() call (as a result of it), decorated_h() changes before
resize() is called... I guess the problem would have been side-stepped
with a hypothetical decoration_h(), which is really what I'm after.

... however I just tested it with window decorations and decorated_h()
is always the same as h() anyway, so whatever it's doing to detect
window decoration on X isn't working for my window manager, which is
fvwm. I just never noticed since I don't use decorations by default.
I looked into Fl_X11_Window_Driver::decorated_win_size, and there's
some stuff I don't understand with separate attributes and
w_attributes, but printf shows that XGetWindowAttributes is returning
the same width and height for decorated and undecorated windows alike.
I don't know enough about X11 to know if that's expected, but it
certainly seems fltk isn't expecting it.

Manolo

unread,
Feb 7, 2022, 12:57:13 PM2/7/22
to fltk.coredev
1) Right. You can't use titlebar_height = 0 as mark of unassigned value when you sometimes
deal with window without decoration. I suggest you use -1 instead.

2) Under X11 you have the certainty to get the resize() method called several times
when a decorated window is first mapped. I see it called 5 times following 5 successive X events.

3) Under strict X11, a decorated window is in fact a set of 2 windows, one slightly taller,
and sometimes wider too, containing the titlebar and the window borders, and the one you
interact with containing all FLTK drawings. You normally totally ignore the big window.
Only Fl_Window::decorated_h() deals with it: it gives the height of this big window,
while Fl_Window::h() gives the height of the FLTK window (let's ignore scaling issues for simplicity).
It turns out that the X11 resize procedure for decorated windows is such that the big window
is resized first and second the smaller one. FL_Window::resize() runs to resize the small window,
but at that moment, the big one has been resized already. This explains your observation.
I don't think it's possible to overcome that under X11.
The take home message is that it's not possible to subtract decorated_h() - h()
during a window resize operation. That operation is best done before, and I recommend
when the window is first mapped.
I will add a note in the doc about that point.

4) I'm not aware of X11 window managers where the titlebar height changes.
If you are in a situation where it does, you should add an int member to your
custom window class and store decorated_h() - h() computed at window creation
therein and use it later.

5) Some window managers don't use strictly X11 to decorate their windows.
Compiz is in that case. For them, there's no big X11 window containing the titlebar and borders,
Fl_Window::decorated_h() returns the same as Fl_Window::h().
I guess your window manager (fvwm) is in that case, so FLTK is unable to compute the height
of the titlebar, nor to print it. It won't be possible with FLTK to compute exactly the size
of the window that will fill the workspace and show the titlebar.

Reply all
Reply to author
Forward
0 new messages