Fixes for Wayland cursor warping:

- Emulate the mouse position, since no motion events are sent after warp
- Do not call wl_surface_commit to prevent an assert
- Re-organize code a bit
This commit is contained in:
Alex Shvartzkop 2023-09-18 06:13:09 +03:00 committed by dsa-t
parent ad62b165fe
commit 06463fd35a
18 changed files with 205 additions and 104 deletions

View File

@ -565,7 +565,7 @@ void ACTION_TOOLBAR::onItemDrag( wxAuiToolBarEvent& aEvent )
void ACTION_TOOLBAR::onTimerDone( wxTimerEvent& aEvent )
{
// We need to search for the tool using the client coordinates
wxPoint mousePos = ScreenToClient( wxGetMousePosition() );
wxPoint mousePos = ScreenToClient( KIPLATFORM::UI::GetMousePosition() );
wxAuiToolBarItem* item = FindToolByPosition( mousePos.x, mousePos.y );

View File

@ -29,6 +29,7 @@
#include <map>
#include <stack>
#include <trace_helpers.h>
#include <kiplatform/ui.h>
#include <wx/event.h>
#include <wx/clipbrd.h>
@ -307,7 +308,7 @@ VECTOR2D TOOL_MANAGER::GetMousePosition() const
if( m_viewControls )
return m_viewControls->GetMousePosition();
else
return ToVECTOR2D( wxGetMousePosition() );
return ToVECTOR2D( KIPLATFORM::UI::GetMousePosition() );
}
@ -316,7 +317,7 @@ VECTOR2D TOOL_MANAGER::GetCursorPosition() const
if( m_viewControls )
return m_viewControls->GetCursorPosition();
else
return ToVECTOR2D( wxGetMousePosition() );
return ToVECTOR2D( KIPLATFORM::UI::GetMousePosition() );
}

View File

@ -1007,7 +1007,7 @@ void WX_VIEW_CONTROLS::refreshMouse( bool aSetModifiers )
wxPoint WX_VIEW_CONTROLS::getMouseScreenPosition() const
{
wxPoint msp = wxGetMousePosition();
wxPoint msp = KIPLATFORM::UI::GetMousePosition();
m_parentPanel->ScreenToClient( &msp.x, &msp.y );
return msp;
}

View File

