blob: b7154a897a975fc6fb069028b6beb55cf31c0ed8 [file] [log] [blame] [view]
# How-To: Write a Module in C++
## Overview
A `Module` is a UI component that can participate in a [Story](link to story doc),
potentially composed of many different `Module`s. A `Module`'s lifecycle is tightly
bound to the story to which it was added. In addition to the capabilities
provided to all Peridot components via `fuchsia::modular::ComponentContext`, a `Module` is given
additional capabilities via its `fuchsia::modular::ModuleContext`.
## `SimpleMod`
`SimpleMod` is a `Module` communicates with `SimpleAgent` via a `fuchsia::modular::MessageQueue`, and
displays the messages from `SimpleAgent` on screen.
### Mod Initialization
The first step to writing a `Module` is implementing the initializer.
```c++
#include <lib/component/cpp/startup_context.h>
#include <lib/app_driver/cpp/module_driver.h>
#include <lib/async-loop/cpp/loop.h>
#include <ui/cpp/fidl.h>
namespace simple {
class SimpleModule : fuchsia::ui::app::ViewProvider {
public:
SimpleModule(
modular::ModuleHost* module_host,
fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider> view_provider_request)
: view_provider_binding_(this) {
view_provider_binding_.Bind(std::move(view_provider_request));
}
private:
modular::ModuleHost* module_host_;
fidl::Binding<fuchsia::ui::app::ViewProvider> view_provider_binding_;
std::set<std::unique_ptr<SimpleView>> views_;
};
} // namespace simple
```
The `ModuleHost` provides `SimpleModule` with its `StartupContext` and
`fuchsia::modular::ModuleContext`.
The `ViewProvider` request allows the system to connect to `SimpleModule`'s view.
TODO: Update guide to explain view connections.
### Connecting to `SimpleAgent`
In order to provide `SimpleAgent` with a message queue `SimpleModule` first
needs to connect to the agent via its `fuchsia::modular::ComponentContext`.
```c++
// Get the component context from the module context.
modular::fuchsia::modular::ComponentContextPtr component_context;
module_host->module_context()->GetComponentContext(
component_context.NewRequest());
// Connect to the agent to retrieve it's outgoing services.
modular::fuchsia::modular::AgentControllerPtr agent_controller;
fuchsia::sys::ServiceProviderPtr agent_services;
component_context->ConnectToAgent("system/bin/simple_agent",
agent_services.NewRequest(),
agent_controller.NewRequest());
```
### Creating a `fuchsia::modular::MessageQueue`
`SimpleModule` needs to create a message queue and retrieve its token to hand
it over to `SimpleAgent` so it can write messages to it.
```c++
// Request a new message queue from the component context.
modular::MessageQueueClient message_queue;
component_context->ObtainMessageQueue("agent_queue",
message_queue.NewRequest());
// Get the token for the message queue and send it to the agent.
message_queue.GetToken(
[agent_service = std::move(agent_service)](fidl::StringPtr token) {
agent_service->SetMessageQueue(token);
});
```
### Communicating with `SimpleAgent
In order to receive messages on the newly created message queue, `SimpleModule`
registers a receiver callback with `MessageQueueClient`. Here, the receiver is a
lambda that gets called with new messages.
```c++
// Register a callback with a message queue client that logs any
// messages that SimpleAgent sends
message_queue.RegsiterReceiver(
[](std::string msg, fit::function<void()> ack) {
ack();
FXL_LOG(INFO) << "New message: " << msg;
});
```
### Running the Module
```c++
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigAttachToThread);
auto context = component::StartupContext::CreateFromStartupInfo();
modular::ModuleDriver<simple::SimpleModule> driver(context.get(),
[&loop] { loop.Quit(); });
loop.Run();
return 0;
}
```
`ModuleDriver` is a helper class that manages the `Module`'s lifecyle. Here it is
given a newly created `StartupContext` and a callback that will be executed
when the `Module` exits. `ModuleDriver` requires `SimpleModule` to implement the
constructor shown above, as well as a `Terminate`:
```c++
void Terminate(fit::function<void()> done);
```
The module is responsible for calling `done` once its shutdown sequence is complete.