Compare commits

...

9 Commits

Author SHA1 Message Date
Jeff Young
18b56539a6 Keep Board Setup in front when called from DRC dialog. 2025-09-11 15:47:13 +01:00
Jeff Young
9b006c4f3b Formatting. 2025-09-11 15:47:13 +01:00
Jeff Young
8035a66152 Flag non-compiling rule conditions when running DRC.
Also, clear custom rules after an error before
trying to reload just implicit rules.
2025-09-11 15:47:13 +01:00
jean-pierre charras
45166bf5c3 Gerbview: fix broken behavior for deprecated command IPPOS and IPNEG
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21715
2025-09-11 14:30:50 +02:00
Jeff Young
6ab6283e2e LIBEVAL::CONTEXT manages its own local VALUEs.
Don't use std::unique_ptr as we'll just free the
value right after storing it.

Also, don't try to execute a non-existent function.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21697
2025-09-11 12:49:41 +01:00
Seth Hillbrand
fc7d91214d Make pasting in lib tables easier
You generally copy/paste whole rows in lib tables, so make this workflow
easier.  Allows pasting rows as new data.  Prevent overwriting existing
data and don't force pasting from the first column
2025-09-11 02:30:49 -07:00
Seth Hillbrand
dcbadb5857 Allow drag-drop for schematic elements
Dragging screen elements over a subsheet allows moving elements into a
subsheet
2025-09-11 02:16:47 -07:00
jean-pierre charras
3b97804cb6 DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR: add missing layers to always allowed list 2025-09-11 11:07:16 +02:00
Mark Roszko
e72def55a9 Remove moronic pybind forcing expectation of python release builds 2025-09-11 07:16:52 +00:00
17 changed files with 288 additions and 46 deletions

View File

@ -19,11 +19,23 @@
#include "lib_table_grid_tricks.h"
#include "lib_table_grid.h"
#include <wx/clipbrd.h>
#include <wx/log.h>
LIB_TABLE_GRID_TRICKS::LIB_TABLE_GRID_TRICKS( WX_GRID* aGrid ) :
GRID_TRICKS( aGrid )
{
m_grid->Disconnect( wxEVT_CHAR_HOOK );
m_grid->Connect( wxEVT_CHAR_HOOK, wxCharEventHandler( LIB_TABLE_GRID_TRICKS::onCharHook ), nullptr, this );
}
LIB_TABLE_GRID_TRICKS::LIB_TABLE_GRID_TRICKS( WX_GRID* aGrid,
std::function<void( wxCommandEvent& )> aAddHandler ) :
GRID_TRICKS( aGrid, aAddHandler )
{
m_grid->Disconnect( wxEVT_CHAR_HOOK );
m_grid->Connect( wxEVT_CHAR_HOOK, wxCharEventHandler( LIB_TABLE_GRID_TRICKS::onCharHook ), nullptr, this );
}
@ -134,6 +146,61 @@ void LIB_TABLE_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
GRID_TRICKS::doPopupSelection( event );
}
}
void LIB_TABLE_GRID_TRICKS::onCharHook( wxKeyEvent& ev )
{
if( ev.GetModifiers() == wxMOD_CONTROL && ev.GetKeyCode() == 'V' && m_grid->IsCellEditControlShown() )
{
wxLogNull doNotLog;
if( wxTheClipboard->Open() )
{
if( wxTheClipboard->IsSupported( wxDF_TEXT ) || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
{
wxTextDataObject data;
wxTheClipboard->GetData( data );
wxString text = data.GetText();
if( !text.Contains( '\t' ) && text.Contains( ',' ) )
text.Replace( ',', '\t' );
if( text.Contains( '\t' ) || text.Contains( '\n' ) || text.Contains( '\r' ) )
{
m_grid->CancelPendingChanges();
int row = m_grid->GetGridCursorRow();
// Check if the current row already has data (has a nickname)
wxGridTableBase* table = m_grid->GetTable();
if( table && row >= 0 && row < table->GetNumberRows() )
{
// Check if the row has a nickname (indicating it has existing data)
wxString nickname = table->GetValue( row, COL_NICKNAME );
if( !nickname.IsEmpty() )
{
// Row already has data, don't allow pasting over it
wxTheClipboard->Close();
wxBell(); // Provide audio feedback
return;
}
}
m_grid->ClearSelection();
m_grid->SelectRow( row );
m_grid->SetGridCursor( row, 0 );
getSelectedArea();
paste_text( text );
wxTheClipboard->Close();
m_grid->ForceRefresh();
return;
}
}
wxTheClipboard->Close();
}
}
GRID_TRICKS::onCharHook( ev );
}
bool LIB_TABLE_GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )

