| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736 |
- /**
- * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved.
- *
- * This file is part of the QCAD project.
- *
- * QCAD 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.
- *
- * QCAD 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 QCAD.
- */
- include("library.js");
- include("AddOn.js");
- include("scripts/File/OpenFile/OpenFile.js");
- include("scripts/File/AutoSave/AutoSave.js");
- include("scripts/Tools/arguments.js");
- // allow plugins to extend autostart through autostart1.js, autostart2.js, ...:
- for (var i=1; i<10; i++) {
- var fn = "scripts/autostart" + i + ".js";
- if (new QFileInfo(fn).exists() || new QFileInfo(":/" + fn).exists()) {
- include(fn);
- }
- }
- /**
- * Prints version information.
- */
- function version() {
- print();
- print("Version: " + RSettings.getVersionString());
- print("Qt Version: " + RSettings.getQtVersionString());
- print("Release Date: " + RSettings.getReleaseDate());
- print();
- }
- /**
- * Prints command line usage information on stdout.
- */
- function usage() {
- print("\nUsage: " + RSettings.getOriginalArguments()[0] + " [Options] [Files to open]\n"
- + "\n"
- + "-allow-multiple-instances Don't try to avoid multiple instances from running\n"
- + " simultaneously.\n"
- + "-always-load-scripts Forces reloading of scripts when they are used.\n"
- + " This is mainly useful during script development,\n"
- + " to apply changes without restarting QCAD.\n"
- + "-app-id [ID] Set application ID. Multiple instances of the\n"
- + " same application (same ID) cannot run\n"
- + " simultaneously unless -allow-multiple-instances\n"
- + " is used.\n"
- + "-autostart [script file] Starts the given script file instead of the default\n"
- + " scripts/autostart.js. Note that with this option,\n"
- + " QCAD is not started but rather the application \n"
- + " implemented in the given script.\n"
- + "-config [path] Reads and stores settings in a configuration file\n"
- + " at the given location instead of the default location.\n"
- + "-debug-action-order Print action order information in menus\n"
- + "-enable-script-debugger Enables the script debugger.\n"
- + " NOT recommended as this may cause unexpected\n"
- + " behavior when using QCAD.\n"
- + "-exec [script file] [options] Executes the given script file directly\n"
- + " after staring QCAD. Options after the script\n"
- + " file are passed on to the script.\n"
- + "-help Displays this help.\n"
- + "-ignore-script-files Ignore script files on disk.\n"
- + " Only load scripts from plugins if applicable.\n"
- + "-locale [locale] Sets the locale to be used (overrides\n"
- + " the language set in the preferences).\n"
- + " E.g. '-locale de' starts QCAD in German.\n"
- + "-no-gui Don't use GUI. X11: don't connect to X11 server.\n"
- + "-no-show Use but don't display GUI.\n"
- + "-filter [filter] Opens the subsequent file(s) with the explicitly \n"
- + " given import filter.\n"
- + "-font-substitution A B Substitute font A with font B.\n"
- + "-rescan Rescan scripts folder for new add-ons\n"
- + "-version Displays the application version.\n"
- + "-enable-xdata Enables XData (custom properties) support.\n"
- + "-quit Quits QCAD, for example after executing the\n"
- + " given script(s).\n"
- );
- printGenericUsage();
- print("\n");
- }
- /**
- * Executes the scripts that are given as arguments of -exec.
- * \return true if the application should quit after executing the
- * scripts, false otherwise.
- */
- function execScripts(args) {
- for (var i = 1; i < args.length; ++i) {
- if (args[i] === "-exec") {
- ++i;
- if (isNull(args[i])) {
- break;
- }
- // check for existing action (e.g. "scripts/File/NewFile/NewFile.js"):
- var action = RGuiAction.getByScriptFile(args[i]);
- // script is not an existing action:
- if (isNull(action)) {
- // include (run) script which might or might not be action:
- var scriptFile = args[i];
- var scriptFilePath = getAbsolutePathForArg(scriptFile);
- if (new QFileInfo(scriptFilePath).exists()) {
- // including the script evaluates (runs) it:
- include(scriptFilePath);
- // check if script created a class of same name as file base name:
- var fi = new QFileInfo(scriptFile);
- var className = fi.baseName();
- if (!isNull(global[className])) {
- // gui action might not exist yet for this script, create one:
- action = new RGuiAction("");
- action.setScriptFile(scriptFilePath);
- action.setRequiresDocument(false);
- }
- }
- else {
- qWarning("script not found: ", args[i]);
- }
- }
- // script is action or created action:
- if (!isNull(action)) {
- ++i;
- // handle script arguments:
- while (!isNull(args[i]) && args[i] !== "-exec") {
- action.addArgument(args[i]);
- ++i;
- }
- --i;
- action.slotTrigger();
- }
- }
- if (args[i] === "-quit") {
- RSettings.setQuitFlag();
- }
- }
- }
- /**
- * Sets up drag and drop support (dropping files on the application window
- * opens them).
- */
- function setUpDragAndDrop(appWin) {
- appWin.dragEnter.connect(function(event) {
- event.acceptProposedAction();
- });
- appWin.drop.connect(function(event) {
- // workaround for Qt keyboard focus bug:
- var appWin = RMainWindowQt.getMainWindow();
- if (!isNull(appWin)) {
- appWin.activateWindow();
- appWin.raise();
- appWin.setFocus(Qt.OtherFocusReason);
- }
- var urls = getUrlsFromMimeData(event.mimeData());
- var urlStrings = [];
- for (var i = 0; i < urls.length; ++i) {
- urlStrings.push(urls[i].toString());
- }
- openFiles(urlStrings, false);
- event.acceptProposedAction();
- });
- }
- /**
- * Loads translations for all add-ons if appropriate.
- */
- function loadTranslations(addOns, splash) {
- var locale = RSettings.getLocale();
- if (!isNull(splash)) {
- // no translations yet:
- splash.showMessage("Loading translations...\n", Qt.AlignBottom);
- }
- // load C++ translations:
- var modules = ["qt", "assistant", "qt_help", "qcadcore", "qcadentity", "qcadgui"];
- if (RSettings.isQt(5) || RSettings.isQt(6)) {
- modules.unshift("qtbase");
- }
- var i;
- var module;
- for (var mi=0; mi<modules.length; ++mi) {
- module = modules[mi];
- RSettings.loadTranslations(module);
- }
- //RSettings.loadTranslations("scripts_" + locale, [autoPath("scripts/ts")]);
- // load translations from arguments:
- var args = RSettings.getOriginalArguments();
- for (i = 0; i < args.length; ++i) {
- if (args[i] === "-ts") {
- if (++i>=args.length) {
- break;
- }
- module = args[i];
- if (++i>=args.length) {
- break;
- }
- var dir = args[i];
- RSettings.loadTranslations(module, [autoPath(dir)]);
- }
- }
- // RSettings.loadTranslations("scripts", [autoPath("ts")]);
- // RSettings.loadTranslations("proscripts", [autoPath("../qcadpro/ts")]);
- // RSettings.loadTranslations("camscripts", [autoPath("../qcadcam/ts")]);
- // install one QTranslator for each script add-on if available:
- if (!isNull(splash)) {
- splash.showMessage(qsTr("Loading add-on translations...") + "\n", Qt.AlignBottom);
- QCoreApplication.processEvents();
- }
- //RSettings.loadTranslations("Scripts_" + locale, [autoPath("scripts/ts")]);
- for (i = 0; i < addOns.length; ++i) {
- var addOn = addOns[i];
- if (isNull(addOn)) {
- qWarning("Null add on found");
- continue;
- }
- var fi = new QFileInfo(addOn.getPath() + "/ts");
- if (!fi.exists()) {
- // no ts dir:
- continue;
- }
- RSettings.loadTranslations(addOn.getClassName(), [addOn.getPath() + "/ts"]);
- }
- }
- /**
- * Loads and initializes all add-ons.
- *
- * \param addOns array of AddOn objects.
- * \param splash the splash window for displaying status updates.
- */
- function loadAddOns(addOns, splash) {
- if (!isNull(splash)) {
- splash.showMessage(qsTr("Loading add-ons...") + "\n", Qt.AlignBottom);
- QCoreApplication.processEvents();
- }
- var addOn;
- var i;
- for (i=0; i<addOns.length; ++i) {
- addOn = addOns[i];
- addOn.load(true);
- }
- }
- function initAddOns(addOns, splash) {
- if (!isNull(splash)) {
- splash.showMessage(qsTr("Initializing add-ons...") + "\n", Qt.AlignBottom);
- QCoreApplication.processEvents();
- }
- var addOn;
- var i;
- var text = "";
- for (i=0; i<addOns.length; ++i) {
- addOn = addOns[i];
- if (i%10===0 && !isNull(splash)) {
- text = qsTr("Initializing add-ons:") +
- " %1%\n%2"
- .arg(Math.round(i/addOns.length*100))
- .arg(addOn.getClassName());
- splash.showMessage(text, Qt.AlignBottom);
- QCoreApplication.processEvents();
- }
- addOn.init(splash, text);
- }
- }
- function postInitAddOns(addOns, splash) {
- // if (!isNull(splash)) {
- // splash.showMessage(qsTr("Post-initializing add-ons...") + "\n", Qt.AlignBottom);
- // QCoreApplication.processEvents();
- // }
- var addOn;
- var i;
- var viewToolBarsAddOn = undefined;
- for (i=0; i<addOns.length; ++i) {
- addOn = addOns[i];
- if (addOn.getClassName()==="ViewToolbars") {
- viewToolBarsAddOn = addOn;
- }
- else {
- addOn.postInit();
- }
- }
- if (!isNull(viewToolBarsAddOn)) {
- viewToolBarsAddOn.postInit();
- }
- }
- function uninitAddOns(addOns) {
- var addOn;
- var i;
- for (i=0; i<addOns.length; ++i) {
- addOn = addOns[i];
- addOn.uninit();
- }
- }
- /**
- * Loads the add-ons and starts QCAD.
- */
- function main() {
- var i;
- var filesToOpen = [];
- // open file that was clicked in Finder if application was not yet running (macOS):
- qApp.fileOpenRequestReceived.connect(function(fileName) {
- filesToOpen.push(fileName);
- });
- // open file(s) given on command line when application is started but
- // already running:
- qApp.messageReceived.connect(function(message) {
- var args = message.split("\n");
- openFiles(args.slice(1), false);
- execScripts(args);
- });
- var args = RSettings.getOriginalArguments();
- if (args.contains("-help") || args.contains("-h")) {
- usage();
- return;
- }
- if (args.contains("-version") || args.contains("-v")) {
- version();
- return;
- }
- if (args.contains("-enable-xdata")) {
- RSettings.setXDataEnabled(true);
- }
- // app name for ini file differs to avoid conflict with
- // older QCAD versions:
- // note that a plugin might have set a name override already:
- if (!RSettings.hasApplicationNameOverride()) {
- RSettings.setApplicationNameOverride("QCAD3");
- }
- // make sure settings file path is reinitialized:
- RSettings.uninit();
- // alternative path for QCAD3.ini:
- for (i=1; i<args.length; ++i) {
- if (args[i] === "-config") {
- ++i;
- if (i < args.length) {
- QSettings.setPath(QSettings.IniFormat, QSettings.UserScope, getAbsolutePathForArg(args[i]));
- }
- }
- }
- // ignore config file if it does not identify itself with a version
- // number or is known as being incompatible
- var reset = false;
- var ignoreDockappWindows = false;
- var newVersion = false;
- if (RSettings.getStringValue("Application/Version", "").length===0) {
- reset = true;
- }
- // Prevent errors with old configuration files:
- // QCAD 3 Beta
- if (RSettings.getIntValue("Application/Version", 0)<=3000003) {
- reset = true;
- }
- // Prevent errors with old configuration files:
- // QCAD 3 RC2/3: Reset toolbar location
- if (RSettings.hasValue("Application/Version") && RSettings.getIntValue("Application/Version", 0)<=3000006) {
- ignoreDockappWindows = true;
- }
- // Prevent errors with old configuration files:
- // QCAD 3 <= RC5: grid color without alpha, RC6: alpha added, ignore some color settings:
- if (RSettings.getIntValue("Application/Version", 0)<=3000008) {
- RSettings.removeValue("GraphicsViewColors/MetaGridColor");
- RSettings.removeValue("GraphicsViewColors/OriginColor");
- RSettings.removeValue("GraphicsViewColors/RelativeZeroColor");
- RSettings.removeValue("GraphicsViewColors/GridColor");
- RSettings.removeValue("GraphicsViewColors/SelectionColor");
- }
- if (RSettings.getStringValue("Application/Version", "")!==RSettings.getNumericalVersionString()) {
- newVersion = true;
- }
- if (reset===true) {
- var configFile = RSettings.getFileName();
- RSettings.uninit();
- var fi = new QFileInfo(configFile);
- if (fi.exists()) {
- var configPath = fi.absolutePath();
- var configBak = configPath + QDir.separator + fi.baseName() + "_old." + fi.suffix();
- qDebug("Renaming old unversioned config file from '" + configFile + "' to '"+ configBak + "'");
- QFile.remove(configBak);
- QFile.rename(configFile, configBak);
- }
- }
- // detect very first start of this installation:
- var fiSettings = new QFileInfo(RSettings.getFileName());
- var isFirstStart = !fiSettings.exists();
- var numPlugins = RPluginLoader.countPlugins();
- var pluginInfo;
- // look up app name override:
- for (i=0; i<numPlugins; i++) {
- pluginInfo = RPluginLoader.getPluginInfo(i);
- var n = pluginInfo.get("NameOverride", "");
- if (n.length>0) {
- qApp.applicationName = n;
- }
- }
- // if locale is given, don't show first start dialog:
- if (isFirstStart && !args.contains("-locale")) {
- include("scripts/Widgets/FirstStart/FirstStart.js");
- var first = new FirstStart();
- first.showDialog();
- }
- RPluginLoader.initTranslations();
- // correct library paths from 'library' to 'libraries':
- if (RSettings.getIntValue("Application/Version", 0)<=3000008) {
- var oldDefaultSource = new QFileInfo("library").absoluteFilePath();
- var librarySources = RSettings.getValue("LibraryBrowser/SourceList", []);
- if (!isNull(librarySources)) {
- for (i=0; i<librarySources.length; i++) {
- if (new QFileInfo(librarySources[i]).absoluteFilePath()===oldDefaultSource) {
- librarySources[i] = new QFileInfo("libraries/default").absoluteFilePath();
- }
- }
- RSettings.setValue("LibraryBrowser/SourceList", librarySources);
- }
- }
- // theme:
- applyTheme();
- // native / non-native menubar:
- if (RSettings.getBoolValue("MenuBar/UseNativeMenuBar", true)===false) {
- QCoreApplication.setAttribute(Qt.AA_DontUseNativeMenuBar);
- }
- // splash:
- var splash = undefined;
- if (RSettings.getBoolValue("Startup/EnableSplashScreen", true)) {
- var fn;
- var key;
- if (RSettings.getDevicePixelRatio()===2) {
- key = "SplashOverride@2x";
- fn = "scripts/splashscreen@2x.png";
- }
- else {
- key = "SplashOverride";
- fn = "scripts/splashscreen.png";
- }
- // look up slash screen override:
- var maxPri = undefined;
- for (i=0; i<numPlugins; i++) {
- pluginInfo = RPluginLoader.getPluginInfo(i);
- // override priority:
- var pri = pluginInfo.get("OverridePriority");
- if (!isNull(pri)) {
- pri = parseInt(pri);
- }
- else {
- // default to lowest priority:
- pri = 0;
- }
- if (isNull(maxPri) || pri>maxPri) {
- var s = pluginInfo.get(key);
- if (!isNull(s)) {
- fn = s;
- maxPri = pri;
- }
- }
- }
- var pixmap = new QPixmap(fn);
- splash = new QSplashScreen(pixmap);
- splash.objectName = "Splash";
- if (!args.contains("-no-show")) {
- splash.show();
- }
- }
- RFontList.initSubstitutions();
- // mark config file with current version number:
- var previousVersion = RSettings.getStringValue("Application/Version", "");
- RSettings.setValue("Application/Version", RSettings.getNumericalVersionString());
- // save first start information:
- RSettings.setFirstStart(isFirstStart);
- // save new version information:
- RSettings.setNewVersion(newVersion);
- RSettings.setPreviousVersion(parseInt(previousVersion, 10));
- RPluginLoader.postInitPlugins(RPluginInterface.GotSplashWindow);
- // scan for script add-ons (forced for first start, first start after
- // update, if configured):
- var addOns;
- var addOnFilePaths = RSettings.getValue("AddOns/List", []);
- if (addOnFilePaths.length===0 || newVersion ||
- args.contains("-rescan") ||
- RSettings.getBoolValue("Scripting/Rescan", true)===true) {
- if (!isNull(splash)) {
- // no translations yet:
- splash.showMessage("...\n", Qt.AlignBottom);
- QCoreApplication.processEvents();
- }
- addOns = AddOn.getAddOns();
- var addOnList = [];
- for (i = 0; i < addOns.length; ++i) {
- addOnList.push(addOns[i].getFilePath());
- }
- RSettings.setValue("AddOns/List", addOnList);
- }
- else {
- addOns = [];
- for (i=0; i<addOnFilePaths.length; i++) {
- addOns.push(new AddOn(addOnFilePaths[i]));
- }
- }
- // load add-on translations:
- loadTranslations(addOns, splash);
- // create application window:
- var appWin = new RMainWindowQt();
- // Note: animated MUST be true for Qt 5.7:
- // Qt 5.7.0 will not allow tabifying dock widgets if animations are turned off:
- if (RSettings.getQtVersion()<0x050600) {
- // animated must be false for Qt 4.7.2, 5.6.0 (crashes):
- appWin.animated = false;
- }
- appWin.objectName = "MainWindow";
- appWin.windowTitle = qApp.applicationName;
- // save locale
- appWin.setProperty("Locale", RSettings.getLocale());
-
- // set some object names for later look up:
- var menuBar = appWin.menuBar();
- menuBar.objectName = "MenuBar";
- // load add-ons:
- //RDebug.startTimer(0);
- loadAddOns(addOns, splash);
- //RDebug.stopTimer(0, "loading add-ons");
- //RDebug.startTimer(0);
- initAddOns(addOns, splash);
- //RDebug.stopTimer(0, "initializing add-ons");
- // auto load scripts in AutoLoad folders for global script engine:
- var files;
- if (RSettings.isQt(6)) {
- files = RAutoLoadJs.getAutoLoadFiles();
- }
- else {
- files = RAutoLoadEcma.getAutoLoadFiles();
- }
- for (i=0; i<files.length; i++) {
- include(files[i]);
- }
- RPluginLoader.postInitPlugins(RPluginInterface.AddOnsInitialized);
- addActionsToWidgets();
- RPluginLoader.postInitPlugins(RPluginInterface.ActionsAdded);
- appWin.updateGuiActions();
- appWin.acceptDrops = true;
- appWin.dockNestingEnabled = true;
- appWin.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea);
- appWin.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea);
- appWin.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea);
- appWin.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea);
- appWin.setTabPosition(Qt.RightDockWidgetArea, QTabWidget.West);
- appWin.setTabPosition(Qt.LeftDockWidgetArea, QTabWidget.East);
- appWin.setTabPosition(Qt.TopDockWidgetArea, QTabWidget.South);
- appWin.setTabPosition(Qt.BottomDockWidgetArea, QTabWidget.South);
- setUpDragAndDrop(appWin);
- appWin.windowIcon = new QIcon(autoPath("scripts/qcad_icon.png"));
- if (!ignoreDockappWindows) {
- appWin.readSettings();
- }
- RPluginLoader.postInitPlugins(RPluginInterface.GotMainWindowBeforeShow);
- if (!args.contains("-no-show")) {
- appWin.show();
- }
- if (!isNull(splash)) {
- splash.close();
- destr(splash);
- }
- postInitAddOns(addOns);
- RPluginLoader.postInitPlugins(RPluginInterface.GotMainWindow);
- // if the window position / size was not read from a config file,
- // automatically position property editor and library browser (first start):
- if (isFirstStart) {
- var propertyEditorDock = appWin.findChild("PropertyEditorDock");
- if (!isNull(propertyEditorDock)) {
- if (propertyEditorDock["Initialized"]!==true) {
- propertyEditorDock.floating = true;
- propertyEditorDock.move(appWin.x + appWin.width + 20, appWin.y);
- propertyEditorDock.resize(300, appWin.height/3*2);
- }
- }
- var libraryBrowserDock = appWin.findChild("LibraryBrowserDock");
- if (!isNull(libraryBrowserDock)) {
- if (libraryBrowserDock["Initialized"]!==true) {
- libraryBrowserDock.floating = true;
- libraryBrowserDock.move(appWin.x + appWin.width + 20, appWin.y + appWin.height/3*2+20);
- libraryBrowserDock.resize(300, appWin.height/3-20);
- }
- }
- }
- // open files clicked in Finder if application is already running (Mac):
- qApp.fileOpenRequestReceived.connect(function(fileName) {
- openFiles([fileName], false);
- });
- // recover auto save files:
- var recovered = AutoSave.recoverUntitled();
- // open files given on command line:
- var clickedFilesAndArgs = filesToOpen.concat(args.slice(1));
- QCoreApplication.processEvents();
- appWin.setProperty("starting", true);
- var restored = false;
- if (typeof(restoreFiles)=="function") {
- restored = restoreFiles();
- }
- openFiles(clickedFilesAndArgs, !recovered && !restored);
- appWin.setProperty("starting", false);
- RPluginLoader.postInitPlugins(RPluginInterface.LoadedFiles);
- // execute scripts given on command line:
- execScripts(args);
- RPluginLoader.postInitPlugins(RPluginInterface.ScriptsExecuted);
- //var statusBar = appWin.statusBar();
- //var action = RGuiAction.getByScriptFile("scripts/View/ToggleStatusBar/ToggleStatusBar.js");
- //action.setChecked(statusBar.visible);
- RPluginLoader.postInitPlugins(RPluginInterface.AllDone);
- appWin.setFocus();
- var w = appWin.getMdiChild();
- if (!isNull(w)) {
- w.setFocus();
- }
- if (!RSettings.hasQuitFlag()) {
- // start and enter the main application loop:
- QCoreApplication.exec();
- }
- uninitAddOns(addOns);
- // don't use RSettings below this point
- // and we're done:
- destr(appWin);
- qDebug("done");
- }
- // call main here, in the global context:
- main();
|