diff options
author | Marius Vlad <marius.vlad@collabora.com> | 2022-09-30 17:10:39 +0300 |
---|---|---|
committer | Marius Vlad <marius.vlad@collabora.com> | 2022-10-27 17:24:04 +0300 |
commit | 4df12ac75fc5b65b7cfbf9d1829c0f780595d43e (patch) | |
tree | 847ee27ef2a555b665cce86eef1a689c39e83365 | |
parent | 9f39c12f709fc9f59b06b0ebbc6b1d9065846f98 (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.build | 61 | ||||
-rw-r--r-- | src/AglShellGrpcClient.cpp | 58 | ||||
-rw-r--r-- | src/AglShellGrpcClient.h | 97 | ||||
-rw-r--r-- | src/agl_shell.proto | 50 | ||||
-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); |