Improves usability on Linux. No more double title bar#12600
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes the double title bar issue on Linux by suppressing the window manager's native title bar (via m_gdkDecor = 0) and implementing a custom WM-integrated resize border handler (GtkResizeBorderHandler) and drag/maximize interactions using native GTK APIs. It also removes a stray task.md developer-notes file.
Changes:
- Adds a
GtkResizeBorderHandlerwxEventFilter class to provide resize cursor andgtk_window_begin_resize_drag()-based window resizing for the borderless GTK window. - Implements GTK-specific window drag (
gtk_window_begin_move_drag) and maximize/unmaximize (gtk_window_maximize/gtk_window_unmaximize) in the topbar. - Removes the
task.mddeveloper scratch file from the repo.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
task.md |
Removes an accidentally committed developer scratch/notes file unrelated to the PR |
src/slic3r/GUI/MainFrame.hpp |
Declares the nested GtkResizeBorderHandler class and its pointer member for GTK builds |
src/slic3r/GUI/MainFrame.cpp |
Implements GtkResizeBorderHandler, sets m_gdkDecor = 0 to remove WM title bar, installs/cleans up the handler |
src/slic3r/GUI/BBLTopbar.cpp |
Adds GTK-native window move drag and maximize/restore in the topbar event handlers |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| GdkCursorType cursor_type; | ||
| switch (edge) { | ||
| case GDK_WINDOW_EDGE_NORTH: cursor_type = GDK_TOP_SIDE; break; | ||
| case GDK_WINDOW_EDGE_SOUTH: cursor_type = GDK_BOTTOM_SIDE; break; | ||
| case GDK_WINDOW_EDGE_WEST: cursor_type = GDK_LEFT_SIDE; break; | ||
| case GDK_WINDOW_EDGE_EAST: cursor_type = GDK_RIGHT_SIDE; break; | ||
| case GDK_WINDOW_EDGE_NORTH_WEST: cursor_type = GDK_TOP_LEFT_CORNER; break; | ||
| case GDK_WINDOW_EDGE_NORTH_EAST: cursor_type = GDK_TOP_RIGHT_CORNER; break; | ||
| case GDK_WINDOW_EDGE_SOUTH_WEST: cursor_type = GDK_BOTTOM_LEFT_CORNER; break; | ||
| case GDK_WINDOW_EDGE_SOUTH_EAST: cursor_type = GDK_BOTTOM_RIGHT_CORNER;break; | ||
| default: return; | ||
| } | ||
|
|
||
| GdkDisplay* display = gtk_widget_get_display(m_frame->m_widget); | ||
| GdkCursor* cursor = gdk_cursor_new_for_display(display, cursor_type); | ||
| gdk_window_set_cursor(gtk_widget_get_window(m_frame->m_widget), cursor); | ||
| g_object_unref(cursor); | ||
| m_cursor_set = true; | ||
| } | ||
|
|
||
| MainFrame* m_frame; | ||
| bool m_cursor_set{false}; |
There was a problem hiding this comment.
The set_cursor_for_edge() method is called unconditionally on every wxEVT_MOTION event when the cursor is near a border edge. It creates and destroys a new GdkCursor object each time via gdk_cursor_new_for_display() + g_object_unref(). Since mouse motion events can fire many times per second, this causes frequent GDK cursor object allocation and deallocation.
Additionally, gdk_cursor_new_for_display() with a GdkCursorType argument has been deprecated since GTK 3.16. The recommended replacement is gdk_cursor_new_from_name().
To fix the performance issue, track the last edge that was set (e.g., add a GdkWindowEdge m_last_edge member and a sentinel value like -1 or an std::optional<GdkWindowEdge>) and skip recreating the cursor when the edge hasn't changed. To fix the deprecation, use gdk_cursor_new_from_name() with CSS cursor names (e.g., "n-resize", "s-resize", "w-resize", "e-resize", "nw-resize", "ne-resize", "sw-resize", "se-resize").
| GdkCursorType cursor_type; | |
| switch (edge) { | |
| case GDK_WINDOW_EDGE_NORTH: cursor_type = GDK_TOP_SIDE; break; | |
| case GDK_WINDOW_EDGE_SOUTH: cursor_type = GDK_BOTTOM_SIDE; break; | |
| case GDK_WINDOW_EDGE_WEST: cursor_type = GDK_LEFT_SIDE; break; | |
| case GDK_WINDOW_EDGE_EAST: cursor_type = GDK_RIGHT_SIDE; break; | |
| case GDK_WINDOW_EDGE_NORTH_WEST: cursor_type = GDK_TOP_LEFT_CORNER; break; | |
| case GDK_WINDOW_EDGE_NORTH_EAST: cursor_type = GDK_TOP_RIGHT_CORNER; break; | |
| case GDK_WINDOW_EDGE_SOUTH_WEST: cursor_type = GDK_BOTTOM_LEFT_CORNER; break; | |
| case GDK_WINDOW_EDGE_SOUTH_EAST: cursor_type = GDK_BOTTOM_RIGHT_CORNER;break; | |
| default: return; | |
| } | |
| GdkDisplay* display = gtk_widget_get_display(m_frame->m_widget); | |
| GdkCursor* cursor = gdk_cursor_new_for_display(display, cursor_type); | |
| gdk_window_set_cursor(gtk_widget_get_window(m_frame->m_widget), cursor); | |
| g_object_unref(cursor); | |
| m_cursor_set = true; | |
| } | |
| MainFrame* m_frame; | |
| bool m_cursor_set{false}; | |
| // Avoid recreating the cursor if the edge (and thus cursor shape) has not changed. | |
| if (m_last_edge_valid && m_cursor_set && edge == m_last_edge) | |
| return; | |
| const char *cursor_name = nullptr; | |
| switch (edge) { | |
| case GDK_WINDOW_EDGE_NORTH: cursor_name = "n-resize"; break; | |
| case GDK_WINDOW_EDGE_SOUTH: cursor_name = "s-resize"; break; | |
| case GDK_WINDOW_EDGE_WEST: cursor_name = "w-resize"; break; | |
| case GDK_WINDOW_EDGE_EAST: cursor_name = "e-resize"; break; | |
| case GDK_WINDOW_EDGE_NORTH_WEST: cursor_name = "nw-resize"; break; | |
| case GDK_WINDOW_EDGE_NORTH_EAST: cursor_name = "ne-resize"; break; | |
| case GDK_WINDOW_EDGE_SOUTH_WEST: cursor_name = "sw-resize"; break; | |
| case GDK_WINDOW_EDGE_SOUTH_EAST: cursor_name = "se-resize"; break; | |
| default: | |
| return; | |
| } | |
| GdkDisplay* display = gtk_widget_get_display(m_frame->m_widget); | |
| GdkCursor* cursor = gdk_cursor_new_from_name(display, cursor_name); | |
| if (!cursor) | |
| return; | |
| gdk_window_set_cursor(gtk_widget_get_window(m_frame->m_widget), cursor); | |
| g_object_unref(cursor); | |
| m_cursor_set = true; | |
| m_last_edge = edge; | |
| m_last_edge_valid = true; | |
| } | |
| MainFrame* m_frame; | |
| bool m_cursor_set{false}; | |
| GdkWindowEdge m_last_edge; | |
| bool m_last_edge_valid{false}; |
Test Results0 tests 0 ✅ 0s ⏱️ Results for commit aa631b0. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (t == wxEVT_LEFT_DOWN) { | ||
| gtk_window_begin_resize_drag( | ||
| GTK_WINDOW(m_frame->m_widget), | ||
| edge, | ||
| 1, // left mouse button | ||
| mouse.x, mouse.y, | ||
| gtk_get_current_event_time()); | ||
| return Event_Processed; |
There was a problem hiding this comment.
The GtkResizeBorderHandler::FilterEvent intercepts all wxEVT_LEFT_DOWN events application-wide without checking whether the event's target window is the main frame or one of its direct children. If any widget (e.g., a popup menu, dropdown, or child panel) is positioned within the 8-pixel border zone at the frame's edge, a left-click on it will be intercepted and transformed into a resize drag operation instead, eating the click entirely (since Event_Processed is returned).
To fix this, the filter should check that the event's target window (event.GetEventObject()) is either the main frame itself or that no other top-level window is active/focused (e.g., using wxWindow::FindFocus() or checking wxGetTopLevelParent of the event object). Alternatively, set the cursor only on the root GDK window but only begin the drag when the event source is the main GDK window.
* Fix double title bar on Linux by removing WM decorations for GTK * wip * fix * finish * address review comment
…variables (#12602) * Linux: prefer discrete GPU on dual-GPU systems via PRIME environment variables Set DRI_PRIME=1 for AMD/nouveau PRIME setups and __NV_PRIME_RENDER_OFFLOAD/__GLX_VENDOR_LIBRARY_NAME for NVIDIA proprietary driver, only when the NVIDIA kernel module is detected. Uses replace=false to respect user overrides. * Improves usability on Linux. No more double title bar (#12600) * Fix double title bar on Linux by removing WM decorations for GTK * wip * fix * finish * address review comment
* Fix double title bar on Linux by removing WM decorations for GTK * wip * fix * finish * address review comment
…variables (OrcaSlicer#12602) * Linux: prefer discrete GPU on dual-GPU systems via PRIME environment variables Set DRI_PRIME=1 for AMD/nouveau PRIME setups and __NV_PRIME_RENDER_OFFLOAD/__GLX_VENDOR_LIBRARY_NAME for NVIDIA proprietary driver, only when the NVIDIA kernel module is detected. Uses replace=false to respect user overrides. * Improves usability on Linux. No more double title bar (OrcaSlicer#12600) * Fix double title bar on Linux by removing WM decorations for GTK * wip * fix * finish * address review comment
* Fix double title bar on Linux by removing WM decorations for GTK * wip * fix * finish * address review comment
…variables (OrcaSlicer#12602) * Linux: prefer discrete GPU on dual-GPU systems via PRIME environment variables Set DRI_PRIME=1 for AMD/nouveau PRIME setups and __NV_PRIME_RENDER_OFFLOAD/__GLX_VENDOR_LIBRARY_NAME for NVIDIA proprietary driver, only when the NVIDIA kernel module is detected. Uses replace=false to respect user overrides. * Improves usability on Linux. No more double title bar (OrcaSlicer#12600) * Fix double title bar on Linux by removing WM decorations for GTK * wip * fix * finish * address review comment

Description
This PR improves usability on Linux. No more double title bar on Linux now.
We now have the same sleek look on Linux as on Windows and macOS.
Screenshots/Recordings/Graphs
Before:

Now:

Tests