ADDED: A new IPC API based on protobuf and nng

Details, documentation, and language bindings are works in
progress and will be evolving over the course of KiCad 9
development.
This commit is contained in:
Jon Evans 2023-01-29 13:06:05 -05:00
parent 77eaa75db1
commit f613cd1cb4
75 changed files with 6286 additions and 41 deletions

View File

@ -247,6 +247,10 @@ option( KICAD_GAL_PROFILE
"Enable profiling info for GAL"
OFF )
option( KICAD_IPC_API
"Enable experimental IPC API"
OFF )
# Global setting: exports are explicit
set( CMAKE_CXX_VISIBILITY_PRESET "hidden" )
set( CMAKE_VISIBILITY_INLINES_HIDDEN ON )
@ -278,6 +282,10 @@ if( KICAD_WAYLAND )
add_compile_definitions( KICAD_WAYLAND )
endif()
if( KICAD_IPC_API )
add_definitions( -DKICAD_IPC_API )
endif()
# Ensure DEBUG is defined for all platforms in Debug builds
add_compile_definitions( $<$<CONFIG:Debug>:DEBUG> )
@ -842,6 +850,26 @@ if( OCC_VERSION_STRING VERSION_LESS 7.5.0 )
endif()
include_directories( SYSTEM ${OCC_INCLUDE_DIR} )
option( KICAD_USE_CMAKE_FINDPROTOBUF "Use FindProtobuf provided by CMake" OFF )
mark_as_advanced( KICAD_USE_CMAKE_FINDPROTOBUF )
if( KICAD_USE_CMAKE_FINDPROTOBUF )
include( FindProtobuf )
find_package( Protobuf REQUIRED )
else()
find_package( Protobuf REQUIRED CONFIG )
set( Protobuf_LIBRARY "protobuf::libprotobuf" )
endif()
if( NOT Protobuf_PROTOC_EXECUTABLE )
set( Protobuf_PROTOC_EXECUTABLE "protobuf::protoc" )
endif()
if( NOT Protobuf_INCLUDE_DIR )
get_target_property( Protobuf_INCLUDE_DIR protobuf::libprotobuf INTERFACE_INCLUDE_DIRECTORIES )
endif()
# Assist with header file searching optimization:
# INC_BEFORE and INC_AFTER are two lists which go at the front and back of the
# header file search lists, respectively.
@ -1051,6 +1079,10 @@ if( KICAD_USE_SENTRY )
add_compile_definitions( KICAD_USE_SENTRY )
endif()
if( KICAD_IPC_API )
find_package( nng REQUIRED )
endif()
#================================================
# Add the doxygen target
#================================================
@ -1121,6 +1153,8 @@ if( KICAD_BUILD_I18N )
add_subdirectory( translation )
endif()
add_subdirectory( api )
if( APPLE )
set( KICAD_OSX_CODESIGN ON
CACHE BOOL "Sign KiCad.app on macOS" FORCE )

118
api/CMakeLists.txt Normal file
View File

