| 715 | } |
| 716 | |
| 717 | void RuntimeManager::HandleRuntimeMessage(std::shared_ptr<RuntimeSession> session, ipc::Message message) { |
| 718 | if (message.type == "runtime.state") { |
| 719 | session->info.state = VmStateFromString(message.fields["state"]); |
| 720 | if (message.fields.count("exit_code")) { |
| 721 | session->info.exit_code = std::stoi(message.fields["exit_code"]); |
| 722 | } |
| 723 | // Once the runtime confirms it is running, clear any stale preflight |
| 724 | // failure so the console can stop nagging the user. We deliberately |
| 725 | // keep `last_failure` set during `kStarting` because that is when an |
| 726 | // earlier crash banner is most useful. |
| 727 | if (session->info.state == VmState::kRunning) { |
| 728 | session->info.last_failure.reset(); |
| 729 | } |
| 730 | store_.UpdateRuntime(session->spec.vm_id, session->info); |
| 731 | NotifyStateChanged(session->spec.vm_id, session->info); |
| 732 | return; |
| 733 | } |
| 734 | if (message.type == "guest_agent.state") { |
| 735 | // Runtime emits "1" / "0"; treat anything non-empty-non-"0" as connected. |
| 736 | const auto it = message.fields.find("connected"); |
| 737 | const bool connected = (it != message.fields.end()) && |
| 738 | !(it->second.empty() || it->second == "0" || it->second == "false"); |
| 739 | if (session->info.guest_agent_connected != connected) { |
| 740 | session->info.guest_agent_connected = connected; |
| 741 | store_.UpdateRuntime(session->spec.vm_id, session->info); |
| 742 | NotifyStateChanged(session->spec.vm_id, session->info); |
| 743 | } |
| 744 | return; |
| 745 | } |
| 746 | if (message.type == "console.data") { |
| 747 | std::string data; |
| 748 | auto it = message.fields.find("data_hex"); |
| 749 | if (it != message.fields.end()) { |
| 750 | auto bytes = HexDecode(it->second); |
| 751 | data.assign(bytes.begin(), bytes.end()); |
| 752 | } else { |
| 753 | data.assign(message.payload.begin(), message.payload.end()); |
| 754 | } |
| 755 | BroadcastConsole(session, data); |
| 756 | return; |
| 757 | } |
| 758 | if (message.channel == ipc::Channel::kDisplay) { |
| 759 | nlohmann::json cursor_event; |
| 760 | if (message.type == "display.state") { |
| 761 | std::lock_guard<std::mutex> lock(session->console_mutex); |
| 762 | session->display_state = { |
| 763 | {"active", message.fields["active"] == "1" || message.fields["active"] == "true"}, |
| 764 | {"width", FieldU32(message, "width")}, |
| 765 | {"height", FieldU32(message, "height")}, |
| 766 | }; |
| 767 | } else if (message.type == "display.frame_ready") { |
| 768 | { |
| 769 | std::lock_guard<std::mutex> lock(session->console_mutex); |
| 770 | session->last_frame = { |
| 771 | {"seq", FieldU64(message, "seq")}, |
| 772 | {"width", FieldU32(message, "width")}, |
| 773 | {"height", FieldU32(message, "height")}, |
| 774 | {"stride", FieldU32(message, "stride")}, |
nothing calls this directly
no test coverage detected