KiCad PCB EDA Suite
Loading...
Searching...
No Matches
command_api_server.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <csignal>
22#include <atomic>
23#include <algorithm>
24#include <vector>
25
29#include <api/api_utils.h>
30#include <api/api_server.h>
31#include <cli/exit_codes.h>
32#include <lib_id.h>
35#include <wx/app.h>
36#include <wx/crt.h>
37#include <wx/filename.h>
38
39#include "command_api_server.h"
40
41#define ARG_PATH "path"
42#define ARG_SOCKET "--socket"
43
44
45std::atomic_bool g_apiServerExitRequested{ false };
46
48{
49 g_apiServerExitRequested.store( true );
50}
51
52
54 COMMAND( "api-server" )
55{
56 m_argParser.add_description( UTF8STDSTR( _( "Run the KiCad IPC API server in headless mode" ) ) );
57
58 m_argParser.add_argument( ARG_PATH )
59 .default_value( std::string() )
60 .nargs( argparse::nargs_pattern::optional )
61 .help( UTF8STDSTR( _( "Optional path to a .kicad_pro, .kicad_pcb, or .kicad_sch file to pre-load" ) ) )
62 .metavar( "PROJECT_OR_FILE" );
63
64 m_argParser.add_argument( ARG_SOCKET )
65 .default_value( std::string() )
66 .help( UTF8STDSTR( _( "Override API socket path" ) ) )
67 .metavar( "SOCKET_PATH" );
68}
69
70
72{
73 using namespace kiapi::common;
74
75 std::unique_ptr<KICAD_API_SERVER> server = std::make_unique<KICAD_API_SERVER>( false );
76 API_HANDLER_COMMON commonHandler;
77 API_HANDLER_LIBRARIES designBlockLibrariesHandler( LIBRARY_TABLE_TYPE::DESIGN_BLOCK );
78
79 // The design block library handler handles LoadAllLibraries commands which need to be able
80 // to lazy-load the eeschema/pcbnew faces if they aren't loaded
81 designBlockLibrariesHandler.SetKiway( &aKiway );
82 designBlockLibrariesHandler.SetLibraryHandlerRegistrar(
83 [&server]( KIFACE* aKiface )
84 {
85 aKiface->RegisterLibraryHandlers( server.get() );
86 } );
87
88 wxString socketPath = wxString::FromUTF8( m_argParser.get<std::string>( ARG_SOCKET ) );
89
90 if( !socketPath.IsEmpty() )
91 server->SetSocketPath( socketPath );
92
93 // Eventually we might support opening multiple projects at once, but for now
94 // we support one project at a time, but multiple documents within that project
95 // (e.g. up to one schematic, up to one board, and arbitrarily many library files
96 // which are not associated with the project)
97 std::optional<wxFileName> openProjectPath;
98
99 struct OPEN_DOCUMENT
100 {
101 types::DocumentType type;
102 wxString fileName;
103 wxString specPath;
104 LIB_ID libId;
105
106 KIFACE::DOCUMENT_SPEC Spec() const
107 {
109
110 if( type == types::DOCTYPE_FOOTPRINT || type == types::DOCTYPE_SYMBOL )
111 {
113 spec.libId = libId;
114 }
115 else
116 {
118 spec.path = specPath.IsEmpty() ? fileName : specPath;
119 }
120
121 return spec;
122 }
123 };
124
125 std::vector<OPEN_DOCUMENT> openDocuments;
126
127 auto faceForDocument = []( types::DocumentType aType ) -> KIWAY::FACE_T
128 {
129 switch( aType )
130 {
131 case types::DOCTYPE_SCHEMATIC: return KIWAY::FACE_SCH;
132 case types::DOCTYPE_SYMBOL: return KIWAY::FACE_SCH;
133 case types::DOCTYPE_PCB: return KIWAY::FACE_PCB;
134 case types::DOCTYPE_FOOTPRINT: return KIWAY::FACE_PCB;
135 default: return KIWAY::KIWAY_FACE_COUNT;
136 }
137 };
138
139 auto closeAllDocuments =
140 [&]( const commands::CloseAllDocuments& aRequest ) -> HANDLER_RESULT<google::protobuf::Empty>
141 {
142 wxString errors;
143
144 for( const OPEN_DOCUMENT& doc : openDocuments )
145 {
146 // The project has no document face; it is released by UnloadProject below.
147 if( doc.type == types::DOCTYPE_PROJECT )
148 continue;
149
150 wxString error;
151
152 if( !aKiway.ProcessApiCloseDocument( faceForDocument( doc.type ), doc.Spec(), server.get(), &error ) )
153 {
154 if( !errors.IsEmpty() )
155 errors.Append( wxS( "; " ) );
156
157 errors.Append( wxString::Format( wxS( "Failed to close %s: %s" ), doc.fileName, error ) );
158 }
159 }
160
161 openDocuments.clear();
162
163 if( openProjectPath )
164 {
167 }
168
169 openProjectPath.reset();
170
171 if( !errors.IsEmpty() )
172 {
173 ApiResponseStatus e;
174 e.set_status( ApiStatusCode::AS_INTERNAL_ERROR );
175 e.set_error_message( errors.ToStdString() );
176 return tl::unexpected( e );
177 }
178
179 return google::protobuf::Empty();
180 };
181
182 auto openDocument = [&]( const commands::OpenDocument& aRequest )
184 {
185 types::DocumentType requestType = aRequest.type();
186
187 if( requestType != types::DOCTYPE_PCB && requestType != types::DOCTYPE_SCHEMATIC
188 && requestType != types::DOCTYPE_PROJECT && requestType != types::DOCTYPE_FOOTPRINT
189 && requestType != types::DOCTYPE_SYMBOL )
190 {
191 ApiResponseStatus e;
192 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
193 e.set_error_message( "Only PCB, schematic, footprint, symbol, and project document types are supported" );
194 return tl::unexpected( e );
195 }
196
197 wxString inputPath = wxString::FromUTF8( aRequest.path() );
198
199 if( inputPath.IsEmpty() )
200 {
201 ApiResponseStatus e;
202 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
203 e.set_error_message( "OpenDocument requires a non-empty path" );
204 return tl::unexpected( e );
205 }
206
207 if( requestType == types::DOCTYPE_FOOTPRINT )
208 {
209 LIB_ID fpid;
210
211 if( fpid.Parse( inputPath ) >= 0 )
212 {
213 ApiResponseStatus e;
214 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
215 e.set_error_message( wxString::Format( wxS( "Invalid footprint LIB_ID: %s" ),
216 inputPath ).ToStdString() );
217 return tl::unexpected( e );
218 }
219
222 spec.libId = fpid;
223
224 if( openProjectPath )
225 spec.path = openProjectPath->GetFullPath();
226
227 wxString error;
228
229 if( !aKiway.ProcessApiOpenDocument( KIWAY::FACE_PCB, spec, server.get(), &error ) )
230 {
231 ApiResponseStatus e;
232 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
233 e.set_error_message( error.ToStdString() );
234 return tl::unexpected( e );
235 }
236
237 OPEN_DOCUMENT doc;
238 doc.type = requestType;
239 doc.libId = fpid;
240 openDocuments.push_back( doc );
241
242 commands::OpenDocumentResponse response;
243 types::DocumentSpecifier* docSpec = response.mutable_document();
244 docSpec->set_type( requestType );
245 docSpec->mutable_lib_id()->set_library_nickname( fpid.GetUniStringLibNickname() );
246 docSpec->mutable_lib_id()->set_entry_name( fpid.GetUniStringLibItemName() );
247
248 return response;
249 }
250 if( requestType == types::DOCTYPE_SYMBOL )
251 {
252 LIB_ID libId;
253
254 if( libId.Parse( inputPath ) >= 0 )
255 {
256 ApiResponseStatus e;
257 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
258 e.set_error_message( wxString::Format( wxS( "Invalid symbol LIB_ID: %s" ), inputPath ).ToStdString() );
259 return tl::unexpected( e );
260 }
261
264 spec.libId = libId;
265
266 if( openProjectPath )
267 spec.path = openProjectPath->GetFullPath();
268
269 wxString error;
270
271 if( !aKiway.ProcessApiOpenDocument( KIWAY::FACE_SCH, spec, server.get(), &error ) )
272 {
273 ApiResponseStatus e;
274 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
275 e.set_error_message( error.ToStdString() );
276 return tl::unexpected( e );
277 }
278
279 OPEN_DOCUMENT doc;
280 doc.type = requestType;
281 doc.libId = libId;
282 openDocuments.push_back( doc );
283
284 commands::OpenDocumentResponse response;
285 types::DocumentSpecifier* docSpec = response.mutable_document();
286 docSpec->set_type( requestType );
287 docSpec->mutable_lib_id()->set_library_nickname( libId.GetUniStringLibNickname() );
288 docSpec->mutable_lib_id()->set_entry_name( libId.GetUniStringLibItemName() );
289
290 return response;
291 }
292
293 wxFileName projectPath( inputPath );
294 projectPath.SetExt( FILEEXT::ProjectFileExtension );
295 projectPath.MakeAbsolute();
296
297 if( openProjectPath && projectPath.GetFullPath() != openProjectPath->GetFullPath() )
298 {
299 ApiResponseStatus e;
300 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
301 e.set_error_message( wxString::Format( "cannot open a document from project '%s' because project "
302 "'%s' is already open.",
303 projectPath.GetFullName(), openProjectPath->GetFullName() )
304 .ToStdString() );
305 return tl::unexpected( e );
306 }
307
308 if( requestType == types::DOCTYPE_PROJECT )
309 {
310 if( !openProjectPath )
311 {
312 if( !openDocuments.empty() )
313 {
314 auto closeResult = closeAllDocuments( commands::CloseAllDocuments() );
315
316 if( !closeResult )
317 return tl::unexpected( closeResult.error() );
318 }
319
320 if( !Pgm().GetSettingsManager().LoadProject( projectPath.GetFullPath(), true ) )
321 {
322 wxLogTrace( traceApi, "Warning: no project file found for %s", inputPath );
323 }
324
325 if( !Pgm().GetSettingsManager().GetProject( projectPath.GetFullPath() ) )
326 {
327 ApiResponseStatus e;
328 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
329 e.set_error_message( wxString::Format( "failed to load project '%s'", projectPath.GetFullPath() )
330 .ToStdString() );
331 return tl::unexpected( e );
332 }
333
334 openProjectPath = projectPath;
335 }
336
337 if( std::ranges::find_if( openDocuments,
338 []( const OPEN_DOCUMENT& d )
339 {
340 return d.type == types::DOCTYPE_PROJECT;
341 } ) == openDocuments.end() )
342 {
343 OPEN_DOCUMENT doc;
344 doc.type = types::DOCTYPE_PROJECT;
345 doc.fileName = projectPath.GetFullName();
346 openDocuments.push_back( doc );
347 }
348
349 commands::OpenDocumentResponse response;
350 types::DocumentSpecifier* doc = response.mutable_document();
352
353 doc->set_type( types::DOCTYPE_PROJECT );
354 doc->mutable_project()->set_name( project.GetProjectName().ToUTF8() );
355 doc->mutable_project()->set_path( project.GetProjectPath().ToUTF8() );
356
357 return response;
358 }
359
360 KIWAY::FACE_T face = faceForDocument( requestType );
361
362 if( face == KIWAY::KIWAY_FACE_COUNT )
363 {
364 ApiResponseStatus e;
365 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
366 e.set_error_message( "unsupported document type" );
367 return tl::unexpected( e );
368 }
369
370 if( requestType == types::DOCTYPE_PCB || requestType == types::DOCTYPE_SCHEMATIC )
371 {
372 auto existing = std::ranges::find_if( openDocuments,
373 [&]( const OPEN_DOCUMENT& d )
374 {
375 return d.type == requestType;
376 } );
377
378 if( existing != openDocuments.end() )
379 {
380 ApiResponseStatus e;
381 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
382 e.set_error_message( "a document of this type is already open" );
383 return tl::unexpected( e );
384 }
385 }
386
389 spec.path = projectPath.GetFullPath();
390
391 wxString error;
392
393 if( !aKiway.ProcessApiOpenDocument( face, spec, server.get(), &error ) )
394 {
395 ApiResponseStatus e;
396 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
397 e.set_error_message( error.ToStdString() );
398 return tl::unexpected( e );
399 }
400
401 wxFileName docFile( inputPath );
402 docFile.MakeAbsolute();
403
404 OPEN_DOCUMENT doc;
405 doc.type = requestType;
406 doc.fileName = docFile.GetFullName();
407 doc.specPath = docFile.GetFullPath();
408 openDocuments.push_back( doc );
409
410 openProjectPath = projectPath;
411
412 commands::OpenDocumentResponse response;
413 types::DocumentSpecifier* docSpec = response.mutable_document();
415
416 docSpec->set_type( requestType );
417
418 if( requestType == types::DOCTYPE_PCB )
419 docSpec->set_board_filename( doc.fileName.ToStdString() );
420
421 docSpec->mutable_project()->set_name( project.GetProjectName().ToUTF8() );
422 docSpec->mutable_project()->set_path( project.GetProjectPath().ToUTF8() );
423
424 return response;
425 };
426
427 auto createDocument =
428 [&]( const commands::CreateDocument& aRequest ) -> HANDLER_RESULT<commands::OpenDocumentResponse>
429 {
430 types::DocumentType requestType = aRequest.type();
431
432 // TODO could allow creating entire projects in one go
433 // or expose create from template
434 if( requestType != types::DOCTYPE_PCB && requestType != types::DOCTYPE_SCHEMATIC )
435 {
436 ApiResponseStatus e;
437 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
438 e.set_error_message( "Only PCB and schematic documents can be created" );
439 return tl::unexpected( e );
440 }
441
442 wxString inputPath = wxString::FromUTF8( aRequest.path() );
443
444 if( inputPath.IsEmpty() )
445 {
446 ApiResponseStatus e;
447 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
448 e.set_error_message( "CreateDocument requires a non-empty path" );
449 return tl::unexpected( e );
450 }
451
452 wxFileName docPath( inputPath );
453 docPath.MakeAbsolute();
454 docPath.SetExt( requestType == types::DOCTYPE_PCB ? FILEEXT::KiCadPcbFileExtension
456
459 spec.path = docPath.GetFullPath();
460
461 KIWAY::FACE_T face = ( requestType == types::DOCTYPE_PCB ) ? KIWAY::FACE_PCB : KIWAY::FACE_SCH;
462 wxString error;
463
464 if( !aKiway.ProcessApiOpenDocument( face, spec, server.get(), &error ) )
465 {
466 ApiResponseStatus e;
467 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
468 e.set_error_message( error.ToStdString() );
469 return tl::unexpected( e );
470 }
471
473 openProjectPath =
474 wxFileName( project.GetProjectPath(), project.GetProjectName(), FILEEXT::ProjectFileExtension );
475
476 OPEN_DOCUMENT doc;
477 doc.type = requestType;
478 doc.fileName = docPath.GetFullName();
479 doc.specPath = docPath.GetFullPath();
480
481 std::erase_if( openDocuments,
482 [&]( const OPEN_DOCUMENT& d )
483 {
484 return d.type == requestType;
485 } );
486
487 openDocuments.push_back( doc );
488
489 commands::OpenDocumentResponse response;
490 types::DocumentSpecifier* docSpec = response.mutable_document();
491
492 docSpec->set_type( requestType );
493
494 if( requestType == types::DOCTYPE_PCB )
495 docSpec->set_board_filename( doc.fileName.ToStdString() );
496
497 PackProject( *docSpec->mutable_project(), project );
498
499 return response;
500 };
501
502 auto closeDocument =
503 [&]( const commands::CloseDocument& aRequest ) -> HANDLER_RESULT<google::protobuf::Empty>
504 {
505 if( openDocuments.empty() )
506 {
507 ApiResponseStatus e;
508 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
509 e.set_error_message( "No document is currently open" );
510 return tl::unexpected( e );
511 }
512
513 auto it = openDocuments.end();
514
515 if( aRequest.has_document() )
516 {
517 types::DocumentType typeToClose = aRequest.document().type();
518
519 it = std::ranges::find_if( openDocuments,
520 [&]( const OPEN_DOCUMENT& d )
521 {
522 return d.type == typeToClose;
523 } );
524
525 if( it == openDocuments.end() )
526 {
527 ApiResponseStatus e;
528 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
529 e.set_error_message( "Requested document type does not match any open document" );
530 return tl::unexpected( e );
531 }
532
533 if( typeToClose == types::DOCTYPE_PCB
534 && !aRequest.document().board_filename().empty()
535 && it->fileName != wxString::FromUTF8( aRequest.document().board_filename() ) )
536 {
537 ApiResponseStatus e;
538 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
539 e.set_error_message( "Requested document does not match the open document" );
540 return tl::unexpected( e );
541 }
542
543 if( ( typeToClose == types::DOCTYPE_SCHEMATIC || typeToClose == types::DOCTYPE_PROJECT )
544 && aRequest.document().has_project()
545 && openProjectPath
546 && aRequest.document().project().name() != openProjectPath->GetName().ToStdString() )
547 {
548 ApiResponseStatus e;
549 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
550 e.set_error_message( "Requested document does not match the open project" );
551 return tl::unexpected( e );
552 }
553
554 if( ( typeToClose == types::DOCTYPE_FOOTPRINT || typeToClose == types::DOCTYPE_SYMBOL )
555 && aRequest.document().has_lib_id() )
556 {
557 LIB_ID docId = UnpackLibId( aRequest.document().lib_id() );
558
559 if( !docId.IsValid() )
560 {
561 ApiResponseStatus e;
562 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
563 e.set_error_message( wxString::Format( wxS( "Invalid library item LIB_ID: %s" ),
564 docId.GetUniStringLibId() ).ToStdString() );
565 return tl::unexpected( e );
566 }
567
568 if( it->libId != docId )
569 {
570 ApiResponseStatus e;
571 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
572 e.set_error_message( "Requested document does not match the open document" );
573 return tl::unexpected( e );
574 }
575 }
576 }
577 else
578 {
579 // No document specifier: close the first open document.
580 it = openDocuments.begin();
581 }
582
583 if( it->type == types::DOCTYPE_PROJECT )
584 {
585 return closeAllDocuments( commands::CloseAllDocuments() );
586 }
587 else
588 {
589 wxString error;
590
591 if( !aKiway.ProcessApiCloseDocument( faceForDocument( it->type ), it->Spec(),
592 server.get(), &error ) )
593 {
594 ApiResponseStatus e;
595 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
596 e.set_error_message( error.ToStdString() );
597 return tl::unexpected( e );
598 }
599 }
600
601 openDocuments.erase( it );
602
603 if( openDocuments.empty() && openProjectPath )
604 {
607 openProjectPath.reset();
608 }
609
610 return google::protobuf::Empty();
611 };
612
613 commonHandler.SetOpenDocumentHandler( openDocument );
614 commonHandler.SetCreateDocumentHandler( createDocument );
615 commonHandler.SetCloseDocumentHandler( closeDocument );
616 commonHandler.SetCloseAllDocumentsHandler( closeAllDocuments );
617
618 server->SetRenameListener(
619 [&]( types::DocumentType aType, const wxString& aOldPath, const wxString& aNewPath )
620 {
621 auto it = std::ranges::find_if( openDocuments,
622 [&]( const OPEN_DOCUMENT& d )
623 {
624 return d.type == aType && d.specPath == aOldPath;
625 } );
626
627 if( it == openDocuments.end() )
628 return;
629
630 wxFileName newFile( aNewPath );
631 newFile.MakeAbsolute();
632
633 it->fileName = newFile.GetFullName();
634 it->specPath = newFile.GetFullPath();
635
636 if( openProjectPath )
637 {
638 wxFileName newProject( newFile );
639 newProject.SetExt( FILEEXT::ProjectFileExtension );
640 openProjectPath = newProject;
641 }
642 } );
643
644 server->RegisterHandler( &commonHandler );
645 server->RegisterHandler( &designBlockLibrariesHandler );
646 server->Start();
647
648 if( !server->Running() )
649 {
650 wxFprintf( stderr, _( "Failed to start API server\n" ) );
652 }
653
654 wxString preloadPath = wxString::FromUTF8( m_argParser.get<std::string>( ARG_PATH ) );
655
656 if( !preloadPath.IsEmpty() )
657 {
658 using namespace kiapi::common;
659
660 wxFileName preloadFile( preloadPath );
661 types::DocumentType preloadType = types::DOCTYPE_PROJECT;
662
663 if( preloadFile.GetExt() == FILEEXT::KiCadSchematicFileExtension )
664 preloadType = types::DOCTYPE_SCHEMATIC;
665 else if( preloadFile.GetExt() == FILEEXT::KiCadPcbFileExtension )
666 preloadType = types::DOCTYPE_PCB;
667
668 commands::OpenDocument request;
669 request.set_type( preloadType );
670 request.set_path( preloadPath.ToStdString() );
671
672 auto preloadResult = openDocument( request );
673
674 if( !preloadResult )
675 {
676 wxFprintf( stderr, "%s\n", preloadResult.error().error_message() );
677 server->DeregisterHandler( &commonHandler );
678 server->DeregisterHandler( &designBlockLibrariesHandler );
680 }
681 }
682
683 server->SetReadyToReply( true );
684
685 wxString listenPath = wxString::FromUTF8( server->SocketPath() );
686 wxFprintf( stdout, "KiCad API server listening at %s\n", listenPath );
687
688 auto oldSigInt = std::signal( SIGINT, apiServerSignalHandler );
689#ifdef SIGTERM
690 auto oldSigTerm = std::signal( SIGTERM, apiServerSignalHandler );
691#endif
692
693 g_apiServerExitRequested.store( false );
694
695 while( !g_apiServerExitRequested.load() )
696 {
697 wxTheApp->ProcessPendingEvents();
698 wxMilliSleep( 10 );
699 }
700
701 std::signal( SIGINT, oldSigInt );
702#ifdef SIGTERM
703 std::signal( SIGTERM, oldSigTerm );
704#endif
705
706 wxFprintf( stdout, "Shutting down\n" );
707
708 commands::CloseAllDocuments closeAllReq;
709 closeAllDocuments( closeAllReq );
710 server->DeregisterHandler( &commonHandler );
711 server->DeregisterHandler( &designBlockLibrariesHandler );
712
713 return EXIT_CODES::OK;
714}
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
Definition api_handler.h:47
void SetOpenDocumentHandler(OPEN_DOCUMENT_HANDLER aHandler)
void SetCloseAllDocumentsHandler(CLOSE_ALL_DOCUMENTS_HANDLER aHandler)
void SetCreateDocumentHandler(CREATE_DOCUMENT_HANDLER aHandler)
void SetCloseDocumentHandler(CLOSE_DOCUMENT_HANDLER aHandler)
Base class for API handlers related to library management.
void SetKiway(KIWAY *aKiway)
void SetLibraryHandlerRegistrar(std::function< void(KIFACE *)> aRegistrar)
Set a callback to register the handler on the API server in headless mode.
int doPerform(KIWAY &aKiway) override
The internal handler that should be overloaded to implement command specific processing and work.
argparse::ArgumentParser m_argParser
Definition command.h:113
COMMAND(const std::string &aName)
Define a new COMMAND instance.
Definition command.cpp:30
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:348
bool ProcessApiOpenDocument(KIWAY::FACE_T aFace, const KIFACE::DOCUMENT_SPEC &aSpec, KICAD_API_SERVER *aServer, wxString *aError=nullptr)
Definition kiway.cpp:756
FACE_T
Known KIFACE implementations.
Definition kiway.h:354
@ KIWAY_FACE_COUNT
Definition kiway.h:363
@ FACE_SCH
eeschema DSO
Definition kiway.h:355
@ FACE_PCB
pcbnew DSO
Definition kiway.h:356
bool ProcessApiCloseDocument(KIWAY::FACE_T aFace, const KIFACE::DOCUMENT_SPEC &aSpec, KICAD_API_SERVER *aServer, wxString *aError=nullptr)
Definition kiway.cpp:773
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
wxString GetUniStringLibId() const
Definition lib_id.h:144
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const wxString GetUniStringLibNickname() const
Definition lib_id.h:84
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:123
Container for project specific data.
Definition project.h:63
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
#define UTF8STDSTR(s)
Definition command.h:27
std::atomic_bool g_apiServerExitRequested
#define ARG_PATH
void apiServerSignalHandler(int)
#define ARG_SOCKET
static std::string ToStdString(const wxString &aStr)
#define _(s)
static const std::string ProjectFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string KiCadPcbFileExtension
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition api_utils.cpp:33
static const int ERR_ARGS
Definition exit_codes.h:31
static const int OK
Definition exit_codes.h:30
static const int ERR_UNKNOWN
Definition exit_codes.h:32
KICOMMON_API void PackProject(types::ProjectSpecifier &aOutput, const PROJECT &aInput)
KICOMMON_API LIB_ID UnpackLibId(const types::LibraryIdentifier &aId)
PGM_BASE & Pgm()
The global program "get" accessor.
wxString path
File path (KIND::FILE_KIND) or project path (KIND::LIBID_KIND).
Definition kiway.h:164
LIB_ID libId
Library identifier; valid when kind == KIND::LIBID_KIND.
Definition kiway.h:165
@ LIBID_KIND
Open the library element named by libId.
Definition kiway.h:159
@ FILE_KIND
Open the file at path.
Definition kiway.h:158
@ CREATE_KIND
Create a new document at path and open it (in memory, not persisted)
Definition kiway.h:160
Implement a participant in the KIWAY alchemy.
Definition kiway.h:153
virtual void RegisterLibraryHandlers(KICAD_API_SERVER *aServer)
Register this face's library API handlers on the given server.
Definition kiway.h:309
Definition of file extensions used in Kicad.