app: Convert to meson build system and use gRPC 80/29480/1
authorMarius Vlad <marius.vlad@collabora.com>
Fri, 24 Nov 2023 12:59:17 +0000 (14:59 +0200)
committerMarius Vlad <marius.vlad@collabora.com>
Mon, 27 Nov 2023 15:13:56 +0000 (17:13 +0200)
This converts to using newer meson build system, and makes use of gRPC
API rather using native agl-shell-desktop protocol. We still need to use
wayland for window set-up so xdg-shell is still there and supported.

Bug-AGL: SPEC-4987
Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
Change-Id: I81eefe103c67b8d490fd33c14cc8f4b04feceaed

app/AglShellGrpcClient.cpp [new file with mode: 0644]
app/AglShellGrpcClient.h [new file with mode: 0644]
app/CMakeLists.txt [deleted file]
app/main.cpp
app/meson.build [new file with mode: 0644]
app/protocol/agl_shell.proto [new file with mode: 0644]
meson.build [moved from CMakeLists.txt with 57% similarity]

diff --git a/app/AglShellGrpcClient.cpp b/app/AglShellGrpcClient.cpp
new file mode 100644 (file)
index 0000000..8577f16
--- /dev/null
@@ -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 (file)
index 0000000..19cfb75
--- /dev/null
@@ -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/CMakeLists.txt b/app/CMakeLists.txt
deleted file mode 100644 (file)
index d7935f3..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-###########################################################################
-# Copyright 2018,2022 Konsulko Group
-# Copyright 2020 Collabora, Ltd.
-#
-# Author: Scott Murray <scott.murray@konsulko.com>
-# Author: Marius Vlad <marius.vlad@collabora.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-###########################################################################
-
-project(camera-gstreamer)
-
-if(CMAKE_VERSION VERSION_LESS "3.7.0")
-    set(CMAKE_INCLUDE_CURRENT_DIR ON)
-endif()
-
-set(CMAKE_CXX_STANDARD 14)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-find_package(PkgConfig REQUIRED)
-find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
-
-add_custom_command(
-       OUTPUT  agl-shell-desktop-client-protocol.h
-       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header
-       < ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell-desktop.xml
-       > ${CMAKE_SOURCE_DIR}/app/agl-shell-desktop-client-protocol.h
-       DEPENDS ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell-desktop.xml
-)
-
-add_custom_command(
-       OUTPUT  ${CMAKE_BINARY_DIR}/app/agl-shell-desktop-client-protocol.h
-       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header
-       < ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell-desktop.xml
-       > ${CMAKE_SOURCE_DIR}/app/agl-shell-desktop-client-protocol.h
-       DEPENDS ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell-desktop.xml
-)
-
-add_custom_command(
-       OUTPUT  agl-shell-desktop-protocol.c
-       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code
-       < ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell-desktop.xml
-       > ${CMAKE_BINARY_DIR}/app/agl-shell-desktop-protocol.c
-       DEPENDS ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell-desktop.xml
-)
-
-pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
-pkg_check_modules(GSTREAMER_PLUGINS_BASE REQUIRED gstreamer-plugins-base-1.0)
-pkg_check_modules(GSTREAMER_VIDEO REQUIRED gstreamer-video-1.0)
-pkg_check_modules(GSTREAMER_PLUGINS_BAD REQUIRED gstreamer-plugins-bad-1.0)
-
-pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
-pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols>=1.18)
-pkg_get_variable(WAYLAND_PROTOCOLS_BASE wayland-protocols pkgdatadir)
-
-add_custom_command(
-       OUTPUT  xdg-shell-client-protocol.h
-       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header
-       < ${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml
-       > ${CMAKE_SOURCE_DIR}/app/xdg-shell-client-protocol.h
-       DEPENDS ${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml
-)
-
-add_custom_command(
-       OUTPUT  ${CMAKE_BINARY_DIR}/app/xdg-shell-client-protocol.h
-       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header
-       < ${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml
-       > ${CMAKE_SOURCE_DIR}/app/xdg-shell-client-protocol.h
-       DEPENDS ${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml
-)
-
-add_custom_command(
-       OUTPUT  xdg-shell-protocol.c
-       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code
-       < ${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml
-       > ${CMAKE_BINARY_DIR}/app/xdg-shell-protocol.c
-       DEPENDS ${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml
-)
-
-add_executable(${PROJECT_NAME}
-       main.cpp
-       utils.h
-       utils.cpp
-       agl-shell-desktop-protocol.c
-       agl-shell-desktop-client-protocol.h
-       xdg-shell-protocol.c
-       xdg-shell-client-protocol.h
-       ${RESOURCES}
-)
-
-include_directories(
-       "${GSTREAMER_INCLUDE_DIRS}"
-       "${GSTREAMER_PLUGINS_BASE_INCLUDE_DIRS}"
-       "${GSTREAMER_PLUGINS_BAD_INCLUDE_DIRS}"
-       "${GSTREAMER_VIDEO_INCLUDE_DIRS}"
-)
-
-target_link_libraries(${PROJECT_NAME}
-       ${GSTREAMER_LIBRARIES}
-       "${GSTREAMER_PLUGINS_BASE_LIBRARIES}"
-       "${GSTREAMER_PLUGINS_BAD_LIBRARIES}"
-       "${GSTREAMER_VIDEO_LIBRARIES}"
-       ${WAYLAND_CLIENT_LIBRARIES}
-       -lgstwayland-1.0
-)
-
-install(TARGETS ${PROJECT_NAME} DESTINATION bin)
-include(GNUInstallDirs)
-install(FILES ${PROJECT_NAME}.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
-install(FILES still-image.jpg DESTINATION ${CMAKE_INSTALL_DATADIR}/applications/data)
-add_definitions(-DAPP_DATA_PATH=${CMAKE_INSTALL_FULL_DATADIR}/applications/data)
index 83f7c28..0247d3e 100644 (file)
@@ -14,9 +14,8 @@
 #include <fcntl.h>
 #include <assert.h>
 
-#include "xdg-shell-client-protocol.h"
-#include "agl-shell-desktop-client-protocol.h"
 #include "utils.h"
+#include "xdg-shell-client-protocol.h"
 
 #include <gst/gst.h>
 
 #define gst_wl_display_handle_context_new gst_wayland_display_handle_context_new
 #endif
 
+#ifndef APP_DATA_PATH
+#define APP_DATA_PATH /usr/share/applications/data
+#endif
+
 // these only applies if the window is a dialog/pop-up one
 // by default the compositor make the window maximized
 #define WINDOW_WIDTH_SIZE      640
@@ -57,8 +60,6 @@ struct display {
        } output_data;
 
        struct xdg_wm_base *wm_base;
-       struct agl_shell_desktop *agl_shell_desktop;
-
        int has_xrgb;
 };
 
@@ -366,32 +367,6 @@ static const struct wl_output_listener output_listener = {
        display_handle_scale
 };
 
-static void
-application_id(void *data, struct agl_shell_desktop *agl_shell_desktop,
-               const char *app_id)
-{
-       (void) data;
-       (void) agl_shell_desktop;
-       (void) app_id;
-}
-
-static void
-application_id_state(void *data, struct agl_shell_desktop *agl_shell_desktop,
-                    const char *app_id, const char *app_data,
-                    uint32_t app_state, uint32_t app_role)
-{
-        (void) data;
-        (void) app_data;
-        (void) agl_shell_desktop;
-        (void) app_id;
-        (void) app_state;
-        (void) app_role;
-}
-
-static const struct agl_shell_desktop_listener agl_shell_desktop_listener = {
-        application_id,
-        application_id_state,
-};
 
 static void
 registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
@@ -411,12 +386,6 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
                d->shm = static_cast<struct wl_shm *>(wl_registry_bind(registry,
                                id, &wl_shm_interface, 1));
                wl_shm_add_listener(d->shm, &shm_listener, d);
-       } else if (strcmp(interface, "agl_shell_desktop") == 0) {
-               d->agl_shell_desktop = static_cast<struct agl_shell_desktop *>(wl_registry_bind(registry, id,
-                                                       &agl_shell_desktop_interface, 1));
-               /* as an example, show how to register for events from the compositor */
-               agl_shell_desktop_add_listener(d->agl_shell_desktop,
-                                              &agl_shell_desktop_listener, d);
        } else if (strcmp(interface, "wl_output") == 0) {
                d->wl_output = static_cast<struct wl_output *>(wl_registry_bind(registry, id,
                                             &wl_output_interface, 1));
@@ -682,11 +651,6 @@ create_display(int argc, char *argv[])
                return NULL;
        }
 
-       if (display->agl_shell_desktop == NULL) {
-               fprintf(stderr, "No agl_shell extension present\n");
-               return NULL;
-       }
-
        wl_display_roundtrip(display->wl_display);
 
        if (!display->has_xrgb) {
@@ -706,9 +670,6 @@ destroy_display(struct display *display)
        if (display->wm_base)
                xdg_wm_base_destroy(display->wm_base);
 
-       if (display->agl_shell_desktop)
-               agl_shell_desktop_destroy(display->agl_shell_desktop);
-
        if (display->wl_compositor)
                wl_compositor_destroy(display->wl_compositor);
 
@@ -819,13 +780,6 @@ int main(int argc, char* argv[])
        if (!display)
                return -1;
 
-       // if you'd want to place the video in a pop-up/dialog type of window:
-       // agl_shell_desktop_set_app_property(display->agl_shell_desktop, app_id,
-       //                                 AGL_SHELL_DESKTOP_APP_ROLE_POPUP,
-       //                                 WINDOW_WIDTH_POS_X, WINDOW_WIDTH_POS_Y,
-       //                                 0, 0, WINDOW_WIDTH_SIZE, WINDOW_HEIGHT_SIZE,
-       //                                 display->wl_output);
-
        // we use the role to set a correspondence between the top level
        // surface and our application, with the previous call letting the
        // compositor know that we're one and the same
diff --git a/app/meson.build b/app/meson.build
new file mode 100644 (file)
index 0000000..2eba85c
--- /dev/null
@@ -0,0 +1,120 @@
+cpp = meson.get_compiler('cpp')
+dep_wayland_client = dependency('wayland-client', version: '>= 1.20.0')
+
+dep_wp = dependency('wayland-protocols', version: '>= 1.24')
+dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir')
+
+grpcpp_reflection_dep = cpp.find_library('grpc++_reflection')
+protoc = find_program('protoc')
+grpc_cpp = find_program('grpc_cpp_plugin')
+
+dep_scanner = dependency('wayland-scanner')
+prog_scanner = find_program('wayland-scanner')
+
+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,
+]
+
+
+protocols = [
+        [ 'xdg-shell', 'stable' ],
+]
+
+foreach proto: protocols
+        proto_name = proto[0]
+        if proto[1] == 'internal'
+                base_file = proto_name
+                xml_path = '@0@.xml'.format(proto_name)
+        elif proto[1] == 'stable'
+                base_file = proto_name
+                xml_path = '@0@/stable/@1@/@1@.xml'.format(dir_wp_base, base_file)
+        else
+                base_file = '@0@-unstable-@1@'.format(proto_name, proto[1])
+                xml_path = '@0@/unstable/@1@/@2@.xml'.format(dir_wp_base, proto_name, base_file)
+        endif
+
+        foreach output_type: [ 'client-header', 'server-header', 'private-code' ]
+                if output_type == 'client-header'
+                        output_file = '@0@-client-protocol.h'.format(base_file)
+                elif output_type == 'server-header'
+                        output_file = '@0@-server-protocol.h'.format(base_file)
+                else
+                        output_file = '@0@-protocol.c'.format(base_file)
+                        if dep_scanner.version().version_compare('< 1.14.91')
+                                output_type = 'code'
+                        endif
+                endif
+
+                var_name = output_file.underscorify()
+                target = custom_target(
+                        '@0@ @1@'.format(base_file, output_type),
+                        command: [ prog_scanner, output_type, '@INPUT@', '@OUTPUT@' ],
+                        input: xml_path,
+                        output: output_file,
+                )
+
+                set_variable(var_name, target)
+        endforeach
+endforeach
+
+depnames_gstreamer = [
+        'gstreamer-1.0', 'gstreamer-plugins-bad-1.0', 'gstreamer-wayland-1.0',
+        'gstreamer-video-1.0', 'gstreamer-plugins-base-1.0',
+]
+
+deps_gstreamer = []
+foreach depname : depnames_gstreamer
+        dep = dependency(depname, required: false)
+        if not dep.found()
+                error('Required @0@ which was not found. '.format(depname) + user_hint)
+        endif
+        deps_gstreamer += dep
+endforeach
+
+
+camera_gstreamer_dep = [
+    dep_wayland_client,
+    deps_gstreamer,
+    grpc_deps
+]
+
+camera_gstreamer_src_headers = [
+  xdg_shell_client_protocol_h,
+  'utils.h',
+  'AglShellGrpcClient.h',
+]
+
+camera_gstreamer_src = [
+  xdg_shell_protocol_c,
+  'utils.cpp',
+  'AglShellGrpcClient.cpp',
+  'main.cpp',
+  generated_protoc_sources,
+  generated_grpc_sources
+]
+
+install_data('still-image.jpg', install_dir: get_option('datadir') / 'applications/data')
+
+executable('camera-gstreamer', camera_gstreamer_src, camera_gstreamer_src_headers,
+            dependencies : camera_gstreamer_dep,
+            install: true)
diff --git a/app/protocol/agl_shell.proto b/app/protocol/agl_shell.proto
new file mode 100644 (file)
index 0000000..c4f3dfe
--- /dev/null
@@ -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 {
+};
similarity index 57%
rename from CMakeLists.txt
rename to meson.build
index c21c5bf..f941aee 100644 (file)
@@ -1,19 +1,32 @@
-# Copyright 2021 Collabora, Ltd.
-# Copyright 2022 Konsulko Group
+#
+# Copyright ©, 2023 Collabora, Ltd.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
-#     http://www.apache.org/licenses/LICENSE-2.0
+#      http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-###########################################################################
+#
 
-CMAKE_MINIMUM_REQUIRED(VERSION 3.3)
+project (
+  'camera-gstreamer',
+  ['c', 'cpp'],
+  version : '1.0.0',
+  license : 'Apache-2.0',
+  meson_version : '>= 0.60.0',
+  default_options :
+    [
+      'warning_level=1',
+      'buildtype=debugoptimized',
+      'c_std=c17',
+      'cpp_std=c++17'
+    ],
+)
 
-add_subdirectory(app)
+subdir('app')