View File

@ -1160,7 +1160,9 @@ void UOP::Exec( CONTEXT* ctx )
return;
case TR_OP_METHOD_CALL:
if( m_func )
m_func( ctx, m_ref.get() );
return;
default:
@ -1321,9 +1323,8 @@ VALUE* UCODE::Run( CONTEXT* ctx )
}
catch(...)
{
// rules which fail outright should not be fired
std::unique_ptr<VALUE> temp_false = std::make_unique<VALUE>( 0 );
return ctx->StoreValue( temp_false.get() );
// rules which fail outright should not be fired; return 0/false
return ctx->StoreValue( new VALUE( 0 ) );
}
if( ctx->SP() == 1 )
@ -1339,8 +1340,7 @@ VALUE* UCODE::Run( CONTEXT* ctx )
wxASSERT( ctx->SP() == 1 );
// non-well-formed rules should not be fired on a release build
std::unique_ptr<VALUE> temp_false = std::make_unique<VALUE>( 0 );
return ctx->StoreValue( temp_false.get() );
return ctx->StoreValue( new VALUE( 0 ) );
}
}

View File

@ -38,6 +38,7 @@
#include <lib_table_grid.h>
#include <wildcards_and_files_ext.h>
#include <env_paths.h>
#include <functional>
#include <eeschema_id.h>
#include <symbol_edit_frame.h>
#include <symbol_viewer_frame.h>
@ -153,6 +154,13 @@ public:
{
}
SYMBOL_GRID_TRICKS( DIALOG_EDIT_LIBRARY_TABLES* aParent, WX_GRID* aGrid,
std::function<void( wxCommandEvent& )> aAddHandler ) :
LIB_TABLE_GRID_TRICKS( aGrid, aAddHandler ),
m_dialog( aParent )
{
}
protected:
DIALOG_EDIT_LIBRARY_TABLES* m_dialog;
@ -224,8 +232,21 @@ protected:
}
else
{
// paste spreadsheet formatted text.
GRID_TRICKS::paste_text( cb_text );
wxString text = cb_text;
if( !text.Contains( '\t' ) && text.Contains( ',' ) )
text.Replace( ',', '\t' );
if( text.Contains( '\t' ) )
{
int row = m_grid->GetGridCursorRow();
m_grid->ClearSelection();
m_grid->SelectRow( row );
m_grid->SetGridCursor( row, 0 );
getSelectedArea();
}
GRID_TRICKS::paste_text( text );
m_grid->AutoSizeColumns( false );
}
@ -250,7 +271,8 @@ void PANEL_SYM_LIB_TABLE::setupGrid( WX_GRID* aGrid )
};
// add Cut, Copy, and Paste to wxGrids
aGrid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, aGrid ) );
aGrid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, aGrid,
[this]( wxCommandEvent& event ) { appendRowHandler( event ); } ) );
aGrid->SetSelectionMode( wxGrid::wxGridSelectRows );

View File

