diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/AglShellGrpcClient.cpp | 225 | ||||
-rw-r--r-- | app/AglShellGrpcClient.h | 111 | ||||
-rw-r--r-- | app/CMakeLists.txt | 121 | ||||
-rw-r--r-- | app/agl_shell.proto | 113 | ||||
-rw-r--r-- | app/main.cpp | 186 | ||||
-rw-r--r-- | app/meson.build | 118 | ||||
-rw-r--r-- | app/protocol/agl-shell-desktop.xml | 142 |
7 files changed, 670 insertions, 346 deletions
diff --git a/app/AglShellGrpcClient.cpp b/app/AglShellGrpcClient.cpp new file mode 100644 index 0000000..c7b76d5 --- /dev/null +++ b/app/AglShellGrpcClient.cpp @@ -0,0 +1,225 @@ +//include stuff here +#include <cstdio> +#include <time.h> + +#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() +{ + int retries = 10; + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + throw std::runtime_error("Failed CLOCK_MONOTONIC"); + + auto channel = grpc::CreateChannel(kDefaultGrpcServiceAddress, + grpc::InsecureChannelCredentials()); + + ts.tv_nsec = 500 * 1000 * 1000; + ts.tv_sec = 0; + + auto state = channel->GetState(true); + // artificial delay otherwise to be sure req calls succeed + while (retries-- > 0) { + state = channel->GetState(true); + if (state == GRPC_CHANNEL_READY) + break; + nanosleep(&ts, NULL); + } + + if (state != GRPC_CHANNEL_READY) + fprintf(stderr, "WARNING: channel not in ready state: %d\n", state); + + 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(); +} + +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(); +} + + +bool +GrpcClient::SetAppSplit(const std::string& app_id, uint32_t orientation, + int32_t width, int32_t sticky, const std::string& output_name) +{ + agl_shell_ipc::SplitRequest request; + + request.set_app_id(app_id); + request.set_output_name(output_name); + request.set_tile_orientation(orientation); + request.set_width(width); + request.set_sticky(sticky); + + grpc::ClientContext context; + ::agl_shell_ipc::SplitResponse reply; + + grpc::Status status = m_stub->SetAppSplit(&context, request, &reply); + return status.ok(); +} + +grpc::Status +GrpcClient::Wait(void) +{ + return reader->Await(); +} + +void +GrpcClient::AppStatusState(Callback callback) +{ + reader->AppStatusState(callback); +} + +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..f66b44c --- /dev/null +++ b/app/AglShellGrpcClient.h @@ -0,0 +1,111 @@ +#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); + +class Reader : public grpc::ClientReadReactor<::agl_shell_ipc::AppStateResponse> { +public: + Reader(agl_shell_ipc::AglShellManagerService::Stub *stub) + : m_stub(stub) + { + } + + void AppStatusState(Callback callback) + { + ::agl_shell_ipc::AppStateRequest request; + + // set up the callback + m_callback = callback; + 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); + + // 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; + + Callback m_callback; + + + 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); + 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); + bool SetAppSplit(const std::string& app_id, uint32_t orientation, + int32_t width, int32_t sticky, const std::string& output_name); + std::vector<std::string> GetOutputs(); + void GetAppState(); + void AppStatusState(Callback callback); + grpc::Status Wait(); + +private: + Reader *reader; + std::unique_ptr<agl_shell_ipc::AglShellManagerService::Stub> m_stub; +}; + diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt deleted file mode 100644 index 4ac3455..0000000 --- a/app/CMakeLists.txt +++ /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(xdg-cluster-receiver VERSION 2.0.0 LANGUAGES CXX) - -if(CMAKE_VERSION VERSION_LESS "3.7.0") - set(CMAKE_INCLUDE_CURRENT_DIR ON) -endif() -set(OE_QMAKE_PATH_EXTERNAL_HOST_BINS $ENV{OE_QMAKE_PATH_HOST_BINS}) - -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(PkgConfig REQUIRED) -find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner) - -pkg_check_modules(AGL_COMPOSITOR_PROTOCOLS REQUIRED agl-compositor-0.0.24-protocols) -pkg_get_variable(AGL_COMPOSITOR_PROTOCOLS_PKGDATADIR agl-compositor-0.0.24-protocols pkgdatadir) -set(AGL_COMPOSITOR_PROTOCOLS_PATH ${AGL_COMPOSITOR_PROTOCOLS_PKGDATADIR}) - -add_custom_command( - OUTPUT agl-shell-desktop-client-protocol.h - COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header - < ${AGL_COMPOSITOR_PROTOCOLS_PATH}/agl-shell-desktop.xml - > ${CMAKE_SOURCE_DIR}/app/agl-shell-desktop-client-protocol.h - DEPENDS ${AGL_COMPOSITOR_PROTOCOLS_PATH}/agl-shell-desktop.xml -) - -add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/app/agl-shell-desktop-client-protocol.h - COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header - < ${AGL_COMPOSITOR_PROTOCOLS_PATH}/agl-shell-desktop.xml - > ${CMAKE_SOURCE_DIR}/app/agl-shell-desktop-client-protocol.h - DEPENDS ${AGL_COMPOSITOR_PROTOCOLS_PATH}/agl-shell-desktop.xml -) - -add_custom_command( - OUTPUT agl-shell-desktop-protocol.c - COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code - < ${AGL_COMPOSITOR_PROTOCOLS_PATH}/agl-shell-desktop.xml - > ${CMAKE_BINARY_DIR}/app/agl-shell-desktop-protocol.c - DEPENDS ${AGL_COMPOSITOR_PROTOCOLS_PATH}/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 - 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) diff --git a/app/agl_shell.proto b/app/agl_shell.proto new file mode 100644 index 0000000..d38d896 --- /dev/null +++ b/app/agl_shell.proto @@ -0,0 +1,113 @@ +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; + int32 width = 3; + int32 sticky = 4; + string output_name = 5; +} + +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 { +}; diff --git a/app/main.cpp b/app/main.cpp index 2d081c9..3953646 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2020 Collabora, Ltd. + * Copyright © 2020, 2024 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -41,7 +41,6 @@ #include <glib.h> #include "xdg-shell-client-protocol.h" -#include "agl-shell-desktop-client-protocol.h" #include "zalloc.h" #include <gst/gst.h> @@ -49,6 +48,9 @@ #include <gst/video/videooverlay.h> #include <gst/wayland/wayland.h> +#include "AglShellGrpcClient.h" + + #if !GST_CHECK_VERSION(1, 22, 0) #define gst_is_wl_display_handle_need_context_message gst_is_wayland_display_handle_need_context_message #define gst_wl_display_handle_context_new gst_wayland_display_handle_context_new @@ -84,7 +86,6 @@ struct display { } output_data; struct xdg_wm_base *wm_base; - struct agl_shell_desktop *agl_shell_desktop; int has_xrgb; }; @@ -127,7 +128,7 @@ static void redraw(void *data, struct wl_callback *callback, uint32_t time); static void -paint_pixels(void *image, int padding, int width, int height, uint32_t time) +paint_pixels(void *image, int width, int height) { memset(image, 0x00, width * height * 4); } @@ -136,6 +137,8 @@ static void buffer_release(void *data, struct wl_buffer *buffer) { struct buffer *mybuf = static_cast<struct buffer *>(data); + (void) buffer; + mybuf->busy = 0; } @@ -359,6 +362,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) { struct window *window = static_cast<struct window *>(data); struct buffer *buffer; + (void) time; buffer = get_next_buffer(window); if (!buffer) { @@ -369,7 +373,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) } // do the actual painting - paint_pixels(buffer->shm_data, 0x0, window->width, window->height, time); + paint_pixels(buffer->shm_data, window->width, window->height); wl_surface_attach(window->surface, buffer->buffer, 0, 0); wl_surface_damage(window->surface, 0, 0, window->width, window->height); @@ -388,6 +392,7 @@ static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { struct display *d = static_cast<struct display *>(data); + (void) wl_shm; if (format == WL_SHM_FORMAT_XRGB8888) d->has_xrgb = true; @@ -400,6 +405,7 @@ static const struct wl_shm_listener shm_listener = { static void xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { + (void) data; xdg_wm_base_pong(shell, serial); } @@ -429,6 +435,7 @@ display_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int width, int height, int refresh) { struct display *d = static_cast<struct display *>(data); + (void) refresh; if (wl_output == d->wl_output && (flags & WL_OUTPUT_MODE_CURRENT)) { d->output_data.width = width; @@ -454,38 +461,29 @@ display_handle_done(void *data, struct wl_output *wl_output) (void) wl_output; } -static const struct wl_output_listener output_listener = { - display_handle_geometry, - display_handle_mode, - display_handle_done, - display_handle_scale -}; - static void -application_id(void *data, struct agl_shell_desktop *agl_shell_desktop, - const char *app_id) +display_handle_name(void *data, struct wl_output *wl_output, const char *name) { (void) data; - (void) agl_shell_desktop; - (void) app_id; + (void) wl_output; + (void) name; } 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) +display_handle_desc(void *data, struct wl_output *wl_output, const char *desc) { - (void) data; - (void) app_data; - (void) agl_shell_desktop; - (void) app_id; - (void) app_state; - (void) app_role; + (void) data; + (void) wl_output; + (void) desc; } -static const struct agl_shell_desktop_listener agl_shell_desktop_listener = { - application_id, - application_id_state, +static const struct wl_output_listener output_listener = { + display_handle_geometry, + display_handle_mode, + display_handle_done, + display_handle_scale, + display_handle_name, + display_handle_desc, }; static void @@ -493,6 +491,7 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { struct display *d = static_cast<struct display *>(data); + (void) version; if (strcmp(interface, "wl_compositor") == 0) { d->wl_compositor = @@ -506,12 +505,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)); @@ -536,6 +529,7 @@ static const struct wl_registry_listener registry_listener = { static void error_cb(GstBus *bus, GstMessage *msg, gpointer user_data) { + (void) bus; struct cluster_receiver_data *d = static_cast<struct cluster_receiver_data *>(user_data); @@ -558,6 +552,7 @@ error_cb(GstBus *bus, GstMessage *msg, gpointer user_data) static GstBusSyncReply bus_sync_handler(GstBus *bus, GstMessage *message, gpointer user_data) { + (void) bus; struct cluster_receiver_data *d = static_cast<struct cluster_receiver_data *>(user_data); @@ -623,6 +618,7 @@ handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, { struct window *window = static_cast<struct window *>(data); uint32_t *p; + (void) xdg_toplevel; window->fullscreen = 0; window->maximized = 0; @@ -670,12 +666,37 @@ handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, static void handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { + (void) xdg_toplevel; + (void) data; running = 0; } +static void +handle_xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height) +{ + (void) data; + (void) xdg_toplevel; + (void) width; + (void) height; +} + + +static void +handle_xdg_toplevel_wm_caps(void *data, struct xdg_toplevel *xdg_toplevel, + struct wl_array *caps) +{ + (void) data; + (void) xdg_toplevel; + (void) caps; +} + + static const struct xdg_toplevel_listener xdg_toplevel_listener = { handle_xdg_toplevel_configure, handle_xdg_toplevel_close, + handle_xdg_toplevel_configure_bounds, + handle_xdg_toplevel_wm_caps, }; static struct window * @@ -737,12 +758,19 @@ destroy_window(struct window *window) static void signal_int(int sig, siginfo_t *si, void *_unused) { + (void) sig; + (void) _unused; + (void) si; + running = 0; } static struct display * create_display(int argc, char *argv[]) { + (void) argc; + (void) argv; + struct display *display; display = static_cast<struct display *>(zalloc(sizeof(*display))); @@ -764,11 +792,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) { @@ -788,9 +811,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); @@ -800,42 +820,42 @@ destroy_display(struct display *display) free(display); } -void read_config(void) +void +read_config(void) { GKeyFile *conf_file; gchar *value; + GError *err = NULL; + + bool ret; + int n; + unsigned width, height, x, y; // Load settings from configuration file if it exists conf_file = g_key_file_new(); - if(conf_file && - g_key_file_load_from_dirs(conf_file, - "AGL.conf", - (const gchar**) g_get_system_config_dirs(), - NULL, - G_KEY_FILE_KEEP_COMMENTS, - NULL) == TRUE) { - GError *err = NULL; - value = g_key_file_get_string(conf_file, - "receiver", - "geometry", - &err); - if(value) { - int n; - unsigned width, height, x, y; - n = sscanf(value, "%ux%u+%u,%u", &width, &height, &x, &y); - if (n == 4) { - g_window_width = width; - g_window_height = height; - g_window_pos_x = x; - g_window_pos_y = y; - printf("Using window geometry %dx%d+%d,%d", - g_window_width, g_window_height, g_window_pos_x, g_window_pos_y); - } else { - fprintf(stderr, "Invalid value for \"geometry\" key!"); - } - } else { - fprintf(stderr, "Invalid value for \"geometry\" key!"); - } + + ret = g_key_file_load_from_dirs(conf_file, "AGL.conf", + (const gchar**) g_get_system_config_dirs(), + NULL, G_KEY_FILE_KEEP_COMMENTS, NULL); + if (!ret) + return; + + value = g_key_file_get_string(conf_file, "receiver", "geometry", &err); + if (!value) { + return; + } + + n = sscanf(value, "%ux%u+%u,%u", &width, &height, &x, &y); + if (n == 4) { + g_window_width = width; + g_window_height = height; + g_window_pos_x = x; + g_window_pos_y = y; + fprintf(stdout, "Using window geometry %dx%d+%d,%d\n", + g_window_width, g_window_height, + g_window_pos_x, g_window_pos_y); + } else { + fprintf(stderr, "Invalid value for \"geometry\" key\n!"); } } @@ -847,7 +867,8 @@ int main(int argc, char *argv[]) struct display *display; struct window *window; - std::string role = "receiver"; + std::string role = "cluster-receiver"; + GrpcClient *client = new GrpcClient(); sa.sa_sigaction = signal_int; sigemptyset(&sa.sa_mask); @@ -875,20 +896,17 @@ int main(int argc, char *argv[]) if (!display) return -1; - // this will set-up the x and y position and a bounding box that will - // be used to clip out the surface. Note, that in this example, the - // surface area is the same as the bounding box. - agl_shell_desktop_set_app_property(display->agl_shell_desktop, role.c_str(), - AGL_SHELL_DESKTOP_APP_ROLE_POPUP, - g_window_pos_x, g_window_pos_y, - 0, 0, g_window_width, g_window_height, - display->wl_output); + + // this will set-up the x and y position to the same app as this one. + // Note, that in this example, the surface area is the same as the + // output dimensions streamed by the remote compositor + client->SetAppFloat(role, g_window_pos_x, g_window_pos_y); // 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 - window = create_window(display, g_window_width, - g_window_height, role.c_str()); + // + window = create_window(display, g_window_width, g_window_height, role.c_str()); if (!window) return -1; @@ -929,5 +947,7 @@ int main(int argc, char *argv[]) gst_element_set_state(pipeline, GST_STATE_NULL); gst_object_unref(pipeline); + delete client; + return ret; } diff --git a/app/meson.build b/app/meson.build new file mode 100644 index 0000000..d0ea031 --- /dev/null +++ b/app/meson.build @@ -0,0 +1,118 @@ +pkgconfig = import('pkgconfig') +cpp = meson.get_compiler('cpp') + +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@/', + '--cpp_out=@BUILD_DIR@', + '@INPUT@']) + +generated_protoc_sources = protoc_gen.process('agl_shell.proto') + +grpc_gen = generator(protoc, \ + output : ['@BASENAME@.grpc.pb.cc', '@BASENAME@.grpc.pb.h'], + arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/', + '--grpc_out=@BUILD_DIR@', + '--plugin=protoc-gen-grpc=' + grpc_cpp.path(), + '@INPUT@']) +generated_grpc_sources = grpc_gen.process('agl_shell.proto') + +grpc_deps = [ + dependency('protobuf'), + dependency('grpc'), + dependency('grpc++'), + grpcpp_reflection_dep, +] + +gstreamer_deps = [ + 'gstreamer-1.0', 'gstreamer-allocators-1.0', + 'gstreamer-app-1.0', 'gstreamer-video-1.0', + 'gstreamer-plugins-bad-1.0', + 'gstreamer-plugins-base-1.0', + 'gobject-2.0', 'glib-2.0', +] + +deps_remoting = [] +foreach depname : gstreamer_deps + dep = dependency(depname, required: false) + if not dep.found() + error('requires @0@ which was not found. '.format(depname)) + endif + deps_remoting += dep +endforeach + + + +dep_wayland_client = dependency('wayland-client', version: '>= 1.17.0') +dep_scanner = dependency('wayland-scanner', native: true) +prog_scanner = find_program(dep_scanner.get_variable(pkgconfig: 'wayland_scanner')) +dep_wp = dependency('wayland-protocols', version: '>= 1.18') +dir_wp_base = dep_wp.get_variable(pkgconfig: 'pkgdatadir') + +cluster_receiver_dep = [ + grpc_deps, deps_remoting, dep_wayland_client, cpp.find_library('gstwayland-1.0'), +] + +protocols = [ + { 'name': 'xdg-shell', 'source': 'wp-stable' }, +] + +foreach proto: protocols + proto_name = proto['name'] + if proto['source'] == 'wp-stable' + base_file = proto_name + xml_path = join_paths(dir_wp_base, 'stable', proto_name, '@0@.xml'.format(base_file)) + else + base_file = '@0@-unstable-@1@'.format(proto_name, proto['version']) + xml_path = join_paths(dir_wp_base, 'unstable', proto_name, '@0@.xml'.format(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 + +cluster_receiver_src_headers = [ + 'AglShellGrpcClient.h', + xdg_shell_client_protocol_h, +] + +cluster_receiver_src = [ + 'AglShellGrpcClient.cpp', + 'main.cpp', + xdg_shell_protocol_c, + generated_grpc_sources, + generated_protoc_sources, +] + +prefix_path = get_option('prefix') +binplugin_dir = join_paths(prefix_path, get_option('bindir')) + +executable('xdg-cluster-receiver', + sources: [ cluster_receiver_src, cluster_receiver_src_headers ], + dependencies : cluster_receiver_dep, + install_rpath: binplugin_dir, + install: true) diff --git a/app/protocol/agl-shell-desktop.xml b/app/protocol/agl-shell-desktop.xml deleted file mode 100644 index e8ae153..0000000 --- a/app/protocol/agl-shell-desktop.xml +++ /dev/null @@ -1,142 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<protocol name="agl_shell_desktop"> - <copyright> - Copyright © 2020 Collabora, Ltd. - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - </copyright> - <interface name="agl_shell_desktop" version="1"> - <description summary="Private extension to allow applications activate other apps"> - This extension can be used by regular application to instruct to compositor - to activate or switch to other running (regular) applications. The client - is responsbile for filtering their own app_id when receiving application id. - - The compositor will allow clients to bind to this interface only if the - policy engine allows it. - </description> - - <enum name="app_role"> - <entry name="popup" value="0"/> - <entry name="fullscreen" value="1"/> - <entry name="split_vertical" value="2"/> - <entry name="split_horizontal" value="3"/> - <entry name="remote" value="4"/> - </enum> - - <enum name="app_state"> - <entry name="activated" value="0"/> - <entry name="deactivated" value="1"/> - </enum> - - <event name="application"> - <description summary="advertise application id"> - The compositor may choose to advertise one or more application ids which - can be used to activate/switch to. - - When this global is bound, the compositor will send all application ids - available for activation, but may send additional application id at any - time (when they've been mapped in the compositor). - </description> - <arg name="app_id" type="string"/> - </event> - - <request name="activate_app"> - <description summary="make client current window"> - Ask the compositor to make a toplevel to become the current/focused - window for window management purposes. - - See xdg_toplevel.set_app_id from the xdg-shell protocol for a - description of app_id. - </description> - <arg name="app_id" type="string"/> - <arg name="app_data" type="string" allow-null="true"/> - <arg name="output" type="object" interface="wl_output"/> - </request> - - <request name="set_app_property"> - <description summary="set properties for a client identified by app_id"> - Ask the compositor to make a top-level window obey the 'app_role' enum - and, depending on that role, to use some of the arguments as initial - values to take into account. - - Note that x, y, bx, by, width and height would only make sense for the - pop-up role, with the output argument being applicable to all the roles. - The width and height values define the maximum area which the - top-level window should be placed into. Note this doesn't correspond to - top-level surface size, but to a bounding box which will be used to - clip the surface to, in case the surface area extends that of this - bounding box. Both of these values need to be larger than 0 (zero) to be - taken into account by the compositor. Any negative values for the width - and height will be discarded. - - The x and y values will serve as the (initial) position values. - The bx and by values are the top-left x and y value of the bounding box. - Any clipping happening to the bounding box will not affect the surface - size or the position of the underlying surface backing the top-level - window. The bx and by values, like the positional values, could be - both set to zero, or even negative values. The compositor will pass - those on without any further validation. - - The initial position values and the bounding rectangle will still be - in effect on a subsequent activation request of the 'app_id', assuming - it was previously de-activated at some point in time. - - See xdg_toplevel.set_app_id from the xdg-shell protocol for a - description of app_id. - </description> - <arg name="app_id" type="string"/> - <arg name="role" type="uint" enum="app_role"/> - <arg name="x" type="int"/> - <arg name="y" type="int"/> - <arg name="bx" type="int"/> - <arg name="by" type="int"/> - <arg name="width" type="int"/> - <arg name="height" type="int"/> - <arg name="output" type="object" interface="wl_output"/> - </request> - - <request name="deactivate_app"> - <description summary="de-activate/hide window identified by app_id"> - Ask the compositor to hide the toplevel window for window - management purposes. Depending on the window role, this request - will either display the previously active window (or the background - in case there's no previously activate surface) or temporarly (or - until a 'activate_app' is called upon) hide the surface. All - the surfaces are identifiable by using the app_id, and no actions are - taken in case the app_id is not/was not present. - - See xdg_toplevel.set_app_id from the xdg-shell protocol for a - description of app_id. - </description> - <arg name="app_id" type="string"/> - </request> - - <event name="state_app"> - <description summary="event sent when application has suffered state modification"> - Notifies application(s) when other application have suffered state modifications. - </description> - <arg name="app_id" type="string"/> - <arg name="app_data" type="string" allow-null="true"/> - <arg name="state" type="uint" enum="app_state"/> - <arg name="role" type="uint" enum="app_role"/> - </event> - - </interface> -</protocol> |