@@ -37,12 +37,56 @@ std::vector<TabState> g_tabs;
3737int g_active_tab = 0 ;
3838std::mutex g_mutex;
3939
40+ extern " C" bool g_headless_mode;
4041static std::thread g_cmd_thread;
4142static std::mutex g_cmd_mutex;
4243static std::condition_variable g_cmd_cv;
4344struct CmdItem { int tab; std::string cmd; };
4445static std::queue<CmdItem> g_cmd_queue;
4546static std::atomic_bool g_cmd_thread_running{false };
47+ static std::thread g_headless_stdin_thread;
48+
49+ static void headless_stdin_worker (void ) {
50+ std::string line;
51+ while (std::getline (std::cin, line)) {
52+ if (line.empty ()) continue ;
53+
54+ if (line == " __dump_state__" ) {
55+ std::lock_guard<std::mutex> lk (g_mutex);
56+ std::cout << " \n ---STATE_DUMP_START---\n " ;
57+ std::cout << " {\n " ;
58+ std::cout << " \" active_tab\" : " << g_active_tab << " ,\n " ;
59+ std::cout << " \" tabs\" : [\n " ;
60+ for (size_t i = 0 ; i < g_tabs.size (); ++i) {
61+ std::string title_utf8 = g_tabs[i].title ;
62+ std::string raw_input = g_tabs[i].input_buffer ;
63+ std::string escaped_input;
64+ for (char c : raw_input) {
65+ if (c == ' \\ ' ) escaped_input += " \\\\ " ;
66+ else if (c == ' "' ) escaped_input += " \\\" " ;
67+ else escaped_input += c;
68+ }
69+ std::cout << " {\n " ;
70+ std::cout << " \" index\" : " << i << " ,\n " ;
71+ std::cout << " \" title\" : \" " << title_utf8 << " \" ,\n " ;
72+ std::cout << " \" input_buffer\" : \" " << escaped_input << " \" ,\n " ;
73+ std::cout << " \" plugin_owner\" : \" " << (g_tabs[i].plugin_owner .empty () ? " " : g_tabs[i].plugin_owner ) << " \"\n " ;
74+ std::cout << " }" << (i + 1 < g_tabs.size () ? " ," : " " ) << " \n " ;
75+ }
76+ std::cout << " ]\n " ;
77+ std::cout << " }\n " ;
78+ std::cout << " ---STATE_DUMP_END---\n " << std::endl;
79+ continue ;
80+ }
81+
82+ // push command to queue
83+ {
84+ std::lock_guard<std::mutex> clk (g_cmd_mutex);
85+ g_cmd_queue.push ({g_active_tab, line});
86+ g_cmd_cv.notify_one ();
87+ }
88+ }
89+ }
4690thread_local int g_print_tab = -1 ;
4791int g_hover_close_tab = -1 ;
4892std::vector<HitBox> g_tab_close_boxes;
@@ -429,7 +473,7 @@ static void on_drag_end(GtkGestureDrag *gesture, double offset_x, double offset_
429473}
430474
431475static gboolean on_tick (GtkWidget *widget, GdkFrameClock *frame_clock, gpointer user_data) {
432- // Check if command thread is active (pseudo-logic for now to match Windows)
476+ // check if command thread is active
433477 if (!g_cmd_thread_running) {
434478 g_cmd_thread_running = true ;
435479 g_cmd_thread = std::thread ([]() {
@@ -450,6 +494,9 @@ static gboolean on_tick(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer
450494 }
451495 }
452496 });
497+ if (g_headless_mode) {
498+ g_headless_stdin_thread = std::thread (headless_stdin_worker);
499+ }
453500 }
454501
455502 {
@@ -546,6 +593,9 @@ void main_l_cleanup(void) {
546593 g_object_unref (g_app);
547594 g_app = NULL ;
548595 }
596+ if (g_headless_stdin_thread.joinable ()) {
597+ g_headless_stdin_thread.detach ();
598+ }
549599}
550600
551601void main_l_print_info (const char * msg) {
0 commit comments