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);
}