@ -361,7 +361,7 @@ protected:
{
// Generate synthetic (but reliable) MouseMoved events
static wxPoint lastPos;
wxPoint screenPos = wxGetMousePosition();
wxPoint screenPos = KIPLATFORM::UI::GetMousePosition();
if( screenPos != lastPos )
{

View File

@ -24,6 +24,7 @@
#include <memory>
#include <kiplatform/ui.h>
#include <tools/sch_drawing_tools.h>
#include <tools/sch_line_wire_bus_tool.h>
#include <tools/ee_selection_tool.h>
@ -1345,7 +1346,8 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
{
m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
m_statusPopup->SetText( _( "Click over a sheet." ) );
m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition()
+ wxPoint( 20, 20 ) );
m_statusPopup->PopupFor( 2000 );
item = nullptr;
}
@ -1357,7 +1359,8 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
{
m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition()
+ wxPoint( 20, 20 ) );
m_statusPopup->PopupFor( 2000 );
item = nullptr;
@ -1442,7 +1445,8 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
{
m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( m_frame );
m_statusPopup->SetText( _( "No new hierarchical labels found." ) );
m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition()
+ wxPoint( 20, 20 ) );
m_statusPopup->PopupFor( 2000 );
item = nullptr;

View File

@ -210,49 +210,35 @@ bool KIPLATFORM::UI::AllowIconsInMenus()
return !!allowed;
}
#ifdef KICAD_WAYLAND
static bool wl_initialized = false;
static struct wl_compositor* compositor = NULL;
static struct zwp_pointer_constraints_v1* pointer_constraints = NULL;
static struct zwp_confined_pointer_v1* confined_pointer = NULL;
static struct wl_region* confinement_region = NULL;
// GDK doesn't know if we've moved the cursor using pointer constraints.
// So emulate the actual position here
static wxPoint s_warped_from;
static wxPoint s_warped_to;
static void handle_global( void* data, struct wl_registry* registry, uint32_t name,
const char* interface, uint32_t version )
wxPoint KIPLATFORM::UI::GetMousePosition()
{
if( strcmp( interface, wl_compositor_interface.name ) == 0 )
wxPoint wx_pos = wxGetMousePosition();
if( wx_pos == s_warped_from )
{
compositor = static_cast<wl_compositor*>(
wl_registry_bind( registry, name, &wl_compositor_interface, version ) );
return s_warped_to;
}
else if( strcmp( interface, zwp_pointer_constraints_v1_interface.name ) == 0 )
else
{
pointer_constraints = static_cast<zwp_pointer_constraints_v1*>( wl_registry_bind(
registry, name, &zwp_pointer_constraints_v1_interface, version ) );
// Mouse has moved
s_warped_from = wxPoint();
s_warped_to = wxPoint();
}
return wx_pos;
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = NULL,
};
/**
* Initializes `compositor` and `pointer_constraints` global variables.
*/
static void initialize_wayland( wl_display* wldisp )
{
if( wl_initialized )
{
return;
}
#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
struct wl_registry* registry = wl_display_get_registry( wldisp );
wl_registry_add_listener( registry, &registry_listener, NULL );
wl_display_roundtrip( wldisp );
wl_initialized = true;
}
static bool wayland_warp_pointer( GtkWidget* aWidget, GdkDisplay* aDisplay, GdkWindow* aWindow,
GdkDevice* aPtrDev, int aX, int aY );
#endif
@ -274,44 +260,14 @@ void KIPLATFORM::UI::WarpPointer( wxWindow* aWindow, int aX, int aY )
#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
if( GDK_IS_WAYLAND_DISPLAY( disp ) )
{
wxPoint initialPos = wxGetMousePosition();
GdkWindow* win = aWindow->GTKGetDrawingWindow();
wl_display* wldisp = gdk_wayland_display_get_wl_display( disp );
wl_surface* wlsurf = gdk_wayland_window_get_wl_surface( win );
wl_pointer* wlptr = gdk_wayland_device_get_wl_pointer( ptrdev );
initialize_wayland( wldisp );
// temporary disable confinement to allow pointer warping
if( confinement_region != NULL )
if( wayland_warp_pointer( widget, disp, win, ptrdev, aX, aY ) )
{
zwp_confined_pointer_v1_destroy( confined_pointer );
confined_pointer = NULL;
s_warped_from = initialPos;
s_warped_to = aWindow->ClientToScreen( wxPoint( aX, aY ) );
}
struct zwp_locked_pointer_v1* locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
pointer_constraints, wlsurf, wlptr, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT );
gint wx, wy;
gtk_widget_translate_coordinates( widget, gtk_widget_get_toplevel( widget ), 0, 0, &wx,
&wy );
zwp_locked_pointer_v1_set_cursor_position_hint(
locked_pointer, wl_fixed_from_int( aX + wx ), wl_fixed_from_int( aY + wy ) );
zwp_locked_pointer_v1_destroy( locked_pointer );
// restore confinement
if( confinement_region != NULL )
{
confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
pointer_constraints, wlsurf, wlptr, confinement_region,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT );
}
wl_surface_commit( wlsurf );
wl_display_roundtrip( wldisp );
}
#endif
#ifdef GDK_WINDOWING_X11
@ -337,11 +293,101 @@ void KIPLATFORM::UI::ImmControl( wxWindow* aWindow, bool aEnable )
{
}
//
// **** Wayland hacks ahead ****
//
#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
static bool s_wl_initialized = false;
static struct wl_compositor* s_wl_compositor = NULL;
static struct zwp_pointer_constraints_v1* s_wl_pointer_constraints = NULL;
static struct zwp_confined_pointer_v1* s_wl_confined_pointer = NULL;
static struct wl_region* s_wl_confinement_region = NULL;
static void handle_global( void* data, struct wl_registry* registry, uint32_t name,
const char* interface, uint32_t version )
{
if( strcmp( interface, wl_compositor_interface.name ) == 0 )
{
s_wl_compositor = static_cast<wl_compositor*>(
wl_registry_bind( registry, name, &wl_compositor_interface, version ) );
}
else if( strcmp( interface, zwp_pointer_constraints_v1_interface.name ) == 0 )
{
s_wl_pointer_constraints = static_cast<zwp_pointer_constraints_v1*>( wl_registry_bind(
registry, name, &zwp_pointer_constraints_v1_interface, version ) );
}
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = NULL,
};
/**
* Initializes `compositor` and `pointer_constraints` global variables.
*/
static void initialize_wayland( wl_display* wldisp )
{
if( s_wl_initialized )
{
return;
}
struct wl_registry* registry = wl_display_get_registry( wldisp );
wl_registry_add_listener( registry, &registry_listener, NULL );
wl_display_roundtrip( wldisp );
s_wl_initialized = true;
}
static bool wayland_warp_pointer( GtkWidget* aWidget, GdkDisplay* aDisplay, GdkWindow* aWindow,
GdkDevice* aPtrDev, int aX, int aY )
{
wl_display* wldisp = gdk_wayland_display_get_wl_display( aDisplay );
wl_surface* wlsurf = gdk_wayland_window_get_wl_surface( aWindow );
wl_pointer* wlptr = gdk_wayland_device_get_wl_pointer( aPtrDev );
initialize_wayland( wldisp );
// temporary disable confinement to allow pointer warping
if( s_wl_confinement_region != NULL )
{
zwp_confined_pointer_v1_destroy( s_wl_confined_pointer );
s_wl_confined_pointer = NULL;
}
struct zwp_locked_pointer_v1* locked_pointer =
zwp_pointer_constraints_v1_lock_pointer( s_wl_pointer_constraints, wlsurf, wlptr, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT );
gint wx, wy;
gtk_widget_translate_coordinates( aWidget, gtk_widget_get_toplevel( aWidget ), 0, 0, &wx, &wy );
zwp_locked_pointer_v1_set_cursor_position_hint( locked_pointer, wl_fixed_from_int( aX + wx ),
wl_fixed_from_int( aY + wy ) );
zwp_locked_pointer_v1_destroy( locked_pointer );
// restore confinement
if( s_wl_confinement_region != NULL )
{
s_wl_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
s_wl_pointer_constraints, wlsurf, wlptr, s_wl_confinement_region,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT );
}
wl_display_roundtrip( wldisp );
return true;
}
void KIPLATFORM::UI::InfiniteDragPrepareWindow( wxWindow* aWindow )
{
#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
if( confined_pointer != NULL )
if( s_wl_confined_pointer != NULL )
{
KIPLATFORM::UI::InfiniteDragReleaseWindow();
}
@ -368,30 +414,51 @@ void KIPLATFORM::UI::InfiniteDragPrepareWindow( wxWindow* aWindow )
gdk_window_get_geometry( win, &x, &y, &width, &height );
gtk_widget_translate_coordinates( widget, gtk_widget_get_toplevel( widget ), 0, 0, &wx, &wy );
confinement_region = wl_compositor_create_region( compositor );
wl_region_add( confinement_region, x + wx - 1, y + wy - 1, width + 2, height + 2 );
s_wl_confinement_region = wl_compositor_create_region( s_wl_compositor );
wl_region_add( s_wl_confinement_region, x + wx - 1, y + wy - 1, width + 2, height + 2 );
confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
pointer_constraints, wlsurf, wlptr, confinement_region,
s_wl_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
s_wl_pointer_constraints, wlsurf, wlptr, s_wl_confinement_region,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT );
wl_surface_commit( wlsurf );
wl_display_roundtrip( wldisp );
#endif
};
void KIPLATFORM::UI::InfiniteDragReleaseWindow()
{
#if defined( GDK_WINDOWING_WAYLAND ) && defined( KICAD_WAYLAND )
if( confined_pointer == NULL )
if( s_wl_confined_pointer == NULL )
{
return;
}
zwp_confined_pointer_v1_destroy( confined_pointer );
wl_region_destroy( confinement_region );
zwp_confined_pointer_v1_destroy( s_wl_confined_pointer );
wl_region_destroy( s_wl_confinement_region );
confined_pointer = NULL;
confinement_region = NULL;
#endif
s_wl_confined_pointer = NULL;
s_wl_confinement_region = NULL;
};
#else // No Wayland support
void KIPLATFORM::UI::InfiniteDragPrepareWindow( wxWindow* aWindow )
{
// Not needed on X11
};
void KIPLATFORM::UI::InfiniteDragReleaseWindow()
{
// Not needed on X11
};
wxPoint KIPLATFORM::UI::GetMousePosition()
{
return wxGetMousePosition();
}
#endif