@ -46,6 +46,9 @@
#include <pgm_base.h>
#include <view/view_controls.h>
#include <settings/settings_manager.h>
#include <math/box2.h>
#include <base_units.h>
#include <sch_screen.h>
#include "sch_move_tool.h"
@ -500,6 +503,7 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
TOOL_EVENT* evt = &copy;
VECTOR2I prevPos;
GRID_HELPER_GRIDS snapLayer = GRID_CURRENT;
SCH_SHEET* hoverSheet = nullptr;
m_cursor = controls->GetCursorPosition();
@ -771,7 +775,65 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
snapLayer, selection );
// Determine potential target sheet.
SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( m_frame->GetScreen()->GetItem( m_cursor, 0,
SCH_SHEET_T ) );
if( sheet && sheet->IsSelected() )
sheet = nullptr; // Never target a selected sheet
if( !sheet )
{
// Build current selection bounding box in its (already moved) position.
BOX2I selBBox;
for( EDA_ITEM* it : selection )
{
if( SCH_ITEM* schIt = dynamic_cast<SCH_ITEM*>( it ) )
selBBox.Merge( schIt->GetBoundingBox() );
}
if( selBBox.GetWidth() > 0 && selBBox.GetHeight() > 0 )
{
VECTOR2I selCenter( selBBox.GetX() + selBBox.GetWidth() / 2,
selBBox.GetY() + selBBox.GetHeight() / 2 );
// Find first non-selected sheet whose body fully contains the selection
// or at least contains its center point.
for( SCH_ITEM* it : m_frame->GetScreen()->Items().OfType( SCH_SHEET_T ) )
{
SCH_SHEET* candidate = static_cast<SCH_SHEET*>( it );
if( candidate->IsSelected() || candidate->IsRootSheet() )
continue;
BOX2I body = candidate->GetBodyBoundingBox();
if( body.Contains( selBBox ) || body.Contains( selCenter ) )
{
sheet = candidate;
break;
}
}
}
}
if( sheet != hoverSheet )
{
if( hoverSheet )
{
hoverSheet->ClearFlags( BRIGHTENED );
m_frame->UpdateItem( hoverSheet, false );
}
hoverSheet = sheet;
if( hoverSheet )
{
hoverSheet->SetFlags( BRIGHTENED );
m_frame->UpdateItem( hoverSheet, false );
}
}
m_frame->GetCanvas()->SetCurrentCursor( hoverSheet ? KICURSOR::PLACE
: KICURSOR::MOVING );
VECTOR2I delta( m_cursor - prevPos );
m_anchorPos = m_cursor;
@ -1032,6 +1094,22 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
} while( ( evt = Wait() ) ); //Should be assignment not equality test
SCH_SHEET* targetSheet = hoverSheet;
if( hoverSheet )
{
hoverSheet->ClearFlags( BRIGHTENED );
m_frame->UpdateItem( hoverSheet, false );
}
if( targetSheet )
{
moveSelectionToSheet( selection, targetSheet, aCommit );
m_toolMgr->RunAction( ACTIONS::selectionClear );
m_newDragLines.clear();
m_changedDragLines.clear();
}
// Create a selection of original selection, drag selected/changed items, and new
// bend lines for later before we clear them in the aCommit. We'll need these
// to check for new junctions needed, etc.
@ -1135,6 +1213,60 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
}
void SCH_MOVE_TOOL::moveSelectionToSheet( SCH_SELECTION& aSelection, SCH_SHEET* aTargetSheet,
SCH_COMMIT* aCommit )
{
SCH_SCREEN* destScreen = aTargetSheet->GetScreen();
SCH_SCREEN* srcScreen = m_frame->GetScreen();
BOX2I bbox;
for( EDA_ITEM* item : aSelection )
bbox.Merge( static_cast<SCH_ITEM*>( item )->GetBoundingBox() );
VECTOR2I offset = VECTOR2I( 0, 0 ) - bbox.GetPosition();
int step = schIUScale.MilsToIU( 50 );
bool overlap = false;
do
{
BOX2I moved = bbox;
moved.Move( offset );
overlap = false;
for( SCH_ITEM* existing : destScreen->Items() )
{
if( moved.Intersects( existing->GetBoundingBox() ) )
{
overlap = true;
break;
}
}
if( overlap )
offset += VECTOR2I( step, step );
} while( overlap );
for( EDA_ITEM* item : aSelection )
{
SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item );
// Remove from current screen and view manually
m_frame->RemoveFromScreen( schItem, srcScreen );
// Move the item
schItem->Move( offset );
// Add to destination screen manually (won't add to view since it's not current)
destScreen->Append( schItem );
// Record in commit with CHT_DONE flag to bypass automatic screen/view operations
aCommit->Stage( schItem, CHT_REMOVE | CHT_DONE, srcScreen );
aCommit->Stage( schItem, CHT_ADD | CHT_DONE, destScreen );
}
}
void SCH_MOVE_TOOL::trimDanglingLines( SCH_COMMIT* aCommit )
{
// Need a local cleanup first to ensure we remove unneeded junctions
@ -1163,8 +1295,7 @@ void SCH_MOVE_TOOL::trimDanglingLines( SCH_COMMIT* aCommit )
{
line->SetFlags( STRUCT_DELETED );
aCommit->Removed( line, m_frame->GetScreen() );
updateItem( line, false );
updateItem( line, false ); // Update any cached visuals before commit processes
m_frame->RemoveFromScreen( line, m_frame->GetScreen() );
}
}

