Browse Source
ADDED: A new IPC API based on protobuf and nng
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.newinvert
75 changed files with 6286 additions and 41 deletions
-
34CMakeLists.txt
-
118api/CMakeLists.txt
-
43api/enums/CMakeLists.txt
-
91api/enums/enum_exporter.cpp
-
110api/proto/board/board_types.proto
-
33api/proto/common/commands/base_commands.proto
-
251api/proto/common/commands/editor_commands.proto
-
90api/proto/common/envelope.proto
-
217api/proto/common/types/base_types.proto
-
26cmake/Findnng.cmake
-
27common/CMakeLists.txt
-
5common/advanced_config.cpp
-
74common/api/api_handler.cpp
-
52common/api/api_handler_common.cpp
-
193common/api/api_server.cpp
-
109common/dialogs/panel_python_settings.cpp
-
70common/dialogs/panel_python_settings_base.cpp
-
371common/dialogs/panel_python_settings_base.fbp
-
56common/dialogs/panel_python_settings_base.h
-
5common/eda_base_frame.cpp
-
6common/kiid.cpp
-
11common/paths.cpp
-
4common/pgm_base.cpp
-
6common/settings/common_settings.cpp
-
17common/single_top.cpp
-
2common/tool/tool_manager.cpp
-
4eeschema/sch_marker.cpp
-
4eeschema/sch_marker.h
-
10eeschema/schematic.cpp
-
7include/advanced_config.h
-
137include/api/api_handler.h
-
42include/api/api_handler_common.h
-
97include/api/api_server.h
-
49include/dialogs/panel_python_settings.h
-
6include/eda_item.h
-
1include/kiid.h
-
5include/layer_ids.h
-
5include/paths.h
-
11include/pgm_base.h
-
4include/properties/property.h
-
7include/settings/common_settings.h
-
5kicad/CMakeLists.txt
-
16kicad/kicad.cpp
-
4libs/CMakeLists.txt
-
38libs/kinng/CMakeLists.txt
-
64libs/kinng/include/kinng.h
-
129libs/kinng/src/kinng.cpp
-
10pcbnew/CMakeLists.txt
-
493pcbnew/api/api_handler_pcb.cpp
-
86pcbnew/api/api_handler_pcb.h
-
6pcbnew/board.cpp
-
12pcbnew/dialogs/dialog_drc.cpp
-
20pcbnew/pcb_edit_frame.cpp
-
9pcbnew/pcb_edit_frame.h
-
6pcbnew/pcb_marker.cpp
-
4pcbnew/pcb_marker.h
-
194pcbnew/pcb_track.cpp
-
9pcbnew/pcb_track.h
-
4qa/tests/libs/CMakeLists.txt
-
41qa/tests/libs/kinng/CMakeLists.txt
-
22qa/tests/libs/kinng/kinng_test_module.cpp
-
34qa/tests/libs/kinng/test_kinng.cpp
-
2qa/tests/pcbnew/drc/test_custom_rule_severities.cpp
-
2qa/tests/pcbnew/drc/test_drc_copper_graphics.cpp
-
2qa/tests/pcbnew/drc/test_drc_regressions.cpp
-
2qa/tests/pcbnew/drc/test_solder_mask_bridging.cpp
-
1scripting/CMakeLists.txt
-
73scripting/python_manager.cpp
-
48scripting/python_manager.h
-
1thirdparty/CMakeLists.txt
-
7thirdparty/expected/CMakeLists.txt
-
121thirdparty/expected/COPYING
-
4thirdparty/expected/README.txt
-
2444thirdparty/expected/include/tl/expected.hpp
-
4vcpkg.json
@ -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() |
@ -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() |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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) |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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 ); |
|||
} |
@ -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 ); |
|||
} ); |
|||
} |
@ -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 ); |
|||
|
|||
} |
@ -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> |
@ -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(); |
|||
|
|||
}; |
|||
|
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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} |
|||
) |
@ -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 |
@ -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 ); |
|||
} |
@ -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; |
|||
} |
@ -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 |
@ -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 ) |
@ -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>
|
@ -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() |
@ -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 ); |
|||
} |
@ -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 |
@ -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 |
|||
) |
@ -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. |
@ -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. |
|||
|
2444
thirdparty/expected/include/tl/expected.hpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue