55#include < strings.h>
66
77#include < array>
8+ #include < charconv>
89#include < climits>
910#include < clocale>
1011#include < csignal>
1112#include < cstdio>
1213#include < cstdlib>
14+ #include < cstring>
1315#include < filesystem>
1416#include < iostream>
1517#include < memory>
3739#include " sta/StringUtil.hh"
3840#include " utl/Logger.h"
3941#include " utl/decode.h"
42+ #include " web/web.h"
4043
4144#ifdef BAZEL_CURRENT_REPOSITORY
4245#include " bazel/tcl_library_init.h"
@@ -93,6 +96,8 @@ static const char* metrics_filename = nullptr;
9396static const char * read_odb_filename = nullptr ;
9497static bool no_settings = false ;
9598static bool minimize = false ;
99+ static bool web_enabled = false ;
100+ static const char * web_port_arg = nullptr ;
96101
97102static const char * init_filename = " .openroad" ;
98103
@@ -249,6 +254,8 @@ int main(int argc, char* argv[])
249254 read_odb_filename = findCmdLineKey (argc, argv, " -db" );
250255 no_settings = findCmdLineFlag (argc, argv, " -no_settings" );
251256 minimize = findCmdLineFlag (argc, argv, " -minimize" );
257+ web_enabled = findCmdLineFlag (argc, argv, " -web" );
258+ web_port_arg = findCmdLineKey (argc, argv, " -web_port" );
252259
253260 cmd_argc = argc;
254261 cmd_argv = argv;
@@ -375,6 +382,22 @@ static int tclAppInit(int& argc,
375382 ord::initOpenRoad (
376383 interp, log_filename, metrics_filename, exit_after_cmd_file);
377384
385+ // Start the web server before splash/thread output so the
386+ // WebLogSink captures all startup messages for the browser console.
387+ if (web_enabled) {
388+ int port = 0 ;
389+ if (web_port_arg) {
390+ const char * end = web_port_arg + std::strlen (web_port_arg);
391+ auto [ptr, ec] = std::from_chars (web_port_arg, end, port);
392+ if (ec != std::errc{} || ptr != end || port < 0 || port > 65535 ) {
393+ fprintf (
394+ stderr, " Error: invalid -web_port value '%s'\n " , web_port_arg);
395+ exit (EXIT_FAILURE );
396+ }
397+ }
398+ ord::OpenRoad::openRoad ()->getWebServer ()->serve (port);
399+ }
400+
378401 bool no_splash = findCmdLineFlag (argc, argv, " -no_splash" );
379402 if (!no_splash) {
380403 showSplash ();
@@ -389,7 +412,11 @@ static int tclAppInit(int& argc,
389412 ord::OpenRoad::openRoad ()->getThreadCount (), false );
390413 }
391414
392- const bool gui_enabled = gui::Gui::enabled ();
415+ // gui::Gui::enabled() is true when a HeadlessViewer is installed
416+ // (which the web server does). But addRestoreStateCommand() only
417+ // works with the Qt event loop — the web server executes scripts
418+ // directly on the main thread, like the non-GUI path.
419+ const bool gui_enabled = gui::Gui::enabled () && !web_enabled;
393420
394421 if (read_odb_filename) {
395422 std::string cmd = fmt::format (" read_db {{{}}}" , read_odb_filename);
@@ -448,6 +475,12 @@ static int tclAppInit(int& argc,
448475 }
449476 }
450477 }
478+
479+ // Block until the web server is stopped (like QApplication::exec()
480+ // for the GUI). After this returns, fall through to readline.
481+ if (web_enabled) {
482+ ord::OpenRoad::openRoad ()->getWebServer ()->waitForStop ();
483+ }
451484 }
452485#ifdef ENABLE_READLINE
453486 // Initialize readline unless the Qt GUI is active (it has its own
@@ -484,15 +517,18 @@ int ord::tclInit(Tcl_Interp* interp)
484517static void showUsage (const char * prog, const char * init_filename)
485518{
486519 printf (" Usage: %s [-help] [-version] [-no_init] [-no_splash] [-exit] " , prog);
487- printf (" [-gui] [-threads count|max] [-log file_name] [-metrics file_name] " );
488- printf (" [-db file_name] [-no_settings] [-minimize] cmd_file\n " );
520+ printf (" [-gui] [-web] [-threads count|max] [-log file_name] " );
521+ printf (" [-metrics file_name] [-db file_name] [-no_settings] [-minimize] " );
522+ printf (" cmd_file\n " );
489523 printf (" -help show help and exit\n " );
490524 printf (" -version show version and exit\n " );
491525 printf (" -no_init do not read %s init file\n " , init_filename);
492526 printf (" -threads count|max use count threads\n " );
493527 printf (" -no_splash do not show the license splash at startup\n " );
494528 printf (" -exit exit after reading cmd_file\n " );
495529 printf (" -gui start in gui mode\n " );
530+ printf (" -web start in web viewer mode\n " );
531+ printf (" -web_port port web server port (default auto-assigned)\n " );
496532 printf (" -minimize start the gui minimized\n " );
497533 printf (" -no_settings do not load the previous gui settings\n " );
498534#ifdef ENABLE_PYTHON3
0 commit comments