blob: e1ca20e5deee67a490da7db31ab21cc615cbbdb0 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a simple example application (with an embeddable view), which embeds
// the Moterm view, uses it to prompt the user, etc.
#include <string.h>
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "mojo/application/application_runner_chromium.h"
#include "mojo/common/binding_set.h"
#include "mojo/public/c/system/main.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
#include "mojo/public/interfaces/application/shell.mojom.h"
#include "mojo/services/files/interfaces/file.mojom.h"
#include "mojo/services/files/interfaces/types.mojom.h"
#include "mojo/services/terminal/interfaces/terminal.mojom.h"
#include "mojo/services/terminal/interfaces/terminal_client.mojom.h"
#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
#include "mojo/services/ui/views/interfaces/views.mojom.h"
#include "mojo/ui/view_provider_app.h"
// Kind of like |fputs()| (doesn't wait for result).
void Fputs(mojo::files::File* file, const char* s) {
size_t length = strlen(s);
auto a = mojo::Array<uint8_t>::New(length);
memcpy(&a[0], s, length);
file->Write(a.Pass(), 0, mojo::files::Whence::FROM_CURRENT,
mojo::files::File::WriteCallback());
}
class MotermExampleAppView {
public:
MotermExampleAppView(
mojo::Shell* shell,
mojo::InterfaceRequest<mojo::ui::ViewOwner> view_owner_request)
: shell_(shell), weak_factory_(this) {
// Connect to the moterm app.
LOG(INFO) << "Connecting to moterm";
mojo::ServiceProviderPtr moterm_app;
shell->ConnectToApplication("mojo:moterm", GetProxy(&moterm_app), nullptr);
// Create the moterm view and pass it back to the client directly.
mojo::ConnectToService(moterm_app.get(), GetProxy(&moterm_view_provider_));
mojo::ServiceProviderPtr moterm_service_provider;
moterm_view_provider_->CreateView(view_owner_request.Pass(),
GetProxy(&moterm_service_provider));
// Connect to the moterm terminal service associated with the view
// we just created.
mojo::ConnectToService(moterm_service_provider.get(),
GetProxy(&moterm_terminal_));
// Start running.
StartPrompt(true);
}
~MotermExampleAppView() {}
private:
void Resize() {
moterm_terminal_->SetSize(0, 0, false, [](mojo::files::Error error,
uint32_t rows, uint32_t columns) {
DCHECK_EQ(error, mojo::files::Error::OK);
DVLOG(1) << "New size: " << rows << "x" << columns;
});
}
void StartPrompt(bool first_time) {
if (!moterm_file_) {
moterm_terminal_->Connect(GetProxy(&moterm_file_), false,
[](mojo::files::Error error) {
DCHECK_EQ(error, mojo::files::Error::OK);
});
}
if (first_time) {
// Display some welcome text.
Fputs(moterm_file_.get(),
"Welcome to "
"\x1b[1m\x1b[34mM\x1b[31mo\x1b[33mt\x1b[34me\x1b[32mr\x1b[31mm\n"
"\n");
}
Fputs(moterm_file_.get(), "\x1b[0m\nWhere do you want to go today?\n:) ");
moterm_file_->Read(1000, 0, mojo::files::Whence::FROM_CURRENT,
base::Bind(&MotermExampleAppView::OnInputFromPrompt,
weak_factory_.GetWeakPtr()));
}
void OnInputFromPrompt(mojo::files::Error error,
mojo::Array<uint8_t> bytes_read) {
if (error != mojo::files::Error::OK || !bytes_read) {
// TODO(vtl): Handle errors?
NOTIMPLEMENTED();
return;
}
std::string dest_url;
if (bytes_read.size() >= 1) {
base::TrimWhitespaceASCII(
std::string(reinterpret_cast<const char*>(&bytes_read[0]),
bytes_read.size()),
base::TRIM_ALL, &dest_url);
}
if (dest_url.empty()) {
Fputs(moterm_file_.get(), "\nError: no destination URL given\n");
StartPrompt(false);
return;
}
Fputs(moterm_file_.get(),
base::StringPrintf("\nGoing to %s ...\n", dest_url.c_str()).c_str());
moterm_file_.reset();
mojo::ServiceProviderPtr dest_sp;
shell_->ConnectToApplication(dest_url, GetProxy(&dest_sp), nullptr);
mojo::terminal::TerminalClientPtr dest_terminal_client;
mojo::ConnectToService(dest_sp.get(), GetProxy(&dest_terminal_client));
moterm_terminal_->ConnectToClient(
std::move(dest_terminal_client), true,
base::Bind(&MotermExampleAppView::OnDestinationDone,
weak_factory_.GetWeakPtr()));
}
void OnDestinationDone(mojo::files::Error error) {
// We should always succeed. (It'll only fail on synchronous failures, which
// only occur when it's busy.)
DCHECK_EQ(error, mojo::files::Error::OK);
StartPrompt(false);
}
mojo::Shell* const shell_;
mojo::ui::ViewProviderPtr moterm_view_provider_;
mojo::terminal::TerminalPtr moterm_terminal_;
// Valid while prompting.
mojo::files::FilePtr moterm_file_;
base::WeakPtrFactory<MotermExampleAppView> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MotermExampleAppView);
};
class MotermExampleApp : public mojo::ui::ViewProviderApp {
public:
MotermExampleApp() {}
~MotermExampleApp() override {}
// |ViewProviderApp|:
void CreateView(
const std::string& connection_url,
mojo::InterfaceRequest<mojo::ui::ViewOwner> view_owner_request,
mojo::InterfaceRequest<mojo::ServiceProvider> services) override {
new MotermExampleAppView(app_impl()->shell(), view_owner_request.Pass());
}
private:
DISALLOW_COPY_AND_ASSIGN(MotermExampleApp);
};
MojoResult MojoMain(MojoHandle application_request) {
mojo::ApplicationRunnerChromium runner(new MotermExampleApp());
return runner.Run(application_request);
}