x11gtkmm4

X windows, GTKMM, re-parent window, draws child window into parent


I am attempting to re-parent a child window Gtk::Window so that I can emulate the deprecated GTKMM 4 call to position the window in the centre of the parent (which was present in GTKMM 3). I have searched for XReparentWindow posts and the only relevant one advised to use XSetTransientForHint this which did not work for me. The code that I have written is:

    void MyGtk::Window_Centre_Child_On_Parent(
        Gtk::Window *parent,
        Gtk::Window *child
    )
    {
        int child_height;

        int child_width;

        int parent_height;

        int parent_width;

        int position_x;

        int position_y;


        //
        //First we must validate all parameters and terminate if any fail.
        //
        assert(parent);

        assert(child);

        //
        //Now we need to fetch the size of the child window so that we can then
        //re-parent the window at the centre of the parent window thus emulating
        //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
        //
        Widget_Get_Size(
            child,
            child_width,
            child_height
        );

        //
        //Now we need to ensure that the above returned sensible values and we
        //terminate if not.
        //
        assert(child_height > 0);

        assert(child_width > 0);

        //
        //Now we need to fetch the size of the parent window so that we can then
        //re-parent the window at the centre of the parent window thus emulating
        //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
        //
        Widget_Get_Size(
            parent,
            parent_width,
            parent_height
        );

        //
        //Now we need to ensure that the above returned sensible values and we
        //terminate if not.
        //
        assert(parent_height > 0);

        assert(parent_width > 0);

        //
        //Now we need to fetch the X windows ID for the child window.  This ID
        //is what is used to refer to windows within the X windows library.
        //
        auto child_window = Window_Get_X_Windows_ID(child);
        assert(child_window > 0);

        //
        //Now we need to fetch the X windows ID for the parent window.  This ID
        //is what is used to refer to windows within the X windows library.
        //
        auto parent_window = Window_Get_X_Windows_ID(parent);
        assert(parent_window > 0);

        //
        //Now we need to open the X windows display for the X server instance
        //that is running.  We could also use the DISPLAY environment variable
        //but the below is faster as it does not have to fetch the environment.
        //
        auto display = XOpenDisplay(XDisplayName(nullptr));
        assert(display);

        //
        //Now we need to calculate where to place the child window within the
        //parent window.  This depends on the size of the child as using the
        //centre of the parent can mean that the child window does not fit on
        //the display.
        //
        position_x = abs(
            std::min(
                parent_width / 2,
                (parent_width - child_width) / 2
            )
        );

        position_y = abs(
            std::min(
                parent_height / 2,
                (parent_height - child_height) / 2
            )
        );

        //
        //Now we need to ensure that the child window will be transient for the
        //parent window.
        //
        XSetTransientForHint(
            display,
            child_window,
            parent_window
        );

        //
        //Now we need to re-parent the child window to the parent window and we
        //set the (X, Y) co-ordinate such that the top left corner of the child
        //window will be in the centre of the parent window.
        //
        XReparentWindow(
            display,
            child_window,
            parent_window,
            position_x,
            position_y
        );

        //
        //Now we need to flush any changes made to the display.
        //
        XFlush(display);

        //
        //Finally, we need to close the display and thus release any resources
        //associated with it.
        //
        XCloseDisplay(display);
    }

What I see is that all of the decorations for the child window are removed but if I do not use the above code and simply display the dialog the decorations are rendered correctly. I would very much appreciate any hints on how to solve this problem.

Searched for XReparentWindow within stack overflow and found a possible solution (see link) that did not work for me.


