Gtkmm4 x11 zoom event problem when mouse not moved

There is a sample which can use opencascade viewer but have problem with zoom by mouse wheel, it work but only when I move mouse. Is it gtkmm problem?

//build: g++ -std=c++17 -g main.cpp -o app $(pkg-config --cflags --libs gtkmm-4.0 glibmm-2.68 epoxy librsvg-2.0 cairomm-1.0 gtk4-x11) -I./ -I/usr/include/glibmm-2.68 -I/usr/include/opencascade -I/usr/include/opencascade/Standard -L/usr/local/lib/x86_64-linux-gnu -lTKBinL -lTKBin -lTKBinTObj -lTKBinXCAF -lTKBool -lTKBO -lTKBRep -lTKCAF -lTKCDF -lTKDCAF -lTKDECascade -lTKDEGLTF -lTKDEIGES -lTKDEOBJ -lTKDEPLY -lTKDE -lTKDESTEP -lTKDESTL -lTKDEVRML -lTKDraw -lTKernel -lTKExpress -lTKFeat -lTKFillet -lTKG2d -lTKG3d -lTKGeomAlgo -lTKGeomBase -lTKHLR -lTKLCAF -lTKMath -lTKMesh -lTKMeshVS -lTKOffset -lTKOpenGl -lTKOpenGlTest -lTKPrim -lTKQADraw -lTKRWMesh -lTKService -lTKShHealing -lTKStdL -lTKStd -lTKTObjDRAW -lTKTObj -lTKTopAlgo -lTKTopTest -lTKV3d -lTKVCAF -lTKViewerTest -lTKXCAF -lTKXDEDRAW -lTKXMesh -lTKXmlL -lTKXml -lTKXmlTObj -lTKXmlXCAF -lTKXSBase -lTKXSDRAWDE -lTKXSDRAWGLTF -lTKXSDRAWIGES -lTKXSDRAWOBJ -lTKXSDRAWPLY -lTKXSDRAW -lTKXSDRAWSTEP -lTKXSDRAWSTL -lTKXSDRAWVRML -D_USE_MATH_DEFINES -lX11 -lGL
//run: GSK_RENDERER=cairo GDK_BACKEND=x11 ./app

#include <gtkmm.h>

#include
#include
#include

#include <gdkmm/display.h>
#include <gdkmm/surface.h>
#include <gdk/x11/gdkx.h>
#include <gdkmm/seat.h>
#include <gdkmm/device.h>
#include <glibmm/main.h>
#include <gdk/gdk.h>

// — 2. OPEN CASCADE —
#include <Xw_Window.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <AIS_Shape.hxx>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

// GTKMM
#include <sigc++/connection.h>

// X11/GLX
#include <X11/Xlib.h>
#include <GL/glx.h>
#include <gdk/gdk.h>

// OCCT
#include <OpenGl_GraphicDriver.hxx>
#include <V3d_Viewer.hxx>
#include <V3d_View.hxx>
#include <AIS_InteractiveContext.hxx>
#include <AIS_ViewController.hxx>
#include <AIS_ViewCube.hxx>
#include <AIS_AnimationCamera.hxx>
#include <Aspect_ScrollDelta.hxx>

#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <Quantity_Color.hxx>
#include <Prs3d_Drawer.hxx>
#include <Prs3d_ShadingAspect.hxx>
#include <Prs3d_LineAspect.hxx>
#include <Aspect_TypeOfLine.hxx>
#include <Aspect_GradientFillMethod.hxx>

class OcctGLCanvas : public Gtk::DrawingArea, public AIS_ViewController
{
public:
OcctGLCanvas();
virtual ~OcctGLCanvas() override;

protected:
void on_realize() override;
void on_unrealize() override;
void snapshot_vfunc(const Glib::RefPtrGtk::Snapshot& snapshot) override;
void on_resize(int width, int height);

void on_mouse_motion(double x, double y);
void on_mouse_enter(double x, double y);
void on_drag_begin(double x, double y);
void on_drag_update(double offset_x, double offset_y);
void on_drag_end(double offset_x, double offset_y);
bool on_scroll(double dx, double dy);
void on_scroll_begin();
void on_scroll_end();

void initPixelScaleRatio();
virtual void handleViewRedraw(const Handle(AIS_InteractiveContext) & ctx,
                              const Handle(V3d_View) & view) override;

Handle(OpenGl_GraphicDriver) m_driver;
Handle(V3d_Viewer) m_viewer;
Handle(V3d_View) m_view;
Handle(AIS_InteractiveContext) m_context;

Handle(AIS_ViewCube) m_view_cube;
Handle(AIS_AnimationCamera) myViewAnimation;

private:
bool m_view_initialized = false;

 Display* m_xdisplay = nullptr;
Window m_socket_xid = 0;
Window m_plug_window = 0;

Glib::RefPtr<Gtk::GestureDrag> m_drag_controller;
Glib::RefPtr<Gtk::EventControllerScroll> m_scroll_controller;
Glib::RefPtr<Gtk::EventControllerMotion> m_motion_controller;

double m_last_mouse_x = 0.0;
double m_last_mouse_y = 0.0;
double m_device_pixel_ratio = 1.0;
Aspect_VKeyMouse m_drag_button = Aspect_VKeyMouse_NONE;
double m_drag_start_x = 0.0;
double m_drag_start_y = 0.0;

};

static Aspect_VKeyMouse mouseButtonFromGtk(guint theButton)
{
switch (theButton)
{
case GDK_BUTTON_PRIMARY:
return Aspect_VKeyMouse_LeftButton;
case GDK_BUTTON_MIDDLE:
return Aspect_VKeyMouse_MiddleButton;
case GDK_BUTTON_SECONDARY:
return Aspect_VKeyMouse_RightButton;
}
return Aspect_VKeyMouse_NONE;
}