View File

@ -138,6 +138,12 @@ namespace KIPLATFORM
*/
bool AllowIconsInMenus();
/**
* Returns the mouse position in screen coordinates.
* If we've just warped the cursor, returns the new coordinates.
*/
wxPoint GetMousePosition();
/**
* Move the mouse cursor to a specific position relative to the window
* @param aWindow Window in which to position to mouse cursor

View File

@ -158,6 +158,12 @@ bool KIPLATFORM::UI::AllowIconsInMenus()
}
wxPoint KIPLATFORM::UI::GetMousePosition()
{
return wxGetMousePosition();
}
void KIPLATFORM::UI::WarpPointer( wxWindow* aWindow, int aX, int aY )
{
aWindow->WarpPointer( aX, aY );

View File

@ -166,6 +166,12 @@ bool KIPLATFORM::UI::AllowIconsInMenus()
}
wxPoint KIPLATFORM::UI::GetMousePosition()
{
return wxGetMousePosition();
}
void KIPLATFORM::UI::WarpPointer( wxWindow* aWindow, int aX, int aY )
{
aWindow->WarpPointer( aX, aY );

View File

@ -21,6 +21,7 @@
#include <class_draw_panel_gal.h>
#include <dialogs/dialog_pns_length_tuning_settings.h>
#include <kiplatform/ui.h>
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
#include <tools/zone_filler_tool.h>
@ -129,7 +130,7 @@ void LENGTH_TUNER_TOOL::Reset( RESET_REASON aReason )
void LENGTH_TUNER_TOOL::updateStatusPopup( PNS_TUNE_STATUS_POPUP& aPopup )
{
// fixme: wx code not allowed inside tools!
wxPoint p = wxGetMousePosition();
wxPoint p = KIPLATFORM::UI::GetMousePosition();
p.x += 20;
p.y += 20;

View File

@ -23,6 +23,7 @@
*/
#include <kiplatform/ui.h>
#include <confirm.h>
#include <pcb_base_frame.h>
#include <widgets/layer_box_selector.h>
@ -149,7 +150,7 @@ void PCB_ONE_LAYER_SELECTOR::OnMouseMove( wxUpdateUIEvent& aEvent )
/// MouseMove events. It seems to only get them on the edges. So, for now we use this
/// workaround
wxPoint mouse_pos = wxGetMousePosition();
wxPoint mouse_pos = KIPLATFORM::UI::GetMousePosition();
wxPoint left_pos = m_leftGridLayers->ScreenToClient( mouse_pos );
wxPoint right_pos = m_rightGridLayers->ScreenToClient( mouse_pos );

