diff options
-rw-r--r-- | .gitreview | 5 | ||||
-rw-r--r-- | COPYING | 22 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | include/os-compatibility.h | 58 | ||||
-rw-r--r-- | meson.build | 126 | ||||
-rwxr-xr-x | script/agl-activator | 27 | ||||
-rw-r--r-- | src/main.c | 200 | ||||
-rw-r--r-- | src/os-compatibility.c | 204 |
8 files changed, 646 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..019a07b --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.automotivelinux.org +port=29418 +project=src/agl-shell-activator +defaultbranch=master @@ -0,0 +1,22 @@ +Copyright 2021 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..adf1324 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# agl-shell-activator + +Simple application that will activate/display another application using +agl-shell-desktop protocol. diff --git a/include/os-compatibility.h b/include/os-compatibility.h new file mode 100644 index 0000000..690f229 --- /dev/null +++ b/include/os-compatibility.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2012 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. + */ + +#ifndef OS_COMPATIBILITY_H +#define OS_COMPATIBILITY_H + +#include <sys/types.h> + +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +#else +static inline int +backtrace(void **buffer, int size) +{ + return 0; +} +#endif + +int +os_fd_set_cloexec(int fd); + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv); + +int +os_epoll_create_cloexec(void); + +int +os_create_anonymous_file(off_t size); + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c); +#endif + +#endif /* OS_COMPATIBILITY_H */ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..f03bd47 --- /dev/null +++ b/meson.build @@ -0,0 +1,126 @@ +project('agl-shell-activator', + 'c', + version: '0.0.1', + default_options: [ + 'warning_level=3', + 'c_std=gnu99', + ], + meson_version: '>= 0.50', + license: 'MIT/Expat', +) + +pkgconfig = import('pkgconfig') +cc = meson.get_compiler('c') + +add_project_arguments( + cc.get_supported_arguments([ + '-Wno-unused-parameter', + '-Wno-pedantic', + '-Wextra', + '-Werror' + ]), + language: 'c' +) + +add_project_arguments([ + '-DPACKAGE_STRING="agl-shell-activator @0@"'.format(meson.project_version()), + '-D_GNU_SOURCE', + '-D_ALL_SOURCE', + ], + language: 'c' +) + +optional_libc_funcs = [ 'memfd_create', 'strchrnul' ] +foreach func: optional_libc_funcs + if cc.has_function(func) + add_project_arguments('-DHAVE_@0@=1'.format(func.to_upper()), language: 'c') + endif +endforeach + +env_modmap = '' + +libwayland_dep = dependency('wayland-client') +agl_compositor_dep = dependency('agl-compositor-0.0.19-protocols') + +dep_scanner = dependency('wayland-scanner', native: true) +prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner')) +dep_wp = dependency('wayland-protocols', version: '>= 1.18') +dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir') +dir_agl_compositor_base = agl_compositor_dep.get_pkgconfig_variable('pkgdatadir') + +protocols = [ + { 'name': 'agl-shell-desktop', 'source': 'agl-compositor' }, +] + +foreach proto: protocols + proto_name = proto['name'] + if proto['source'] == 'internal' + base_file = proto_name + xml_path = join_paths('protocol', '@0@.xml'.format(base_file)) + elif proto['source'] == 'wp-stable' + base_file = proto_name + xml_path = join_paths(dir_wp_base, 'stable', proto_name, '@0@.xml'.format(base_file)) + elif proto['source'] == 'agl-compositor' + base_file = proto_name + xml_path = join_paths(dir_agl_compositor_base, '@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 + +prefix_path = get_option('prefix') +binplugin_dir = join_paths(prefix_path, get_option('bindir')) +common_inc = include_directories('include') + +deps_agl_activator = [ + libwayland_dep, +] + +srcs_agl_activator = [ + 'src/main.c', + agl_shell_desktop_client_protocol_h, + agl_shell_desktop_protocol_c, +] + +exe_wth_receiver = executable( + 'agl-shell-activator', + srcs_agl_activator, + include_directories: common_inc, + dependencies: deps_agl_activator, + install_rpath: binplugin_dir, + install: true +) + +cp = find_program('cp') +script_target = custom_target('install_script', + command: [ cp, '@INPUT@', '@OUTPUT@' ], + output : 'agl-activator', + input : 'script/agl-activator', + install : true, + install_dir: binplugin_dir, +) + diff --git a/script/agl-activator b/script/agl-activator new file mode 100755 index 0000000..b8fd20f --- /dev/null +++ b/script/agl-activator @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright 2021 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. +# + +XDG_RUNTIME_DIR=/run/platform/display /usr/bin/agl-shell-activator $1 diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..831bea6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,200 @@ +/* + * Copyright 2021 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <assert.h> + +#include <wayland-client.h> +#include <wayland-util.h> +#include "agl-shell-desktop-client-protocol.h" + + +struct display; + +struct window_output { + struct display *display; + struct wl_output *output; + struct wl_list link; /** display::output_list */ +}; + +struct display { + struct wl_display *display; + struct wl_registry *registry; + struct agl_shell_desktop *agl_shell_desktop; + + struct wl_list output_list; /** window_output::link */ +}; + +static void +display_handle_geometry(void *data, struct wl_output *wl_output, + int x, int y, int physical_width, int physical_height, + int subpixel, const char *make, const char *model, int transform) +{ +} + +static void +display_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, + int width, int height, int refresh) +{ +} + +static void +display_handle_done(void *data, struct wl_output *wl_output) +{ +} + +static void +display_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) +{ +} + +static const struct wl_output_listener output_listener = { + display_handle_geometry, + display_handle_mode, + display_handle_done, + display_handle_scale, +}; + + + static void +display_add_output(struct display *display, uint32_t id) +{ + struct window_output *w_output; + + w_output = calloc(1, sizeof(*w_output)); + w_output->display = display; + w_output->output = + wl_registry_bind(display->registry, id, &wl_output_interface, 2); + + wl_list_insert(&display->output_list, &w_output->link); + + wl_output_add_listener(w_output->output, &output_listener, display); +} + +static void +destroy_output(struct window_output *w_output) +{ + wl_list_remove(&w_output->link); + free(w_output); +} + +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "agl_shell_desktop") == 0) { + d->agl_shell_desktop = + wl_registry_bind(registry, id, &agl_shell_desktop_interface, 1); + } else if (strcmp(interface, "wl_output") == 0) { + display_add_output(d, id); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static struct display * +create_display(void) +{ + struct display *display; + + display = malloc(sizeof *display); + if (display == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + wl_list_init(&display->output_list); + display->display = wl_display_connect(NULL); + assert(display->display); + + display->registry = wl_display_get_registry(display->display); + + wl_registry_add_listener(display->registry, ®istry_listener, display); + wl_display_roundtrip(display->display); + + if (display->agl_shell_desktop == NULL) { + fprintf(stderr, "No agl_shell_desktop extension present\n"); + } + + wl_display_roundtrip(display->display); + return display; +} + +static void +destroy_display(struct display *display) +{ + struct window_output *w_output, *w_output_next; + + wl_list_for_each_safe(w_output, w_output_next, &display->output_list, link) + destroy_output(w_output); + + wl_registry_destroy(display->registry); + + wl_display_flush(display->display); + wl_display_disconnect(display->display); + + free(display); +} + +int main(int argc, char *argv[]) +{ + struct display *display; + const char *app_id = NULL; + struct window_output *w_output; + + if (argc < 2) { + exit(EXIT_FAILURE); + } + + display = create_display(); + + /* the app has to be already started, or not already active */ + app_id = argv[1]; + if (app_id && strlen(app_id) == 0) + exit(EXIT_FAILURE); + + /* pick the first one available */ + w_output = wl_container_of(display->output_list.prev, w_output, link); + agl_shell_desktop_activate_app(display->agl_shell_desktop, app_id, + NULL, w_output->output); + + destroy_display(display); + return 0; +} diff --git a/src/os-compatibility.c b/src/os-compatibility.c new file mode 100644 index 0000000..d9502e5 --- /dev/null +++ b/src/os-compatibility.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2012 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/epoll.h> +#include <string.h> +#include <stdlib.h> + +#include "os-compatibility.h" + +int +os_fd_set_cloexec(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; + + return 0; +} + +static int +set_cloexec_or_close(int fd) +{ + if (os_fd_set_cloexec(fd) != 0) { + close(fd); + return -1; + } + return fd; +} + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); + if (ret == 0 || errno != EINVAL) + return ret; +#endif + + ret = socketpair(domain, type, protocol, sv); + if (ret < 0) + return ret; + + sv[0] = set_cloexec_or_close(sv[0]); + sv[1] = set_cloexec_or_close(sv[1]); + + if (sv[0] != -1 && sv[1] != -1) + return 0; + + close(sv[0]); + close(sv[1]); + return -1; +} + +int +os_epoll_create_cloexec(void) +{ + int fd; + +#ifdef EPOLL_CLOEXEC + fd = epoll_create1(EPOLL_CLOEXEC); + if (fd >= 0) + return fd; + if (errno != EINVAL) + return -1; +#endif + + fd = epoll_create(1); + return set_cloexec_or_close(fd); +} + +static int +create_tmpfile_cloexec(char *tmpname) +{ + int fd; + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); +#else + fd = mkstemp(tmpname); + if (fd >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } +#endif + + return fd; +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + * + * If the C library implements posix_fallocate(), it is used to + * guarantee that disk space is available for the file at the + * given size. If disk space is insufficent, errno is set to ENOSPC. + * If posix_fallocate() is not supported, program may receive + * SIGBUS on accessing mmap()'ed file contents instead. + */ +int +os_create_anonymous_file(off_t size) +{ + static const char template[] = "/weston-shared-XXXXXX"; + const char *path; + char *name; + int fd; + int ret; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + name = malloc(strlen(path) + sizeof(template)); + if (!name) + return -1; + + strcpy(name, path); + strcat(name, template); + + fd = create_tmpfile_cloexec(name); + + free(name); + + if (fd < 0) + return -1; + +#ifdef HAVE_POSIX_FALLOCATE + ret = posix_fallocate(fd, 0, size); + if (ret != 0) { + close(fd); + errno = ret; + return -1; + } +#else + ret = ftruncate(fd, size); + if (ret < 0) { + close(fd); + return -1; + } +#endif + + return fd; +} + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} +#endif |