static Aspect_VKeyFlags mouseFlagsFromGtk(Gdk::ModifierType theFlags)
{
Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
if ((theFlags & Gdk::ModifierType::SHIFT_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_SHIFT; }
if ((theFlags & Gdk::ModifierType::CONTROL_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_CTRL; }
if ((theFlags & Gdk::ModifierType::META_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_META; }
if ((theFlags & Gdk::ModifierType::ALT_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_ALT; }
return aFlags;
}

OcctGLCanvas::OcctGLCanvas()
{
this->signal_realize().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_realize));
this->signal_unrealize().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_unrealize));
this->signal_resize().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_resize));

m_motion_controller = Gtk::EventControllerMotion::create();
m_motion_controller->signal_motion().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_mouse_motion));

m_motion_controller->signal_enter().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_mouse_enter));

this->add_controller(m_motion_controller);

m_drag_controller = Gtk::GestureDrag::create();
m_drag_controller->set_button(0);

m_drag_controller->signal_drag_begin().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_drag_begin));
m_drag_controller->signal_drag_update().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_drag_update));
m_drag_controller->signal_drag_end().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_drag_end));
this->add_controller(m_drag_controller);

m_scroll_controller = Gtk::EventControllerScroll::create();
m_scroll_controller->set_flags(
    Gtk::EventControllerScroll::Flags::VERTICAL |
    Gtk::EventControllerScroll::Flags::DISCRETE
);
m_scroll_controller->signal_scroll().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_scroll), false);
m_scroll_controller->signal_scroll_begin().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_scroll_begin));
m_scroll_controller->signal_scroll_end().connect(
    sigc::mem_fun(*this, &OcctGLCanvas::on_scroll_end));

this->add_controller(m_scroll_controller);

AIS_MouseGestureMap& aGestureMap = this->ChangeMouseGestureMap();
aGestureMap.Clear(Aspect_VKeyMouse_LeftButton);
aGestureMap.Clear(Aspect_VKeyMouse_MiddleButton);
aGestureMap.Clear(Aspect_VKeyMouse_RightButton);

aGestureMap.Bind(Aspect_VKeyMouse_LeftButton, AIS_MouseGesture_RotateOrbit);
aGestureMap.Bind(Aspect_VKeyMouse_MiddleButton, AIS_MouseGesture_Pan);
aGestureMap.Bind(Aspect_VKeyMouse_RightButton, AIS_MouseGesture_Zoom);
this->ChangeMouseSelectionSchemes().Bind(Aspect_VKeyMouse_RightButton, AIS_SelectionScheme_Replace);

}

OcctGLCanvas::~OcctGLCanvas()
{
}

void OcctGLCanvas::initPixelScaleRatio()
{
m_device_pixel_ratio = (float)this->get_scale_factor();
SetTouchToleranceScale (m_device_pixel_ratio);
if (!m_view.IsNull())
{ m_view->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * m_device_pixel_ratio + 0.5); }
if (!m_context.IsNull())
{ m_context->SetPixelTolerance (int(m_device_pixel_ratio * 6.0)); }

static const double THE_CUBE_SIZE = 60.0;
if (!m_view_cube.IsNull())
{
    m_view_cube->SetSize (m_device_pixel_ratio * THE_CUBE_SIZE, false);
    m_view_cube->SetBoxFacetExtension (m_view_cube->Size() * 0.15);
    m_view_cube->SetAxesPadding (m_view_cube->Size() * 0.10);
    m_view_cube->SetFontHeight (THE_CUBE_SIZE * 0.16);
    if (m_view_cube->HasInteractiveContext())
    { m_context->Redisplay (m_view_cube, false); }
}

}

void OcctGLCanvas::on_realize()
{
Gtk::Widget::on_realize();
try
{
auto gdk_surface = this->get_native()->get_surface();
if (!gdk_surface)
throw std::runtime_error(“Cannot get GdkSurface.”);

    GdkSurface *gdk_surface_c = gdk_surface->gobj();
    auto gdk_display = Gdk::Display::get_default();
    if (!gdk_display)
        throw std::runtime_error("Cannot get Gdk::Display.");
    GdkDisplay *gdk_display_c = gdk_display->gobj();

    m_xdisplay = gdk_x11_display_get_xdisplay(gdk_display_c);
    m_socket_xid = gdk_x11_surface_get_xid(gdk_surface_c);

    if (m_xdisplay == nullptr || m_socket_xid == 0)
    {
        throw std::runtime_error("Error getting X11 Display/Window ID. Is GDK_BACKEND=x11?");
    }

}
catch (const std::exception &e)
{
    std::cerr << "Error in on_realize (std::exception): " << e.what() << std::endl;
}

}

void OcctGLCanvas::on_unrealize()
{

if (m_plug_window != 0 && m_xdisplay != nullptr)
{
    XDestroyWindow(m_xdisplay, m_plug_window);
    XFlush(m_xdisplay);
    m_plug_window = 0;
}
if (!m_view.IsNull())
{
    m_view->Remove();
    m_view.Nullify();
}
if (!m_context.IsNull())
{
    m_context.Nullify();
}
m_view_initialized = false;
Gtk::Widget::on_unrealize();

}

void OcctGLCanvas::snapshot_vfunc(const Glib::RefPtrGtk::Snapshot &snapshot)
{
(void)snapshot;
}