@ -0,0 +1,118 @@
# This program source code file is part of KiCad, a free EDA CAD application.
#
# Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
# Search paths for protoc when generating code
set( Protobuf_IMPORT_DIRS ${Protobuf_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/proto )
set( KIAPI_PROTO_SRCS
common/envelope.proto
common/types/base_types.proto
common/commands/base_commands.proto
common/commands/editor_commands.proto
board/board_types.proto
)
# Generated C++ code must be in the build dir; it is dependent on the version of protoc installed
set( KIAPI_CPP_BASEPATH ${CMAKE_CURRENT_BINARY_DIR}/cpp/api )
foreach( PROTO_SRC ${KIAPI_PROTO_SRCS} )
string( REGEX REPLACE "\.proto$" ".pb.cc" CPP_SRC ${PROTO_SRC} )
string( REGEX REPLACE "\.proto$" ".pb.h" CPP_HEADER ${PROTO_SRC} )
set( KIAPI_CPP_SRCS ${KIAPI_CPP_SRCS} ${KIAPI_CPP_BASEPATH}/${CPP_SRC} )
set( KIAPI_CPP_HEADERS ${KIAPI_CPP_HEADERS} ${KIAPI_CPP_BASEPATH}/${CPP_HEADER} )
set( KIAPI_PROTO_SRC_FULLPATHS ${KIAPI_PROTO_SRC_FULLPATHS} ${CMAKE_CURRENT_SOURCE_DIR}/proto/${PROTO_SRC} )
endforeach ()
add_custom_command( COMMAND ${CMAKE_COMMAND} -E make_directory ${KIAPI_CPP_BASEPATH}
COMMAND ${Protobuf_PROTOC_EXECUTABLE}
--cpp_out=dllexport_decl=KIAPI_IMPORTEXPORT:${KIAPI_CPP_BASEPATH}
--proto_path=${CMAKE_CURRENT_SOURCE_DIR}/proto
${KIAPI_PROTO_SRCS}
COMMENT "Generating API protobuf source files from proto definitions..."
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${KIAPI_PROTO_SRC_FULLPATHS}
OUTPUT ${KIAPI_CPP_SRCS} ${KIAPI_CPP_HEADERS}
)
# kiapi must be a shared DLL because the protobuf messages can only be initialized once
add_library( kiapi SHARED
${CMAKE_CURRENT_SOURCE_DIR}/../include/import_export.h
${KIAPI_CPP_SRCS}
${KIAPI_CPP_HEADERS}
)
target_compile_definitions( kiapi PRIVATE KIAPI_IMPORTEXPORT=APIEXPORT )
target_compile_definitions( kiapi INTERFACE KIAPI_IMPORTEXPORT=APIIMPORT )
# https://groups.google.com/g/protobuf/c/PDR1bqRazts
if(MSVC)
target_compile_options( kiapi PRIVATE /FI${CMAKE_CURRENT_SOURCE_DIR}/../include/import_export.h )
else()
add_definitions( -include ${CMAKE_CURRENT_SOURCE_DIR}/../include/import_export.h )
endif()
if( APPLE )
# puts library into the main kicad.app bundle in build tree
set_target_properties( kiapi PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${OSX_BUNDLE_BUILD_LIB_DIR}"
INSTALL_NAME_DIR "${OSX_BUNDLE_BUILD_LIB_DIR}"
)
endif()
install( TARGETS
kiapi
RUNTIME DESTINATION ${KICAD_LIB}
LIBRARY DESTINATION ${KICAD_LIB}
COMPONENT binary
)
if( KICAD_WIN32_INSTALL_PDBS )
# Get the PDBs to copy over for MSVC
install(FILES $<TARGET_PDB_FILE:kiapi> DESTINATION ${KICAD_BIN})
endif()
# Because CMake doesn't guess this from the .cc extension generated by protoc
set_target_properties( kiapi PROPERTIES LINKER_LANGUAGE CXX )
target_include_directories( kiapi SYSTEM PUBLIC ${Protobuf_INCLUDE_DIRS} )
target_link_libraries( kiapi protobuf::libprotobuf )
target_include_directories( kiapi INTERFACE
${CMAKE_CURRENT_BINARY_DIR}/cpp # Leaving off the /api/ to make #include statments less ambiguous
)
# Because when building internally, the generated files do not include the "api" base path
target_include_directories( kiapi PUBLIC ${KIAPI_CPP_BASEPATH} )
option( KICAD_BUILD_ENUM_EXPORTER
"Build the enum exporter used as part of generating the IPC APIs"
OFF )
if( KICAD_BUILD_ENUM_EXPORTER )
add_subdirectory( enums )
add_custom_target( enum_definitions
COMMAND $<TARGET_FILE:enum_exporter> ${CMAKE_CURRENT_BINARY_DIR}/enums.json
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating API definitions from KiCad enums..."
DEPENDS enum_exporter
)
endif()

43
api/enums/CMakeLists.txt Normal file
View File

@ -0,0 +1,43 @@
# This program source code file is part of KiCad, a free EDA CAD application.
#
# Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
include_directories( BEFORE ${INC_BEFORE} )
include_directories(
${INC_AFTER}
)
add_executable( enum_exporter WIN32
enum_exporter.cpp
)
target_link_libraries( enum_exporter
common
)
target_include_directories( enum_exporter PRIVATE
$<TARGET_PROPERTY:magic_enum,INTERFACE_INCLUDE_DIRECTORIES>
)
if( MSVC )
# The cli needs subsystem:console or else we can't link wmain/main
set_target_properties(enum_exporter PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
set_target_properties(enum_exporter PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(enum_exporter PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
endif()

View File

@ -0,0 +1,91 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <filesystem>
#include <iostream>
#include <string>
#include <argparse/argparse.hpp>
#include <fmt.h>
#include <nlohmann/json.hpp>
#define MAGIC_ENUM_RANGE_MAX 1024
#include <magic_enum.hpp>
#include <layer_ids.h>
#include <eda_shape.h>
#include <core/typeinfo.h>
template<typename T>
nlohmann::json FormatEnum()
{
nlohmann::json js;
js["type"] = magic_enum::enum_type_name<T>();
js["values"] = nlohmann::json::array();
for( const std::pair<T, std::string_view>& entry : magic_enum::enum_entries<T>() )
{
js["values"].emplace_back( nlohmann::json( {
{ "key", entry.second },
{ "value", static_cast<int>( entry.first ) }
} ) );
}
return js;
}
int main( int argc, char* argv[] )
{
argparse::ArgumentParser args( "enum_exporter" );
args.add_argument( "output_dir" ).default_value( std::string() );
try
{
args.parse_args( argc, argv );
}
catch( const std::runtime_error& err )
{
std::cerr << err.what() << std::endl;
std::cerr << args;
std::exit( 1 );
}
std::filesystem::path path( args.get<std::string>( "output_dir" ) );
std::ofstream outfile;
if( !path.empty() )
{
path = std::filesystem::absolute( path );
outfile.open( path );
}
std::ostream& out = outfile.is_open() ? outfile : std::cout;
nlohmann::json js = nlohmann::json::array();
js += FormatEnum<PCB_LAYER_ID>();
js += FormatEnum<SHAPE_T>();
js += FormatEnum<KICAD_T>();
out << js.dump( 4 ) << std::endl;
}

View File

@ -0,0 +1,110 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.board.types;
import "common/types/base_types.proto";
/// Represents a track segment on a board
message Track
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Point2D start = 2;
kiapi.common.types.Point2D end = 3;
kiapi.common.types.Distance width = 4;
kiapi.common.types.LockedState locked = 5;
kiapi.common.types.BoardLayer layer = 6;
kiapi.common.types.Net net = 7;
}
/// Represents an arc track (not a PCB_SHAPE in arc shape)
/// Arc tracks in KiCad store start, midpoint, and end.
/// All other values (center point, angles, etc) are inferred.
message Arc
{
kiapi.common.types.KIID id = 1;
kiapi.common.types.Point2D start = 2;
kiapi.common.types.Point2D mid = 3; /// Arc midpoint
kiapi.common.types.Point2D end = 4;
kiapi.common.types.Distance width = 5;
kiapi.common.types.LockedState locked = 6;
kiapi.common.types.BoardLayer layer = 7;
kiapi.common.types.Net net = 8;
}
enum PadStackType
{
PST_UNKNOWN = 0;
PST_THROUGH = 1; /// Through all layers; same shape on all layers
PST_BLIND_BURIED = 2; /// From a start layer to end layer (inclusive); same shape on all included layers
}
enum UnconnectedLayerRemoval
{
ULR_UNKNOWN = 0;
/// Keep annular rings on all layers
ULR_KEEP = 1;
/// Remove annular rings on unconnected layers, including start and end layers.
ULR_REMOVE = 2;
/// Remove annular rings on unconnected layers, but preserve start and end layers even if unconnected.
ULR_REMOVE_EXCEPT_START_AND_END = 3;
}
/// A pad stack definition for a multilayer pad or via.
message PadStack
{
/// What type of pad stack this represents.
PadStackType type = 1;
/// Lowest (closest to F_Cu) layer this stack exists on. Ignored if type == PST_THROUGH.
kiapi.common.types.BoardLayer start_layer = 2;
/// Highest (closest to B_Cu) layer this stack exists on. Ignored if type == PST_THROUGH.
kiapi.common.types.BoardLayer end_layer = 3;
/// How to treat annular rings on unconnected layers.
UnconnectedLayerRemoval unconnected_layer_removal = 4;
}
/// Represents a via
message Via
{
/// The unique identifier of the via
kiapi.common.types.KIID id = 1;
/// The location of the via's center point
kiapi.common.types.Point2D position = 2;
/// The diameter of the via's circular copper pad
kiapi.common.types.Distance pad_diameter = 4;
/// The diameter of the via's drilled hole
kiapi.common.types.Distance drill_diameter = 5;
/// The pad stack definition for this via. The via's VIATYPE (blind/buried/normal) is inferred from this.
PadStack pad_stack = 6;
kiapi.common.types.LockedState locked = 7;
kiapi.common.types.Net net = 8;
}

View File

@ -0,0 +1,33 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.common.commands;
import "common/types/base_types.proto";
message GetVersion
{
}
message GetVersionResponse
{
kiapi.common.types.KiCadVersion version = 1;
}

View File

@ -0,0 +1,251 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Commands and responses related to manipulating editor windows
*/
syntax = "proto3";
package kiapi.common.commands;
import "google/protobuf/any.proto";
import "common/types/base_types.proto";
/// Refreshes the given frame, if that frame is open
message RefreshEditor
{
kiapi.common.types.FrameType frame = 1;
}
/// Retrieves a list of open documents of the given type
message GetOpenDocuments
{
/// Which type of documents to query
kiapi.common.types.DocumentType type = 1;
}
message GetOpenDocumentsResponse
{
repeated kiapi.common.types.DocumentSpecifier documents = 1;
}
/*
* Runs a TOOL_ACTION using the TOOL_MANAGER of a given frame.
* WARNING: The TOOL_ACTIONs are specifically *not* an API.
* Command names may change as code is refactored, and commands may disappear.
* This API method is provided for low-level prototyping purposes only.
*/
message RunAction
{
string action = 1; // Action name, like "eeschema.InteractiveSelection.ClearSelection"
}
enum RunActionStatus
{
RAS_UNKNOWN = 0;
RAS_OK = 1; // The action was submitted successfully.
RAS_INVALID = 2; // The action was unknown for the targeted frame.
RAS_FRAME_NOT_OPEN = 3; // The targeted frame was not open when the call was submitted.
}
/*
* NOTE: At the moment, RAS_FRAME_NOT_OPEN won't be returned as the handler is inside the frame.
*/
message RunActionResponse
{
RunActionStatus status = 1;
}
/*
* Begins a staged set of changes. Any modifications made to a document through the API after this
* call will be saved to a pending commit, and will not appear in KiCad until a matching call to
* END_COMMIT.
*/
message BeginCommit
{
}
message BeginCommitResponse
{
}
enum CommitResult
{
CR_UNKNOWN = 0;
CR_OK = 1; // Commit was pushed successfully
CR_NO_COMMIT = 2; // There was no commit started
}
message EndCommit
{
// Optional message describing this changeset
string message = 1;
}
message EndCommitResponse
{
CommitResult result = 1;
}
/// Creates new items on a given document
message CreateItems
{
/// Specifies which document to create on, which fields are included, etc.
kiapi.common.types.ItemHeader header = 1;
/// List of items to create
repeated google.protobuf.Any items = 2;
/// Items may be created on a top-level document (sheet, board, etc) or inside a container
/// (symbol, footprint). If this field is not empty, it holds the ID of a symbol or footprint
/// that the items should be added to. This ID must be an existing symbol (for schematic
/// documents) or footprint (for board documents). If the given container does not exist or is
/// not the correct item type, the CreateItems call will fail.
kiapi.common.types.KIID container = 3;
}
enum ItemCreationStatus
{
ICS_UNKNOWN = 0;
ICS_OK = 1; /// The item was created
ICS_INVALID_TYPE = 2; /// The item's type is not valid for the given document
ICS_EXISTING = 3; /// The item had a specified KIID and that KIID was already in use
}
message ItemCreationResult
{
ItemCreationStatus status = 1;
/// The created version of the item, including an updated KIID as applicable
google.protobuf.Any item = 2;
}
message CreateItemsResponse
{
/// Specifies which document was modified, which fields are included in created_items, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were created
kiapi.common.types.ItemRequestStatus status = 2;
/// Status of each item to be created
repeated ItemCreationResult created_items = 3;
}
message GetItems
{
/// Specifies which document to query, which fields to return, etc.
kiapi.common.types.ItemHeader header = 1;
/// List of one or more types of items to retreive
repeated kiapi.common.types.ItemType types = 2;
}
message GetItemsResponse
{
/// Specifies which document was modified, which fields are included in items, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were retrieved
kiapi.common.types.ItemRequestStatus status = 2;
repeated google.protobuf.Any items = 3;
}
/// Updates items in a given document
message UpdateItems
{
/// Specifies which document to modify, which fields are included, etc.
kiapi.common.types.ItemHeader header = 1;
/// List of items to modify
repeated google.protobuf.Any items = 2;
}
enum ItemUpdateStatus
{
IUS_UNKNOWN = 0;
IUS_OK = 1; /// The item was updated
IUS_INVALID_TYPE = 2; /// The item's type is not valid for the given document
IUS_NONEXISTENT = 3; /// The item did not exist in the given document
IUS_IMMUTABLE = 4; /// The item is not allowed to be modified by the API
}
message ItemUpdateResult
{
ItemUpdateStatus status = 1;
/// The update version of the item
google.protobuf.Any item = 2;
}
message UpdateItemsResponse
{
/// Specifies which document was modified, which fields are included in updated_items, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were modified
kiapi.common.types.ItemRequestStatus status = 2;
/// Status of each item to be created
repeated ItemUpdateResult updated_items = 3;
}
/// Deletes items in a given document
message DeleteItems
{
/// Specifies which document to modify
kiapi.common.types.ItemHeader header = 1;
/// List of item KIIDs to delete
repeated kiapi.common.types.KIID item_ids = 2;
}
enum ItemDeletionStatus
{
IDS_UNKNOWN = 0;
IDS_OK = 1;
IDS_NONEXISTENT = 2; /// The item did not exist in the given document
IDS_IMMUTABLE = 3; /// The item is not allowed to be modified by the API
}
message ItemDeletionResult
{
kiapi.common.types.KIID id = 1;
ItemDeletionStatus status = 2;
}
message DeleteItemsResponse
{
/// Specifies which document was modified, etc.
kiapi.common.types.ItemHeader header = 1;
/// Status of the overall request; may return IRS_OK even if no items were deleted
kiapi.common.types.ItemRequestStatus status = 2;
/// Status of each item requested to be deleted
repeated ItemDeletionResult deleted_items = 3;
}

View File

@ -0,0 +1,90 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto3";
package kiapi.common;
import "google/protobuf/any.proto";
enum ApiStatusCode
{
AS_UNKNOWN = 0;
AS_OK = 1; // Request succeeded
AS_TIMEOUT = 2; // Request timed out
AS_BAD_REQUEST = 3; // The request had invalid parameters or otherwise was illegal
AS_NOT_READY = 4; // KiCad was not (yet) in a state where it could handle API requests
AS_UNHANDLED = 5; // The request was not handled by KiCad
AS_TOKEN_MISMATCH = 6; // The kicad_token in the request didn't match this KiCad's token
}
/*
* For future expansion: any header fields that should be sent with a request
*/
message ApiRequestHeader
{
// An opaque string identifying a running instance of KiCad. If this is set to a non-empty
// string in an API request, KiCad will reject the request if the value doesn't match its own
// token. This can be used to let API clients make sure they are still talking to the same
// instance of KiCad if they are long-running.
string kicad_token = 1;
// A string identifying an API client. Should be set by the client to a value that is unique
// to a specific instance of a client, for example the package name of the client plus its
// process ID or a random string, e.g. "com.github.me.my_awesome_plugin-73951". The main purpose
// of this name is to identify the client in debug logs.
string client_name = 2;
}
/*
* The top-level envelope container for an API request (message from a client to the KiCad API server)
*/
message ApiRequest
{
ApiRequestHeader header = 1;
google.protobuf.Any message = 2;
}
/*
* For future expansion: any header fields that should be sent with a response
*/
message ApiResponseHeader
{
/// An opaque string identifying a running instance of KiCad.
string kicad_token = 1;
}
message ApiResponse
{
ApiResponseHeader header = 1;
ApiResponseStatus status = 2;
google.protobuf.Any message = 3;
}
message ApiResponseStatus
{
/// A code describing the category of error (or AS_OK if no error)
ApiStatusCode status = 1;
/// A human-readable description of the error, if any
string error_message = 2;
}

View File

@ -0,0 +1,217 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* base_types.proto
* Includes types used in many parts of the API
*/
syntax = "proto3";
package kiapi.common.types;
import "google/protobuf/field_mask.proto";
enum CommandStatus
{
CS_UNKNOWN = 0;
CS_OK = 1; // Command succeeded
CS_FAILED = 2; // Command failed
}
message CommandStatusResponse
{
CommandStatus status = 1;
}
/**
* Describes a particular version of KiCad
*/
message KiCadVersion
{
uint32 major = 1;
uint32 minor = 2;
uint32 patch = 3;
// Full identifier string, potentially containing git hashes, packager-added info, etc.
string full_version = 4;
}
/**
* Some commands are specific to a KiCad window (frame). This list contains all addressable frames.
*/
enum FrameType
{
FT_UNKNOWN = 0;
FT_PROJECT_MANAGER = 1;
FT_SCHEMATIC_EDITOR = 2;
FT_PCB_EDITOR = 3;
FT_SPICE_SIMULATOR = 4;
FT_SYMBOL_EDITOR = 5;
FT_FOOTPRINT_EDITOR = 6;
FT_DRAWING_SHEET_EDITOR = 7;
}
/**
* Describes a KIID, or UUID of an object in a KiCad editor model.
*/
message KIID
{
// The KIID's value in standard UUID format, stored as a string for easy portability
string value = 1;
}
/**
* Identifier for the type of document being targeted by a request
*/
enum DocumentType
{
DOCTYPE_UNKNOWN = 0;
DOCTYPE_SCHEMATIC = 1;
DOCTYPE_SYMBOL = 2;
DOCTYPE_PCB = 3;
DOCTYPE_FOOTPRINT = 4;
DOCTYPE_DRAWING_SHEET = 5;
}
/**
* Describes a KiCad LIB_ID; a unique identifier for a loaded symbol or footprint
*/
message LibraryIdentifier
{
/// The library portion of the LIB_ID
string library_nickname = 1;
/// The symbol or footprint name
string entry_name = 2;
}
/**
* Describes a unique sheet in a schematic
*/
message SheetPath
{
/// The canonical path to the sheet. The first KIID will be the root sheet, etc.
repeated KIID path = 1;
/// The path converted to a human readable form such as "/", "/child", or "/child/grandchild"
string path_human_readable = 2;
}
/**
* Describes a document that will be the target of a request
*/
message DocumentSpecifier
{
DocumentType type = 1;
oneof identifier
{
/// If type == DT_SYMBOL or DT_FOOTPRINT, identifies a certain library entry
LibraryIdentifier lib_id = 2;
/// If type == DT_SCHEMATIC, identifies a sheet with a given path
SheetPath sheet_path = 3;
/// If type == DT_PCB, identifies a PCB with a given filename, e.g. "board.kicad_pcb"
string board_filename = 4;
}
}
/**
* Describes the type of a KiCad item (wrapper for KICAD_T)
*/
message ItemType
{
/// Must be a valid value in the KICAD_T C++ enum (see typeinfo.h)
int32 type = 1;
}
/**
* This header is included in requests and responses about item(s) in a document
*/
message ItemHeader
{
/// Which document is this request targeting?
DocumentSpecifier document = 1;
/// Which fields on the item(s) are included with this request or response
google.protobuf.FieldMask field_mask = 2;
}
/**
* Status of a request that included an ItemHeader
*/
enum ItemRequestStatus
{
IRS_UNKNOWN = 0;
IRS_OK = 1;
IRS_DOCUMENT_NOT_FOUND = 2; /// The given document is not open in KiCad
IRS_FIELD_MASK_INVALID = 3; /// The given field_mask contains invalid specifiers
}
/// Describes a point in 2D space. All coordinates are in nanometers.
message Point2D
{
int64 x_nm = 1;
int64 y_nm = 2;
}
/// Describes a point in 3D space. All coordinates are in nanometers.
message Point3D
{
int64 x_nm = 1;
int64 y_nm = 2;
int64 z_nm = 3;
}
/// Describes a quantity of distance (size, length, etc). All coordinates are in nanometers.
message Distance
{
int64 value_nm = 1;
}
/// Describes whether or not an item is locked for editing or movement
enum LockedState
{
LS_UNKNOWN = 0;
LS_UNLOCKED = 1;
LS_LOCKED = 2;
}
message BoardLayer
{
int32 layer_id = 1; /// From PCB_LAYER_T
}
/// Describes a copper item's net
message Net
{
/// A unique code representing this net
int32 code = 1;
/// Human-readable net name
string name = 2;
}
/// Describes a net class (a grouping of nets)
message NetClass
{
string name = 1;
}

26
cmake/Findnng.cmake Normal file
View File

@ -0,0 +1,26 @@
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(_NNG nng)
endif (PKG_CONFIG_FOUND)
FIND_PATH(NNG_INCLUDE_DIR
NAMES
nng/nng.h
PATH_SUFFIXES
include
)
FIND_LIBRARY(NNG_LIBRARY
NAMES
nng
PATH_SUFFIXES
"lib"
"local/lib"
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(nng
REQUIRED_VARS NNG_INCLUDE_DIR NNG_LIBRARY)
MARK_AS_ADVANCED(NNG_INCLUDE_DIR NNG_LIBRARY)

View File

@ -186,6 +186,7 @@ set_target_properties(kicommon PROPERTIES CXX_VISIBILITY_PRESET hidden)
target_link_libraries( kicommon
core
kiapi
kimath
kiplatform
nlohmann_json
@ -254,6 +255,7 @@ target_include_directories( kicommon
.
${CMAKE_BINARY_DIR}
$<TARGET_PROPERTY:pegtl,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:kiapi,INTERFACE_INCLUDE_DIRECTORIES>
)
add_dependencies( kicommon pegtl version_header )
@ -335,6 +337,8 @@ set( COMMON_DLG_SRCS
dialogs/panel_mouse_settings_base.cpp
dialogs/panel_packages_and_updates.cpp
dialogs/panel_packages_and_updates_base.cpp
dialogs/panel_python_settings.cpp
dialogs/panel_python_settings_base.cpp
dialogs/panel_setup_netclasses.cpp
dialogs/panel_setup_netclasses_base.cpp
dialogs/panel_setup_severities.cpp
@ -608,6 +612,15 @@ set( COMMON_SRCS
)
if( KICAD_IPC_API )
set( COMMON_SRCS
${COMMON_SRCS}
api/api_server.cpp
api/api_handler.cpp
api/api_handler_common.cpp
)
endif()
add_library( common STATIC
${COMMON_SRCS}
)
@ -647,6 +660,12 @@ if( KICAD_USE_SENTRY )
sentry )
endif()
if( KICAD_IPC_API )
target_link_libraries( common
kinng
)
endif()
target_include_directories( common
PUBLIC
.
@ -656,19 +675,17 @@ target_include_directories( common
# text markup support
add_dependencies( common pegtl )
target_include_directories( common PUBLIC
$<TARGET_PROPERTY:pegtl,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:magic_enum,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:expected,INTERFACE_INCLUDE_DIRECTORIES>
)
target_include_directories( common SYSTEM PUBLIC
$<TARGET_PROPERTY:nanodbc,INTERFACE_INCLUDE_DIRECTORIES>
)
target_include_directories( common PUBLIC
$<TARGET_PROPERTY:magic_enum,INTERFACE_INCLUDE_DIRECTORIES>
)
set( PCB_COMMON_SRCS
fp_lib_table.cpp
hash_eda.cpp

View File

@ -110,6 +110,7 @@ static const wxChar OcePluginAngularDeflection[] = wxT( "OcePluginAngularDeflect
static const wxChar TriangulateSimplificationLevel[] = wxT( "TriangulateSimplificationLevel" );
static const wxChar TriangulateMinimumArea[] = wxT( "TriangulateMinimumArea" );
static const wxChar EnableCacheFriendlyFracture[] = wxT( "EnableCacheFriendlyFracture" );
static const wxChar EnableAPILogging[] = wxT( "EnableAPILogging" );
} // namespace KEYS
@ -244,6 +245,7 @@ ADVANCED_CFG::ADVANCED_CFG()
m_3DRT_BevelExtentFactor = 1.0 / 16.0;
m_UseClipper2 = true;
m_EnableAPILogging = false;
m_Use3DConnexionDriver = true;
@ -444,6 +446,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGenerators,
&m_EnableGenerators, m_EnableGenerators ) );
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableAPILogging,
&m_EnableAPILogging, m_EnableAPILogging ) );
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGit,
&m_EnableGit, m_EnableGit ) );

View File

@ -0,0 +1,74 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <api/api_handler.h>
using kiapi::common::ApiRequest, kiapi::common::ApiResponse, kiapi::common::ApiResponseStatus;
API_RESULT API_HANDLER::Handle( ApiRequest& aMsg )
{
ApiResponseStatus status;
if( !aMsg.has_message() )
{
status.set_status( ApiStatusCode::AS_BAD_REQUEST );
status.set_error_message( "request has no inner message" );
return tl::unexpected( status );
}
std::string typeName;
if( !google::protobuf::Any::ParseAnyTypeUrl( aMsg.message().type_url(), &typeName ) )
{
status.set_status( ApiStatusCode::AS_BAD_REQUEST );
status.set_error_message( "could not parse inner message type" );
return tl::unexpected( status );
}
auto it = m_handlers.find( typeName );
if( it != m_handlers.end() )
{
REQUEST_HANDLER& handler = it->second;
return handler( aMsg );
}
status.set_status( ApiStatusCode::AS_UNHANDLED );
// This response is used internally; no need for an error message
return tl::unexpected( status );
}
std::optional<KICAD_T> API_HANDLER::TypeNameFromAny( const google::protobuf::Any& aMessage )
{
static const std::map<std::string, KICAD_T> s_types = {
{ "type.googleapis.com/kiapi.board.types.Track", PCB_TRACE_T },
{ "type.googleapis.com/kiapi.board.types.Arc", PCB_ARC_T },
{ "type.googleapis.com/kiapi.board.types.Via", PCB_VIA_T },
};
auto it = s_types.find( aMessage.type_url() );
if( it != s_types.end() )
return it->second;
return std::nullopt;
}

View File

@ -0,0 +1,52 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <tuple>
#include <api/api_handler_common.h>
#include <build_version.h>
#include <pgm_base.h>
#include <wx/string.h>
using namespace kiapi::common::commands;
using namespace kiapi::common::types;
using google::protobuf::Empty;
API_HANDLER_COMMON::API_HANDLER_COMMON() :
API_HANDLER()
{
registerHandler<GetVersion, GetVersionResponse>( &API_HANDLER_COMMON::handleGetVersion );
}
HANDLER_RESULT<GetVersionResponse> API_HANDLER_COMMON::handleGetVersion( GetVersion& aMsg )
{
GetVersionResponse reply;
reply.mutable_version()->set_full_version( GetBuildVersion().ToStdString() );
std::tuple<int, int, int> version = GetMajorMinorPatchTuple();
reply.mutable_version()->set_major( std::get<0>( version ) );
reply.mutable_version()->set_minor( std::get<1>( version ) );
reply.mutable_version()->set_patch( std::get<2>( version ) );
return reply;
}

193
common/api/api_server.cpp Normal file
View File

@ -0,0 +1,193 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <wx/app.h>
#include <wx/datetime.h>
#include <wx/event.h>
#include <advanced_config.h>
#include <api/api_server.h>
#include <api/api_handler_common.h>
#include <kiid.h>
#include <kinng.h>
#include <paths.h>
#include <pgm_base.h>
#include <string_utils.h>
#include <api/common/envelope.pb.h>
using kiapi::common::ApiRequest, kiapi::common::ApiResponse, kiapi::common::ApiStatusCode;
wxString KICAD_API_SERVER::s_logFileName = "api.log";
wxDEFINE_EVENT( API_REQUEST_EVENT, wxCommandEvent );
KICAD_API_SERVER::KICAD_API_SERVER() :
wxEvtHandler(),
m_token( KIID().AsStdString() ),
m_readyToReply( false )
{
m_server = std::make_unique<KINNG_REQUEST_SERVER>();
m_server->SetCallback( [&]( std::string* aRequest ) { onApiRequest( aRequest ); } );
m_commonHandler = std::make_unique<API_HANDLER_COMMON>();
RegisterHandler( m_commonHandler.get() );
m_logFilePath.AssignDir( PATHS::GetLogsPath() );
m_logFilePath.SetName( s_logFileName );
if( ADVANCED_CFG::GetCfg().m_EnableAPILogging )
PATHS::EnsurePathExists( PATHS::GetLogsPath() );
log( "--- KiCad API server started ---\n" );
Bind( API_REQUEST_EVENT, &KICAD_API_SERVER::handleApiEvent, this );
}
KICAD_API_SERVER::~KICAD_API_SERVER()
{
}
void KICAD_API_SERVER::RegisterHandler( API_HANDLER* aHandler )
{
wxCHECK( aHandler, /* void */ );
m_handlers.insert( aHandler );
}
void KICAD_API_SERVER::DeregisterHandler( API_HANDLER* aHandler )
{
m_handlers.erase( aHandler );
}
void KICAD_API_SERVER::onApiRequest( std::string* aRequest )
{
if( !m_readyToReply )
{
ApiResponse notHandled;
notHandled.mutable_status()->set_status( ApiStatusCode::AS_NOT_READY );
notHandled.mutable_status()->set_error_message( "KiCad is not ready to reply" );
m_server->Reply( notHandled.SerializeAsString() );
log( "Got incoming request but was not yet ready to reply." );
return;
}
wxCommandEvent* evt = new wxCommandEvent( API_REQUEST_EVENT );
// We don't actually need write access to this string, but client data is non-const
evt->SetClientData( static_cast<void*>( aRequest ) );
// Takes ownership and frees the wxCommandEvent
QueueEvent( evt );
}
void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent )
{
std::string& requestString = *static_cast<std::string*>( aEvent.GetClientData() );
ApiRequest request;
if( !request.ParseFromString( requestString ) )
{
ApiResponse error;
error.mutable_header()->set_kicad_token( m_token );
error.mutable_status()->set_status( ApiStatusCode::AS_BAD_REQUEST );
error.mutable_status()->set_error_message( "request could not be parsed" );
m_server->Reply( error.SerializeAsString() );
log( "Response (ERROR): " + error.Utf8DebugString() );
}
log( "Request: " + request.Utf8DebugString() );
if( !request.header().kicad_token().empty() &&
request.header().kicad_token().compare( m_token ) != 0 )
{
ApiResponse error;
error.mutable_header()->set_kicad_token( m_token );
error.mutable_status()->set_status( ApiStatusCode::AS_TOKEN_MISMATCH );
error.mutable_status()->set_error_message(
"the provided kicad_token did not match this KiCad instance's token" );
m_server->Reply( error.SerializeAsString() );
log( "Response (ERROR): " + error.Utf8DebugString() );
}
API_RESULT result;
for( API_HANDLER* handler : m_handlers )
{
result = handler->Handle( request );
if( result.has_value() )
break;
else if( result.error().status() != ApiStatusCode::AS_UNHANDLED )
break;
}
// Note: at the point we call Reply(), we no longer own requestString.
if( result.has_value() )
{
result->mutable_header()->set_kicad_token( m_token );
m_server->Reply( result->SerializeAsString() );
log( "Response: " + result->Utf8DebugString() );
}
else
{
ApiResponse error;
error.mutable_status()->CopyFrom( result.error() );
error.mutable_header()->set_kicad_token( m_token );
if( result.error().status() == ApiStatusCode::AS_UNHANDLED )
{
std::string type = "<unparseable Any>";
google::protobuf::Any::ParseAnyTypeUrl( request.message().type_url(), &type );
std::string msg = fmt::format( "no handler available for request of type {}", type );
error.mutable_status()->set_error_message( msg );
}
m_server->Reply( error.SerializeAsString() );
log( "Response (ERROR): " + error.Utf8DebugString() );
}
}
void KICAD_API_SERVER::log( const std::string& aOutput )
{
if( !ADVANCED_CFG::GetCfg().m_EnableAPILogging )
return;
FILE* fp = wxFopen( m_logFilePath.GetFullPath(), wxT( "a" ) );
if( !fp )
return;
wxString out;
wxDateTime now = wxDateTime::Now();
fprintf( fp, "%s", TO_UTF8( out.Format( wxS( "%s: %s" ),
now.FormatISOCombined(), aOutput ) ) );
fclose( fp );
}

View File

@ -0,0 +1,109 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dialogs/panel_python_settings.h>
#include <widgets/ui_common.h>
#include <pgm_base.h>
#include <python_manager.h>
#include <settings/common_settings.h>
#include <settings/settings_manager.h>
PANEL_PYTHON_SETTINGS::PANEL_PYTHON_SETTINGS( wxWindow* aParent ) :
PANEL_PYTHON_SETTINGS_BASE( aParent )
{
wxFont helpFont = KIUI::GetInfoFont( this ).Italic();
m_stPythonStatus->SetFont( helpFont );
}
void PANEL_PYTHON_SETTINGS::ResetPanel()
{
}
bool PANEL_PYTHON_SETTINGS::TransferDataToWindow()
{
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
COMMON_SETTINGS* settings = mgr.GetCommonSettings();
m_pickerPythonInterpreter->SetFileName( settings->m_Python.interpreter_path );
validateInterpreter();
return true;
}
bool PANEL_PYTHON_SETTINGS::TransferDataFromWindow()
{
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
COMMON_SETTINGS* settings = mgr.GetCommonSettings();
if( m_interpreterValid )
settings->m_Python.interpreter_path = m_pickerPythonInterpreter->GetTextCtrlValue();
return true;
}
void PANEL_PYTHON_SETTINGS::OnPythonInterpreterChanged( wxFileDirPickerEvent& event )
{
validateInterpreter();
}
void PANEL_PYTHON_SETTINGS::OnBtnDetectAutomaticallyClicked( wxCommandEvent& aEvent )
{
}
void PANEL_PYTHON_SETTINGS::validateInterpreter()
{
m_interpreterValid = false;
wxFileName pythonExe( m_pickerPythonInterpreter->GetTextCtrlValue() );
if( !pythonExe.FileExists() )
{
m_stPythonStatus->SetLabel( _( "No valid Python interpreter chosen; external Python "
"plugins will not be available" ) );
return;
}
PYTHON_MANAGER manager( pythonExe.GetFullPath() );
manager.Execute( wxS( "--version" ),
[&]( int aRetCode, const wxString& aStdOut )
{
wxString msg;
if( aRetCode == 0 && aStdOut.Contains( wxS( "Python 3" ) ) )
{
msg = wxString::Format( _( "Found %s" ), aStdOut );
m_interpreterValid = true;
}
else
{
msg = _( "Not a valid Python 3 interpreter" );
}
m_stPythonStatus->SetLabel( msg );
} );
}

View File

@ -0,0 +1,70 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "panel_python_settings_base.h"
///////////////////////////////////////////////////////////////////////////
PANEL_PYTHON_SETTINGS_BASE::PANEL_PYTHON_SETTINGS_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : RESETTABLE_PANEL( parent, id, pos, size, style, name )
{
wxBoxSizer* bPanelSizer;
bPanelSizer = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bSizer8;
bSizer8 = new wxBoxSizer( wxVERTICAL );
wxStaticBoxSizer* sbSizer1;
sbSizer1 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Python Interpreter") ), wxVERTICAL );
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxHORIZONTAL );
m_staticText2 = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Path to Python interpreter:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText2->Wrap( -1 );
bSizer4->Add( m_staticText2, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_pickerPythonInterpreter = new wxFilePickerCtrl( sbSizer1->GetStaticBox(), wxID_ANY, wxEmptyString, _("Select the path to a Python interpreter"), _("*.*"), wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE|wxFLP_USE_TEXTCTRL );
bSizer4->Add( m_pickerPythonInterpreter, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
m_btnDetectAutomatically = new wxButton( sbSizer1->GetStaticBox(), wxID_ANY, _("Detect Automatically"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer4->Add( m_btnDetectAutomatically, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
sbSizer1->Add( bSizer4, 0, wxEXPAND, 5 );
m_stPythonStatus = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("No Python interpreter chosen; external Python plugins will not be available"), wxDefaultPosition, wxDefaultSize, 0 );
m_stPythonStatus->Wrap( -1 );
m_stPythonStatus->SetToolTip( _("Python interpreter status") );
sbSizer1->Add( m_stPythonStatus, 0, wxALL, 5 );
bSizer8->Add( sbSizer1, 0, wxALL|wxEXPAND, 5 );
bSizer8->Add( 0, 0, 1, wxEXPAND, 5 );
bPanelSizer->Add( bSizer8, 1, wxEXPAND, 5 );
this->SetSizer( bPanelSizer );
this->Layout();
bPanelSizer->Fit( this );
// Connect Events
m_pickerPythonInterpreter->Connect( wxEVT_COMMAND_FILEPICKER_CHANGED, wxFileDirPickerEventHandler( PANEL_PYTHON_SETTINGS_BASE::OnPythonInterpreterChanged ), NULL, this );
m_btnDetectAutomatically->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PYTHON_SETTINGS_BASE::OnBtnDetectAutomaticallyClicked ), NULL, this );
}
PANEL_PYTHON_SETTINGS_BASE::~PANEL_PYTHON_SETTINGS_BASE()
{
// Disconnect Events
m_pickerPythonInterpreter->Disconnect( wxEVT_COMMAND_FILEPICKER_CHANGED, wxFileDirPickerEventHandler( PANEL_PYTHON_SETTINGS_BASE::OnPythonInterpreterChanged ), NULL, this );
m_btnDetectAutomatically->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_PYTHON_SETTINGS_BASE::OnBtnDetectAutomaticallyClicked ), NULL, this );
}