View File

@ -35,6 +35,9 @@ class SCH_LINE;
class SCH_LABEL_BASE;
class SCH_SHEET_PIN;
class SCH_JUNCTION;
class SCH_SELECTION;
class SCH_SHEET;
class SCH_COMMIT;
struct SPECIAL_CASE_LABEL_INFO
@ -81,6 +84,8 @@ private:
void orthoLineDrag( SCH_COMMIT* aCommit, SCH_LINE* line, const VECTOR2I& splitDelta,
int& xBendCount, int& yBendCount, const EE_GRID_HELPER& grid );
void moveSelectionToSheet( SCH_SELECTION& aSelection, SCH_SHEET* aTarget, SCH_COMMIT* aCommit );
///< Clears the new drag lines and removes them from the screen
void clearNewDragLines();

View File

@ -671,8 +671,7 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
break;
case IMAGE_POLARITY:
// These commands are deprecated since 2012.
// So do nothing and prompt the user about this command
// Note: these commands IPPOS and IPNEG are deprecated since 2012.
if( strncasecmp( aText, "NEG", 3 ) == 0 )
{
m_ImageNegative = true;
@ -688,7 +687,6 @@ bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
// actual effect. Just skip it.
}
ok = false;
break;
case LOAD_POLARITY:

View File

@ -18,6 +18,7 @@
*/
#include "grid_tricks.h"
#include <functional>
class LIB_TABLE_GRID_TRICKS : public GRID_TRICKS
{
@ -33,6 +34,7 @@ class LIB_TABLE_GRID_TRICKS : public GRID_TRICKS
public:
explicit LIB_TABLE_GRID_TRICKS( WX_GRID* aGrid );
LIB_TABLE_GRID_TRICKS( WX_GRID* aGrid, std::function<void( wxCommandEvent& )> aAddHandler );
virtual ~LIB_TABLE_GRID_TRICKS(){};
@ -43,5 +45,7 @@ protected:
virtual void optionsEditor( int aRow ) = 0;
bool handleDoubleClick( wxGridEvent& aEvent ) override;
void onCharHook( wxKeyEvent& ev );
virtual bool supportsVisibilityColumn() { return false; }
};

View File