void OcctGLCanvas::on_resize(int width, int height)
{
Gtk::Allocation alloc = get_allocation();
const int x_offset = alloc.get_x();
const int y_offset = alloc.get_y();

try
{
    if (!m_view_initialized)
    {
        if (m_socket_xid == 0)
        {
            return;
        }

        XWindowAttributes parent_attr;
        XGetWindowAttributes(m_xdisplay, m_socket_xid, &parent_attr);

        XSetWindowAttributes attr;
        attr.background_pixel = WhitePixel(m_xdisplay, DefaultScreen(m_xdisplay));

        m_plug_window = XCreateWindow(
            m_xdisplay,
            m_socket_xid,
            x_offset, y_offset,
            width, height,
            0,
            parent_attr.depth,
            InputOutput,
            parent_attr.visual,
            CWBackPixel,
            &attr);

        if (m_plug_window == 0)
            throw std::runtime_error("Error creating nested X11 window.");

        XMapWindow(m_xdisplay, m_plug_window);
        XFlush(m_xdisplay);

        Handle(Aspect_DisplayConnection) aDispConn;
        if (m_driver.IsNull()) {
            aDispConn = new Aspect_DisplayConnection(reinterpret_cast<Aspect_XDisplay*>(m_xdisplay));
            m_driver = new OpenGl_GraphicDriver(aDispConn, false);
        } else {
            aDispConn = m_driver->GetDisplayConnection();
        }

        m_viewer = new V3d_Viewer(m_driver);
        m_viewer->SetDefaultLights();
        m_viewer->SetLightOn();
        m_viewer->SetDefaultRenderingParams(Graphic3d_RenderingParams());

        m_view = m_viewer->CreateView();
        m_context = new AIS_InteractiveContext(m_viewer);

        Handle(Aspect_Window) aWindow = new Xw_Window(aDispConn, m_plug_window);
        m_view->SetWindow(aWindow);

        Quantity_Color aColorTop(Quantity_NOC_LIGHTSKYBLUE1);
        Quantity_Color aColorBottom(Quantity_NOC_GRAY70);
        m_view->SetBgGradientColors(aColorTop, aColorBottom, Aspect_GFM_VER);

        TopoDS_Shape myCube = BRepPrimAPI_MakeBox(20.0, 20.0, 20.0).Shape();
        Quantity_Color colors[] = {
            Quantity_Color(Quantity_NOC_RED), Quantity_Color(Quantity_NOC_GREEN),
            Quantity_Color(Quantity_NOC_BLUE), Quantity_Color(Quantity_NOC_YELLOW),
            Quantity_Color(Quantity_NOC_CYAN), Quantity_Color(Quantity_NOC_WHITE)
        };
        int i = 0;
        for (TopExp_Explorer face_explorer(myCube, TopAbs_FACE); face_explorer.More(); face_explorer.Next()) {
            if (i >= 6) break;
            TopoDS_Face aFace = TopoDS::Face(face_explorer.Current());
            Handle(AIS_Shape) aAisFace = new AIS_Shape(aFace);
            aAisFace->SetDisplayMode(AIS_Shaded);
            Handle(Prs3d_Drawer) aDrawer = aAisFace->Attributes();
            Handle(Prs3d_ShadingAspect) aShadingAspect = new Prs3d_ShadingAspect();
            aShadingAspect->SetColor(colors[i]);
            aDrawer->SetShadingAspect(aShadingAspect);
            Handle(Prs3d_LineAspect) aLineAspect =
                new Prs3d_LineAspect(Quantity_NOC_BLACK, Aspect_TOL_SOLID, 1.0);
            aDrawer->SetWireAspect(aLineAspect);
            aDrawer->SetWireDraw(true);
            m_context->Display(aAisFace, false);
            m_context->Activate(aAisFace, AIS_Shape::SelectionMode(TopAbs_FACE));
            i++;
        }

        m_view_cube = new AIS_ViewCube();
        m_view_cube->SetFixedAnimationLoop(false);
        m_view_cube->SetAutoStartAnimation(true);
        myViewAnimation = new AIS_AnimationCamera(TCollection_AsciiString("ViewAnim"), m_view);
        m_view_cube->SetViewAnimation(myViewAnimation);
        m_context->Display(m_view_cube, 0, 0, false);

        initPixelScaleRatio();
        m_view->FitAll();
        m_view_initialized = true;
        m_view->MustBeResized();
    }
    else
    {
        if (!m_view.IsNull() && m_plug_window != 0)
        {
            XMoveResizeWindow(m_xdisplay, m_plug_window, x_offset, y_offset, width, height);
            m_view->Window()->Size(width, height);
            m_view->MustBeResized();
        }
    }
}
catch (const Standard_Failure &e) {
    std::cerr << "Error in on_resize (OCCT): " << e.GetMessageString() << std::endl;
}
catch (const std::exception &e) {
    std::cerr << "Error in on_resize (std): " << e.what() << std::endl;
}

}

void OcctGLCanvas::on_mouse_motion(double x, double y)
{
m_last_mouse_x = x;
m_last_mouse_y = y;

if (m_drag_button != Aspect_VKeyMouse_NONE)
{
    return;
}
if (!m_view_initialized) return;

std::cerr << "===== GTK Event: on_mouse_motion (Hover) =====" << std::endl;

const Graphic3d_Vec2d aPos(x * m_device_pixel_ratio, y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));
auto aFlags = mouseFlagsFromGtk(m_motion_controller->get_current_event_state());

try {
    UpdateMousePosition(aPosI, PressedMouseButtons(), aFlags, false);
    FlushViewEvents(m_context, m_view, true);
} catch (...) {}

}

void OcctGLCanvas::on_mouse_enter(double x, double y)
{
std::cerr << “===== GTK Event: on_mouse_enter() na (” << x << ", " << y << “) =====” << std::endl;
m_last_mouse_x = x;
m_last_mouse_y = y;
}

void OcctGLCanvas::on_drag_begin(double x, double y)
{
std::cerr << “===== GTK Event: on_drag_begin() =====” << std::endl;
if (!m_view_initialized) return;

m_drag_start_x = x;
m_drag_start_y = y;

const Graphic3d_Vec2d aPos(x * m_device_pixel_ratio, y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));

