aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/AglShellGrpcClient.cpp225
-rw-r--r--app/AglShellGrpcClient.h111
-rw-r--r--app/CMakeLists.txt121
-rw-r--r--app/agl_shell.proto113
-rw-r--r--app/main.cpp186
-rw-r--r--app/meson.build118
-rw-r--r--app/protocol/agl-shell-desktop.xml142
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>