View File

@ -0,0 +1,371 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="16" />
<object class="Project" expanded="1">
<property name="class_decoration"></property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="disconnect_mode">source_name</property>
<property name="disconnect_php_events">0</property>
<property name="disconnect_python_events">0</property>
<property name="embedded_files_path">res</property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">panel_python_settings_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">PanelPythonSettings</property>
<property name="namespace"></property>
<property name="path">.</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_lua_events">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_array_enum">0</property>
<property name="use_enum">1</property>
<property name="use_microsoft_bom">0</property>
<object class="Panel" expanded="1">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">PANEL_PYTHON_SETTINGS_BASE</property>
<property name="pos"></property>
<property name="size">-1,-1</property>
<property name="subclass">RESETTABLE_PANEL; widgets/resettable_panel.h; Not forward_declare</property>
<property name="tooltip"></property>
<property name="two_step_creation">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bPanelSizer</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer8</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxStaticBoxSizer" expanded="1">
<property name="id">wxID_ANY</property>
<property name="label">Python Interpreter</property>
<property name="minimum_size"></property>
<property name="name">sbSizer1</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer4</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Path to Python interpreter:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText2</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="proportion">1</property>
<object class="wxFilePickerCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="message">Select the path to a Python interpreter</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_pickerPythonInterpreter</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxFLP_DEFAULT_STYLE|wxFLP_USE_TEXTCTRL</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="wildcard">*.*</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnFileChanged">OnPythonInterpreterChanged</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Detect Automatically</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_btnDetectAutomatically</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">OnBtnDetectAutomaticallyClicked</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">No Python interpreter chosen; external Python plugins will not be available</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_stPythonStatus</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">public</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Python interpreter status</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -0,0 +1,56 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include "widgets/resettable_panel.h"
#include <wx/string.h>
#include <wx/stattext.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/filepicker.h>
#include <wx/button.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/sizer.h>
#include <wx/statbox.h>
#include <wx/panel.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class PANEL_PYTHON_SETTINGS_BASE
///////////////////////////////////////////////////////////////////////////////
class PANEL_PYTHON_SETTINGS_BASE : public RESETTABLE_PANEL
{
private:
protected:
wxStaticText* m_staticText2;
wxFilePickerCtrl* m_pickerPythonInterpreter;
wxButton* m_btnDetectAutomatically;
// Virtual event handlers, override them in your derived class
virtual void OnPythonInterpreterChanged( wxFileDirPickerEvent& event ) { event.Skip(); }
virtual void OnBtnDetectAutomaticallyClicked( wxCommandEvent& event ) { event.Skip(); }
public:
wxStaticText* m_stPythonStatus;
PANEL_PYTHON_SETTINGS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_PYTHON_SETTINGS_BASE();
};