Solution

  • From further research it appears that the reparent and set transient for hint routines will always remove the decorations. So to solve the problem I changed the above code to the below.

            int child_height;
    
            int child_width;
    
            int parent_display_x;
    
            int parent_display_y;
    
            int parent_height;
    
            int parent_width;
    
            int move_window_x;
    
            int move_window_y;
    
            int position_x;
    
            int position_y;
    
            Window temp_child;
    
            XWindowAttributes child_window_attributes;
    
            XWindowAttributes parent_window_attributes;
    
    
            //
            //First we must validate all parameters and terminate if any fail.
            //
            assert(parent);
    
            assert(child);
    
            //
            //Now we need to fetch the X windows ID for the child window.  This ID
            //is what is used to refer to windows within the X windows library.
            //
            auto child_window = Window_Get_X_Windows_ID(child);
            assert(child_window > 0);
    
            //
            //Now we need to fetch the X windows ID for the parent window.  This ID
            //is what is used to refer to windows within the X windows library.
            //
            auto parent_window = Window_Get_X_Windows_ID(parent);
            assert(parent_window > 0);
    
            //
            //Now we need to open the X windows display for the X server instance
            //that is running.  We could also use the DISPLAY environment variable
            //but the below is faster as it does not have to fetch the environment.
            //
            auto display = XOpenDisplay(XDisplayName(nullptr));
            assert(display);
    
            //
            //Now we need to fetch the root window for the display.  This allows us
            //to fetch the parent window co-ordinates relative to this window and
            //thus have an absolute screen position.
            //
            auto root_window = XDefaultRootWindow(display);
            assert(root_window > 0);
    
            //
            //Now that we have the child window, we can now fetch the attributes of
            //the window and thus be able to obtain such information as the height
            //and width.
            //
            XGetWindowAttributes(
                display,
                child_window,
                &child_window_attributes
            );
    
            //
            //Now we need to fetch the size of the child window so that we can then
            //re-parent the window at the centre of the parent window thus emulating
            //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
            //
            child_height = child_window_attributes.height;
    
            child_width = child_window_attributes.width;
    
            //
            //Now we need to ensure that the above returned sensible values and we
            //terminate if not.
            //
            assert(child_height > 0);
    
            assert(child_width > 0);
    
            //
            //Now that we have the parent window, we can now fetch the attributes of
            //the window and thus be able to obtain such information as the height
            //and width.
            //
            XGetWindowAttributes(
                display,
                parent_window,
                &parent_window_attributes
            );
    
            //
            //Now we need to fetch the size of the parent window so that we can then
            //re-parent the window at the centre of the parent window thus emulating
            //the GTKMM 3 set position call.  This has been deprecated in GTKMM 4.
            //
            parent_height = parent_window_attributes.height;
    
            parent_width = parent_window_attributes.width;
    
            //
            //Now we need to ensure that the above returned sensible values and we
            //terminate if not.
            //
            assert(parent_height > 0);
    
            assert(parent_width > 0);
    
            //
            //Now we need to calculate where to place the child window within the
            //parent window.  This depends on the size of the child as using the
            //centre of the parent can mean that the child window does not fit on
            //the display.
            //
            position_x =
                std::min(
                    parent_width / 2,
                    (parent_width - child_width) / 2
                );
    
            position_y =
                std::min(
                    parent_height / 2,
                    (parent_height - child_height) / 2
                );
    
            //
            //Now we need to ensure that the child window will be transient for the
            //parent window.
            //
            XSetTransientForHint(
                display,
                child_window,
                parent_window
            );
    
            //
            //Now we need to re-parent the child window to the parent window and we
            //set the (X, Y) co-ordinate such that the top left corner of the child
            //window will be in the centre of the parent window.
            //
            // XReparentWindow(
            //     display,
            //     child_window,
            //     parent_window,
            //     position_x,
            //     position_y
            // );
    
            //
            //Now we need to translate the co-ordinates so that we find the position
            //of the parent window with respect to the display's root window and
            //thus convert (0, 0) which is the top left of the parent window to the
            //actual screen co-ordinates.
            //
            XTranslateCoordinates(
                display,
                parent_window,
                root_window,
                0,
                0,
                &parent_display_x,
                &parent_display_y,
                &temp_child
            );
    
            //
            //Now we need to calculate the co-ordinates of where we wish to move the
            //child window to.
            //
            move_window_x =
                parent_display_x -
                parent_window_attributes.x +
                position_x;
    
            move_window_y =
                parent_display_y -
                parent_window_attributes.y +
                position_y;
    
            //
            //Now we must check the X co-ordinate and if it is negative (ie the child
            //width is more than the parent width) we will set it to zero.
            //
            if (move_window_x < 0)
            {
                move_window_x = 0;
            }
    
            //
            //Now we must check the Y co-ordinate and if it is negative (ie the child
            //height is more than the parent height) we will set it to zero.
            //
            if (move_window_y < 0)
            {
                move_window_y = 0;
            }
    
            //
            //Note: The above call to re-parent the child window to the parent window
            //causes the decorations on the child window to be removed and the child
            //is drawn as part of the parent.  What is required to emulate the GTKMM
            //3 function is for the window to be decorated and a separate and moveable
            //window.  So we will move the child window to the required location.  The
            //only issue is that we need to determine the position of the parent window
            //so that we can move the child relative to this.  This step is performed
            //above via the XTranslateCoordinates function.
            //
            XMoveWindow(
                display,
                child_window,
                move_window_x,
                move_window_y
            );
    
            //
            //Now we need to flush any changes made to the display.
            //
            XFlush(display);
    
            //
            //Finally, we need to close the display and thus release any resources
            //associated with it.
            //
            XCloseDisplay(display);
    

    So first you obtain your x window coordinates with respect to the root window, perform some calculations to centre the window on the parent and then move the window. Not exactly what I wanted as there is a flicker before the child window centres (due to the realising of the window in gtkmm) but at least I can emulate the obsolete call.