@ -327,7 +327,9 @@ class INFOBAR_REPORTER : public REPORTER
{
public:
INFOBAR_REPORTER( WX_INFOBAR* aInfoBar ) :
REPORTER(), m_messageSet( false ), m_infoBar( aInfoBar ),
REPORTER(),
m_messageSet( false ),
m_infoBar( aInfoBar ),
m_severity( RPT_SEVERITY_UNDEFINED )
{
}

View File

@ -55,8 +55,8 @@
#define RESOLVE_PAGE( T, pageIndex ) static_cast<T*>( m_treebook->ResolvePage( pageIndex ) )
DIALOG_BOARD_SETUP::DIALOG_BOARD_SETUP( PCB_EDIT_FRAME* aFrame ) :
PAGED_DIALOG( aFrame, _( "Board Setup" ), false, false,
DIALOG_BOARD_SETUP::DIALOG_BOARD_SETUP( PCB_EDIT_FRAME* aFrame, wxWindow* aParent ) :
PAGED_DIALOG( aParent ? aParent : aFrame, _( "Board Setup" ), false, false,
_( "Import Settings from Another Board..." ), wxSize( 980, 600 ) ),
m_frame( aFrame ),
m_layers( nullptr ),

View File

@ -42,7 +42,7 @@ class PANEL_TEXT_VARIABLES;
class DIALOG_BOARD_SETUP : public PAGED_DIALOG
{
public:
DIALOG_BOARD_SETUP( PCB_EDIT_FRAME* aFrame );
DIALOG_BOARD_SETUP( PCB_EDIT_FRAME* aFrame, wxWindow* aWindow = nullptr );
~DIALOG_BOARD_SETUP();
protected:

View File

@ -303,7 +303,7 @@ void DIALOG_DRC::OnMenu( wxCommandEvent& event )
void DIALOG_DRC::OnErrorLinkClicked( wxHtmlLinkEvent& event )
{
m_frame->ShowBoardSetupDialog( _( "Custom Rules" ) );
m_frame->ShowBoardSetupDialog( _( "Custom Rules" ), this );
}
@ -993,7 +993,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
}
case ID_EDIT_SEVERITIES:
m_frame->ShowBoardSetupDialog( _( "Violation Severity" ) );
m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
break;
}
@ -1035,7 +1035,7 @@ void DIALOG_DRC::OnIgnoredItemRClick( wxListEvent& event )
void DIALOG_DRC::OnEditViolationSeverities( wxHyperlinkEvent& aEvent )
{
m_frame->ShowBoardSetupDialog( _( "Violation Severity" ) );
m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
}

View File

@ -674,6 +674,7 @@ bool DIALOG_FOOTPRINT_PROPERTIES_FP_EDITOR::Validate()
// Check that the user isn't trying to remove a layer that is used by the footprint
usedLayers &= ~getCustomLayersFromControls();
usedLayers &= ~LSET::AllTechMask();
usedLayers &= ~LSET::UserMask();
if( usedLayers.any() )
{

View File

@ -38,6 +38,7 @@
#include <wx/dirdlg.h>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#include <functional>
#include <project.h>
#include <env_vars.h>
@ -147,6 +148,12 @@ public:
m_dialog( aParent )
{ }
FP_GRID_TRICKS( DIALOG_EDIT_LIBRARY_TABLES* aParent, WX_GRID* aGrid,
std::function<void( wxCommandEvent& )> aAddHandler ) :
LIB_TABLE_GRID_TRICKS( aGrid, aAddHandler ),
m_dialog( aParent )
{ }
protected:
DIALOG_EDIT_LIBRARY_TABLES* m_dialog;
@ -217,8 +224,21 @@ protected:
}
else
{
// paste spreadsheet formatted text.
GRID_TRICKS::paste_text( cb_text );
wxString text = cb_text;
if( !text.Contains( '\t' ) && text.Contains( ',' ) )
text.Replace( ',', '\t' );
if( text.Contains( '\t' ) )
{
int row = m_grid->GetGridCursorRow();
m_grid->ClearSelection();
m_grid->SelectRow( row );
m_grid->SetGridCursor( row, 0 );
getSelectedArea();
}
GRID_TRICKS::paste_text( text );
m_grid->AutoSizeColumns( false );
}
@ -254,7 +274,8 @@ void PANEL_FP_LIB_TABLE::setupGrid( WX_GRID* aGrid )
aGrid->SetRowSize( ii, aGrid->GetDefaultRowSize() + 4 );
// add Cut, Copy, and Paste to wxGrids
aGrid->PushEventHandler( new FP_GRID_TRICKS( m_parent, aGrid ) );
aGrid->PushEventHandler( new FP_GRID_TRICKS( m_parent, aGrid,
[this]( wxCommandEvent& event ) { appendRowHandler( event ); } ) );
aGrid->SetSelectionMode( wxGrid::wxGridSelectRows );

View File

@ -527,10 +527,9 @@ void DRC_ENGINE::loadRules( const wxFileName& aPath )
void DRC_ENGINE::compileRules()
{
if( m_logReporter )
{
m_logReporter->Report( ( wxString::Format( wxT( "Compiling Rules (%d rules): " ),
(int) m_rules.size() ) ) );
}
m_logReporter->Report( wxT( "Compiling Rules" ) );
REPORTER error_semaphore;
for( std::shared_ptr<DRC_RULE>& rule : m_rules )
{
@ -539,9 +538,12 @@ void DRC_ENGINE::compileRules()
if( rule->m_Condition && !rule->m_Condition->GetExpression().IsEmpty() )
{
condition = rule->m_Condition;
condition->Compile( nullptr );
condition->Compile( &error_semaphore );
}
if( error_semaphore.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name, rule->m_Condition->GetExpression(), 0, 0 );
for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
{
if( !m_constraintMap.count( constraint.m_Type ) )
@ -594,6 +596,8 @@ void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
}
catch( PARSE_ERROR& original_parse_error )
{
m_rules.clear();
try // try again with just our implicit rules
{
loadImplicitRules();

View File

@ -109,6 +109,7 @@
#include <widgets/pcb_net_inspector_panel.h>
#include <widgets/wx_aui_utils.h>
#include <kiplatform/app.h>
#include <kiplatform/ui.h>
#include <core/profile.h>
#include <math/box2_minmax.h>
#include <view/wx_view_controls.h>
@ -1466,7 +1467,7 @@ void PCB_EDIT_FRAME::ActivateGalCanvas()
}
void PCB_EDIT_FRAME::ShowBoardSetupDialog( const wxString& aInitialPage )
void PCB_EDIT_FRAME::ShowBoardSetupDialog( const wxString& aInitialPage, wxWindow* aParent )
{
static std::mutex dialogMutex; // Local static mutex
@ -1486,7 +1487,7 @@ void PCB_EDIT_FRAME::ShowBoardSetupDialog( const wxString& aInitialPage )
// Make sure everything's up-to-date
GetBoard()->BuildListOfNets();
DIALOG_BOARD_SETUP dlg( this );
DIALOG_BOARD_SETUP dlg( this, aParent );
if( !aInitialPage.IsEmpty() )
dlg.SetInitialPage( aInitialPage, wxEmptyString );

View File

@ -301,7 +301,7 @@ public:
///< @copydoc EDA_DRAW_FRAME::UseGalCanvas()
void ActivateGalCanvas() override;
void ShowBoardSetupDialog( const wxString& aInitialPage = wxEmptyString );
void ShowBoardSetupDialog( const wxString& aInitialPage = wxEmptyString, wxWindow* aParent = nullptr );
void PrepareLayerIndicator( bool aForceRebuild = false );

View File

@ -15,20 +15,6 @@
// To maximize reusability:
// DO NOT ADD CODE THAT REQUIRES C++ EXCEPTION HANDLING.
// Disable linking to pythonX_d.lib on Windows in debug mode.
#if defined(_MSC_VER) && defined(_DEBUG) && !defined(Py_DEBUG)
// Workaround for a VS 2022 issue.
// See https://github.com/pybind/pybind11/pull/3497 for full context.
// NOTE: This workaround knowingly violates the Python.h include order
// requirement (see above).
# include <yvals.h>
# if _MSVC_STL_VERSION >= 143
# include <crtdefs.h>
# endif
# define PYBIND11_DEBUG_MARKER
# undef _DEBUG
#endif
// Don't let Python.h #define (v)snprintf as macro because they are implemented
// properly in Visual Studio since 2015.
#if defined(_MSC_VER)