View File

@ -32,6 +32,7 @@
#include <dialogs/panel_common_settings.h>
#include <dialogs/panel_mouse_settings.h>
#include <dialogs/panel_data_collection.h>
#include <dialogs/panel_python_settings.h>
#include <eda_dde.h>
#include <file_history.h>
#include <id.h>
@ -1235,6 +1236,10 @@ void EDA_BASE_FRAME::ShowPreferences( wxString aStartPage, wxString aStartParent
{
}
#ifdef KICAD_IPC_API
book->AddPage( new PANEL_PYTHON_SETTINGS( book ), _( "Python Scripting" ) );
#endif
// Update all of the action hotkeys. The process of loading the actions through
// the KiFACE will only get us the default hotkeys
ReadHotKeyConfigIntoActions( wxEmptyString, hotkeysPanel->ActionsList() );

View File

@ -260,6 +260,12 @@ wxString KIID::AsString() const
}
std::string KIID::AsStdString() const
{
return boost::uuids::to_string( m_uuid );
}
wxString KIID::AsLegacyTimestampString() const
{
return wxString::Format( "%8.8lX", (unsigned long) AsLegacyTimestamp() );

View File

@ -399,6 +399,17 @@ wxString PATHS::GetInstanceCheckerPath()
}
wxString PATHS::GetLogsPath()
{
wxFileName tmp;
getUserDocumentPath( tmp );
tmp.AppendDir( wxT( "logs" ) );
return tmp.GetPath();
}
bool PATHS::EnsurePathExists( const wxString& aPath )
{
wxFileName path( aPath );

View File

@ -74,6 +74,10 @@
#include <build_version.h>
#endif
#ifdef KICAD_IPC_API
#include <api/api_server.h>
#endif
/**
* Current list of languages supported by KiCad.
*

View File

@ -53,7 +53,8 @@ COMMON_SETTINGS::COMMON_SETTINGS() :
m_System(),
m_DoNotShowAgain(),
m_NetclassPanel(),
m_PackageManager()
m_PackageManager(),
m_Python()
{
/*
* Automatic dark mode detection works fine on Mac.
@ -397,7 +398,8 @@ COMMON_SETTINGS::COMMON_SETTINGS() :
m_params.emplace_back( new PARAM<bool>( "git.useDefaultAuthor",
&m_Git.useDefaultAuthor, true ) );
m_params.emplace_back( new PARAM<wxString>( "python.interpreter_path",
&m_Python.interpreter_path, wxS( "" ) ) );
registerMigration( 0, 1, std::bind( &COMMON_SETTINGS::migrateSchema0to1, this ) );
registerMigration( 1, 2, std::bind( &COMMON_SETTINGS::migrateSchema1to2, this ) );

View File

@ -58,6 +58,10 @@
#include <sentry.h>
#endif
#ifdef KICAD_IPC_API
#include <api/api_server.h>
#endif
// Only a single KIWAY is supported in this single_top top level component,
// which is dedicated to loading only a single DSO.
KIWAY Kiway( KFCTL_STANDALONE );
@ -78,6 +82,10 @@ static struct PGM_SINGLE_TOP : public PGM_BASE
Kiway.OnKiwayEnd();
#ifdef KICAD_IPC_API
m_api_server.reset();
#endif
if( m_settings_manager && m_settings_manager->IsOK() )
{
SaveCommonSettings();
@ -350,6 +358,11 @@ bool PGM_SINGLE_TOP::OnPgmInit()
GetSettingsManager().RegisterSettings( new KICAD_SETTINGS );
#ifdef KICAD_IPC_API
// Create the API server thread once the app event loop exists
m_api_server = std::make_unique<KICAD_API_SERVER>();
#endif
// Use KIWAY to create a top window, which registers its existence also.
// "TOP_FRAME" is a macro that is passed on compiler command line from CMake,
// and is one of the types in FRAME_T.
@ -424,5 +437,9 @@ bool PGM_SINGLE_TOP::OnPgmInit()
frame->OpenProjectFiles( fileArgs );
}
#ifdef KICAD_IPC_API
m_api_server->SetReadyToReply();
#endif
return true;
}

View File

@ -302,7 +302,7 @@ bool TOOL_MANAGER::doRunAction( const std::string& aActionName, bool aNow, const
doRunAction( *action, aNow, aParam, aCommit );
return false;
return true;
}

View File

@ -88,7 +88,7 @@ void SCH_MARKER::SwapData( SCH_ITEM* aItem )
}
wxString SCH_MARKER::Serialize() const
wxString SCH_MARKER::SerializeToString() const
{
std::shared_ptr<ERC_ITEM> erc = std::static_pointer_cast<ERC_ITEM>( m_rcItem );
wxString sheetSpecificPath, mainItemPath, auxItemPath;
@ -109,7 +109,7 @@ wxString SCH_MARKER::Serialize() const
}
SCH_MARKER* SCH_MARKER::Deserialize( SCHEMATIC* schematic, const wxString& data )
SCH_MARKER* SCH_MARKER::DeserializeFromString( SCHEMATIC* schematic, const wxString& data )
{
wxArrayString props = wxSplit( data, '|' );
VECTOR2I markerPos( (int) strtol( props[1].c_str(), nullptr, 10 ),

View File

@ -53,8 +53,8 @@ public:
void SwapData( SCH_ITEM* aItem ) override;
wxString Serialize() const;
static SCH_MARKER* Deserialize( SCHEMATIC* schematic, const wxString& data );
wxString SerializeToString() const;
static SCH_MARKER* DeserializeFromString( SCHEMATIC* schematic, const wxString& data );
void ViewGetLayers( int aLayers[], int& aCount ) const override;

View File

@ -312,7 +312,7 @@ std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
for( auto it = settings.m_ErcExclusions.begin(); it != settings.m_ErcExclusions.end(); )
{
SCH_MARKER* testMarker = SCH_MARKER::Deserialize( this, *it );
SCH_MARKER* testMarker = SCH_MARKER::DeserializeFromString( this, *it );
if( testMarker->IsLegacyMarker() )
{
@ -322,7 +322,7 @@ std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
&& settingsKey != wxT( "hier_label_mismatch" )
&& settingsKey != wxT( "different_unit_net" ) )
{
migratedExclusions.insert( testMarker->Serialize() );
migratedExclusions.insert( testMarker->SerializeToString() );
}
it = settings.m_ErcExclusions.erase( it );
@ -344,7 +344,7 @@ std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
{
SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
std::set<wxString>::iterator it = settings.m_ErcExclusions.find( serialized );
if( it != settings.m_ErcExclusions.end() )
@ -359,7 +359,7 @@ std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
for( const wxString& serialized : settings.m_ErcExclusions )
{
SCH_MARKER* marker = SCH_MARKER::Deserialize( this, serialized );
SCH_MARKER* marker = SCH_MARKER::DeserializeFromString( this, serialized );
if( marker )
{
@ -811,7 +811,7 @@ void SCHEMATIC::RecordERCExclusions()
if( marker->IsExcluded() )
{
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
ercSettings.m_ErcExclusions.insert( serialized );
ercSettings.m_ErcExclusionComments[ serialized ] = marker->GetComment();
}

View File

@ -576,8 +576,11 @@ public:
*/
bool m_EnableCacheFriendlyFracture;
///@}
/**
* Log IPC API requests and responses
*/
bool m_EnableAPILogging;
///@}
private:
ADVANCED_CFG();

137
include/api/api_handler.h Normal file
View File

@ -0,0 +1,137 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_HANDLER_H
#define KICAD_API_HANDLER_H
#include <functional>
#include <optional>
#include <fmt/format.h>
#include <tl/expected.hpp>
#include <wx/debug.h>
#include <wx/string.h>
#include <google/protobuf/message.h>
#include <import_export.h>
#include <api/common/envelope.pb.h>
#include <core/typeinfo.h>
using kiapi::common::ApiRequest, kiapi::common::ApiResponse;
using kiapi::common::ApiResponseStatus, kiapi::common::ApiStatusCode;
typedef tl::expected<ApiResponse, ApiResponseStatus> API_RESULT;
template <typename T>
using HANDLER_RESULT = tl::expected<T, ApiResponseStatus>;
class API_HANDLER
{
public:
API_HANDLER() {}
/**
* Attempt to handle the given API request, if a handler exists in this class for the message.
* @param aMsg is a request to attempt to handle
* @return a response to send to the client, or an appropriate error
*/
API_RESULT Handle( ApiRequest& aMsg );
static std::optional<KICAD_T> TypeNameFromAny( const google::protobuf::Any& aMessage );
protected:
/**
* A handler for outer messages (envelopes) that will unpack to inner messages and call a
* specific handler function. @see registerHandler.
*/
typedef std::function<HANDLER_RESULT<ApiResponse>( ApiRequest& )> REQUEST_HANDLER;
/**
* Registers an API command handler for the given message types.
*
* When an API request matching the given type comes in, the handler will be called and its
* response will be packed into an envelope for sending back to the API client.
*
* If the given message does not unpack into the request type, an envelope is returned with
* status AS_BAD_REQUEST, which probably indicates corruption in the message.
*
* @tparam RequestType is a protobuf message type containing a command
* @tparam ResponseType is a protobuf message type containing a command response
* @tparam HandlerType is the implied type of the API_HANDLER subclass
* @param aHandler is the handler function for the given request and response types
*/
template <class RequestType, class ResponseType, class HandlerType>
void registerHandler( HANDLER_RESULT<ResponseType>( HandlerType::* aHandler )( RequestType& ) )
{
std::string typeName = RequestType().GetTypeName();
wxASSERT_MSG( !m_handlers.count( typeName ),
wxString::Format( "Duplicate API handler for type %s", typeName ) );
m_handlers[typeName] =
[=]( ApiRequest& aRequest ) -> API_RESULT
{
RequestType command;
ApiResponse envelope;
if( !tryUnpack( aRequest, envelope, command ) )
return envelope;
HANDLER_RESULT<ResponseType> response =
std::invoke( aHandler, static_cast<HandlerType*>( this ), command );
if( response.has_value() )
{
envelope.mutable_status()->set_status( ApiStatusCode::AS_OK );
envelope.mutable_message()->PackFrom( *response );
return envelope;
}
else
{
return tl::unexpected( response.error() );
}
};
}
/// Maps type name (without the URL prefix) to a handler method
std::map<std::string, REQUEST_HANDLER> m_handlers;
private:
template<typename MessageType>
bool tryUnpack( ApiRequest& aRequest, ApiResponse& aReply, MessageType& aDest )
{
if( !aRequest.message().UnpackTo( &aDest ) )
{
std::string msg = fmt::format( "could not unpack message of type {} from request",
aDest.GetTypeName() );
aReply.mutable_status()->set_status( ApiStatusCode::AS_BAD_REQUEST );
aReply.mutable_status()->set_error_message( msg );
return false;
}
return true;
}
};
#endif //KICAD_API_HANDLER_H

View File

@ -0,0 +1,42 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_HANDLER_COMMON_H
#define KICAD_API_HANDLER_COMMON_H
#include <api/api_handler.h>
#include <api/common/commands/base_commands.pb.h>
#include <api/common/commands/editor_commands.pb.h>
#include <google/protobuf/empty.pb.h>
using namespace kiapi;
using namespace kiapi::common;
class API_HANDLER_COMMON : public API_HANDLER
{
public:
API_HANDLER_COMMON();
private:
HANDLER_RESULT<commands::GetVersionResponse> handleGetVersion( commands::GetVersion& aMsg );
};
#endif //KICAD_API_HANDLER_COMMON_H

97
include/api/api_server.h Normal file
View File

@ -0,0 +1,97 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_SERVER_H
#define KICAD_API_SERVER_H
#include <memory>
#include <set>
#include <string>
#include <wx/event.h>
#include <wx/filename.h>
class API_HANDLER;
class API_HANDLER_COMMON;
class KINNG_REQUEST_SERVER;
class wxEvtHandler;
wxDECLARE_EVENT( API_REQUEST_EVENT, wxCommandEvent );
class KICAD_API_SERVER : public wxEvtHandler
{
public:
KICAD_API_SERVER();
~KICAD_API_SERVER();
/**
* Adds a new request handler to the server. Each handler maintains its own list of API
* messages that it knows how to handle, and the server will pass every incoming message to all
* handlers in succession until one of them handles it.
*
* The caller is responsible for the lifetime of the handler and must call DeregisterHandler
* before the pointer is freed.
*
* @param aHandler is a pointer (non-owned) to API_HANDLER
*/
void RegisterHandler( API_HANDLER* aHandler );
void DeregisterHandler( API_HANDLER* aHandler );
void SetReadyToReply( bool aReady = true ) { m_readyToReply = aReady; }
private:
/**
* Callback that executes on the server thread and generates an event that will be handled by
* the wxWidgets event loop to process an incoming request. Temporarily takes ownership of the
* request pointer so that it can be passed through the event system.
*
* @param aRequest is a pointer to a string containing bytes that came in over the wire
*/
void onApiRequest( std::string* aRequest );
/**
* Event handler that receives the event on the main thread sent by onApiRequest
* @param aEvent will contain a pointer to an incoming API request string in the client data
*/
void handleApiEvent( wxCommandEvent& aEvent );
void log( const std::string& aOutput );
std::unique_ptr<KINNG_REQUEST_SERVER> m_server;
std::set<API_HANDLER*> m_handlers;
std::string m_token;
bool m_readyToReply;
std::unique_ptr<API_HANDLER_COMMON> m_commonHandler;
static wxString s_logFileName;
wxFileName m_logFilePath;
};
#endif //KICAD_API_SERVER_H

View File

@ -0,0 +1,49 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_PANEL_PYTHON_SETTINGS_H
#define KICAD_PANEL_PYTHON_SETTINGS_H
#include <dialogs/panel_python_settings_base.h>
class PAGED_DIALOG;
class PANEL_PYTHON_SETTINGS : public PANEL_PYTHON_SETTINGS_BASE
{
public:
PANEL_PYTHON_SETTINGS( wxWindow* aParent );
void ResetPanel() override;
protected:
bool TransferDataFromWindow() override;
bool TransferDataToWindow() override;
void OnPythonInterpreterChanged( wxFileDirPickerEvent& event ) override;
void OnBtnDetectAutomaticallyClicked( wxCommandEvent& aEvent ) override;
private:
void validateInterpreter();
bool m_interpreterValid;
};
#endif //KICAD_PANEL_PYTHON_SETTINGS_H

View File

@ -52,6 +52,8 @@ class UNITS_PROVIDER;
class EDA_DRAW_FRAME;
class MSG_PANEL_ITEM;
namespace google { namespace protobuf { class Any; } }
/**
* Used to inspect and possibly collect the (search) results of iterating over a list or
@ -436,6 +438,10 @@ public:
virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
virtual void Serialize( google::protobuf::Any &aContainer ) const {}
virtual bool Deserialize( const google::protobuf::Any &aContainer ) { return false; }
#if defined(DEBUG)
/**

View File

@ -64,6 +64,7 @@ public:
wxString AsString() const;
wxString AsLegacyTimestampString() const;
std::string AsStdString() const;
/**
* Returns true if a string has the correct formatting to be a KIID.

View File

@ -61,8 +61,7 @@ enum PCB_LAYER_ID: int
UNDEFINED_LAYER = -1,
UNSELECTED_LAYER = -2,
PCBNEW_LAYER_ID_START = 0,
F_Cu = PCBNEW_LAYER_ID_START,
F_Cu = 0,
In1_Cu,
In2_Cu,
In3_Cu,
@ -138,6 +137,8 @@ enum PCB_LAYER_ID: int
PCB_LAYER_ID_COUNT
};
constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START = F_Cu;
#define MAX_CU_LAYERS (B_Cu - F_Cu + 1)
/**

View File

@ -153,6 +153,11 @@ public:
*/
static wxString GetInstanceCheckerPath();
/**
* Gets a path to use for user-visible log files
*/
static wxString GetLogsPath();
/**
* Attempts to create a given path if it does not exist
*/

View File

@ -52,6 +52,10 @@ class COMMON_SETTINGS;
class SETTINGS_MANAGER;
class SCRIPTING;
#ifdef KICAD_IPC_API
class KICAD_API_SERVER;
#endif
/**
* A small class to handle the list of existing translations.
*
@ -142,6 +146,10 @@ public:
virtual NOTIFICATIONS_MANAGER& GetNotificationsManager() const { return *m_notifications_manager; }
#ifdef KICAD_IPC_API
KICAD_API_SERVER& GetApiServer() { return *m_api_server; }
#endif
virtual void SetTextEditor( const wxString& aFileName );
/**
@ -403,6 +411,9 @@ protected:
/// Checks if there is another copy of Kicad running at the same time
std::unique_ptr<wxSingleInstanceChecker> m_pgm_checker;
#ifdef KICAD_IPC_API
std::unique_ptr<KICAD_API_SERVER> m_api_server;
#endif
wxString m_kicad_env; /// The KICAD system environment variable.

View File

@ -37,6 +37,10 @@
#include <wx/validate.h> // required for propgrid
#include <wx/propgrid/property.h>
#ifdef DEBUG
#include <wx/wxcrt.h>
#endif
#include <functional>
#include <unordered_map>
#include <memory>

View File

@ -166,6 +166,11 @@ public:
wxString authorEmail;
};
struct PYTHON
{
wxString interpreter_path;
};
COMMON_SETTINGS();
virtual ~COMMON_SETTINGS() {}
@ -217,6 +222,8 @@ public:
PACKAGE_MANAGER m_PackageManager;
GIT m_Git;
PYTHON m_Python;
};
#endif

View File

@ -108,6 +108,11 @@ if( MSVC )
set_target_properties(kicad-cli PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(kicad-cli PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
set_target_properties(kicad-cli PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
# Allow running from build dir
add_custom_command( TARGET kicad POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:kiapi>" "$<TARGET_FILE_DIR:kicad>"
)
endif()
if( UNIX )

View File

@ -57,6 +57,10 @@
#include <kiplatform/app.h>
#include <kiplatform/environment.h>
#ifdef KICAD_IPC_API
#include <api/api_server.h>
#endif
// a dummy to quiet linking with EDA_BASE_FRAME::config();
#include <kiface_base.h>
@ -237,6 +241,10 @@ bool PGM_KICAD::OnPgmInit()
KICAD_SETTINGS* settings = static_cast<KICAD_SETTINGS*>( PgmSettings() );
#ifdef KICAD_IPC_API
m_api_server = std::make_unique<KICAD_API_SERVER>();
#endif
wxString projToLoad;
HideSplash();
@ -348,6 +356,10 @@ bool PGM_KICAD::OnPgmInit()
frame->Show( true );
frame->Raise();
#ifdef KICAD_IPC_API
m_api_server->SetReadyToReply();
#endif
return true;
}
@ -362,6 +374,10 @@ void PGM_KICAD::OnPgmExit()
{
Kiway.OnKiwayEnd();
#ifdef KICAD_IPC_API
m_api_server.reset();
#endif
if( m_settings_manager && m_settings_manager->IsOK() )
{
SaveCommonSettings();

View File

@ -26,3 +26,7 @@ add_subdirectory( core )
add_subdirectory( kimath )
add_subdirectory( kiplatform )
add_subdirectory( sexpr )
if( KICAD_IPC_API )
add_subdirectory( kinng )
endif()

38
libs/kinng/CMakeLists.txt Normal file
View File

@ -0,0 +1,38 @@
# This program source code file is part of KiCad, a free EDA CAD application.
#
# Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
set( KINNG_SRCS
src/kinng.cpp
)
add_library( kinng STATIC
${KINNG_SRCS}
)
target_link_libraries( kinng
${NNG_LIBRARY}
)
target_include_directories( kinng PUBLIC
${PROJECT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_include_directories( kinng PRIVATE
${PROJECT_SOURCE_DIR}/include
${NNG_INCLUDE_DIR}
)

View File

@ -0,0 +1,64 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_KINNG_H
#define KICAD_KINNG_H
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
class KINNG_REQUEST_SERVER
{
public:
KINNG_REQUEST_SERVER();
~KINNG_REQUEST_SERVER();
bool Start();
void Stop();
void SetCallback( std::function<void(std::string*)> aFunc ) { m_callback = aFunc; }
void Reply( const std::string& aReply );
private:
void listenThread();
std::thread m_thread;
std::atomic<bool> m_shutdown;
std::string m_socketUrl;
std::function<void(std::string*)> m_callback;
std::string m_pendingReply;
std::condition_variable m_replyReady;
std::mutex m_mutex;
};
#endif //KICAD_KINNG_H

129
libs/kinng/src/kinng.cpp Normal file
View File

@ -0,0 +1,129 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <kinng.h>
#include <nng/nng.h>
#include <nng/protocol/reqrep0/rep.h>
#include <nng/protocol/reqrep0/req.h>
KINNG_REQUEST_SERVER::KINNG_REQUEST_SERVER() :
m_callback()
{
#ifdef WIN32
m_socketUrl = "ipc://\\.\\pipe\\kicad";
#else
m_socketUrl = "ipc:///tmp/kicad.sock";
#endif
Start();
}
KINNG_REQUEST_SERVER::~KINNG_REQUEST_SERVER()
{
Stop();
}
bool KINNG_REQUEST_SERVER::Start()
{
m_shutdown.store( false );
m_thread = std::thread( [&]() { listenThread(); } );
return true;
}
void KINNG_REQUEST_SERVER::Stop()
{
if( !m_thread.joinable() )
return;
{
std::lock_guard<std::mutex> lock( m_mutex );
m_replyReady.notify_all();
}
m_shutdown.store( true );
m_thread.join();
}
void KINNG_REQUEST_SERVER::Reply( const std::string& aReply )
{
std::lock_guard<std::mutex> lock( m_mutex );
m_pendingReply = aReply;
m_replyReady.notify_all();
}
void KINNG_REQUEST_SERVER::listenThread()
{
nng_socket socket;
nng_listener listener;
int retCode = 0;
retCode = nng_rep0_open( &socket );
if( retCode != 0 )
return;
retCode = nng_listener_create( &listener, socket, m_socketUrl.c_str() );
if( retCode != 0 )
return;
nng_socket_set_ms( socket, NNG_OPT_RECVTIMEO, 500 );
nng_listener_start( listener, 0 );
while( !m_shutdown.load() )
{
char* buf = nullptr;
size_t sz;
uint64_t val;
retCode = nng_recv( socket, &buf, &sz, NNG_FLAG_ALLOC );
if( retCode == NNG_ETIMEDOUT )
continue;
if( retCode != 0 )
{
nng_free( buf, sz );
break;
}
std::string message( buf, sz );
if( m_callback )
m_callback( &message );
std::unique_lock<std::mutex> lock( m_mutex );
m_replyReady.wait( lock, [&]() { return !m_pendingReply.empty(); } );
retCode = nng_send( socket, const_cast<std::string::value_type*>( m_pendingReply.c_str() ),
m_pendingReply.length(), 0 );
m_pendingReply.clear();
}
nng_close( socket );
}

View File

@ -425,6 +425,12 @@ set( PCBNEW_SCRIPTING_PYTHON_HELPERS
python/scripting/pcb_scripting_tool.cpp
)
if( KICAD_IPC_API )
set( PCBNEW_SRCS ${PCBNEW_SRCS}
api/api_handler_pcb.cpp
)
endif()
if( COMPILER_SUPPORTS_WARNINGS )
# Only compile our source files with the warnings, since the SWIG generated
@ -719,7 +725,6 @@ set( PCBNEW_KIFACE_LIBRARIES
${PCBNEW_EXTRA_LIBS} # -lrt must follow Boost
)
target_link_libraries( pcbnew_kiface
PRIVATE
${PCBNEW_KIFACE_LIBRARIES}
@ -742,6 +747,9 @@ if( WIN32 )
add_custom_command( TARGET pcbnew POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:kicad_3dsg>" "$<TARGET_FILE_DIR:pcbnew>"
)
add_custom_command( TARGET pcbnew POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:kiapi>" "$<TARGET_FILE_DIR:pcbnew>"
)
endif()
# these 2 binaries are a matched set, keep them together:

View File

@ -0,0 +1,493 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <magic_enum.hpp>
#include <api/api_handler_pcb.h>
#include <board_commit.h>
#include <pcb_edit_frame.h>
#include <pcb_track.h>
#include <tool/tool_manager.h>
#include <api/common/types/base_types.pb.h>
using namespace kiapi::common::commands;
using kiapi::common::types::CommandStatus;
using kiapi::common::types::DocumentType;
using kiapi::common::types::ItemRequestStatus;
static const wxString s_defaultCommitMessage = wxS( "Modification from API" );
API_HANDLER_PCB::API_HANDLER_PCB( PCB_EDIT_FRAME* aFrame ) :
API_HANDLER(),
m_frame( aFrame )
{
registerHandler<RunAction, RunActionResponse>( &API_HANDLER_PCB::handleRunAction );
registerHandler<GetOpenDocuments, GetOpenDocumentsResponse>(
&API_HANDLER_PCB::handleGetOpenDocuments );
registerHandler<BeginCommit, BeginCommitResponse>( &API_HANDLER_PCB::handleBeginCommit );
registerHandler<EndCommit, EndCommitResponse>( &API_HANDLER_PCB::handleEndCommit );
registerHandler<CreateItems, CreateItemsResponse>( &API_HANDLER_PCB::handleCreateItems );
registerHandler<GetItems, GetItemsResponse>( &API_HANDLER_PCB::handleGetItems );
registerHandler<UpdateItems, UpdateItemsResponse>( &API_HANDLER_PCB::handleUpdateItems );
registerHandler<DeleteItems, DeleteItemsResponse>( &API_HANDLER_PCB::handleDeleteItems );
}
HANDLER_RESULT<RunActionResponse> API_HANDLER_PCB::handleRunAction( RunAction& aRequest )
{
RunActionResponse response;
if( m_frame->GetToolManager()->RunAction( aRequest.action(), true ) )
response.set_status( RunActionStatus::RAS_OK );
else
response.set_status( RunActionStatus::RAS_INVALID );
return response;
}
HANDLER_RESULT<GetOpenDocumentsResponse> API_HANDLER_PCB::handleGetOpenDocuments(
GetOpenDocuments& aMsg )
{
if( aMsg.type() != DocumentType::DOCTYPE_PCB )
{
ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
GetOpenDocumentsResponse response;
common::types::DocumentSpecifier doc;
wxFileName fn( m_frame->GetCurrentFileName() );
doc.set_type( DocumentType::DOCTYPE_PCB );
doc.set_board_filename( fn.GetFullName() );
response.mutable_documents()->Add( std::move( doc ) );
return response;
}
HANDLER_RESULT<BeginCommitResponse> API_HANDLER_PCB::handleBeginCommit( BeginCommit& aMsg )
{
BeginCommitResponse response;
if( m_commit )
{
// TODO: right now there is no way for m_transactionInProgress to be true here, but
// we should still check it as a safety measure and return a specific error
//if( !m_transactionInProgress )
m_commit->Revert();
}
m_commit.reset( new BOARD_COMMIT( m_frame ) );
// TODO: return an opaque ID for this new commit to make this more robust
m_transactionInProgress = true;
return response;
}
HANDLER_RESULT<EndCommitResponse> API_HANDLER_PCB::handleEndCommit( EndCommit& aMsg )
{
EndCommitResponse response;
// TODO: return more specific error if m_transactionInProgress is false
if( !m_transactionInProgress )
{
// Make sure we don't get stuck with a commit we can never push
m_commit.reset();
response.set_result( CommitResult::CR_NO_COMMIT );
return response;
}
if( !m_commit )
{
response.set_result( CommitResult::CR_NO_COMMIT );
return response;
}
pushCurrentCommit( aMsg.message() );
m_transactionInProgress = false;
response.set_result( CommitResult::CR_OK );
return response;
}
BOARD_COMMIT* API_HANDLER_PCB::getCurrentCommit()
{
if( !m_commit )
m_commit.reset( new BOARD_COMMIT( m_frame ) );
return m_commit.get();
}
void API_HANDLER_PCB::pushCurrentCommit( const std::string& aMessage )
{
wxCHECK( m_commit, /* void */ );
wxString msg( aMessage.c_str(), wxConvUTF8 );
if( msg.IsEmpty() )
msg = s_defaultCommitMessage;
m_commit->Push( msg );
m_commit.reset();
m_frame->Refresh();
}
bool API_HANDLER_PCB::validateItemHeaderDocument( const common::types::ItemHeader& aHeader )
{
// TODO: this should return a more complex error type.
// We should provide detailed feedback when a header fails validation, and distinguish between
// "skip this handler" and "this is the right handler, but the request is invalid"
if( !aHeader.has_document() || aHeader.document().type() != DocumentType::DOCTYPE_PCB )
return false;
wxFileName fn( m_frame->GetCurrentFileName() );
return aHeader.document().board_filename().compare( fn.GetFullName() ) == 0;
}
std::unique_ptr<BOARD_ITEM> API_HANDLER_PCB::createItemForType( KICAD_T aType,
BOARD_ITEM_CONTAINER* aContainer )
{
switch( aType )
{
case PCB_TRACE_T: return std::make_unique<PCB_TRACK>( aContainer );
case PCB_ARC_T: return std::make_unique<PCB_ARC>( aContainer );
case PCB_VIA_T: return std::make_unique<PCB_VIA>( aContainer );
default: return nullptr;
}
}
HANDLER_RESULT<CreateItemsResponse> API_HANDLER_PCB::handleCreateItems( CreateItems& aMsg )
{
ApiResponseStatus e;
if( !validateItemHeaderDocument( aMsg.header() ) )
{
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
BOARD* board = m_frame->GetBoard();
BOARD_ITEM_SET boardItems = board->GetItemSet();
std::map<KIID, BOARD_ITEM*> itemUuidMap;
std::for_each( boardItems.begin(), boardItems.end(),
[&]( BOARD_ITEM* aItem )
{
itemUuidMap[aItem->m_Uuid] = aItem;
} );
BOARD_COMMIT* commit = getCurrentCommit();
CreateItemsResponse response;
for( const google::protobuf::Any& anyItem : aMsg.items() )
{
ItemCreationResult itemResult;
std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
if( !type )
{
itemResult.set_status( ItemCreationStatus::ICS_INVALID_TYPE );
response.mutable_created_items()->Add( std::move( itemResult ) );
continue;
}
std::unique_ptr<BOARD_ITEM> item = createItemForType( *type, board );
if( !item )
{
itemResult.set_status( ItemCreationStatus::ICS_INVALID_TYPE );
e.set_error_message( fmt::format( "item type {} not supported for board",
magic_enum::enum_name( *type ) ) );
response.mutable_created_items()->Add( std::move( itemResult ) );
continue;
}
if( !item->Deserialize( anyItem ) )
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "could not unpack {} from request",
item->GetClass().ToStdString() ) );
return tl::unexpected( e );
}
if( itemUuidMap.count( item->m_Uuid ) )
{
itemResult.set_status( ItemCreationStatus::ICS_EXISTING );
response.mutable_created_items()->Add( std::move( itemResult ) );
continue;
}
itemResult.set_status( ItemCreationStatus::ICS_OK );
item->Serialize( *itemResult.mutable_item() );
commit->Add( item.release() );
response.mutable_created_items()->Add( std::move( itemResult ) );
}
pushCurrentCommit( "Added items via API" );
response.set_status( ItemRequestStatus::IRS_OK );
return response;
}
HANDLER_RESULT<GetItemsResponse> API_HANDLER_PCB::handleGetItems( GetItems& aMsg )
{
if( !validateItemHeaderDocument( aMsg.header() ) )
{
ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
GetItemsResponse response;
BOARD* board = m_frame->GetBoard();
std::vector<BOARD_ITEM*> items;
std::set<KICAD_T> typesRequested, typesInserted;
bool handledAnything = false;
for( const common::types::ItemType& typeMessage : aMsg.types() )
{
KICAD_T type;
if( std::optional<KICAD_T> opt_type = magic_enum::enum_cast<KICAD_T>( typeMessage.type() ) )
type = *opt_type;
else
continue;
typesRequested.emplace( type );
if( typesInserted.count( type ) )
continue;
switch( type )
{
case PCB_TRACE_T:
case PCB_ARC_T:
case PCB_VIA_T:
handledAnything = true;
std::copy( board->Tracks().begin(), board->Tracks().end(),
std::back_inserter( items ) );
typesInserted.insert( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } );
break;
default:
break;
}
}
if( !handledAnything )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "none of the requested types are valid for a Board object" );
return tl::unexpected( e );
}
for( const BOARD_ITEM* item : items )
{
if( !typesRequested.count( item->Type() ) )
continue;
google::protobuf::Any itemBuf;
item->Serialize( itemBuf );
response.mutable_items()->Add( std::move( itemBuf ) );
}
response.set_status( ItemRequestStatus::IRS_OK );
return response;
}
HANDLER_RESULT<UpdateItemsResponse> API_HANDLER_PCB::handleUpdateItems( UpdateItems& aMsg )
{
ApiResponseStatus e;
if( !validateItemHeaderDocument( aMsg.header() ) )
{
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
BOARD* board = m_frame->GetBoard();
BOARD_ITEM_SET boardItems = board->GetItemSet();
std::map<KIID, BOARD_ITEM*> itemUuidMap;
std::for_each( boardItems.begin(), boardItems.end(),
[&]( BOARD_ITEM* aItem )
{
itemUuidMap[aItem->m_Uuid] = aItem;
} );
BOARD_COMMIT* commit = getCurrentCommit();
UpdateItemsResponse response;
for( const google::protobuf::Any& anyItem : aMsg.items() )
{
ItemUpdateResult itemResult;
std::optional<KICAD_T> type = TypeNameFromAny( anyItem );
if( !type )
{
itemResult.set_status( ItemUpdateStatus::IUS_INVALID_TYPE );
response.mutable_updated_items()->Add( std::move( itemResult ) );
continue;
}
std::unique_ptr<BOARD_ITEM> temporaryItem = createItemForType( *type, board );
if( !temporaryItem )
{
itemResult.set_status( ItemUpdateStatus::IUS_INVALID_TYPE );
response.mutable_updated_items()->Add( std::move( itemResult ) );
continue;
}
if( !temporaryItem->Deserialize( anyItem ) )
{
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( fmt::format( "could not unpack {} from request",
magic_enum::enum_name( *type ) ) );
return tl::unexpected( e );
}
if( !itemUuidMap.count( temporaryItem->m_Uuid ) )
{
itemResult.set_status( ItemUpdateStatus::IUS_NONEXISTENT );
response.mutable_updated_items()->Add( std::move( itemResult ) );
continue;
}
BOARD_ITEM* boardItem = itemUuidMap[temporaryItem->m_Uuid];
boardItem->SwapItemData( temporaryItem.get() );
itemResult.set_status( ItemUpdateStatus::IUS_OK );
boardItem->Serialize( *itemResult.mutable_item() );
commit->Modify( boardItem );
itemResult.set_status( ItemUpdateStatus::IUS_OK );
response.mutable_updated_items()->Add( std::move( itemResult ) );
}
response.set_status( ItemRequestStatus::IRS_OK );
pushCurrentCommit( "Updated items via API" );
return response;
}
HANDLER_RESULT<DeleteItemsResponse> API_HANDLER_PCB::handleDeleteItems( DeleteItems& aMsg )
{
if( !validateItemHeaderDocument( aMsg.header() ) )
{
ApiResponseStatus e;
// No message needed for AS_UNHANDLED; this is an internal flag for the API server
e.set_status( ApiStatusCode::AS_UNHANDLED );
return tl::unexpected( e );
}
std::map<KIID, ItemDeletionStatus> itemsToDelete;
for( const common::types::KIID& kiidBuf : aMsg.item_ids() )
{
if( !kiidBuf.value().empty() )
{
KIID kiid( kiidBuf.value() );
itemsToDelete[kiid] = ItemDeletionStatus::IDS_NONEXISTENT;
}
}
if( itemsToDelete.empty() )
{
ApiResponseStatus e;
e.set_status( ApiStatusCode::AS_BAD_REQUEST );
e.set_error_message( "no valid items to delete were given" );
return tl::unexpected( e );
}
BOARD* board = m_frame->GetBoard();
// This is somewhat inefficient on paper, but the total number of items on a board is
// not computationally-speaking very high even on what we'd consider a large design.
// If this ends up not being the case, we should consider doing something like refactoring
// BOARD to contain all items in a contiguous memory arena and constructing views over it
// when we want to filter to just tracks, etc.
BOARD_ITEM_SET items = board->GetItemSet();
std::vector<BOARD_ITEM*> validatedItems;
for( BOARD_ITEM* item : items )
{
if( itemsToDelete.count( item->m_Uuid ) )
{
validatedItems.push_back( item );
itemsToDelete[item->m_Uuid] = ItemDeletionStatus::IDS_OK;
}
// Note: we don't currently support locking items from API modification, but here is where
// to add it in the future (and return IDS_IMMUTABLE)
}
BOARD_COMMIT* commit = getCurrentCommit();
for( BOARD_ITEM* item : validatedItems )
commit->Remove( item );
if( !m_transactionInProgress )
pushCurrentCommit( "Deleted items via API" );
DeleteItemsResponse response;
for( const auto& [id, status] : itemsToDelete )
{
ItemDeletionResult result;
result.mutable_id()->set_value( id.AsStdString() );
result.set_status( status );
}
response.set_status( ItemRequestStatus::IRS_OK );
return response;
}

View File

@ -0,0 +1,86 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_API_HANDLER_PCB_H
#define KICAD_API_HANDLER_PCB_H
#include <google/protobuf/empty.pb.h>
#include <api/api_handler.h>
#include <api/common/commands/editor_commands.pb.h>
#include <properties/property_mgr.h>
using namespace kiapi;
using namespace kiapi::common;
using google::protobuf::Empty;
class BOARD_COMMIT;
class BOARD_ITEM;
class BOARD_ITEM_CONTAINER;
class EDA_ITEM;
class PCB_EDIT_FRAME;
class PCB_TRACK;
class PROPERTY_BASE;
class API_HANDLER_PCB : public API_HANDLER
{
public:
API_HANDLER_PCB( PCB_EDIT_FRAME* aFrame );
private:
typedef std::map<std::string, PROPERTY_BASE*> PROTO_PROPERTY_MAP;
static std::unique_ptr<BOARD_ITEM> createItemForType( KICAD_T aType,
BOARD_ITEM_CONTAINER* aContainer );
HANDLER_RESULT<commands::RunActionResponse> handleRunAction( commands::RunAction& aMsg );
HANDLER_RESULT<commands::GetOpenDocumentsResponse> handleGetOpenDocuments(
commands::GetOpenDocuments& aMsg );
HANDLER_RESULT<commands::BeginCommitResponse> handleBeginCommit( commands::BeginCommit& aMsg );
HANDLER_RESULT<commands::EndCommitResponse> handleEndCommit( commands::EndCommit& aMsg );
HANDLER_RESULT<commands::CreateItemsResponse> handleCreateItems( commands::CreateItems& aMsg );
HANDLER_RESULT<commands::GetItemsResponse> handleGetItems( commands::GetItems& aMsg );
HANDLER_RESULT<commands::UpdateItemsResponse> handleUpdateItems( commands::UpdateItems& aMsg );
HANDLER_RESULT<commands::DeleteItemsResponse> handleDeleteItems( commands::DeleteItems& aMsg );
private:
bool validateItemHeaderDocument( const common::types::ItemHeader& aHeader );
BOARD_COMMIT* getCurrentCommit();
void pushCurrentCommit( const std::string& aMessage );
PCB_EDIT_FRAME* m_frame;
std::unique_ptr<BOARD_COMMIT> m_commit;
bool m_transactionInProgress;
};
#endif //KICAD_API_HANDLER_PCB_H

View File

@ -337,7 +337,7 @@ void BOARD::RecordDRCExclusions()
{
if( marker->IsExcluded() )
{
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
m_designSettings->m_DrcExclusions.insert( serialized );
m_designSettings->m_DrcExclusionComments[ serialized ] = marker->GetComment();
}
@ -355,7 +355,7 @@ std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions( bool aCreateMarkers )
for( PCB_MARKER* marker : GetBoard()->Markers() )
{
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
std::set<wxString>::iterator it = exclusions.find( serialized );
if( it != exclusions.end() )
@ -376,7 +376,7 @@ std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions( bool aCreateMarkers )
{
for( const wxString& serialized : exclusions )
{
PCB_MARKER* marker = PCB_MARKER::Deserialize( serialized );
PCB_MARKER* marker = PCB_MARKER::DeserializeFromString( serialized );
if( !marker )
continue;

View File

@ -710,7 +710,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
marker->SetExcluded( true, dlg.GetValue() );
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
m_frame->GetDesignSettings().m_DrcExclusions.insert( serialized );
m_frame->GetDesignSettings().m_DrcExclusionComments[ serialized ] = dlg.GetValue();
@ -726,7 +726,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
{
marker->SetExcluded( false );
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
m_frame->GetDesignSettings().m_DrcExclusions.erase( serialized );
m_frame->GetDesignSettings().m_DrcExclusionComments.erase( serialized );
@ -758,7 +758,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
marker->SetExcluded( true, dlg.GetValue() );
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
m_frame->GetDesignSettings().m_DrcExclusions.insert( serialized );
m_frame->GetDesignSettings().m_DrcExclusionComments[ serialized ] = dlg.GetValue();
@ -792,7 +792,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
{
marker->SetExcluded( false );
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
m_frame->GetDesignSettings().m_DrcExclusions.erase( serialized );
m_frame->GetDesignSettings().m_DrcExclusionComments.erase( serialized );
}
@ -812,7 +812,7 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
{
marker->SetExcluded( true );
wxString serialized = marker->Serialize();
wxString serialized = marker->SerializeToString();
m_frame->GetDesignSettings().m_DrcExclusions.insert( serialized );
}
}
@ -1082,7 +1082,7 @@ void DIALOG_DRC::ExcludeMarker()
if( marker && marker->GetSeverity() != RPT_SEVERITY_EXCLUSION )
{
marker->SetExcluded( true );
m_frame->GetDesignSettings().m_DrcExclusions.insert( marker->Serialize() );
m_frame->GetDesignSettings().m_DrcExclusions.insert( marker->SerializeToString() );
m_frame->GetCanvas()->GetView()->Update( marker );
// Update view

View File

@ -109,6 +109,11 @@
#include <footprint_viewer_frame.h>
#include <footprint_chooser_frame.h>
#ifdef KICAD_IPC_API
#include <api/api_server.h>
#include <api/api_handler_pcb.h>
#endif
#include <action_plugin.h>
#include <pcbnew_scripting_helpers.h>
#include "../scripting/python_scripting.h"
@ -426,6 +431,11 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
// Sync action plugins in case they changed since the last time the frame opened
GetToolManager()->RunAction( PCB_ACTIONS::pluginsReload );
#ifdef KICAD_IPC_API
m_apiHandler = std::make_unique<API_HANDLER_PCB>( this );
Pgm().GetApiServer().RegisterHandler( m_apiHandler.get() );
#endif
GetCanvas()->SwitchBackend( m_canvasType );
ActivateGalCanvas();
@ -517,6 +527,16 @@ PCB_EDIT_FRAME::~PCB_EDIT_FRAME()
delete m_eventCounterTimer;
}
#ifdef KICAD_IPC_API
Pgm().GetApiServer().DeregisterHandler( m_apiHandler.get() );
#endif
// Close modeless dialogs
wxWindow* open_dlg = wxWindow::FindWindowByName( DIALOG_DRC_WINDOW_NAME );
if( open_dlg )
open_dlg->Close( true );
// Shutdown all running tools
if( m_toolManager )
m_toolManager->ShutdownAllTools();

View File

@ -58,6 +58,11 @@ class ACTION_MENU;
class TOOL_ACTION;
class STRING_UTF8_MAP;
#ifdef KICAD_IPC_API
class KICAD_API_SERVER;
class API_HANDLER_PCB;
#endif
enum LAST_PATH_TYPE : unsigned int;
namespace PCB { struct IFACE; } // KIFACE is in pcbnew.cpp
@ -856,6 +861,10 @@ private:
wxTimer m_redrawNetnamesTimer;
wxTimer* m_eventCounterTimer;
#ifdef KICAD_IPC_API
std::unique_ptr<API_HANDLER_PCB> m_apiHandler;
#endif
};
#endif // __PCB_EDIT_FRAME_H__

View File

@ -93,7 +93,7 @@ PCB_MARKER::~PCB_MARKER()
}
wxString PCB_MARKER::Serialize() const
wxString PCB_MARKER::SerializeToString() const
{
if( m_rcItem->GetErrorCode() == DRCE_COPPER_SLIVER )
{
@ -137,7 +137,7 @@ wxString PCB_MARKER::Serialize() const
}
PCB_MARKER* PCB_MARKER::Deserialize( const wxString& data )
PCB_MARKER* PCB_MARKER::DeserializeFromString( const wxString& data )
{
auto getMarkerLayer =
[]( const wxString& layerName ) -> int
@ -367,4 +367,4 @@ static struct PCB_MARKER_DESC
_HKI( "Locked" ),
[]( INSPECTABLE* aItem ) { return false; } );
}
} _PCB_MARKER_DESC;
} _PCB_MARKER_DESC;

View File

@ -52,9 +52,9 @@ public:
const KIID GetUUID() const override { return m_Uuid; }
wxString Serialize() const;
wxString SerializeToString() const;
static PCB_MARKER* Deserialize( const wxString& data );
static PCB_MARKER* DeserializeFromString( const wxString& data );
void Move( const VECTOR2I& aMoveVector ) override
{

View File

@ -23,6 +23,7 @@
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <magic_enum.hpp>
#include <pcb_base_frame.h>
#include <core/mirror.h>
@ -44,6 +45,9 @@
#include <pcb_painter.h>
#include <trigo.h>
#include <google/protobuf/any.pb.h>
#include <api/board/board_types.pb.h>
using KIGFX::PCB_PAINTER;
using KIGFX::PCB_RENDER_SETTINGS;
@ -287,6 +291,196 @@ double PCB_VIA::Similarity( const BOARD_ITEM& aOther ) const
}
void PCB_TRACK::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::Track track;
track.mutable_id()->set_value( m_Uuid.AsStdString() );
track.mutable_start()->set_x_nm( GetPosition().x );
track.mutable_start()->set_y_nm( GetPosition().y );
track.mutable_end()->set_x_nm( GetEnd().x );
track.mutable_end()->set_y_nm( GetEnd().y );
track.mutable_width()->set_value_nm( GetWidth() );
track.mutable_layer()->set_layer_id( GetLayer() );
track.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
track.mutable_net()->set_code( GetNetCode() );
track.mutable_net()->set_name( GetNetname() );
aContainer.PackFrom( track );
}
bool PCB_TRACK::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::Track track;
if( !aContainer.UnpackTo( &track ) )
return false;
const_cast<KIID&>( m_Uuid ) = KIID( track.id().value() );
SetStart( VECTOR2I( track.start().x_nm(), track.start().y_nm() ) );
SetEnd( VECTOR2I( track.end().x_nm(), track.end().y_nm() ) );
SetWidth( track.width().value_nm() );
SetLayer( magic_enum::enum_cast<PCB_LAYER_ID>( track.layer().layer_id() ).value_or( F_Cu ) );
SetNetCode( track.net().code() );
SetLocked( track.locked() == kiapi::common::types::LockedState::LS_LOCKED );
return true;
}
void PCB_ARC::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::Arc arc;
arc.mutable_id()->set_value( m_Uuid.AsStdString() );
arc.mutable_start()->set_x_nm( GetPosition().x );
arc.mutable_start()->set_y_nm( GetPosition().y );
arc.mutable_mid()->set_x_nm( GetMid().x );
arc.mutable_mid()->set_y_nm( GetMid().y );
arc.mutable_end()->set_x_nm( GetEnd().x );
arc.mutable_end()->set_y_nm( GetEnd().y );
arc.mutable_width()->set_value_nm( GetWidth() );
arc.mutable_layer()->set_layer_id( GetLayer() );
arc.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
arc.mutable_net()->set_code( GetNetCode() );
arc.mutable_net()->set_name( GetNetname() );
aContainer.PackFrom( arc );
}
bool PCB_ARC::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::Arc arc;
if( !aContainer.UnpackTo( &arc ) )
return false;
const_cast<KIID&>( m_Uuid ) = KIID( arc.id().value() );
SetStart( VECTOR2I( arc.start().x_nm(), arc.start().y_nm() ) );
SetMid( VECTOR2I( arc.mid().x_nm(), arc.mid().y_nm() ) );
SetEnd( VECTOR2I( arc.end().x_nm(), arc.end().y_nm() ) );
SetWidth( arc.width().value_nm() );
SetLayer( magic_enum::enum_cast<PCB_LAYER_ID>( arc.layer().layer_id() ).value_or( F_Cu ) );
SetNetCode( arc.net().code() );
SetLocked( arc.locked() == kiapi::common::types::LockedState::LS_LOCKED );
return true;
}
void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
{
kiapi::board::types::Via via;
via.mutable_id()->set_value( m_Uuid.AsStdString() );
via.mutable_position()->set_x_nm( GetPosition().x );
via.mutable_position()->set_y_nm( GetPosition().y );
via.mutable_pad_diameter()->set_value_nm( GetWidth() );
via.mutable_drill_diameter()->set_value_nm( GetDrillValue() );
kiapi::board::types::PadStack* padstack = via.mutable_pad_stack();
padstack->set_type( GetViaType() == VIATYPE::BLIND_BURIED
? kiapi::board::types::PadStackType::PST_BLIND_BURIED
: kiapi::board::types::PadStackType::PST_THROUGH );
padstack->mutable_start_layer()->set_layer_id( m_layer );
padstack->mutable_end_layer()->set_layer_id( m_bottomLayer );
kiapi::board::types::UnconnectedLayerRemoval ulr;
if( m_removeUnconnectedLayer )
{
if( m_keepStartEndLayer )
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END;
else
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE;
}
else
{
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP;
}
// TODO: Microvia status is ignored here. Do we still need it?
padstack->set_unconnected_layer_removal( ulr );
via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
via.mutable_net()->set_code( GetNetCode() );
via.mutable_net()->set_name( GetNetname() );
aContainer.PackFrom( via );
}
bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
{
kiapi::board::types::Via via;
if( !aContainer.UnpackTo( &via ) )
return false;
const_cast<KIID&>( m_Uuid ) = KIID( via.id().value() );
SetStart( VECTOR2I( via.position().x_nm(), via.position().y_nm() ) );
SetEnd( GetStart() );
SetWidth( via.pad_diameter().value_nm() );
SetDrill( via.drill_diameter().value_nm() );
const kiapi::board::types::PadStack& padstack = via.pad_stack();
switch( padstack.type() )
{
case kiapi::board::types::PadStackType::PST_BLIND_BURIED:
SetViaType( VIATYPE::BLIND_BURIED );
break;
default:
SetViaType( VIATYPE::THROUGH );
break;
}
if( GetViaType() != VIATYPE::THROUGH )
{
m_layer = magic_enum::enum_cast<PCB_LAYER_ID>( padstack.start_layer().layer_id() )
.value_or( F_Cu );
m_bottomLayer = magic_enum::enum_cast<PCB_LAYER_ID>( padstack.end_layer().layer_id() )
.value_or( B_Cu );
}
else
{
m_layer = F_Cu;
m_bottomLayer = B_Cu;
}
switch( padstack.unconnected_layer_removal() )
{
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE:
m_removeUnconnectedLayer = true;
m_keepStartEndLayer = false;
break;
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END:
m_removeUnconnectedLayer = true;
m_keepStartEndLayer = true;
break;
default:
case kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP:
m_removeUnconnectedLayer = false;
m_keepStartEndLayer = false;
break;
}
SetNetCode( via.net().code() );
SetLocked( via.locked() == kiapi::common::types::LockedState::LS_LOCKED );
return true;
}
bool PCB_TRACK::ApproxCollinear( const PCB_TRACK& aTrack )
{
SEG a( m_Start, m_End );

View File

@ -260,6 +260,9 @@ public:
bool operator()( const PCB_TRACK* aFirst, const PCB_TRACK* aSecond ) const;
};
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
#if defined (DEBUG)
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif
@ -361,6 +364,9 @@ public:
bool operator==( const BOARD_ITEM& aOther ) const override;
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
protected:
virtual void swapData( BOARD_ITEM* aImage ) override;
@ -595,6 +601,9 @@ public:
bool operator==( const BOARD_ITEM& aOther ) const override;
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
protected:
void swapData( BOARD_ITEM* aImage ) override;