View File

@ -23,6 +23,7 @@
*/
#include "drawing_tool.h"
#include <kiplatform/ui.h>
#include "pcb_actions.h"
#include <pcb_edit_frame.h>
#include <view/view.h>
@ -621,8 +622,9 @@ int DRAWING_TOOL::InteractivePlaceWithPreview( const TOOL_EVENT& aEvent,
{
if( aLayers != nullptr )
{
PCB_LAYER_ID destLayer = frame()->SelectOneLayer( PCB_LAYER_ID::PCB_LAYER_ID_COUNT,
*aLayers, wxGetMousePosition() );
PCB_LAYER_ID destLayer =
frame()->SelectOneLayer( PCB_LAYER_ID::PCB_LAYER_ID_COUNT, *aLayers,
KIPLATFORM::UI::GetMousePosition() );
view()->ClearPreview();

View File

@ -26,6 +26,7 @@
#include <advanced_config.h>
#include <limits>
#include <kiplatform/ui.h>
#include <board.h>
#include <board_design_settings.h>
#include <footprint.h>
@ -2466,7 +2467,7 @@ bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aS
picker->SetMotionHandler(
[&]( const VECTOR2D& aPos )
{
m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
} );
picker->SetCancelHandler(
@ -2489,7 +2490,7 @@ bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aS
done = true;
} );
m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
m_statusPopup->Popup();
canvas()->SetStatusPopup( m_statusPopup->GetPanel() );

View File

@ -26,6 +26,7 @@
#include <functional>
#include <limits>
#include <kiplatform/ui.h>
#include <board.h>
#include <board_commit.h>
#include <pad.h>
@ -416,7 +417,7 @@ bool EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, BOARD_COMMIT* aCommit
updateStatusPopup( orig_items[ itemIdx ], itemIdx + 1, orig_items.size() );
statusPopup.Popup();
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
canvas()->SetStatusPopup( statusPopup.GetPanel() );
m_selectionTool->ClearSelection();
@ -661,7 +662,7 @@ bool EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, BOARD_COMMIT* aCommit
m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
}
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
m_toolMgr->PostAction( PCB_ACTIONS::updateLocalRatsnest, movement );
}