const guint button = m_drag_controller->get_current_button();
m_drag_button = mouseButtonFromGtk(button);
if (m_drag_button == Aspect_VKeyMouse_NONE) return;

const auto aFlags = mouseFlagsFromGtk(m_drag_controller->get_current_event_state());
PressMouseButton(aPosI, m_drag_button, aFlags, false);
FlushViewEvents(m_context, m_view, true);

}

void OcctGLCanvas::on_drag_update(double offset_x, double offset_y)
{
std::cerr << “===== GTK Event: on_drag_update() =====” << std::endl;
if (!m_view_initialized || m_drag_button == Aspect_VKeyMouse_NONE) return;

double current_x = m_drag_start_x + offset_x;
double current_y = m_drag_start_y + offset_y;

const Graphic3d_Vec2d aPos(current_x * m_device_pixel_ratio, current_y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));
const auto aFlags = mouseFlagsFromGtk(m_drag_controller->get_current_event_state());

try {
    UpdateMousePosition(aPosI, m_drag_button, aFlags, false);
    FlushViewEvents(m_context, m_view, true);
} catch (const Standard_Failure& e) {
    std::cerr << "Exception in on_drag_update: " << e.GetMessageString() << std::endl;
}

}

void OcctGLCanvas::on_drag_end(double offset_x, double offset_y)
{
std::cerr << “===== GTK Event: on_drag_end() =====” << std::endl;
if (!m_view_initialized || m_drag_button == Aspect_VKeyMouse_NONE) return;

double current_x = m_drag_start_x + offset_x;
double current_y = m_drag_start_y + offset_y;

const Graphic3d_Vec2d aPos(current_x * m_device_pixel_ratio, current_y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));
const auto aFlags = mouseFlagsFromGtk(m_drag_controller->get_current_event_state());

try {
    ReleaseMouseButton(aPosI, m_drag_button, aFlags, false);
    FlushViewEvents(m_context, m_view, true);
} catch (const Standard_Failure& e) {
    std::cerr << "Exception v on_drag_end: " << e.GetMessageString() << std::endl;
}

m_drag_button = Aspect_VKeyMouse_NONE;

}

bool OcctGLCanvas::on_scroll(double dx, double dy)
{
(void)dx;
std::cerr << "===== GTK Event: on_scroll(dy = " << dy << “) =====” << std::endl;

if (!m_view_initialized || dy == 0.0)
{
    return false;
}

Gtk::Allocation alloc = get_allocation();
double current_x = (double)alloc.get_width() / 2.0;
double current_y = (double)alloc.get_height() / 2.0;

auto gdk_native = get_native();
auto surface = gdk_native ? gdk_native->get_surface() : nullptr;

bool position_found = false;
if (surface)
{
    auto display = get_display();
    auto seat = display ? display->get_default_seat() : Glib::RefPtr<Gdk::Seat>();

    if (seat)
    {
        auto device = seat->get_pointer();
        if (device)
        {
            double x_rel_surface, y_rel_surface;
            Gdk::ModifierType mask;

            if (surface->get_device_position(device, x_rel_surface, y_rel_surface, mask))
            {
                current_x = x_rel_surface;
                current_y = y_rel_surface;
                position_found = true;

                m_last_mouse_x = current_x;
                m_last_mouse_y = current_y;
            }
        }
    }
}

if (position_found) {
    std::cerr << "  -> (GTK) Get pos from Surface: (" << current_x << ", " << current_y << ")" << std::endl;
} else {
    std::cerr << "  -> (GTK) Pos undefined. Using middle of canvas: (" << current_x << ", " << current_y << ")" << std::endl;
}

const Graphic3d_Vec2d aPos(current_x * m_device_pixel_ratio, current_y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));

const auto aFlags = mouseFlagsFromGtk(m_scroll_controller->get_current_event_state());


double delta = -dy;

delta *= 20.0;

Aspect_ScrollDelta aScrollDelta(aPosI, delta, aFlags);

try {
    UpdateMouseScroll(aScrollDelta);

    FlushViewEvents(m_context, m_view, true);
    this->queue_draw();

} catch (const Standard_Failure& e) {
    std::cerr << "Exception v on_scroll: " << e.GetMessageString() << std::endl;
}

return true;

}

void OcctGLCanvas::on_scroll_begin()
{
std::cerr << “===== GTK Event: on_scroll_begin() =====” << std::endl;
}

void OcctGLCanvas::on_scroll_end()
{
std::cerr << “===== GTK Event: on_scroll_end() =====” << std::endl;
}

void OcctGLCanvas::handleViewRedraw(const Handle(AIS_InteractiveContext)& ctx,
const Handle(V3d_View)& view)
{
std::cerr << " OCCT Callback: handleViewRedraw()";

const bool needs_redraw = myToAskNextFrame;

AIS_ViewController::handleViewRedraw(ctx, view);

if (needs_redraw)
{
    std::cerr << " -> Force queue_draw() (needs_redraw=true)" << std::endl;
    this->queue_draw();
}
else
{
    std::cerr << " -> NIC (needs_redraw=false)" << std::endl;
}

}

class MyWindow : public Gtk::Window
{
public:
MyWindow();
virtual ~MyWindow();
protected:
Gtk::Box m_main_box;
Gtk::Label m_info_label;

OcctGLCanvas m_canvas;

};

MyWindow::MyWindow()
: m_main_box(Gtk::Orientation::VERTICAL, 5)
{
set_title(“OpenCASCADE GTKmm Plug & Socket Viewer”);
set_default_size(800, 600);

m_info_label.set_text("OpenCASCADE Viewer (GTKmm 4 + OCCT)");
m_info_label.set_expand(false);
    m_info_label.set_margin_top(5);
m_info_label.set_margin_bottom(5);
m_info_label.set_margin_start(10);
m_info_label.set_margin_end(10);

m_canvas.set_expand(true);

m_main_box.append(m_info_label);
m_main_box.append(m_canvas);

set_child(m_main_box);

}