View File

@ -23,3 +23,7 @@
add_subdirectory( sexpr )
add_subdirectory( kimath )
if( KICAD_IPC_API )
add_subdirectory( kinng )
endif()

View File

@ -0,0 +1,41 @@
# This program source code file is part of KiCad, a free EDA CAD application.
#
# Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
set( QA_KINNG_SRCS
kinng_test_module.cpp
test_kinng.cpp
)
add_executable( qa_kinng
${QA_KINNG_SRCS}
)
target_link_libraries( qa_kinng
qa_utils
kinng
kiapi
${NNG_LIBRARY}
${wxWidgets_LIBRARIES}
)
target_include_directories( qa_kinng PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/qa/mocks/include
${CMAKE_CURRENT_SOURCE_DIR}
)
kicad_add_boost_test( qa_kinng qa_kinng )

View File

@ -0,0 +1,22 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define BOOST_TEST_MODULE KiNNG
#include <boost/test/unit_test.hpp>

View File

@ -0,0 +1,34 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <kinng.h>
#include <import_export.h>
#include <api/common/envelope.pb.h>
BOOST_AUTO_TEST_SUITE( KiNNG )
BOOST_AUTO_TEST_CASE( CreateIPCResponder )
{
KINNG_REQUEST_SERVER server;
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -67,7 +67,7 @@ BOOST_FIXTURE_TEST_CASE( DRCCustomRuleSeverityTest, DRC_REGRESSION_TEST_FIXTURE
{
PCB_MARKER temp( aItem, aPos );
if( bds.m_DrcExclusions.find( temp.Serialize() ) == bds.m_DrcExclusions.end() )
if( bds.m_DrcExclusions.find( temp.SerializeToString() ) == bds.m_DrcExclusions.end() )
violations.push_back( *aItem );
} );

View File

@ -65,7 +65,7 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperGraphicsTest, DRC_COPPER_GRAPHICS_TEST_FIXTURE
{
PCB_MARKER temp( aItem, aPos );
if( bds.m_DrcExclusions.find( temp.Serialize() ) == bds.m_DrcExclusions.end() )
if( bds.m_DrcExclusions.find( temp.SerializeToString() ) == bds.m_DrcExclusions.end() )
violations.push_back( *aItem );
} );

