52 LOG_INFO(
"App",
"Initializing Application instance...");
67 LOG_INFO(
"App",
"Found pending ROM load: %s", start_path.c_str());
68 }
else if (!start_path.empty()) {
69 LOG_INFO(
"App",
"Using configured startup ROM: %s", start_path.c_str());
71 LOG_INFO(
"App",
"No pending ROM, starting empty.");
74 LOG_INFO(
"App",
"WASM build - ROM loading handled via wasm_bootstrap queue.");
82 LOG_ERROR(
"App",
"Failed to initialize controller: %s",
83 std::string(status.message()).c_str());
89 LOG_INFO(
"App",
"Controller initialized successfully. Active: %s",
95 const std::string& preferred_path,
96 test::TestManager::FailureScreenshotCallback callback) {
97 controller->RequestScreenshot({.preferred_path = preferred_path,
98 .reveal_to_user = false,
99 .callback = std::move(callback)});
107 if (
controller_->editor_manager()->user_settings().prefs().test_mode) {
108 LOG_INFO(
"App",
"Test mode: hiding welcome and editor picker");
124 if (!start_path.empty() &&
controller_->editor_manager()) {
131 LOG_INFO(
"App",
"Initializing Unified gRPC Server...");
132 canvas_automation_service_ =
133 std::make_unique<CanvasAutomationServiceImpl>();
134 grpc_server_ = std::make_unique<YazeGRPCServer>();
137 auto rom_getter = [
this]() {
140 auto rom_loader = [
this](
const std::string& path) ->
bool {
143 auto status =
controller_->editor_manager()->OpenRomOrProject(path);
150 LOG_INFO(
"App",
"Using Mesen2 backend for emulator service");
152 std::make_unique<emu::mesen::MesenEmulatorAdapter>();
153 emulator_interface = emulator_backend_.get();
157 internal_emulator = &
controller_->editor_manager()->emulator();
160 "EditorManager not ready; internal emulator services may be "
165 std::make_unique<emu::InternalEmulatorAdapter>(internal_emulator);
168 adapter->SetRomLoader([
this](
const std::string& path) ->
bool {
171 auto status =
controller_->editor_manager()->OpenRomOrProject(path);
175 adapter->SetRomGetter(
176 [
this]() {
return controller_->GetCurrentRom(); });
178 emulator_backend_ = std::move(adapter);
179 emulator_interface = emulator_backend_.get();
183 auto status = grpc_server_->Initialize(
188 canvas_automation_service_.get());
193 grpc_server_->SetEmulatorScreenshotCapturer([] {
194 static std::atomic<int> next_capture{0};
195 const auto path = std::filesystem::temp_directory_path() /
196 absl::StrFormat(
"yaze_gamestate_%d_%d.png",
197 static_cast<int>(getpid()),
198 next_capture.fetch_add(1));
199 return test::CaptureScreenshotOnRenderThread(
204 status = grpc_server_->StartAsync();
206 LOG_ERROR(
"App",
"Failed to start gRPC server: %s",
207 std::string(status.message()).c_str());
209 LOG_INFO(
"App",
"Unified gRPC server started on port %d",
213 LOG_ERROR(
"App",
"Failed to initialize gRPC server: %s",
214 std::string(status.message()).c_str());
218 if (canvas_automation_service_) {
220 canvas_automation_service_.get());
228 yaze::app::wasm::SetRomLoadHandler(
234 const char* runtime_dir = std::getenv(
"XDG_RUNTIME_DIR");
235 const char* status_dir = (runtime_dir && *runtime_dir) ? runtime_dir :
"/tmp";
236 activity_file_ = std::make_unique<app::ActivityFile>(
237 absl::StrFormat(
"%s/yaze-%d.status", status_dir, pid));
238 UpdateActivityStatus();
239 LOG_INFO(
"App",
"Activity file created: %s",
240 activity_file_->GetPath().c_str());
244void Application::Tick() {
253 absl::Cleanup end_tick = [
this] {
258 auto now = std::chrono::steady_clock::now();
260 delta_time_ = 0.016f;
261 first_frame_ =
false;
263 auto elapsed = std::chrono::duration<float>(now - last_frame_time_);
264 delta_time_ = elapsed.count();
266 last_frame_time_ = now;
269 if (
auto* bus = editor::ContentRegistry::Context::event_bus()) {
270 bus->Publish(editor::FrameBeginEvent::Create(delta_time_));
274 auto& wasm_collab = app::platform::GetWasmCollaborationInstance();
275 wasm_collab.ProcessPendingChanges();
278 controller_->OnInput();
279 auto status = controller_->OnLoad();
281 LOG_ERROR(
"App",
"Controller Load Error: %s",
282 std::string(status.message()).c_str());
284 emscripten_cancel_main_loop();
289 if (!controller_->IsActive()) {
294 controller_->DoRender();
297 if (
auto* bus = editor::ContentRegistry::Context::event_bus()) {
298 bus->Publish(editor::FrameEndEvent::Create(delta_time_));
302void Application::LoadRom(
const std::string& path) {
303 LOG_INFO(
"App",
"Requesting ROM load: %s", path.c_str());
307 yaze::app::wasm::TriggerRomLoad(path);
309 "Forwarded to wasm_bootstrap queue (controller not ready): %s",
313 LOG_INFO(
"App",
"Queued ROM load (controller not ready): %s", path.c_str());
320 if (!controller_->IsActive()) {
321 status = controller_->OnEntry(path);
323 status = controller_->editor_manager()->OpenRomOrProject(path);
327 std::string error_msg =
328 absl::StrCat(
"Failed to load ROM: ", status.message());
329 LOG_ERROR(
"App",
"%s", error_msg.c_str());
334 var msg = UTF8ToString($0);
341 LOG_INFO(
"App",
"ROM loaded successfully: %s", path.c_str());
350#ifndef __EMSCRIPTEN__
352 UpdateActivityStatus();
357 { console.log(
"ROM loaded successfully: " + UTF8ToString($0)); },
372void Application::UpdateActivityStatus() {
377 status.
pid = getpid();
381 if (controller_ && controller_->editor_manager()) {
382 auto* rom = controller_->GetCurrentRom();
387 if (config_.enable_test_harness && config_.test_harness_port > 0) {
389 absl::StrFormat(
"localhost:%d", config_.test_harness_port);
393 activity_file_->Update(status);
401void Application::Shutdown() {
404 LOG_INFO(
"App",
"Syncing filesystem before shutdown...");
410 LOG_INFO(
"App",
"Shutting down Unified gRPC Server...");
411 grpc_server_->Shutdown();
412 grpc_server_.reset();
414 canvas_automation_service_.reset();
415 test::TestManager::Get().SetFailureScreenshotRequester({});
418#ifndef __EMSCRIPTEN__
420 if (activity_file_) {
421 LOG_INFO(
"App",
"Removing activity file: %s",
422 activity_file_->GetPath().c_str());
423 activity_file_.reset();
428 controller_->OnExit();