KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wxgtk/ui.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 Ian McInerney <Ian.S.McInerney at ieee.org>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <kiplatform/ui.h>
22
23#include <wx/choice.h>
24#include <wx/dataview.h>
25#include <wx/dialog.h>
26#include <wx/nonownedwnd.h>
27#include <wx/settings.h>
28#include <wx/window.h>
29#include <wx/log.h>
30
31#include <gtk/gtk.h>
32#include <gdk/gdk.h>
33
34#ifdef GDK_WINDOWING_X11
35#include <gdk/gdkx.h>
36#include <X11/Xutil.h>
37#endif
38
39#ifdef GDK_WINDOWING_WAYLAND
40#include <gdk/gdkwayland.h>
41#endif
42
43#ifdef KICAD_WAYLAND
45#endif
46
47// Set WXTRACE=KICAD_WAYLAND to see logs
48const wxString traceWayland = wxS( "KICAD_WAYLAND" );
49
50
52{
53 wxColour bg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
54
55 // Weighted W3C formula
56 double brightness = ( bg.Red() / 255.0 ) * 0.299 +
57 ( bg.Green() / 255.0 ) * 0.587 +
58 ( bg.Blue() / 255.0 ) * 0.117;
59
60 return brightness < 0.5;
61}
62
63
65{
66 return wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
67}
68
69
70void KIPLATFORM::UI::GetInfoBarColours( wxColour& aFgColour, wxColour& aBgColour )
71{
72 // The GTK3.24 way of getting the colours is to use the style context
73 // Earlier GTKs should be able to use the system settings
74#if( GTK_CHECK_VERSION( 3, 24, 0 ) )
75 GdkRGBA* rgba;
76 GtkWidgetPath* path = gtk_widget_path_new();
77 GtkStyleContext* sc = gtk_style_context_new();
78
79 gtk_widget_path_append_type( path, GTK_TYPE_WINDOW );
80 gtk_widget_path_iter_set_object_name( path, -1, "infobar" );
81 gtk_widget_path_iter_add_class( path, -1, "info" );
82
83 gtk_style_context_set_path( sc, path );
84 gtk_style_context_set_state( sc, GTK_STATE_FLAG_NORMAL );
85
86 gtk_style_context_get( sc, GTK_STATE_FLAG_NORMAL, "background-color", &rgba, NULL );
87 aBgColour = wxColour( *rgba );
88 gdk_rgba_free( rgba );
89
90 gtk_style_context_get( sc, GTK_STATE_FLAG_NORMAL, "color", &rgba, NULL );
91 aFgColour = wxColour( *rgba );
92 gdk_rgba_free( rgba );
93
94 // Some GTK themes use the plain infobar style, but if they don't, the background alpha
95 // is generally 0. In this case, try the revealer and box as these are used for Adwaita
96 // and other themes.
97 if( aBgColour.Alpha() == 0 )
98 {
99 gtk_widget_path_append_type( path, G_TYPE_NONE );
100 gtk_widget_path_iter_set_object_name( path, -1, "revealer" );
101 gtk_widget_path_append_type( path, G_TYPE_NONE );
102 gtk_widget_path_iter_set_object_name( path, -1, "box" );
103
104 gtk_style_context_set_path( sc, path );
105 gtk_style_context_set_state( sc, GTK_STATE_FLAG_NORMAL );
106
107 gtk_style_context_get( sc, GTK_STATE_FLAG_NORMAL, "background-color", &rgba, NULL );
108 aBgColour = wxColour( *rgba );
109 gdk_rgba_free( rgba );
110
111 gtk_style_context_get( sc, GTK_STATE_FLAG_NORMAL, "color", &rgba, NULL );
112 aFgColour = wxColour( *rgba );
113 gdk_rgba_free( rgba );
114 }
115
116 gtk_widget_path_free( path );
117 g_object_unref( sc );
118
119#else
120 aBgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_INFOBK );
121 aFgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_INFOTEXT );
122#endif
123
124}
125
126
127void KIPLATFORM::UI::ForceFocus( wxWindow* aWindow )
128{
129 aWindow->SetFocus();
130
131 // On GTK, SetFocus() on a dialog may not be sufficient to receive keyboard events
132 // (like ESC for closing). We need to ensure the window is presented and active.
133 // Find the top-level window and present it to ensure keyboard focus.
134 wxWindow* tlw = aWindow;
135
136 while( tlw && !tlw->IsTopLevel() )
137 tlw = tlw->GetParent();
138
139 if( tlw )
140 {
141 GtkWidget* widget = static_cast<GtkWidget*>( tlw->GetHandle() );
142
143 if( widget && GTK_IS_WINDOW( widget ) )
144 gtk_window_present( GTK_WINDOW( widget ) );
145 }
146}
147
148
149bool KIPLATFORM::UI::IsWindowActive( wxWindow* aWindow )
150{
151 if( !aWindow )
152 return false;
153
154 GtkWindow* window = GTK_WINDOW( aWindow->GetHandle() );
155
156 if( window )
157 return gtk_window_is_active( window );
158
159 // We shouldn't really ever reach this point
160 return false;
161}
162
163
164void KIPLATFORM::UI::EnsureVisible( wxWindow* aWindow )
165{
166 // Not needed on this platform
167}
168
169
171{
172 if( !aWindow )
173 return;
174
175 GtkWidget* widget = static_cast<GtkWidget*>( aWindow->GetHandle() );
176
177 if( widget && GTK_IS_WINDOW( widget ) )
178 gtk_window_set_gravity( GTK_WINDOW( widget ), GDK_GRAVITY_STATIC );
179}
180
181
182static const char* const WM_CLASS_DATA_KEY = "kicad-wm-class";
183
184
185// Re-assert our application id on the widget's window. Bound to "realize" and "map" because GTK
186// writes WM_CLASS from the program class when it realizes the window, so ours must be applied
187// afterwards to survive; the Wayland application id additionally requires a mapped surface. The
188// id is read from the widget's object data so its lifetime is not tied to the signal closures.
189static void setWindowClassHint( GtkWidget* aWidget, gpointer )
190{
191 const char* id = static_cast<const char*>( g_object_get_data( G_OBJECT( aWidget ),
193
194 if( !id || !*id )
195 return;
196
197 GdkWindow* window = gtk_widget_get_window( aWidget );
198
199 if( !window )
200 return;
201
202#ifdef GDK_WINDOWING_X11
203 if( GDK_IS_X11_WINDOW( window ) )
204 {
205 // wxGTK sets res_class from the app display name; overwrite both WM_CLASS fields with
206 // the desktop id so the window manager matches us to our installed .desktop launcher.
207 if( XClassHint* hints = XAllocClassHint() )
208 {
209 hints->res_name = const_cast<char*>( id );
210 hints->res_class = const_cast<char*>( id );
211
212 XSetClassHint( GDK_WINDOW_XDISPLAY( window ), GDK_WINDOW_XID( window ), hints );
213 XFree( hints );
214 }
215 }
216#endif
217
218#if defined( GDK_WINDOWING_WAYLAND ) && GTK_CHECK_VERSION( 3, 24, 22 )
219 // Covers wx < 3.3.1, which never propagates the class name to the Wayland application id.
220 if( GDK_IS_WAYLAND_WINDOW( window ) && gtk_check_version( 3, 24, 22 ) == nullptr )
221 gdk_wayland_window_set_application_id( window, id );
222#endif
223}
224
225
226void KIPLATFORM::UI::SetWMClass( wxWindow* aWindow, const wxString& aClass )
227{
228 if( !aWindow || aClass.IsEmpty() )
229 return;
230
231 GtkWidget* widget = static_cast<GtkWidget*>( aWindow->GetHandle() );
232
233 if( !widget )
234 return;
235
236 // Own a single copy of the id on the widget; it is freed when the widget is destroyed. The
237 // signal handlers read it back from here rather than capturing it, so repeated calls stay safe.
238 g_object_set_data_full( G_OBJECT( widget ), WM_CLASS_DATA_KEY, g_strdup( aClass.utf8_str() ),
239 g_free );
240
241 // Guard against stacking duplicate handlers if this is called more than once for a widget.
242 g_signal_handlers_disconnect_by_func( widget, reinterpret_cast<void*>( setWindowClassHint ),
243 nullptr );
244
245 g_signal_connect_after( widget, "realize", G_CALLBACK( setWindowClassHint ), nullptr );
246 g_signal_connect_after( widget, "map", G_CALLBACK( setWindowClassHint ), nullptr );
247
248 // Apply immediately for windows already realized when this is called.
249 if( gtk_widget_get_realized( widget ) )
250 setWindowClassHint( widget, nullptr );
251}
252
253
254void KIPLATFORM::UI::ReparentModal( wxNonOwnedWindow* aWindow )
255{
256 // Not needed on this platform
257}
258
259
260void KIPLATFORM::UI::ReparentWindow( wxNonOwnedWindow* aWindow, wxTopLevelWindow* aParent )
261{
262 // Not needed on this platform (only relevant for macOS child window ordering)
263}
264
265
267{
268 // Not needed on this platform
269}
270
271
272bool KIPLATFORM::UI::IsStockCursorOk( wxStockCursor aCursor )
273{
274 switch( aCursor )
275 {
276 case wxCURSOR_BULLSEYE:
277 case wxCURSOR_HAND:
278 case wxCURSOR_ARROW:
279 case wxCURSOR_BLANK:
280 return true;
281 default:
282 return false;
283 }
284}
285
286
295static void disable_area_apply_attributes_cb( GtkWidget* pItem, gpointer userdata )
296{
297 // GTK needs this enormous chain to get the actual type of item that we want
298 GtkMenuItem* pMenuItem = GTK_MENU_ITEM( pItem );
299 GtkWidget* child = gtk_bin_get_child( GTK_BIN( pMenuItem ) );
300 GtkCellView* pCellView = GTK_CELL_VIEW( child );
301 GtkCellLayout* pCellLayout = GTK_CELL_LAYOUT( pCellView );
302 GtkCellArea* pCellArea = gtk_cell_layout_get_area( pCellLayout );
303
304 g_signal_handlers_block_matched( pCellArea, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, userdata );
305}
306
307
308void KIPLATFORM::UI::LargeChoiceBoxHack( wxChoice* aChoice )
309{
310 AtkObject* atkObj = gtk_combo_box_get_popup_accessible( GTK_COMBO_BOX( aChoice->m_widget ) );
311
312 if( !atkObj || !GTK_IS_ACCESSIBLE( atkObj ) )
313 return;
314
315 GtkWidget* widget = gtk_accessible_get_widget( GTK_ACCESSIBLE( atkObj ) );
316
317 if( !widget || !GTK_IS_MENU( widget ) )
318 return;
319
320 GtkMenu* menu = GTK_MENU( widget );
321
322 gtk_container_foreach( GTK_CONTAINER( menu ), disable_area_apply_attributes_cb, menu );
323}
324
325
326void KIPLATFORM::UI::EllipsizeChoiceBox( wxChoice* aChoice )
327{
328 // This function is based on the code inside the function post_process_ui in
329 // gtkfilechooserwidget.c
330 GList* cells = gtk_cell_layout_get_cells( GTK_CELL_LAYOUT( aChoice->m_widget ) );
331
332 if( !cells )
333 return;
334
335 GtkCellRenderer* cell = (GtkCellRenderer*) cells->data;
336
337 if( !cell )
338 return;
339
340 g_object_set( G_OBJECT( cell ), "ellipsize", PANGO_ELLIPSIZE_END, nullptr );
341
342 // Only the list of cells must be freed, the renderer isn't ours to free
343 g_list_free( cells );
344}
345
346
347double KIPLATFORM::UI::GetPixelScaleFactor( const wxWindow* aWindow )
348{
349 double val = 1.0;
350
351 GtkWidget* widget = static_cast<GtkWidget*>( aWindow->GetHandle() );
352
353 if( widget && gtk_check_version( 3, 10, 0 ) == nullptr )
354 val = gtk_widget_get_scale_factor( widget );
355
356 return val;
357}
358
359
360double KIPLATFORM::UI::GetContentScaleFactor( const wxWindow* aWindow )
361{
362 // TODO: Do we need something different here?
363 return GetPixelScaleFactor( aWindow );
364}
365
366
367wxSize KIPLATFORM::UI::GetUnobscuredSize( const wxWindow* aWindow )
368{
369 return wxSize( aWindow->GetSize().GetX() - wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ),
370 aWindow->GetSize().GetY() - wxSystemSettings::GetMetric( wxSYS_HSCROLL_Y ) );
371}
372
373
374void KIPLATFORM::UI::SetOverlayScrolling( const wxWindow* aWindow, bool overlay )
375{
376 gtk_scrolled_window_set_overlay_scrolling( GTK_SCROLLED_WINDOW( aWindow->GetHandle() ),
377 overlay );
378}
379
380
382{
383 gboolean allowed = 1;
384
385 g_object_get( gtk_settings_get_default(), "gtk-menu-images", &allowed, NULL );
386
387 return !!allowed;
388}
389
390
391#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
392
393static bool wayland_warp_pointer( GtkWidget* aWidget, GdkDisplay* aDisplay, GdkWindow* aWindow,
394 GdkDevice* aPtrDev, int aX, int aY );
395
396// GDK doesn't know if we've moved the cursor using Wayland pointer constraints.
397// So emulate the actual position here
398static wxPoint s_warped_from;
399static wxPoint s_warped_to;
400
402{
403 wxPoint wx_pos = wxGetMousePosition();
404
405 if( wx_pos == s_warped_from )
406 {
407 wxLogTrace( traceWayland, wxS( "Faked mouse pos %d %d -> %d %d" ), wx_pos.x, wx_pos.y,
408 s_warped_to.x, s_warped_to.y );
409
410 return s_warped_to;
411 }
412 else
413 {
414 // Mouse has moved
415 s_warped_from = wxPoint();
416 s_warped_to = wxPoint();
417 }
418
419 return wx_pos;
420}
421
422#endif
423
424
425bool KIPLATFORM::UI::WarpPointer( wxWindow* aWindow, int aX, int aY )
426{
427 if( !wxGetEnv( wxT( "WAYLAND_DISPLAY" ), nullptr ) )
428 {
429 aWindow->WarpPointer( aX, aY );
430 return true;
431 }
432 else
433 {
434 GtkWidget* widget = static_cast<GtkWidget*>( aWindow->GetHandle() );
435
436 GdkDisplay* disp = gtk_widget_get_display( widget );
437 GdkSeat* seat = gdk_display_get_default_seat( disp );
438 GdkDevice* ptrdev = gdk_seat_get_pointer( seat );
439
440#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
441 if( GDK_IS_WAYLAND_DISPLAY( disp ) )
442 {
443 wxPoint initialPos = wxGetMousePosition();
444 GdkWindow* win = aWindow->GTKGetDrawingWindow();
445
446 if( wayland_warp_pointer( widget, disp, win, ptrdev, aX, aY ) )
447 {
448 s_warped_from = initialPos;
449 s_warped_to = aWindow->ClientToScreen( wxPoint( aX, aY ) );
450
451 wxLogTrace( traceWayland, wxS( "Set warped from %d %d to %d %d" ), s_warped_from.x,
452 s_warped_from.y, s_warped_to.x, s_warped_to.y );
453
454 return true;
455 }
456
457 wxLogTrace( traceWayland, wxS( "*** Warp to %d %d failed ***" ), aX, aY );
458
459 return false;
460 }
461#endif
462#ifdef GDK_WINDOWING_X11
463 if( GDK_IS_X11_DISPLAY( disp ) )
464 {
465 GdkWindow* win = gdk_device_get_window_at_position( ptrdev, nullptr, nullptr );
466 GdkCursor* blank_cursor = gdk_cursor_new_for_display( disp, GDK_BLANK_CURSOR );
467 GdkCursor* cur_cursor = gdk_window_get_cursor( win );
468
469 if( cur_cursor )
470 g_object_ref( cur_cursor );
471
472 gdk_window_set_cursor( win, blank_cursor );
473 aWindow->WarpPointer( aX, aY );
474 gdk_window_set_cursor( win, cur_cursor );
475
476 if( cur_cursor )
477 g_object_unref( cur_cursor );
478
479 if( blank_cursor )
480 g_object_unref( blank_cursor );
481
482 return true;
483 }
484#endif
485 }
486
487 return false;
488}
489
490
491void KIPLATFORM::UI::ImmControl( wxWindow* aWindow, bool aEnable )
492{
493}
494
495
497{
498 wxWindowGTK* win = static_cast<wxWindowGTK*>( aWindow );
499 if( win )
500 {
501 GtkIMContext* imContext = win->m_imContext;
502 if( imContext )
503 {
504 gtk_im_context_focus_out( imContext );
505 }
506 }
507}
508
509
510void KIPLATFORM::UI::SetFloatLevel( wxWindow* aWindow )
511{
512}
513
514
515void KIPLATFORM::UI::ReleaseChildWindow( wxNonOwnedWindow* aWindow )
516{
517 // Not needed on this platform
518}
519
520
522{
523 GtkWidget* widget = static_cast<GtkWidget*>( aDialog->GetHandle() );
524
525 if( widget && GTK_IS_FILE_CHOOSER( widget ) )
526 gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER( widget ), FALSE );
527}
528
529
530void KIPLATFORM::UI::CancelPendingScroll( wxDataViewCtrl* aCtrl )
531{
532 if( !aCtrl )
533 return;
534
535 GtkWidget* widget = aCtrl->GtkGetTreeView();
536
537 if( !widget || !GTK_IS_TREE_VIEW( widget ) )
538 return;
539
540 GtkTreeView* view = GTK_TREE_VIEW( widget );
541
542 // Need a live model and rbtree for the assertions in scroll_to_cell.
543 if( !gtk_tree_view_get_model( view ) )
544 return;
545
546 GtkTreeViewColumn* column = gtk_tree_view_get_column( view, 0 );
547
548 if( !column )
549 return;
550
551 // A column-only scroll_to_cell enters GTK's deferred-scroll path, which frees
552 // priv->scroll_to_path and only installs scroll_to_column. That breaks the race
553 // where validate_visible_area dereferences a stale row reference after a model
554 // reset. The deferred branch is taken when the widget is not yet allocated, when
555 // alloc is pending, or when rows are invalid -- queue_resize forces the latter so
556 // a steady-state widget also takes that branch and actually clears the reference.
557 gtk_widget_queue_resize( widget );
558 gtk_tree_view_scroll_to_cell( view, nullptr, column, FALSE, 0.0, 0.0 );
559}
560
561//
562// **** Wayland hacks ahead ****
563//
564
565#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
566
567static bool s_wl_initialized = false;
568static struct wl_compositor* s_wl_compositor = NULL;
569static struct zwp_pointer_constraints_v1* s_wl_pointer_constraints = NULL;
570static struct zwp_confined_pointer_v1* s_wl_confined_pointer = NULL;
571static struct wl_region* s_wl_confinement_region = NULL;
572static bool s_wl_locked_flag = false;
573
574static void handle_global( void* data, struct wl_registry* registry, uint32_t name,
575 const char* interface, uint32_t version )
576{
577 wxLogTrace( traceWayland, "handle_global received %s name %u version %u", interface,
578 (unsigned int) name, (unsigned int) version );
579
580 if( strcmp( interface, wl_compositor_interface.name ) == 0 )
581 {
582 s_wl_compositor = static_cast<wl_compositor*>(
583 wl_registry_bind( registry, name, &wl_compositor_interface, version ) );
584 }
585 else if( strcmp( interface, zwp_pointer_constraints_v1_interface.name ) == 0 )
586 {
587 s_wl_pointer_constraints = static_cast<zwp_pointer_constraints_v1*>( wl_registry_bind(
588 registry, name, &zwp_pointer_constraints_v1_interface, version ) );
589 }
590}
591
592static void handle_global_remove( void*, struct wl_registry*, uint32_t name )
593{
594 wxLogTrace( traceWayland, "handle_global_remove name %u", (unsigned int) name );
595}
596
597static const struct wl_registry_listener registry_listener = {
598 .global = handle_global,
599 .global_remove = handle_global_remove,
600};
601
602static void confined_handler( void* data, struct zwp_confined_pointer_v1* zwp_confined_pointer_v1 )
603{
604 wxLogTrace( traceWayland, wxS( "Pointer confined" ) );
605}
606
607static void unconfined_handler( void* data,
608 struct zwp_confined_pointer_v1* zwp_confined_pointer_v1 )
609{
610 wxLogTrace( traceWayland, wxS( "Pointer unconfined" ) );
611}
612
613static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = {
614 .confined = confined_handler,
615 .unconfined = unconfined_handler
616};
617
618static void locked_handler( void* data, struct zwp_locked_pointer_v1* zwp_locked_pointer_v1 )
619{
620 s_wl_locked_flag = true;
621 wxLogTrace( traceWayland, wxS( "Pointer locked" ) );
622}
623
624static void unlocked_handler( void* data, struct zwp_locked_pointer_v1* zwp_locked_pointer_v1 )
625{
626 wxLogTrace( traceWayland, wxS( "Pointer unlocked" ) );
627}
628
629static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = {
630 .locked = locked_handler,
631 .unlocked = unlocked_handler
632};
633
634
638static void initialize_wayland( wl_display* wldisp )
639{
640 if( s_wl_initialized )
641 {
642 return;
643 }
644
645 struct wl_registry* registry = wl_display_get_registry( wldisp );
646 wl_registry_add_listener( registry, &registry_listener, NULL );
647 wl_display_roundtrip( wldisp );
648 s_wl_initialized = true;
649}
650
651
652struct zwp_locked_pointer_v1* s_wl_locked_pointer = NULL;
653
654static int s_after_paint_handler_id = 0;
655
656static void on_frame_clock_after_paint( GdkFrameClock* clock, GtkWidget* widget )
657{
658 if( s_wl_locked_pointer )
659 {
660 zwp_locked_pointer_v1_destroy( s_wl_locked_pointer );
661 s_wl_locked_pointer = NULL;
662
663 wxLogTrace( traceWayland, wxS( "after-paint: locked_pointer destroyed" ) );
664
665 g_signal_handler_disconnect( (gpointer) clock, s_after_paint_handler_id );
666 s_after_paint_handler_id = 0;
667
668 // restore confinement
669 if( s_wl_confinement_region != NULL )
670 {
671 wxLogTrace( traceWayland, wxS( "after-paint: Restoring confinement" ) );
672
673 GdkDisplay* disp = gtk_widget_get_display( widget );
674 GdkSeat* seat = gdk_display_get_default_seat( disp );
675 GdkDevice* ptrdev = gdk_seat_get_pointer( seat );
676 GdkWindow* window = gtk_widget_get_window( widget );
677
678 wl_display* wldisp = gdk_wayland_display_get_wl_display( disp );
679 wl_surface* wlsurf = gdk_wayland_window_get_wl_surface( window );
680 wl_pointer* wlptr = gdk_wayland_device_get_wl_pointer( ptrdev );
681
682 s_wl_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
683 s_wl_pointer_constraints, wlsurf, wlptr, s_wl_confinement_region,
685
686 wl_display_roundtrip( wldisp );
687 }
688 }
689}
690
691
692static bool wayland_warp_pointer( GtkWidget* aWidget, GdkDisplay* aDisplay, GdkWindow* aWindow,
693 GdkDevice* aPtrDev, int aX, int aY )
694{
695 wl_display* wldisp = gdk_wayland_display_get_wl_display( aDisplay );
696 wl_surface* wlsurf = gdk_wayland_window_get_wl_surface( aWindow );
697 wl_pointer* wlptr = gdk_wayland_device_get_wl_pointer( aPtrDev );
698
699 if( s_after_paint_handler_id )
700 {
701 // Previous paint not done yet
702 wxLogTrace( traceWayland, wxS( "Not warping: after-paint pending" ) );
703 return false;
704 }
705
706 initialize_wayland( wldisp );
707
708 if( s_wl_locked_pointer )
709 {
710 // This shouldn't happen but let's be safe
711 wxLogTrace( traceWayland, wxS( "** Destroying previous locked_pointer **" ) );
712 zwp_locked_pointer_v1_destroy( s_wl_locked_pointer );
713 wl_display_roundtrip( wldisp );
714 s_wl_locked_pointer = NULL;
715 }
716
717 // wl_surface_commit causes an assert on GNOME, but has to be called on KDE
718 // before destroying the locked pointer, so wait until GDK commits the surface.
719 GdkFrameClock* frame_clock = gdk_window_get_frame_clock( aWindow );
720 s_after_paint_handler_id = g_signal_connect_after(
721 frame_clock, "after-paint", G_CALLBACK( on_frame_clock_after_paint ), aWidget );
722
723 // temporary disable confinement to allow pointer warping
724 if( s_wl_confinement_region && s_wl_confined_pointer )
725 {
726 zwp_confined_pointer_v1_destroy( s_wl_confined_pointer );
727 wl_display_roundtrip( wldisp );
728 s_wl_confined_pointer = NULL;
729 }
730
731 s_wl_locked_flag = false;
732
733 s_wl_locked_pointer =
734 zwp_pointer_constraints_v1_lock_pointer( s_wl_pointer_constraints, wlsurf, wlptr, NULL,
736
737 zwp_locked_pointer_v1_add_listener(s_wl_locked_pointer, &locked_pointer_listener, NULL);
738
739 gint wx, wy;
740 gtk_widget_translate_coordinates( aWidget, gtk_widget_get_toplevel( aWidget ), 0, 0, &wx, &wy );
741
742 zwp_locked_pointer_v1_set_cursor_position_hint( s_wl_locked_pointer, wl_fixed_from_int( aX + wx ),
743 wl_fixed_from_int( aY + wy ) );
744
745 // Don't call wl_surface_commit, wait for GDK because of an assert on GNOME.
746 wl_display_roundtrip( wldisp ); // To receive "locked" event.
747 gtk_widget_queue_draw( aWidget ); // Needed on some GNOME environment to trigger
748 // the "after-paint" event handler.
749
750 return s_wl_locked_flag;
751}
752
753
754bool KIPLATFORM::UI::InfiniteDragPrepareWindow( wxWindow* aWindow )
755{
756 wxLogTrace( traceWayland, wxS( "InfiniteDragPrepareWindow" ) );
757
758 GtkWidget* widget = static_cast<GtkWidget*>( aWindow->GetHandle() );
759 GdkDisplay* disp = gtk_widget_get_display( widget );
760
761 if( GDK_IS_WAYLAND_DISPLAY( disp ) )
762 {
763 if( s_wl_confined_pointer != NULL )
764 {
766 }
767
768 GdkSeat* seat = gdk_display_get_default_seat( disp );
769 GdkDevice* ptrdev = gdk_seat_get_pointer( seat );
770 GdkWindow* win = aWindow->GTKGetDrawingWindow();
771
772 wl_display* wldisp = gdk_wayland_display_get_wl_display( disp );
773 wl_surface* wlsurf = gdk_wayland_window_get_wl_surface( win );
774 wl_pointer* wlptr = gdk_wayland_device_get_wl_pointer( ptrdev );
775
776 initialize_wayland( wldisp );
777
778 gint x, y, width, height;
779 gdk_window_get_geometry( gdk_window_get_toplevel( win ), &x, &y, &width, &height );
780
781 wxLogTrace( traceWayland, wxS( "Confine region: %d %d %d %d" ), x, y, width, height );
782
783 s_wl_confinement_region = wl_compositor_create_region( s_wl_compositor );
784 wl_region_add( s_wl_confinement_region, x, y, width, height );
785
786 s_wl_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
787 s_wl_pointer_constraints, wlsurf, wlptr, s_wl_confinement_region,
789
790 zwp_confined_pointer_v1_add_listener( s_wl_confined_pointer, &confined_pointer_listener,
791 NULL );
792
793 wl_display_roundtrip( wldisp );
794 }
795 else if( wxGetEnv( wxT( "WAYLAND_DISPLAY" ), nullptr ) )
796 {
797 // Not working under XWayland
798 return false;
799 }
800
801 return true;
802};
803
804
806{
807 wxLogTrace( traceWayland, wxS( "InfiniteDragReleaseWindow" ) );
808
809 if( s_wl_confined_pointer )
810 {
811 zwp_confined_pointer_v1_destroy( s_wl_confined_pointer );
812 s_wl_confined_pointer = NULL;
813 }
814
815 if( s_wl_confinement_region )
816 {
817 wl_region_destroy( s_wl_confinement_region );
818 s_wl_confinement_region = NULL;
819 }
820};
821
822
823#else // No Wayland support
824
825
827{
828 // Not working under XWayland
829 return !wxGetEnv( wxT( "WAYLAND_DISPLAY" ), nullptr );
830};
831
832
834{
835 // Not needed on X11
836};
837
838
840{
841 return wxGetMousePosition();
842}
843
844
845#endif
const char * name
static void zwp_confined_pointer_v1_destroy(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1)
Destroy the confined pointer object.
static int zwp_confined_pointer_v1_add_listener(struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1, const struct zwp_confined_pointer_v1_listener *listener, void *data)
static void zwp_locked_pointer_v1_set_cursor_position_hint(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, wl_fixed_t surface_x, wl_fixed_t surface_y)
Set the cursor position hint relative to the top left corner of the surface.
static int zwp_locked_pointer_v1_add_listener(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, const struct zwp_locked_pointer_v1_listener *listener, void *data)
static void zwp_locked_pointer_v1_destroy(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1)
Destroy the locked pointer object.
static struct zwp_locked_pointer_v1 * zwp_pointer_constraints_v1_lock_pointer(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, struct wl_surface *surface, struct wl_pointer *pointer, struct wl_region *region, uint32_t lifetime)
The lock_pointer request lets the client request to disable movements of the virtual pointer (i....
static struct zwp_confined_pointer_v1 * zwp_pointer_constraints_v1_confine_pointer(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, struct wl_surface *surface, struct wl_pointer *pointer, struct wl_region *region, uint32_t lifetime)
The confine_pointer request lets the client request to confine the pointer cursor to a given region.
@ ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT
the pointer constraint is defunct once deactivated
bool AllowIconsInMenus()
If the user has disabled icons system-wide, we check that here.
Definition wxgtk/ui.cpp:381
void SetFloatLevel(wxWindow *aWindow)
Intended to set the floating window level in macOS on a window.
Definition wxgtk/ui.cpp:510
void GetInfoBarColours(wxColour &aFGColour, wxColour &aBGColour)
Return the background and foreground colors for info bars in the current scheme.
Definition wxgtk/ui.cpp:70
void ReparentWindow(wxNonOwnedWindow *aWindow, wxTopLevelWindow *aParent)
Definition wxgtk/ui.cpp:260
void ReleaseChildWindow(wxNonOwnedWindow *aWindow)
Release a modal window's parent-child relationship with its parent window.
Definition wxgtk/ui.cpp:515
void EllipsizeChoiceBox(wxChoice *aChoice)
Configure a wxChoice control to ellipsize the shown text in the button with the ellipses placed at th...
Definition wxgtk/ui.cpp:326
void FixupCancelButtonCmdKeyCollision(wxWindow *aWindow)
Definition wxgtk/ui.cpp:266
void SetOverlayScrolling(const wxWindow *aWindow, bool overlay)
Used to set overlay/non-overlay scrolling mode in a window.
Definition wxgtk/ui.cpp:374
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:839
void ImmControl(wxWindow *aWindow, bool aEnable)
Configures the IME mode of a given control handle.
Definition wxgtk/ui.cpp:491
void StabilizeWindowPosition(wxWindow *aWindow)
Prepare a top-level window for reliable position round-tripping.
Definition wxgtk/ui.cpp:170
double GetPixelScaleFactor(const wxWindow *aWindow)
Tries to determine the pixel scaling factor currently in use for the window.
Definition wxgtk/ui.cpp:347
void InfiniteDragReleaseWindow()
On Wayland, allows the cursor to freely move again after a drag (see InfiniteDragPrepareWindow).
Definition wxgtk/ui.cpp:833
void EnsureVisible(wxWindow *aWindow)
Ensure that a window is visible on the screen.
Definition wxgtk/ui.cpp:164
bool IsStockCursorOk(wxStockCursor aCursor)
Checks if we designated a stock cursor for this OS as "OK" or else we may need to load a custom one.
Definition wxgtk/ui.cpp:272
double GetContentScaleFactor(const wxWindow *aWindow)
Tries to determine the content scaling factor currently in use for the window.
Definition wxgtk/ui.cpp:360
void ImeNotifyCancelComposition(wxWindow *aWindow)
Asks the IME to cancel.
Definition wxgtk/ui.cpp:496
void LargeChoiceBoxHack(wxChoice *aChoice)
Configure a wxChoice control to support a lot of entries by disabling functionality that makes adding...
Definition wxgtk/ui.cpp:308
bool WarpPointer(wxWindow *aWindow, int aX, int aY)
Move the mouse cursor to a specific position relative to the window.
Definition wxgtk/ui.cpp:425
wxColour GetDialogBGColour()
Definition wxgtk/ui.cpp:64
bool IsWindowActive(wxWindow *aWindow)
Check to see if the given window is the currently active window (e.g.
Definition wxgtk/ui.cpp:149
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition wxgtk/ui.cpp:367
void ForceFocus(wxWindow *aWindow)
Pass the current focus to the window.
Definition wxgtk/ui.cpp:127
bool InfiniteDragPrepareWindow(wxWindow *aWindow)
On Wayland, restricts the pointer movement to a rectangle slightly bigger than the given wxWindow.
Definition wxgtk/ui.cpp:826
void SetWMClass(wxWindow *aWindow, const wxString &aClass)
Tag a top-level window with the freedesktop application id of its installed launcher,...
Definition wxgtk/ui.cpp:226
void ReparentModal(wxNonOwnedWindow *aWindow)
Move a window's parent to be the top-level window and force the window to be on top.
Definition wxgtk/ui.cpp:254
void CancelPendingScroll(wxDataViewCtrl *aCtrl)
Cancel any pending scroll-to-item request on aCtrl.
Definition wxgtk/ui.cpp:530
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition wxgtk/ui.cpp:51
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:521
std::shared_ptr< PNS_LOG_VIEWER_OVERLAY > overlay
std::string path
const struct wl_interface zwp_pointer_constraints_v1_interface
static const char *const WM_CLASS_DATA_KEY
Definition wxgtk/ui.cpp:182
static void setWindowClassHint(GtkWidget *aWidget, gpointer)
Definition wxgtk/ui.cpp:189
static void disable_area_apply_attributes_cb(GtkWidget *pItem, gpointer userdata)
The following two functions are based on the "hack" contained in the attached patch at https://gitlab...
Definition wxgtk/ui.cpp:295
const wxString traceWayland
Definition wxgtk/ui.cpp:48