summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarius Vlad <marius.vlad@collabora.com>2022-09-30 17:10:39 +0300
committerMarius Vlad <marius.vlad@collabora.com>2022-10-27 17:24:04 +0300
commit4df12ac75fc5b65b7cfbf9d1829c0f780595d43e (patch)
tree847ee27ef2a555b665cce86eef1a689c39e83365
parent9f39c12f709fc9f59b06b0ebbc6b1d9065846f98 (diff)
src: Add initial support for gRPC
Brings in initial support for gRPC. It re-uses the same protobuf as supplied by the compositor. With it, this changes from C to CPP as that allows far better and easier integration with gRPC. Bug-AGL: SPEC-4503 Signed-off-by: Marius Vlad <marius.vlad@collabora.com> Change-Id: Idf09db80063dfa0c4cd7f3f345686fe9d2d27622
-rw-r--r--meson.build61
-rw-r--r--src/AglShellGrpcClient.cpp58
-rw-r--r--src/AglShellGrpcClient.h97
-rw-r--r--src/agl_shell.proto50
-rw-r--r--src/main.cpp (renamed from src/main.c)53
5 files changed, 286 insertions, 33 deletions
diff --git a/meson.build b/meson.build
index 0a56242..fa7b1bb 100644
--- a/meson.build
+++ b/meson.build
@@ -1,41 +1,53 @@
project('agl-shell-activator',
- 'c',
- version: '0.0.1',
+ 'c', 'cpp',
+ version: '0.0.2',
default_options: [
'warning_level=3',
'c_std=gnu99',
],
- meson_version: '>= 0.50',
+ meson_version: '>= 0.60',
license: 'MIT/Expat',
)
pkgconfig = import('pkgconfig')
-cc = meson.get_compiler('c')
-
-add_project_arguments(
- cc.get_supported_arguments([
- '-Wno-unused-parameter',
- '-Wno-pedantic',
- '-Wextra',
- '-Werror'
- ]),
- language: 'c'
-)
+cpp = meson.get_compiler('cpp')
+
+grpcpp_reflection_dep = cpp.find_library('grpc++_reflection')
+protoc = find_program('protoc')
+grpc_cpp = find_program('grpc_cpp_plugin')
+
add_project_arguments([
'-DPACKAGE_STRING="agl-shell-activator @0@"'.format(meson.project_version()),
'-D_GNU_SOURCE',
'-D_ALL_SOURCE',
],
- language: 'c'
+ language: 'cpp'
)
-optional_libc_funcs = [ 'memfd_create', 'strchrnul' ]
-foreach func: optional_libc_funcs
- if cc.has_function(func)
- add_project_arguments('-DHAVE_@0@=1'.format(func.to_upper()), language: 'c')
- endif
-endforeach
+protoc_gen = generator(protoc, \
+ output : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],
+ arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/src',
+ '--cpp_out=@BUILD_DIR@',
+ '@INPUT@'])
+
+generated_protoc_sources = protoc_gen.process('src/agl_shell.proto')
+
+grpc_gen = generator(protoc, \
+ output : ['@BASENAME@.grpc.pb.cc', '@BASENAME@.grpc.pb.h'],
+ arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/src',
+ '--grpc_out=@BUILD_DIR@',
+ '--plugin=protoc-gen-grpc=' + grpc_cpp.path(),
+ '@INPUT@'])
+generated_grpc_sources = grpc_gen.process('src/agl_shell.proto')
+
+grpc_deps = [
+ dependency('protobuf'),
+ dependency('grpc'),
+ dependency('grpc++'),
+ grpcpp_reflection_dep,
+]
+
env_modmap = ''
@@ -98,10 +110,15 @@ common_inc = include_directories('include')
deps_agl_activator = [
libwayland_dep,
+ grpc_deps,
]
srcs_agl_activator = [
- 'src/main.c',
+ 'src/main.cpp',
+ 'src/AglShellGrpcClient.cpp',
+ 'src/AglShellGrpcClient.h',
+ generated_protoc_sources,
+ generated_grpc_sources,
agl_shell_desktop_client_protocol_h,
agl_shell_desktop_protocol_c,
]
diff --git a/src/AglShellGrpcClient.cpp b/src/AglShellGrpcClient.cpp
new file mode 100644
index 0000000..fb8ebbe
--- /dev/null
+++ b/src/AglShellGrpcClient.cpp
@@ -0,0 +1,58 @@
+//include stuff here
+#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 "AglShellGrpcClient.h"
+#include "agl_shell.grpc.pb.h"
+
+namespace {
+ const char kDefaultGrpcServiceAddress[] = "127.0.0.1:14005";
+}
+
+GrpcClient::GrpcClient()
+{
+ auto channel = grpc::CreateChannel(kDefaultGrpcServiceAddress,
+ grpc::InsecureChannelCredentials());
+
+ // init the stub here
+ m_stub = agl_shell_ipc::AglShellManagerService::NewStub(channel);
+ reader = new Reader(m_stub.get());
+}
+
+
+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();
+}
+
+grpc::Status
+GrpcClient::Wait(void)
+{
+ return reader->Await();
+}
+
+void
+GrpcClient::AppStatusState(void)
+{
+ reader->AppStatusState();
+}
diff --git a/src/AglShellGrpcClient.h b/src/AglShellGrpcClient.h
new file mode 100644
index 0000000..009bfbf
--- /dev/null
+++ b/src/AglShellGrpcClient.h
@@ -0,0 +1,97 @@
+#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"
+
+class Reader : public grpc::ClientReadReactor<::agl_shell_ipc::AppStateResponse> {
+public:
+ Reader(agl_shell_ipc::AglShellManagerService::Stub *stub)
+ : m_stub(stub)
+ {
+ }
+
+ void AppStatusState()
+ {
+ ::agl_shell_ipc::AppStateRequest request;
+ m_stub->async()->AppStatusState(&m_context, &request, this);
+
+ StartRead(&m_app_state);
+ StartCall();
+ }
+
+ void OnReadDone(bool ok) override
+ {
+ if (ok) {
+ std::cout << "AppState got app_id " <<
+ m_app_state.app_id() <<
+ ", with state " <<
+ m_app_state.state() << std::endl;
+
+ // 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;
+ agl_shell_ipc::AglShellManagerService::Stub *m_stub;
+
+ std::mutex m_mutex;
+ std::condition_variable m_cv;
+ grpc::Status m_status;
+ bool m_done = false;
+};
+
+class GrpcClient {
+public:
+ GrpcClient();
+ bool ActivateApp(const std::string& app_id, const std::string& output_name);
+ void GetAppState();
+ void AppStatusState();
+ grpc::Status Wait();
+
+private:
+ Reader *reader;
+ std::unique_ptr<agl_shell_ipc::AglShellManagerService::Stub> m_stub;
+};
+
diff --git a/src/agl_shell.proto b/src/agl_shell.proto
new file mode 100644
index 0000000..9cf238a
--- /dev/null
+++ b/src/agl_shell.proto
@@ -0,0 +1,50 @@
+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 AppStatusState(AppStateRequest) returns (stream AppStateResponse) {}
+}
+
+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;
+}
+
+message FloatResponse {
+}
+
+message AppStateRequest {
+}
+
+message AppStateResponse {
+ int32 state = 1;
+ string app_id = 2;
+}
diff --git a/src/main.c b/src/main.cpp
index a0242d7..079001f 100644
--- a/src/main.c
+++ b/src/main.cpp
@@ -28,9 +28,12 @@
#include <string.h>
#include <stddef.h>
#include <assert.h>
+#include <thread>
#include <wayland-client.h>
#include <wayland-util.h>
+
+#include "AglShellGrpcClient.h"
#include "agl-shell-desktop-client-protocol.h"
#ifndef MIN
@@ -86,7 +89,7 @@ display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor)
static void
display_handle_name(void *data, struct wl_output *wl_output, const char *name)
{
- struct window_output *woutput = data;
+ struct window_output *woutput = static_cast<struct window_output *>(data);
woutput->name = strdup(name);
fprintf(stderr, "Adding output '%s'\n", name);
@@ -112,15 +115,15 @@ display_add_output(struct display *display, uint32_t id, uint32_t version)
{
struct window_output *w_output;
- w_output = calloc(1, sizeof(*w_output));
+ w_output = static_cast<struct window_output *>(calloc(1, sizeof(*w_output)));
w_output->display = display;
if (version < 4)
w_output->output =
- wl_registry_bind(display->registry, id, &wl_output_interface, MAX(version, 3));
+ static_cast<struct wl_output *>(wl_registry_bind(display->registry, id, &wl_output_interface, MAX(version, 3)));
else
w_output->output =
- wl_registry_bind(display->registry, id, &wl_output_interface, MIN(version, 4));
+ static_cast<struct wl_output *>(wl_registry_bind(display->registry, id, &wl_output_interface, MIN(version, 4)));
wl_list_insert(&display->output_list, &w_output->link);
wl_output_add_listener(w_output->output, &output_listener, w_output);
@@ -159,11 +162,11 @@ static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t id, const char *interface, uint32_t version)
{
- struct display *d = data;
+ struct display *d = static_cast<struct display *>(data);
if (strcmp(interface, "agl_shell_desktop") == 0) {
d->agl_shell_desktop =
- wl_registry_bind(registry, id, &agl_shell_desktop_interface, 1);
+ static_cast<struct agl_shell_desktop *>(wl_registry_bind(registry, id, &agl_shell_desktop_interface, 1));
agl_shell_desktop_add_listener(d->agl_shell_desktop,
&desktop_shell_listener, d);
} else if (strcmp(interface, "wl_output") == 0) {
@@ -191,7 +194,7 @@ create_display(void)
{
struct display *display;
- display = malloc(sizeof *display);
+ display = static_cast<struct display *>(malloc(sizeof *display));
if (display == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
@@ -230,12 +233,19 @@ destroy_display(struct display *display)
free(display);
}
+static void
+run_in_thread(GrpcClient *client)
+{
+ grpc::Status status = client->Wait();
+}
+
int main(int argc, char *argv[])
{
struct display *display;
struct window_output *w_output = NULL;
char *output_name = NULL;
int ret = 0;
+ bool use_grpc_activation = false;
if (argc < 2) {
exit(EXIT_FAILURE);
@@ -245,6 +255,10 @@ int main(int argc, char *argv[])
output_name = argv[2];
}
+ if (argc == 4 && strcmp(argv[3], "use-grpc") == 0)
+ use_grpc_activation = true;
+
+
display = create_display();
/* the app has to be already started, or not already active */
@@ -272,11 +286,28 @@ int main(int argc, char *argv[])
fprintf(stderr, "Activating application '%s' on output '%s'\n",
app_id_to_activate, w_output->name ?
w_output->name : "first default output");
- agl_shell_desktop_activate_app(display->agl_shell_desktop, app_id_to_activate,
- NULL, w_output->output);
- while (running && ret != -1) {
- ret = wl_display_dispatch(display->display);
+ if (use_grpc_activation) {
+ fprintf(stderr, "using grpc\n");
+ GrpcClient *client = new GrpcClient();
+
+ // this blocks in wait
+ std::thread thread(run_in_thread, client);
+
+ // this does the actually call to get events
+ client->AppStatusState();
+
+ client->ActivateApp(std::string(app_id_to_activate),
+ std::string(w_output->name));
+ thread.join();
+ } else {
+ fprintf(stderr, "using agl_shell_desktop\n");
+ agl_shell_desktop_activate_app(display->agl_shell_desktop,
+ app_id_to_activate, NULL, w_output->output);
+
+ while (running && ret != -1) {
+ ret = wl_display_dispatch(display->display);
+ }
}
destroy_display(display);