View File

@ -161,7 +161,7 @@ BOOST_FIXTURE_TEST_CASE( DRCFalseNegativeRegressions, DRC_REGRESSION_TEST_FIXTUR
{
PCB_MARKER temp( aItem, aPos );
if( bds.m_DrcExclusions.find( temp.Serialize() ) == bds.m_DrcExclusions.end() )
if( bds.m_DrcExclusions.find( temp.SerializeToString() ) == bds.m_DrcExclusions.end() )
violations.push_back( *aItem );
} );

View File

@ -62,7 +62,7 @@ BOOST_FIXTURE_TEST_CASE( DRCSolderMaskBridgingTest, DRC_SOLDER_MASK_BRIDGING_TES
{
PCB_MARKER temp( aItem, aPos );
if( bds.m_DrcExclusions.find( temp.Serialize() ) == bds.m_DrcExclusions.end() )
if( bds.m_DrcExclusions.find( temp.SerializeToString() ) == bds.m_DrcExclusions.end() )
violations.push_back( *aItem );
} );

View File

@ -1,5 +1,6 @@
set( KIPYTHON_SRCS
kipython_settings.cpp
python_manager.cpp
)
add_library( scripting STATIC

View File

@ -0,0 +1,73 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <wx/process.h>
#include <utility>
#include "python_manager.h"
class PYTHON_PROCESS : public wxProcess
{
public:
PYTHON_PROCESS( std::function<void(int, const wxString&)> aCallback ) :
wxProcess(),
m_callback( std::move( aCallback ) )
{}
void OnTerminate( int aPid, int aStatus ) override
{
if( m_callback )
{
wxString output;
wxInputStream* processOut = GetInputStream();
size_t bytesRead = 0;
while( processOut->CanRead() && bytesRead < MAX_OUTPUT_LEN )
{
char buffer[4096];
buffer[ processOut->Read( buffer, sizeof( buffer ) - 1 ).LastRead() ] = '\0';
output.append( buffer, sizeof( buffer ) );
bytesRead += processOut->LastRead();
}
m_callback( aStatus, output );
}
}
static constexpr size_t MAX_OUTPUT_LEN = 1024L * 1024L;
private:
std::function<void(int, const wxString&)> m_callback;
};
void PYTHON_MANAGER::Execute( const wxString& aArgs,
const std::function<void( int, const wxString& )>& aCallback )
{
PYTHON_PROCESS* process = new PYTHON_PROCESS( aCallback );
process->Redirect();
wxString cmd = wxString::Format( wxS( "%s %s" ), m_interpreterPath, aArgs );
long pid = wxExecute( cmd, wxEXEC_ASYNC, process );
if( pid == 0 )
aCallback( -1, wxEmptyString );
}

View File

@ -0,0 +1,48 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_PYTHON_MANAGER_H
#define KICAD_PYTHON_MANAGER_H
#include <functional>
#include <optional>
#include <wx/wx.h>
class PYTHON_MANAGER
{
public:
PYTHON_MANAGER( const wxString& aInterpreterPath ) :
m_interpreterPath( aInterpreterPath )
{}
void Execute( const wxString& aArgs,
const std::function<void(int, const wxString&)>& aCallback );
wxString GetInterpreterPath() const { return m_interpreterPath; }
void SetInterpreterPath( const wxString& aPath ) { m_interpreterPath = aPath; }
private:
wxString m_interpreterPath;
};
#endif //KICAD_PYTHON_MANAGER_H

View File

@ -39,6 +39,7 @@ add_subdirectory( clipper2 )
add_subdirectory( compoundfilereader )
add_subdirectory( delaunator )
add_subdirectory( dxflib_qcad )
add_subdirectory( expected )
set( FMT_INSTALL OFF )
add_subdirectory( fmt )
add_subdirectory( gzip-hpp )

7
thirdparty/expected/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,7 @@
add_library( expected INTERFACE )
target_include_directories( expected INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include )
target_sources( expected INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include/tl/expected.hpp
)

121
thirdparty/expected/COPYING vendored Normal file
View File

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

4
thirdparty/expected/README.txt vendored Normal file
View File

@ -0,0 +1,4 @@
This directory contains the expected library from https://github.com/TartanLlama/expected
It is licensed under CC0-1.0, with the license text in the COPYING file in this directory.

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,9 @@
"icu",
"ngspice",
"wxpython",
"libgit2"
"libgit2",
"nng",
"protobuf"
],
"overrides": [
{