aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarius Vlad <marius.vlad@collabora.com>2023-11-30 15:51:56 +0200
committerMarius Vlad <marius.vlad@collabora.com>2023-12-04 23:47:03 +0200
commitf00c1e19f5c4cbcd185c8043f3062612bf1537f7 (patch)
tree453b6571c97cc90dbc98d8a8e3d654f7e9208ad2
parent7fd8b36ce15d7617229a86a05e4e431631e684d5 (diff)
tbtnavi: Add gRPC API support
This adds support for gRPC API specifically specifically to allow placing the application on a different output, the remoting one. Bug-AGL: SPEC-5003 Change-Id: Ibe7047b3b21dc67c44111a8b615a51588d2216d9 Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
-rw-r--r--app/AglShellGrpcClient.cpp208
-rw-r--r--app/AglShellGrpcClient.h114
-rw-r--r--app/main.cpp9
-rw-r--r--app/meson.build36
-rw-r--r--app/protocol/agl_shell.proto110
5 files changed, 473 insertions, 4 deletions
diff --git a/app/AglShellGrpcClient.cpp b/app/AglShellGrpcClient.cpp
new file mode 100644
index 0000000..8577f16
--- /dev/null
+++ b/app/AglShellGrpcClient.cpp
@@ -0,0 +1,208 @@
+//include stuff here
+#include <cstdio>
+
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <grpc/grpc.h>
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+
+#include <grpcpp/ext/proto_server_reflection_plugin.h>
+#include <grpcpp/health_check_service_interface.h>
+
+#include "AglShellGrpcClient.h"
+#include "agl_shell.grpc.pb.h"
+
+using namespace std::chrono;
+
+namespace {
+ const char kDefaultGrpcServiceAddress[] = "127.0.0.1:14005";
+}
+
+GrpcClient::GrpcClient()
+{
+ m_channel = grpc::CreateChannel(kDefaultGrpcServiceAddress,
+ grpc::InsecureChannelCredentials());
+
+ // init the stub here
+ m_stub = agl_shell_ipc::AglShellManagerService::NewStub(m_channel);
+ reader = new Reader(m_stub.get());
+}
+
+void
+GrpcClient::WaitForConnected(int wait_time_ms, int tries_timeout)
+{
+ struct timespec ts;
+ grpc_connectivity_state state;
+ int try_ = 0;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 500 * 1000 * 1000; // 500ms
+
+ while (((state = m_channel->GetState(true)) != GRPC_CHANNEL_READY) &&
+ try_++ < tries_timeout) {
+ fprintf(stderr, "waiting for channel state to be ready, current state %d", state);
+ nanosleep(&ts, NULL);
+ }
+
+}
+
+
+bool
+GrpcClient::ActivateApp(const std::string& app_id, const std::string& output_name)
+{
+ agl_shell_ipc::ActivateRequest request;
+
+ request.set_app_id(app_id);
+ request.set_output_name(output_name);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::ActivateResponse reply;
+
+ grpc::Status status = m_stub->ActivateApp(&context, request, &reply);
+ return status.ok();
+}
+
+bool
+GrpcClient::DeactivateApp(const std::string& app_id)
+{
+ agl_shell_ipc::DeactivateRequest request;
+
+ request.set_app_id(app_id);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::DeactivateResponse reply;
+
+ grpc::Status status = m_stub->DeactivateApp(&context, request, &reply);
+ return status.ok();
+}
+
+bool
+GrpcClient::SetAppFloat(const std::string& app_id, int32_t x_pos, int32_t y_pos)
+{
+ agl_shell_ipc::FloatRequest request;
+
+ request.set_app_id(app_id);
+
+ request.set_x_pos(x_pos);
+ request.set_y_pos(y_pos);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::FloatResponse reply;
+
+ grpc::Status status = m_stub->SetAppFloat(&context, request, &reply);
+ return status.ok();
+}
+
+bool
+GrpcClient::SetAppNormal(const std::string& app_id)
+{
+ agl_shell_ipc::NormalRequest request;
+
+ request.set_app_id(app_id);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::NormalResponse reply;
+
+ grpc::Status status = m_stub->SetAppNormal(&context, request, &reply);
+ return status.ok();
+}
+
+bool
+GrpcClient::SetAppFullscreen(const std::string& app_id)
+{
+ agl_shell_ipc::FullscreenRequest request;
+
+ request.set_app_id(app_id);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::FullscreenResponse reply;
+
+ grpc::Status status = m_stub->SetAppFullscreen(&context, request, &reply);
+ return status.ok();
+}
+
+bool
+GrpcClient::SetAppOnOutput(const std::string& app_id, const std::string &output)
+{
+ agl_shell_ipc::AppOnOutputRequest request;
+
+ request.set_app_id(app_id);
+ request.set_output(output);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::AppOnOutputResponse reply;
+
+ grpc::Status status = m_stub->SetAppOnOutput(&context, request, &reply);
+ return status.ok();
+}
+
+bool
+GrpcClient::SetAppPosition(const std::string& app_id, int32_t x, int32_t y)
+{
+ agl_shell_ipc::AppPositionRequest request;
+
+ request.set_app_id(app_id);
+ request.set_x(x);
+ request.set_y(y);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::AppPositionResponse reply;
+
+ grpc::Status status = m_stub->SetAppPosition(&context, request, &reply);
+ return status.ok();
+}
+
+bool
+GrpcClient::SetAppScale(const std::string& app_id, int32_t width, int32_t height)
+{
+ agl_shell_ipc::AppScaleRequest request;
+
+ request.set_app_id(app_id);
+ request.set_width(width);
+ request.set_height(height);
+
+ grpc::ClientContext context;
+ ::agl_shell_ipc::AppScaleResponse reply;
+
+ grpc::Status status = m_stub->SetAppScale(&context, request, &reply);
+ return status.ok();
+}
+
+
+grpc::Status
+GrpcClient::Wait(void)
+{
+ return reader->Await();
+}
+
+void
+GrpcClient::AppStatusState(Callback callback, void *data)
+{
+ reader->AppStatusState(callback, data);
+}
+
+std::vector<std::string>
+GrpcClient::GetOutputs()
+{
+ grpc::ClientContext context;
+ std::vector<std::string> v;
+
+ ::agl_shell_ipc::OutputRequest request;
+ ::agl_shell_ipc::ListOutputResponse response;
+
+ grpc::Status status = m_stub->GetOutputs(&context, request, &response);
+ if (!status.ok())
+ return std::vector<std::string>();
+
+ for (int i = 0; i < response.outputs_size(); i++) {
+ ::agl_shell_ipc::OutputResponse rresponse = response.outputs(i);
+ v.push_back(rresponse.name());
+ }
+
+ return v;
+}
diff --git a/app/AglShellGrpcClient.h b/app/AglShellGrpcClient.h
new file mode 100644
index 0000000..19cfb75
--- /dev/null
+++ b/app/AglShellGrpcClient.h
@@ -0,0 +1,114 @@
+#pragma once
+
+#include <cstdio>
+
+#include <mutex>
+#include <condition_variable>
+#include <grpc/grpc.h>
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/server.h>
+#include <grpcpp/server_builder.h>
+#include <grpcpp/server_context.h>
+
+#include <grpcpp/ext/proto_server_reflection_plugin.h>
+#include <grpcpp/health_check_service_interface.h>
+
+#include "agl_shell.grpc.pb.h"
+
+typedef void (*Callback)(agl_shell_ipc::AppStateResponse app_response, void *data);
+
+class Reader : public grpc::ClientReadReactor<::agl_shell_ipc::AppStateResponse> {
+public:
+ Reader(agl_shell_ipc::AglShellManagerService::Stub *stub)
+ : m_stub(stub)
+ {
+ }
+
+ void AppStatusState(Callback callback, void *_data)
+ {
+ ::agl_shell_ipc::AppStateRequest request;
+
+ // set up the callback
+ m_callback = callback;
+ m_data = _data;
+ m_stub->async()->AppStatusState(&m_context, &request, this);
+
+ StartRead(&m_app_state);
+ StartCall();
+ }
+
+ void OnReadDone(bool ok) override
+ {
+ if (ok) {
+ m_callback(m_app_state, m_data);
+
+ // blocks in StartRead() if the server doesn't send
+ // antyhing
+ StartRead(&m_app_state);
+ }
+ }
+
+ void SetDone()
+ {
+ fprintf(stderr, "%s()\n", __func__);
+ std::unique_lock<std::mutex> l(m_mutex);
+ m_done = true;
+ }
+
+ void OnDone(const grpc::Status& s) override
+ {
+ fprintf(stderr, "%s()\n", __func__);
+ std::unique_lock<std::mutex> l(m_mutex);
+
+ m_status = s;
+
+ fprintf(stderr, "%s() done\n", __func__);
+ m_cv.notify_one();
+ }
+
+ grpc::Status Await()
+ {
+ std::unique_lock<std::mutex> l(m_mutex);
+
+ m_cv.wait(l, [this] { return m_done; });
+
+ return std::move(m_status);
+ }
+private:
+ grpc::ClientContext m_context;
+ ::agl_shell_ipc::AppStateResponse m_app_state;
+ void *m_data;
+ agl_shell_ipc::AglShellManagerService::Stub *m_stub;
+
+ Callback m_callback;
+
+
+ std::mutex m_mutex;
+ std::condition_variable m_cv;
+ grpc::Status m_status;
+ bool m_done = false;
+};
+
+class GrpcClient {
+public:
+ GrpcClient();
+ void WaitForConnected(int wait_time_ms, int tries_timeout);
+ bool ActivateApp(const std::string& app_id, const std::string& output_name);
+ bool DeactivateApp(const std::string& app_id);
+ bool SetAppFloat(const std::string& app_id, int32_t x_pos, int32_t y_pos);
+ bool SetAppFullscreen(const std::string& app_id);
+ bool SetAppOnOutput(const std::string& app_id, const std::string& output);
+ bool SetAppNormal(const std::string& app_id);
+ bool SetAppPosition(const std::string& app_id, int32_t x, int32_t y);
+ bool SetAppScale(const std::string& app_id, int32_t width, int32_t height);
+ std::vector<std::string> GetOutputs();
+ void GetAppState();
+ void AppStatusState(Callback callback, void *data);
+ grpc::Status Wait();
+
+private:
+ Reader *reader;
+ std::unique_ptr<agl_shell_ipc::AglShellManagerService::Stub> m_stub;
+ std::shared_ptr<grpc::Channel> m_channel;
+};
+
diff --git a/app/main.cpp b/app/main.cpp
index 0b896b4..b3fd47a 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -14,10 +14,12 @@
#include "qcheapruler.hpp"
#include "file_operation.h"
+#include "AglShellGrpcClient.h"
+
int main(int argc, char *argv[])
{
- QString graphic_role = QString("tbtnavi");
- struct agl_shell_desktop *agl_shell_desktop = nullptr;
+ std::string our_name = "tbtnavi";
+ QString graphic_role = QString(our_name.c_str());
setenv("QT_QUICK_CONTROLS_STYLE", "AGL", 1);
@@ -28,6 +30,9 @@ int main(int argc, char *argv[])
QCoreApplication::setApplicationVersion("0.1.0");
app.setDesktopFileName(graphic_role);
+ GrpcClient *client = new GrpcClient();
+ client->SetAppOnOutput(our_name, "remoting-remote-1");
+
// Load qml
QQmlApplicationEngine engine;
diff --git a/app/meson.build b/app/meson.build
index 864edb7..a7d1e41 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -1,6 +1,33 @@
cpp = meson.get_compiler('cpp')
qt5_dep = dependency('qt5', modules: ['Qml', 'Quick', 'Gui', 'Location'])
+grpcpp_reflection_dep = cpp.find_library('grpc++_reflection')
+protoc = find_program('protoc')
+grpc_cpp = find_program('grpc_cpp_plugin')
+
+protoc_gen = generator(protoc, \
+ output : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],
+ arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/protocol',
+ '--cpp_out=@BUILD_DIR@',
+ '@INPUT@'])
+
+generated_protoc_sources = protoc_gen.process('protocol/agl_shell.proto')
+
+grpc_gen = generator(protoc, \
+ output : ['@BASENAME@.grpc.pb.cc', '@BASENAME@.grpc.pb.h'],
+ arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/protocol',
+ '--grpc_out=@BUILD_DIR@',
+ '--plugin=protoc-gen-grpc=' + grpc_cpp.path(),
+ '@INPUT@'])
+generated_grpc_sources = grpc_gen.process('protocol/agl_shell.proto')
+
+grpc_deps = [
+ dependency('protobuf'),
+ dependency('grpc'),
+ dependency('grpc++'),
+ grpcpp_reflection_dep,
+]
+
dep_qtappfw = [
dependency('qtappfw-navigation'),
dependency('qtappfw-vehicle-signals'),
@@ -9,12 +36,14 @@ dep_qtappfw = [
tbtnavi_dep = [
qt5_dep,
dep_qtappfw,
+ grpc_deps
]
tbtnavi_headers = [
'qcheapruler.hpp',
'navigation_client.h',
- 'file_operation.h'
+ 'file_operation.h',
+ 'AglShellGrpcClient.h'
]
moc_files = qt5.compile_moc(headers: tbtnavi_headers,
@@ -31,7 +60,10 @@ tbtnavi_src = [
'main.cpp',
'navigation_client.cpp',
'qcheapruler.cpp',
- 'file_operation.cpp'
+ 'file_operation.cpp',
+ 'AglShellGrpcClient.cpp',
+ generated_protoc_sources,
+ generated_grpc_sources,
]
executable('tbtnavi', tbtnavi_src, resource_files, moc_files,
diff --git a/app/protocol/agl_shell.proto b/app/protocol/agl_shell.proto
new file mode 100644
index 0000000..c4f3dfe
--- /dev/null
+++ b/app/protocol/agl_shell.proto
@@ -0,0 +1,110 @@
+syntax = "proto3";
+// using empty Response suitable better for forward compat
+//import "google/protobuf/empty.proto";
+package agl_shell_ipc;
+
+service AglShellManagerService {
+ rpc ActivateApp(ActivateRequest) returns (ActivateResponse) {}
+ rpc DeactivateApp(DeactivateRequest) returns (DeactivateResponse) {}
+ rpc SetAppSplit(SplitRequest) returns (SplitResponse) {}
+ rpc SetAppFloat(FloatRequest) returns (FloatResponse) {}
+ rpc SetAppFullscreen(FullscreenRequest) returns (FullscreenResponse) {}
+ rpc AppStatusState(AppStateRequest) returns (stream AppStateResponse) {}
+ rpc GetOutputs(OutputRequest) returns (ListOutputResponse) {}
+ rpc SetAppNormal(NormalRequest) returns (NormalResponse) {}
+ rpc SetAppOnOutput(AppOnOutputRequest) returns (AppOnOutputResponse) {}
+ rpc SetAppPosition(AppPositionRequest) returns (AppPositionResponse) {}
+ rpc SetAppScale(AppScaleRequest) returns (AppScaleResponse) {}
+}
+
+message ActivateRequest {
+ string app_id = 1;
+ string output_name = 2;
+}
+
+message ActivateResponse {
+};
+
+
+message DeactivateRequest {
+ string app_id = 1;
+}
+
+message DeactivateResponse {
+}
+
+message SplitRequest {
+ string app_id = 1;
+ int32 tile_orientation = 2;
+}
+
+message SplitResponse {
+}
+
+message FloatRequest {
+ string app_id = 1;
+ int32 x_pos = 2;
+ int32 y_pos = 3;
+}
+
+message FloatResponse {
+}
+
+message AppStateRequest {
+}
+
+message AppStateResponse {
+ int32 state = 1;
+ string app_id = 2;
+}
+
+message OutputRequest {
+};
+
+message OutputResponse {
+ string name = 1;
+};
+
+message ListOutputResponse {
+ repeated OutputResponse outputs = 1;
+};
+
+message NormalRequest {
+ string app_id = 1;
+};
+
+message NormalResponse {
+};
+
+message FullscreenRequest {
+ string app_id = 1;
+};
+
+message FullscreenResponse {
+};
+
+message AppOnOutputRequest {
+ string app_id = 1;
+ string output = 2;
+};
+
+message AppOnOutputResponse {
+};
+
+message AppPositionRequest {
+ string app_id = 1;
+ int32 x = 2;
+ int32 y = 3;
+};
+
+message AppPositionResponse {
+};
+
+message AppScaleRequest {
+ string app_id = 1;
+ int32 width = 2;
+ int32 height = 3;
+};
+
+message AppScaleResponse {
+};