MyWindow::~MyWindow() {}

int main(int argc, char* argv)
{
auto app = Gtk::Application::create(“org.gtkmm.example.occt”);
return app->make_window_and_run(argc, argv);
}

Now I found litle bit different much better solution now I think that I have itegrated Opencascade viewer object in gtkmm4 widget on Wayland, only i need open temporary x11 window for Opencascade initialization and after draw exported images to Gtk::DrawingArea by V3d_View::ToPixMap() it work fine for me now but need testing.

// occtglcanvas.h
#pragma once

#include <gtkmm/drawingarea.h>
#include <gtkmm/snapshot.h>
#include <sigc++/connection.h>
#include <gtkmm/eventcontrollermotion.h>
#include <gtkmm/eventcontrollerscroll.h>
#include <gtkmm/gestureclick.h>
#include <gdk/gdk.h>
#include <X11/Xlib.h>
#include <GL/glx.h>

#include <OpenGl_GraphicDriver.hxx>
#include <V3d_Viewer.hxx>
#include <V3d_View.hxx>
#include <AIS_InteractiveContext.hxx>
#include <AIS_ViewController.hxx>
#include <AIS_ViewCube.hxx>
#include <AIS_Shape.hxx>
#include <Image_PixMap.hxx>

class OcctGLCanvas : public Gtk::DrawingArea, public AIS_ViewController
{
public:
OcctGLCanvas();
virtual ~OcctGLCanvas();

void toggleDisplayMode();

protected:
void on_realize() override;
void on_unrealize() override;
void on_resize(int width, int height) override;
void snapshot_vfunc(const Glib::RefPtrGtk::Snapshot& snapshot) override;
bool on_idle();

void on_mouse_motion(double x, double y);
void on_button_pressed(int n_press, double x, double y);
void on_button_released(int n_press, double x, double y);
bool on_scroll(double dx, double dy);

void handleViewRedraw(const Handle(AIS_InteractiveContext)& ctx,
                      const Handle(V3d_View)& view) override;

void initPixelScaleRatio();

Handle(OpenGl_GraphicDriver) m_driver;
Handle(V3d_Viewer) m_viewer;
Handle(V3d_View) m_view;
Handle(AIS_InteractiveContext) m_context;
Handle(AIS_ViewCube) m_view_cube;
Handle(AIS_Shape) m_ais_box;

private:
bool m_view_initialized = false;
float m_device_pixel_ratio = 1.0f;
sigc::connection m_idle_slot;

Display* m_xdisplay = nullptr;
Window m_unmapped_window = 0;

Glib::RefPtr<Gtk::EventControllerMotion> m_motion_controller;
Glib::RefPtr<Gtk::GestureClick> m_click_controller;
Glib::RefPtr<Gtk::EventControllerScroll> m_scroll_controller;

double m_last_mouse_x = 0.0;
double m_last_mouse_y = 0.0;

Image_PixMap m_pixmap_buffer;

};

// occtglcanvas.cpp

// 1. Standard C++
#include
#include
#include

// 2. Main Header
#include “occtglcanvas.h”

// 3. GTKMM / GDKMM / GLIB
#include <gtkmm/native.h>
#include <gdkmm/display.h>
#include <gdkmm/surface.h>
#include <gdk/x11/gdkx.h>
#include <glibmm/main.h>
#include <gdk/gdk.h>
#include <gtkmm.h>
#include <gdkmm/pixbuf.h>
#include <gdkmm/texture.h>
#include <graphene.h>

// 4. OpenCASCADE (OCCT)
#include <Standard_Failure.hxx>
#include <Aspect_DisplayConnection.hxx>
#include <Xw_Window.hxx>
// #include <BRepPrimAPI_MakeBox.hxx> ← REMOVED (Unused)
#include <AIS_Shape.hxx>
#include <AIS_DisplayMode.hxx>
#include <Graphic3d_RenderingParams.hxx>
#include <Aspect_GradientFillMethod.hxx>
#include <AIS_AnimationCamera.hxx>
#include <BRepPrimAPI_MakeCone.hxx>
#include <TopExp.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <BRep_Tool.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Line.hxx>
#include <PrsDim_DiameterDimension.hxx>
#include <PrsDim_LengthDimension.hxx>
#include <PrsDim_Dimension.hxx>
#include <TopoDS.hxx>
#include <gp_Pln.hxx>

// 5. X11 Libraries
#include <X11/Xlib.h>
#include <X11/Xutil.h>

// — HELPER FUNCTIONS —

static Aspect_VKeyMouse mouseButtonFromGtk (guint theButton)
{
switch (theButton)
{
case GDK_BUTTON_PRIMARY: return Aspect_VKeyMouse_LeftButton;
case GDK_BUTTON_MIDDLE: return Aspect_VKeyMouse_MiddleButton;
case GDK_BUTTON_SECONDARY: return Aspect_VKeyMouse_RightButton;
}
return Aspect_VKeyMouse_NONE;
}

static Aspect_VKeyFlags mouseFlagsFromGtk (Gdk::ModifierType theFlags)
{
Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
if ((theFlags & Gdk::ModifierType::SHIFT_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_SHIFT; }
if ((theFlags & Gdk::ModifierType::CONTROL_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_CTRL; }
if ((theFlags & Gdk::ModifierType::META_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_META; }
if ((theFlags & Gdk::ModifierType::ALT_MASK) != Gdk::ModifierType{})
{ aFlags |= Aspect_VKeyFlags_ALT; }
return aFlags;
}

// — CONSTRUCTOR —

OcctGLCanvas::OcctGLCanvas()
{
// Lifecycle
signal_realize().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_realize));
signal_unrealize().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_unrealize));

