/* * COPYRIGHT * * sch-rnd - modular/flexible schematics editor - sch-rnd (executable) * Copyright (C) 2019..2022 Tibor 'Igor2' Palinkas * * (Supported by NLnet NGI0 PET Fund in 2022) * * 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 2 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, write to the Free Software * Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA. * * Contact: * Project page: http://repo.hu/projects/sch-rnd * contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html * mailing list: http://www.repo.hu/projects/sch-rnd/contact.html */ #include "config.h" #include #include #include #include #include #include /* Seed for srand() */ /* hidlib headers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "conf_core.h" #include "crosshair.h" #include "build_run.h" #include "sheet.h" #include "draw.h" #include "font.h" #include "emergency.h" #include "project.h" #include "multi.h" #include "plug_io_act.h" #include "extobj_act.h" #include "export.h" csch_chdr_t *csch_obj_clicked = NULL; #define pup_buildins sch_rnd_buildins #include "buildin.c" #undef pup_buildins static const char *EXPERIMENTAL = NULL; static const char *menu_file_paths[8]; static const char *menu_name_fmt = "menu-%s.lht"; #define CONF_USER_DIR "~/" DOT_SCH_RND static rnd_conf_ignore_t sch_rnd_conf_ignores[] = { {"stance/opt", 10, 1, NULL}, /* do not warn: dynamically created by the user */ {NULL, 0, 0, NULL} }; static int sch_rnd_human_coord(rnd_coord_t coord, double *out_val, const char **out_suffix) { *out_val = P2C(coord); if (*out_val > 100) { *out_val = *out_val / 1000.0; *out_suffix = "k"; } else *out_suffix = ""; return 1; } #ifdef RND_WANT_FLOATING_FHS char *sch_floating_confdir; #endif static char *main_path_init(char *exec_prefix) { char *tmp; int se = 0; #ifdef RND_WANT_FLOATING_FHS TODO("This ignores ./configure --confdir; the code is in librnd trunk/src/librnd/scconfig/hooks_common.h, search for ''set up confdir'' -> should save a variant usable here, without prefix"); sch_floating_confdir = rnd_concat(rnd_w32_root, "/etc/sch-rnd", NULL); #endif /* export the most important paths and data for child processes (e.g. parametric footprints) */ tmp = rnd_concat(SCHSHAREDIR, "/footprint", NULL); se |= rnd_setenv("SCH_RND_VERSION", SCH_VERSION, 1); se |= rnd_setenv("SCH_RND_REVISION", SCH_REVISION, 1); se |= rnd_setenv("SCH_RND_PCBLIB", tmp, 1); /* for backward compatibility - do not use */ se |= rnd_setenv("SCH_RND_FOOTPRINT", tmp, 1); se |= rnd_setenv("SCH_RND_SHARE", SCHSHAREDIR, 1); se |= rnd_setenv("SCH_RND_LIB", SCHLIBDIR, 1); se |= rnd_setenv("SCH_RND_EXEC_PREFIX", exec_prefix, 1); free(tmp); if (se != 0) fprintf(stderr, "WARNING: setenv() failed - external commands such as parametric footprints may not have a proper environment\n"); menu_file_paths[0] = "./"; menu_file_paths[1] = "~/" DOT_SCH_RND "/"; menu_file_paths[2] = rnd_concat(SCHCONFDIR, "/", NULL); menu_file_paths[3] = NULL; assert((sizeof(menu_file_paths)/sizeof(menu_file_paths[0])) > 3); rnd_conf_postproc(); /* get ~ (home dir) set */ #ifdef RND_WANT_FLOATING_FHS rnd_app.conf_sys_path = rnd_concat(SCHCONFDIR, "/sch-rnd-conf.lht", NULL); #else rnd_app.conf_sys_path = SCHCONFDIR "/sch-rnd-conf.lht"; #endif rnd_app.conf_sysdir_path = SCHSHAREDIR; rnd_app.lib_dir = SCHLIBDIR; rnd_app.conf_userdir_path = CONF_USER_DIR; rnd_app.conf_user_path = rnd_concat(CONF_USER_DIR, "/conf_core.lht", NULL); rnd_app.conf_ignores = sch_rnd_conf_ignores; rnd_app.human_coord = sch_rnd_human_coord; return exec_prefix; } static void main_path_uninit(void) { /* const for all other parts of the code, but we had to concat (alloc) it above */ free((char *)menu_file_paths[2]); free((char *)rnd_app.conf_user_path); } /* initialize signal and error handlers */ static void main_sighand_init(void) { #ifdef RND_HAVE_SIGHUP signal(SIGHUP, sch_rnd_catch_signal); #endif #ifdef RND_HAVE_SIGQUIT signal(SIGQUIT, sch_rnd_catch_signal); #endif #ifdef RND_HAVE_SIGTERM signal(SIGTERM, sch_rnd_catch_signal); #endif #ifdef RND_HAVE_SIGINT signal(SIGINT, sch_rnd_catch_signal); #endif #ifdef NDEBUG /* so that we get a core dump on segfault in debug mode */ # ifdef RND_HAVE_SIGABRT signal(SIGABRT, sch_rnd_catch_signal); # endif # ifdef RND_HAVE_SIGSEGV signal(SIGSEGV, sch_rnd_catch_signal); # endif #endif /* calling external program by popen() may cause a PIPE signal, so we ignore it */ #ifdef RND_HAVE_SIGPIPE signal(SIGPIPE, SIG_IGN); #endif } static void gui_support_plugins(int load) { static int loaded = 0; static pup_plugin_t *puphand; if (load && !loaded) { static const char *plugin_name = "sch_dialogs"; int state = 0; loaded = 1; rnd_message(RND_MSG_DEBUG, "Loading GUI support plugin: '%s'\n", plugin_name); puphand = pup_load(&rnd_pup, (const char **)rnd_pup_paths, plugin_name, 0, &state); if (puphand == NULL) rnd_message(RND_MSG_ERROR, "Error: failed to load GUI support plugin '%s'\n-> expect missing widgets and dialog boxes\n", plugin_name); } if (!load && loaded && (puphand != NULL)) { pup_unload(&rnd_pup, puphand, NULL); loaded = 0; puphand = NULL; } } /* action table number of columns for a single action */ const char *sch_rnd_action_args[] = { /*short, -long, action, help, hint-on-error */ NULL, "-show-actions", "PrintActions()", "Print all available actions (human readable) and exit", NULL, NULL, "-dump-actions", "DumpActions()", "Print all available actions (script readable) and exit", NULL, NULL, "-dump-plugins", "DumpPlugins()", "Print all available plugins (script readable) and exit", NULL, NULL, "-dump-plugindirs", "DumpPluginDirs()", "Print directories plugins might be loaded from and exit", NULL, NULL, "-dump-oflags", "DumpObjFlags()", "Print object flags and exit", NULL, NULL, "-show-paths", "PrintPaths()", "Print all configured paths and exit", NULL, "V", "-version", "PrintVersion()", "Print version info and exit", NULL, "V", "-dump-version", "DumpVersion()", "Print version info in script readable format and exit", NULL, NULL, "-copyright", "PrintCopyright()", "Print copyright and exit", NULL, NULL, NULL, NULL, NULL, NULL /* terminator */ }; #include "funchash_core.h" #define action_entry(x) { #x, F_ ## x}, static rnd_funchash_table_t Functions[] = { #include "funchash_core_list.h" {"F_END", F_END} }; static void gui_act_init(void) { } extern void sch_rnd_main_act_init2(void); extern void sch_rnd_file_act_init2(void); extern void sch_rnd_font_act_init2(void); extern void sch_rnd_select_act_init2(void); extern void sch_rnd_draw_init2(void); extern void sch_rnd_buffer_init2(void); extern void sch_rnd_sheet_init2(void); extern void sch_rnd_project_init(void); /*extern void rnd_main_act_init2(void);*/ /* call before conf init */ static void sch_rnd_csch_init() { csch_init(); sch_rnd_multi_init(); sch_rnd_project_init(); } /* call after conf init */ static void sch_rnd_main_init(void) { csch_init_actions(); sch_rnd_main_act_init2(); sch_rnd_sheet_init2(); sch_rnd_file_act_init2(); sch_rnd_font_act_init2(); sch_rnd_select_act_init2(); sch_rnd_extobj_act_init(); sch_rnd_font_init2(); sch_rnd_draw_init2(); sch_rnd_buffer_init2(); /* rnd_main_act_init2();*/ } #define LIBCSCHEM_CFG_BIND(csch_cfg, native) \ do { \ if (sizeof(*csch_cfg) == sizeof(native)) \ csch_cfg = &native; \ else \ rnd_message(RND_MSG_ERROR, "Can not enable " #native " on this arch. Please report this bug.\n"); \ } while(0) /* Bind conf_core natives to libcschem config API; this is needed because libcschem can't access conf_core directly as it can't depend on app code */ static void libcschem_cfg_init(void) { LIBCSCHEM_CFG_BIND(csch_cfg_multiport_net_merge, conf_core.compile.multiport_net_merge); LIBCSCHEM_CFG_BIND(csch_cfg_hier_net_allow_rename, conf_core.compile.hierarchic.net_allow_rename); LIBCSCHEM_CFG_BIND(csch_cfg_hier_net_rename_prefer_top, conf_core.compile.hierarchic.net_rename_prefer_top); csch_app_load_sheet_cb = sch_rnd_app_load_sheet; } extern void sch_rnd_draw_uninit(void); extern void sch_rnd_buffer_uninit(void); extern void sch_rnd_sheet_uninit(void); extern void sch_rnd_project_uninit(void); void sch_rnd_main_uninit(void) { TODO("librnd4.3: can't enable this because librnd sefaults on exit when it prints a log message and the gui is already uninited; fixed inlibrnd 4.3"); /* rnd_tool_uninit();*/ sch_rnd_sheet_free_all(); sch_rnd_multi_uninit(); rnd_funchash_uninit(); csch_plug_io_act_uninit(); sch_rnd_extobj_act_uninit(); csch_uninit(); sch_rnd_draw_uninit(); sch_rnd_font_uninit(); sch_rnd_buffer_uninit(); sch_rnd_sheet_uninit(); sch_rnd_project_uninit(); gui_support_plugins(0); rnd_hidlib_uninit(); /* plugin unload */ main_path_uninit(); fgw_atexit(); conf_core_uninit(); rnd_log_uninit(); csch_uninit_last(); } extern const char *rnd_conf_internal; extern const char *rnd_hidlib_default_embedded_menu; int main(int argc, char *argv[]) { int n, res = 0; const char *fmt = NULL, *first_fn = NULL; char *exec_prefix; rnd_main_args_t ga; csch_sheet_t *first_sheet = NULL; sch_rnd_export_appspec_t appspec = {0}; srand(time(NULL)); /* Set seed for rand() */ rnd_app.package = "sch-rnd"; rnd_app.version = SCH_VERSION; rnd_app.url = "http://www.repo.hu/projects/sch-rnd"; rnd_app.menu_file_paths = menu_file_paths; rnd_app.menu_name_fmt = menu_name_fmt; rnd_app.default_embedded_menu = rnd_hidlib_default_embedded_menu; rnd_app.conf_core_postproc = sch_rnd_conf_core_postproc; rnd_app.conf_userdir_path = CONF_USER_DIR; rnd_app.conf_user_path = CONF_USER_DIR "/sch-rnd-conf.lht"; rnd_app.conf_internal = rnd_conf_internal; rnd_app.dot_dir = DOT_SCH_RND; rnd_app.crosshair_move_to = sch_rnd_hidlib_crosshair_move_to; rnd_app.draw_attached = sch_rnd_draw_attached; rnd_app.expose_main = sch_rnd_expose_main; rnd_app.expose_preview = sch_rnd_expose_preview; rnd_app.conf_dont_merge_node = sch_rnd_conf_dont_merge_node; rnd_app.multi_design = 1; rnd_printf_app_format = csch_printf_app_format; rnd_fix_locale_and_env(); exec_prefix = rnd_exec_prefix(argv[0], BINDIR, BINDIR_TO_EXECPREFIX); main_path_init(exec_prefix); sch_rnd_csch_init(); rnd_main_args_init(&ga, argc, sch_rnd_action_args); rnd_hidlib_init1(conf_core_init, exec_prefix); free(exec_prefix); csch_event_init_app(); for(n = 1; n < argc; n++) n += rnd_main_args_add(&ga, argv[n], argv[n+1]); rnd_hidlib_init2(pup_buildins, sch_rnd_buildins); csch_init_units(); gui_act_init(); csch_plug_io_act_init(); sch_rnd_main_init(); rnd_conf_set(RND_CFR_CLI, "editor/view/flip_y", 0, "1", RND_POL_OVERWRITE); libcschem_cfg_init(); rnd_funchash_set_table(Functions, RND_ENTRIES(Functions), NULL); /* Would be called from signal handlers too, using exit() */ atexit(sch_rnd_emergency_save); rnd_hidlib_init3_auto(); if (rnd_main_args_setup1(&ga) != 0) { sch_rnd_main_uninit(); rnd_main_args_uninit(&ga); exit(1); } /* Initialize actions only when the gui is already known so only the right one is registered (there can be only one GUI). */ TODO("action list"); /*#include "generated_lists.h"*/ if (rnd_main_args_setup2(&ga, &res) != 0) { sch_rnd_main_uninit(); rnd_main_args_uninit(&ga); exit(res); } for(n = 0; ga.hid_argc > 0; n++, ga.hid_argc--) { const char *fn = ga.hid_argv[n]; csch_sheet_t *sheet; int is_project = 0; sheet = sch_rnd_multi_load(fn, fmt, &is_project); if ((first_fn == NULL) && !is_project) first_fn = fn; /* check if failed to load the sheet */ if (sheet == NULL) { if (rnd_main_exporting) { rnd_message(RND_MSG_ERROR, "Can not load file '%s' (specified on command line) for exporting or printing\n", fn); rnd_log_print_uninit_errs("Export load error"); exit(1); } else if (!sch_rnd_is_fn_project_file(fn)) { sheet = sch_rnd_multi_new_empty(fn); rnd_message(RND_MSG_INFO, "Creating new empty sheet for non-existing file '%s' (specified on command line)\n", fn); if (sheet == NULL) { rnd_log_print_uninit_errs("Sheet creation error"); fprintf(stderr, "Failed to create new sheet (by loading the default sheet), exiting\n"); sch_rnd_main_uninit(); rnd_main_args_uninit(&ga); exit(1); } } else { rnd_log_print_uninit_errs("Project load error"); fprintf(stderr, "Failed to load project '%s', exiting\n", fn); sch_rnd_main_uninit(); rnd_main_args_uninit(&ga); exit(1); } } if (first_sheet == NULL) first_sheet = sheet; else appspec.exp_prj = 1; if (is_project) appspec.exp_prj = 1; } main_sighand_init(); /* switch to the first sheet for export so that the project config is applied */ if (rnd_main_exporting) sch_rnd_multi_switch_to_initial(first_sheet); if (rnd_main_exported(&ga, &first_sheet->hidlib, 0, &appspec)) { rnd_log_print_uninit_errs("Export finished"); sch_rnd_sheet_free_all(); sch_rnd_main_uninit(); rnd_main_args_uninit(&ga); exit(0); } /* started with no sheet, create an unknown */ if (first_sheet == NULL) { first_sheet = sch_rnd_multi_new_empty(NULL); if (first_sheet == NULL) { fprintf(stderr, "Failed to create new sheet (by loading the default sheet)\n"); sch_rnd_main_uninit(); rnd_main_args_uninit(&ga); exit(1); } } sch_rnd_multi_switch_to_initial(first_sheet); sch_rnd_crosshair_gui_init(); sch_rnd_enable_autosave(); /* main loop */ if (RND_HAVE_GUI_ATTR_DLG) gui_support_plugins(1); if (EXPERIMENTAL != NULL) { rnd_message(RND_MSG_ERROR, "******************************** IMPORTANT ********************************\n"); rnd_message(RND_MSG_ERROR, "This revision of sch-rnd is experimental, unstable, do NOT attempt to use\n"); rnd_message(RND_MSG_ERROR, "it for production. The reason for this state is:\n"); rnd_message(RND_MSG_ERROR, "%s\n", EXPERIMENTAL); rnd_message(RND_MSG_ERROR, "******************************** IMPORTANT ********************************\n"); } rnd_mainloop_interactive(&ga, &first_sheet->hidlib); sch_rnd_crosshair_gui_uninit(); sch_rnd_main_uninit(); rnd_main_args_uninit(&ga); return res; }