View File

@ -21,6 +21,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <kiplatform/ui.h>
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_selection_tool.h>
@ -178,7 +179,7 @@ int GROUP_TOOL::PickNewMember( const TOOL_EVENT& aEvent )
picker->SetMotionHandler(
[&] ( const VECTOR2D& aPos )
{
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
} );
picker->SetCancelHandler(
@ -196,7 +197,7 @@ int GROUP_TOOL::PickNewMember( const TOOL_EVENT& aEvent )
done = true;
} );
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
statusPopup.Popup();
canvas()->SetStatusPopup( statusPopup.GetPanel() );

View File

@ -24,6 +24,7 @@
#include "pad_tool.h"
#include "pcb_painter.h"
#include <kiplatform/ui.h>
#include <macros.h>
#include <class_draw_panel_gal.h>
#include <view/view_controls.h>
@ -373,7 +374,7 @@ int PAD_TOOL::EnumeratePads( const TOOL_EVENT& aEvent )
setPopupTextForValue( seqPadNum );
statusPopup.Popup();
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
canvas()->SetStatusPopup( statusPopup.GetPanel() );
while( TOOL_EVENT* evt = Wait() )
@ -507,7 +508,7 @@ int PAD_TOOL::EnumeratePads( const TOOL_EVENT& aEvent )
// Prepare the next loop by updating the old cursor mouse position
// to this last mouse cursor position
oldCursorPos = getViewControls()->GetCursorPosition();
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
}
for( PAD* p : board()->GetFirstFootprint()->Pads() )

View File

@ -23,6 +23,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <kiplatform/ui.h>
#include <tools/edit_tool.h>
#include <tools/board_inspection_tool.h>
#include <router/router_tool.h>
@ -616,7 +617,8 @@ int PCB_CONTROL::DeleteItemCursor( const TOOL_EVENT& aEvent )
m_statusPopup.reset( new STATUS_TEXT_POPUP( m_frame ) );
m_statusPopup->SetText( _( "Item locked." ) );
m_statusPopup->PopupFor( 2000 );
m_statusPopup->Move( wxGetMousePosition() + wxPoint( 20, 20 ) );
m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition()
+ wxPoint( 20, 20 ) );
return true;
}

View File

@ -25,6 +25,7 @@
#include <memory>
using namespace std::placeholders;
#include <kiplatform/ui.h>
#include <tools/position_relative_tool.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_selection_tool.h>
@ -200,7 +201,7 @@ int POSITION_RELATIVE_TOOL::SelectPositionRelativeItem( const TOOL_EVENT& aEvent
picker->SetMotionHandler(
[&] ( const VECTOR2D& aPos )
{
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
} );
picker->SetCancelHandler(
@ -218,7 +219,7 @@ int POSITION_RELATIVE_TOOL::SelectPositionRelativeItem( const TOOL_EVENT& aEvent
done = true;
} );
statusPopup.Move( wxGetMousePosition() + wxPoint( 20, -50 ) );
statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
statusPopup.Popup();
canvas()->SetStatusPopup( statusPopup.GetPanel() );