// Idle loop
m_idle_slot = Glib::signal_idle().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_idle));

// Input Controllers
m_motion_controller = Gtk::EventControllerMotion::create();
m_motion_controller->signal_motion().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_mouse_motion));
add_controller(m_motion_controller);

m_click_controller = Gtk::GestureClick::create();
m_click_controller->set_button(0);
m_click_controller->signal_pressed().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_button_pressed));
m_click_controller->signal_released().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_button_released));
add_controller(m_click_controller);

m_scroll_controller = Gtk::EventControllerScroll::create();
m_scroll_controller->set_flags(Gtk::EventControllerScroll::Flags::VERTICAL);
m_scroll_controller->signal_scroll().connect(sigc::mem_fun(*this, &OcctGLCanvas::on_scroll), false);
add_controller(m_scroll_controller);

}

OcctGLCanvas::~OcctGLCanvas() {}

void OcctGLCanvas::initPixelScaleRatio()
{
m_device_pixel_ratio = (float)this->get_scale_factor();
SetTouchToleranceScale (m_device_pixel_ratio);

if (!m_view.IsNull())
{
    m_view->ChangeRenderingParams().Resolution = (unsigned int )(96.0 * m_device_pixel_ratio + 0.5);
}
if (!m_context.IsNull())
{
    m_context->SetPixelTolerance (int(m_device_pixel_ratio * 6.0));
}

static const double THE_CUBE_SIZE = 60.0;
if (!m_view_cube.IsNull())
{
    m_view_cube->SetSize (m_device_pixel_ratio * THE_CUBE_SIZE, false);
    m_view_cube->SetBoxFacetExtension (m_view_cube->Size() * 0.15);
    m_view_cube->SetAxesPadding (m_view_cube->Size() * 0.10);
    m_view_cube->SetFontHeight (THE_CUBE_SIZE * 0.16);
    if (m_view_cube->HasInteractiveContext())
    { m_context->Redisplay (m_view_cube, false); }
}

}

// — LIFECYCLE —

void OcctGLCanvas::on_realize()
{
Gtk::Widget::on_realize();
try
{
// 1. Get X11 Display
auto gdk_display = Gdk::Display::get_default();
if (!gdk_display) throw std::runtime_error(“No Gdk::Display”);

    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    m_xdisplay = gdk_x11_display_get_xdisplay(gdk_display->gobj());
    #pragma GCC diagnostic pop

    if (!m_xdisplay) throw std::runtime_error("No X Display");

    // 2. Create Hidden X11 Window
    m_unmapped_window = XCreateWindow(
        m_xdisplay, DefaultRootWindow(m_xdisplay),
        0, 0, 1, 1, 0,
        DefaultDepth(m_xdisplay, DefaultScreen(m_xdisplay)),
        InputOutput,
        DefaultVisual(m_xdisplay, DefaultScreen(m_xdisplay)),
        0, NULL);

    if (!m_unmapped_window) throw std::runtime_error("XCreateWindow failed");
    XFlush(m_xdisplay); // Sync

    // 3. Init OCCT
    Handle(Aspect_DisplayConnection) aDispConn = new Aspect_DisplayConnection(reinterpret_cast<Aspect_XDisplay*>(m_xdisplay));
    m_driver = new OpenGl_GraphicDriver(aDispConn, false);

    m_viewer = new V3d_Viewer(m_driver);
    m_viewer->SetDefaultLights();
    m_viewer->SetLightOn();
    m_viewer->SetDefaultRenderingParams(Graphic3d_RenderingParams());

    m_view = m_viewer->CreateView();
    m_context = new AIS_InteractiveContext(m_viewer);

    Handle(Aspect_Window) aWindow = new Xw_Window(aDispConn, m_unmapped_window);
    m_view->SetWindow(aWindow);

    Quantity_Color aColorTop(Quantity_NOC_LIGHTSKYBLUE1);
    Quantity_Color aColorBottom(Quantity_NOC_GRAY70);
    m_view->SetBgGradientColors(aColorTop, aColorBottom, Aspect_GFM_VER);

    // 4. Content (Cone + Dimensions)
    TopoDS_Shape aShape = BRepPrimAPI_MakeCone (100, 10, 100).Solid();
    m_ais_box = new AIS_Shape (aShape);
    m_context->Display (m_ais_box, AIS_Shaded, -1, false);

    // Dimensioning
    TopTools_IndexedMapOfShape anEdges;
    TopExp::MapShapes (aShape, TopAbs_EDGE, anEdges);

    for (TopTools_IndexedMapOfShape::Iterator anEdgeIter (anEdges); anEdgeIter.More(); anEdgeIter.Next())
    {
      const TopoDS_Edge& anEdge = TopoDS::Edge (anEdgeIter.Value());
      Standard_Real aParRange[2] = {};
      Handle(Geom_Curve) aCurve = BRep_Tool::Curve (anEdge, aParRange[0], aParRange[1]);

      if (Handle(Geom_Circle) aCircle = Handle(Geom_Circle)::DownCast (aCurve))
      {
        Handle(PrsDim_DiameterDimension) aDiamDim = new PrsDim_DiameterDimension (anEdge);
        aDiamDim->SetFlyout (aCircle->Radius() + 20.0);
        m_context->Display (aDiamDim, 0, -1, false);
      }
      else if (Handle(Geom_Line) aLine = Handle(Geom_Line)::DownCast (aCurve))
      {
        gp_Pln aPln (aLine->Value (aParRange[0]), gp::DY());
        Handle(PrsDim_LengthDimension) aLenDim = new PrsDim_LengthDimension (anEdge, aPln);
        aLenDim->SetFlyout (20.0);
        m_context->Display (aLenDim, 0, -1, false);
      }
    }

    // 5. ViewCube
    m_view_cube = new AIS_ViewCube();
    m_view_cube->SetFixedAnimationLoop(false);
    m_view_cube->SetAutoStartAnimation(true);
    Handle(AIS_AnimationCamera) myViewAnimation = new AIS_AnimationCamera(TCollection_AsciiString("ViewAnim"), m_view);
    m_view_cube->SetViewAnimation(myViewAnimation);
    m_context->Display(m_view_cube, 0, 0, false);

    initPixelScaleRatio();
    m_view->FitAll();
    m_view_initialized = true;

    // Trigger initial resize
    on_resize(get_width(), get_height());
}
catch (const std::exception& e) {
    std::cerr << "Error in on_realize: " << e.what() << std::endl;
}

}

void OcctGLCanvas::on_unrealize()
{
if (m_idle_slot.connected()) m_idle_slot.disconnect();

if (!m_view.IsNull()) { m_view->Remove(); m_view.Nullify(); }
if (!m_context.IsNull()) { m_context.Nullify(); }

if (m_unmapped_window != 0 && m_xdisplay != nullptr)
{
    XDestroyWindow(m_xdisplay, m_unmapped_window);
    XFlush(m_xdisplay);
    m_unmapped_window = 0;
}

m_view_initialized = false;
Gtk::Widget::on_unrealize();

}

// — RENDERING —

void OcctGLCanvas::on_resize(int width, int height)
{
Gtk::DrawingArea::on_resize(width, height);

if (m_view_initialized && !m_view.IsNull())
{
    const int w_px = width * m_device_pixel_ratio;
    const int h_px = height * m_device_pixel_ratio;

    if (w_px > 0 && h_px > 0)
    {
        XResizeWindow(m_xdisplay, m_unmapped_window, w_px, h_px);
        m_view->MustBeResized();
        m_view->Redraw();
        queue_draw();
    }
}

}

void OcctGLCanvas::snapshot_vfunc(const Glib::RefPtrGtk::Snapshot& snapshot)
{
if (!m_view_initialized || m_view.IsNull()) return;

const int w = get_width();
const int h = get_height();
const int w_px = w * m_device_pixel_ratio;
const int h_px = h * m_device_pixel_ratio;

if (w_px <= 0 || h_px <= 0) return;

// Sync Size logic
static int s_last_w = 0;
static int s_last_h = 0;

if (w_px != s_last_w || h_px != s_last_h)
{
    if (m_unmapped_window != 0) XResizeWindow(m_xdisplay, m_unmapped_window, w_px, h_px);
    m_view->MustBeResized();
    s_last_w = w_px;
    s_last_h = h_px;
}

// FBO Rendering
if (m_pixmap_buffer.SizeX() != (size_t)w_px || m_pixmap_buffer.SizeY() != (size_t)h_px)
{
    m_pixmap_buffer.InitZero(Image_Format_RGBA, w_px, h_px);
}

V3d_ImageDumpOptions aDumpOptions;
aDumpOptions.BufferType = Graphic3d_BT_RGBA;
aDumpOptions.Width = w_px;
aDumpOptions.Height = h_px;

if (!m_view->ToPixMap(m_pixmap_buffer, aDumpOptions)) return;

Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_data(
    m_pixmap_buffer.Data(), Gdk::Colorspace::RGB, true, 8, w_px, h_px, m_pixmap_buffer.Width() * 4
);

Glib::RefPtr<Gdk::Texture> texture = Gdk::Texture::create_for_pixbuf(pixbuf);

// Draw (Flip Y)
snapshot->save();
snapshot->translate(Gdk::Graphene::Point(0.0f, (float)h));
snapshot->scale(1.0f, -1.0f);
snapshot->append_texture(texture, Gdk::Rectangle(0, 0, w, h));
snapshot->restore();

}

bool OcctGLCanvas::on_idle()
{
if (m_view_initialized && !m_view.IsNull())
{
try {
m_view->InvalidateImmediate();
FlushViewEvents(m_context, m_view, true);
queue_draw();
} catch (…) {}
}
return true;
}

// — INPUT —

void OcctGLCanvas::on_mouse_motion(double x, double y)
{
if (!m_view_initialized) return;
m_last_mouse_x = x;
m_last_mouse_y = y;

const Graphic3d_Vec2d aPos(x * m_device_pixel_ratio, y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));
auto aFlags = mouseFlagsFromGtk(m_motion_controller->get_current_event_state());

UpdateMousePosition(aPosI, PressedMouseButtons(), aFlags, false);

}

void OcctGLCanvas::on_button_pressed(int n_press, double x, double y)
{
(void)n_press;
if (!m_view_initialized) return;

const Graphic3d_Vec2d aPos(x * m_device_pixel_ratio, y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));

auto aButton = mouseButtonFromGtk(m_click_controller->get_current_button());
auto aFlags = mouseFlagsFromGtk(m_click_controller->get_current_event_state());

if (aButton != Aspect_VKeyMouse_NONE)
{
    UpdateMousePosition(aPosI, PressedMouseButtons(), aFlags, false);
    PressMouseButton(aPosI, aButton, aFlags, false);
}

}

void OcctGLCanvas::on_button_released(int n_press, double x, double y)
{
(void)n_press;
if (!m_view_initialized) return;

const Graphic3d_Vec2d aPos(x * m_device_pixel_ratio, y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));

auto aButton = mouseButtonFromGtk(m_click_controller->get_current_button());
auto aFlags = mouseFlagsFromGtk(m_click_controller->get_current_event_state());

if (aButton != Aspect_VKeyMouse_NONE)
{
    ReleaseMouseButton(aPosI, aButton, aFlags, false);
}

}

bool OcctGLCanvas::on_scroll(double dx, double dy)
{
(void)dx;
if (!m_view_initialized) return false;

const Graphic3d_Vec2d aPos(m_last_mouse_x * m_device_pixel_ratio, m_last_mouse_y * m_device_pixel_ratio);
const Graphic3d_Vec2i aPosI(aPos + Graphic3d_Vec2d(0.5));

UpdateMouseScroll(Aspect_ScrollDelta(aPosI, -dy));
return true;

}

void OcctGLCanvas::handleViewRedraw(const Handle(AIS_InteractiveContext)& ctx, const Handle(V3d_View)& view)
{
AIS_ViewController::handleViewRedraw(ctx, view);
if (myToAskNextFrame) queue_draw();
}

void OcctGLCanvas::toggleDisplayMode()
{
if (m_context.IsNull() || m_ais_box.IsNull()) return;

int newMode = (m_ais_box->DisplayMode() == AIS_WireFrame) ? AIS_Shaded : AIS_WireFrame;
m_context->SetDisplayMode(m_ais_box, newMode, true);
queue_draw();

}

// mywindow.h
#pragma once

// Include GTKMM headers first to avoid macro conflicts
#include <gtkmm.h>
#include “occtglcanvas.h” // Then include our custom OCCT canvas

// Necessary headers for the specific widgets used in this window
#include <gtkmm/box.h>
#include <gtkmm/button.h>

class MyWindow : public Gtk::Window
{
public:
MyWindow();
virtual ~MyWindow();

protected:
// Signal handler for the button click
void on_toggle_clicked();

// UI Components:
// We use a Gtk::Box for a simple layout (Vertical or Horizontal stacking)
Gtk::Box m_main_box;

// Our custom OpenGL rendering widget acting as the CAD viewport
OcctGLCanvas m_canvas;

// A standard GTK button to interact with the 3D scene
Gtk::Button m_toggle_button;

};

// mywindow.cpp
#include “mywindow.h”

MyWindow::MyWindow()
: m_main_box(Gtk::Orientation::VERTICAL, 1),
m_toggle_button(“Toggle Display Mode”)
{
set_title(“OpenCASCADE with Gtk::Box”);
set_default_size(800, 600);
m_main_box.set_margin(0);

set_child(m_main_box);

m_toggle_button.signal_clicked().connect(
    sigc::mem_fun(*this, &MyWindow::on_toggle_clicked));

// Add widgets
m_main_box.append(m_toggle_button);

m_canvas.set_expand(true);
m_main_box.append(m_canvas);

}

MyWindow::~MyWindow() {}

void MyWindow::on_toggle_clicked()
{
m_canvas.toggleDisplayMode();
}

// main.cpp
//
// Build command (Linux/GCC):
// g++ -std=c++17 -g main.cpp mywindow.cpp occtglcanvas.cpp -o app
// $(pkg-config --cflags --libs gtkmm-4.0 epoxy librsvg-2.0 gtk4-x11)
// -I./ -I/usr/include/opencascade -I/usr/include/opencascade/Standard
// -L/usr/local/lib/x86_64-linux-gnu
// -lTKBinL -lTKBin -lTKBinTObj -lTKBinXCAF -lTKBool -lTKBO -lTKBRep -lTKCAF
// -lTKCDF -lTKDCAF -lTKDECascade -lTKDEGLTF -lTKDEIGES -lTKDEOBJ -lTKDEPLY
// -lTKDE -lTKDESTEP -lTKDESTL -lTKDEVRML -lTKDraw -lTKernel -lTKExpress -lTKFeat
// -lTKFillet -lTKG2d -lTKG3d -lTKGeomAlgo -lTKGeomBase -lTKHLR -lTKLCAF -lTKMath
// -lTKMesh -lTKMeshVS -lTKOffset -lTKOpenGl -lTKOpenGlTest -lTKPrim -lTKQADraw
// -lTKRWMesh -lTKService -lTKShHealing -lTKStdL -lTKStd -lTKTObjDRAW -lTKTObj
// -lTKTopAlgo -lTKTopTest -lTKV3d -lTKVCAF -lTKViewerTest -lTKXCAF -lTKXDEDRAW
// -lTKXMesh -lTKXmlL -lTKXml -lTKXmlTObj -lTKXmlXCAF -lTKXSBase -lTKXSDRAWDE
// -lTKXSDRAWGLTF -lTKXSDRAWIGES -lTKXSDRAWOBJ -lTKXSDRAWPLY -lTKXSDRAW
// -lTKXSDRAWSTEP -lTKXSDRAWSTL -lTKXSDRAWVRML
// -D_USE_MATH_DEFINES -lX11 -lGL

#include <gtkmm/application.h>
#include “mywindow.h”
#include // Required for setenv

int main(int argc, char* argv)
{
// — Environment Configuration —

// Force GTK to use the 'cairo' renderer.
// GTK4 defaults to 'gl' (OpenGL) renderer, but since we are manually managing
// the OpenGL context for OpenCASCADE and copying buffers, using the Cairo backend
// for the UI elements often prevents context conflicts and artifacts.
setenv("GSK_RENDERER", "cairo", 1);

// Force GDK to use the X11 backend.
// OpenCASCADE's standard visualization (V3d_View) relies heavily on X11 Window handles (WindowID)
// on Linux. While GTK4 supports Wayland, OCCT's low-level integration here requires X11.
setenv("GDK_BACKEND", "x11", 1);

// --- End Configuration ---

auto app = Gtk::Application::create("org.gtkmm.example.occt");

// Create the window and start the main event loop
return app->make_window_and_run<MyWindow>(argc, argv);

}