From b74c07c917f4477cdadf8f2f02bb06e206495653 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Mon, 9 Sep 2019 16:07:04 +0300 Subject: [PATCH 01/36] Refactor some bits in util/files.c --- src/util/files.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/util/files.c b/src/util/files.c index 9384001..adaf73f 100644 --- a/src/util/files.c +++ b/src/util/files.c @@ -115,22 +115,24 @@ char *infer_mime_type_from_contents(const char *file_path) { close(pipefd[1]); int wstatus; wait(&wstatus); - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) { - char *res = malloc(256); - size_t len = read(pipefd[0], res, 256); - len--; // trim the newline + + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { close(pipefd[0]); - res[len] = 0; - - if (str_has_prefix(res, "inode/")) { - free(res); - return NULL; - } - - return res; + return NULL; } + + char *res = malloc(256); + size_t len = read(pipefd[0], res, 256); + len--; // trim the newline + res[len] = 0; close(pipefd[0]); - return NULL; + + if (str_has_prefix(res, "inode/")) { + free(res); + return NULL; + } + + return res; } char *infer_mime_type_from_name(const char *file_path) { @@ -184,8 +186,8 @@ char *dump_stdin_into_a_temp_file() { char *res_path = malloc(strlen(dirpath) + 1 + strlen(name) + 1); memcpy(res_path, dirpath, sizeof(dirpath)); - strcat(res_path, "/"); - strcat(res_path, name); + res_path[sizeof(dirpath) - 1] = '/'; + strcpy(res_path + sizeof(dirpath), name); if (fork() == 0) { int fd = creat(res_path, S_IRUSR | S_IWUSR); @@ -205,8 +207,8 @@ char *dump_stdin_into_a_temp_file() { if (original_path != NULL) { free(original_path); } - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0) { - return res_path; + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + bail("Failed to copy the file"); } - bail("Failed to copy the file"); + return res_path; } From bc806e73821d2a46c03dddea993619048c48412d Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 5 Sep 2019 15:53:06 +0300 Subject: [PATCH 02/36] Treat YAML and PGP keys as text Fixes https://github.com/bugaevc/wl-clipboard/issues/44 and https://github.com/bugaevc/wl-clipboard/issues/46 --- src/util/string.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/string.c b/src/util/string.c index 757b87a..b7b3876 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -28,6 +28,8 @@ int mime_type_is_text(const char *mime_type) { || strcmp(mime_type, "UTF8_STRING") == 0 || str_has_suffix(mime_type, "script") || str_has_suffix(mime_type, "xml") + || str_has_suffix(mime_type, "yaml") + || str_has_suffix(mime_type, "pgp-keys") || strstr(mime_type, "json") != NULL; } From 4591982b15c6715ea69095dee23cc58d64849b43 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Mon, 9 Sep 2019 16:18:46 +0300 Subject: [PATCH 03/36] Fix a potential buffer overflow --- src/util/files.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/files.c b/src/util/files.c index adaf73f..b68808d 100644 --- a/src/util/files.c +++ b/src/util/files.c @@ -158,12 +158,12 @@ char *infer_mime_type_from_name(const char *file_path) { // each line consists of a mime type and a list of extensions char mime_type[200]; int consumed; - if (sscanf(line, "%s%n", mime_type, &consumed) != 1) { + if (sscanf(line, "%199s%n", mime_type, &consumed) != 1) { // malformed line? continue; } char *lineptr = line + consumed; - for (char ext[200]; sscanf(lineptr, "%s%n", ext, &consumed) == 1;) { + for (char ext[200]; sscanf(lineptr, "%199s%n", ext, &consumed) == 1;) { if (strcmp(ext, actual_ext) == 0) { fclose(f); return strdup(mime_type); From a1481254caefa9fcc4af19f23b1c77b7e0747773 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Mon, 9 Sep 2019 16:15:01 +0300 Subject: [PATCH 04/36] Tweak comments throughout the codebase --- src/boilerplate.c | 51 +++++++++++++++++++++++++---------------------- src/util/files.c | 29 ++++++++++++++++++++------- src/util/files.h | 7 ++++--- src/wl-copy.c | 26 ++++++++++++++---------- src/wl-paste.c | 9 ++++++--- 5 files changed, 74 insertions(+), 48 deletions(-) diff --git a/src/boilerplate.c b/src/boilerplate.c index 0742bf8..f285d07 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -147,7 +147,7 @@ void keyboard_enter_handler struct wl_array *keys ) { struct wl_seat *this_seat = (struct wl_seat *) data; - // when we get to here, global seat is already initialized + /* When we get to here, global seat is already initialized */ if (this_seat != seat) { return; } @@ -199,7 +199,7 @@ void seat_capabilities_handler struct wl_seat *this_seat, uint32_t capabilities ) { - // stash the capabilities of this seat for later + /* Stash the capabilities of this seat for later */ void *user_data = (void *) (unsigned long) capabilities; wl_seat_set_user_data(this_seat, user_data); @@ -376,7 +376,7 @@ void init_wayland_globals() { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - // wait for the "initial" set of globals to appear + /* Wait for the "initial" set of globals to appear */ wl_display_roundtrip(display); if ( @@ -459,24 +459,26 @@ void ensure_has_primary_selection() { } void popup_tiny_invisible_surface() { - // HACK: - // pop up a tiny invisible surface to get the keyboard focus, - // otherwise we won't be notified of the selection + /* HACK: + * Pop up a tiny invisible surface to get the keyboard focus, + * otherwise we won't be notified of the selection. + */ if (!ensure_seat_has_keyboard()) { return; } - // make sure that we get the keyboard - // object before creating the surface, - // so that we get the enter event + /* Make sure that we get the keyboard + * object before creating the surface, + * so that we get the enter event. + */ wl_display_dispatch(display); surface = wl_compositor_create_surface(compositor); #ifdef HAVE_WLR_LAYER_SHELL if (layer_shell != NULL) { - // use wlr-layer-shell + /* Use wlr-layer-shell */ layer_surface = zwlr_layer_shell_v1_get_layer_surface( layer_shell, surface, @@ -490,29 +492,29 @@ void popup_tiny_invisible_surface() { NULL ); zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, 1); - // signal that the surface is ready to be configured + /* Signal that the surface is ready to be configured */ wl_surface_commit(surface); - // wait for the surface to be configured + /* Wait for the surface to be configured */ wl_display_roundtrip(display); } else #endif if (shell != NULL) { - // use wl_shell + /* Use wl_shell */ shell_surface = wl_shell_get_shell_surface(shell, surface); wl_shell_surface_set_toplevel(shell_surface); wl_shell_surface_set_title(shell_surface, "wl-clipboard"); } else { #ifdef HAVE_XDG_SHELL - // use xdg-shell + /* Use xdg-shell */ xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); xdg_toplevel_set_title(xdg_toplevel, "wl-clipboard"); - // signal that the surface is ready to be configured + /* Signal that the surface is ready to be configured */ wl_surface_commit(surface); - // wait for the surface to be configured + /* Wait for the surface to be configured */ wl_display_roundtrip(display); #else bail("Unreachable: HAVE_XDG_SHELL undefined and no wl_shell"); @@ -520,10 +522,11 @@ void popup_tiny_invisible_surface() { } if (surface == NULL) { - // it's possible that we've been given focus without us - // ever commiting a buffer, in which case the handlers - // may have already destroyed the surface; there's no - // way or need for us to commit a buffer in that case + /* It's possible that we've been given focus without us + * ever commiting a buffer, in which case the handlers + * may have already destroyed the surface; there's no + * way or need for us to commit a buffer in that case. + */ return; } @@ -532,17 +535,17 @@ void popup_tiny_invisible_surface() { int stride = width * 4; int size = stride * height; // bytes - // open an anonymous file and write some zero bytes to it + /* Open an anonymous file and write some zero bytes to it */ int fd = create_anonymous_file(); ftruncate(fd, size); - // turn it into a shared memory pool + /* Turn it into a shared memory pool */ struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); - // allocate the buffer in that pool + /* Allocate the buffer in that pool */ struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); - // zeros in ARGB8888 mean fully transparent + /* Zeros in ARGB8888 mean fully transparent */ wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage(surface, 0, 0, width, height); diff --git a/src/util/files.c b/src/util/files.c index b68808d..c96e6b9 100644 --- a/src/util/files.c +++ b/src/util/files.c @@ -69,14 +69,15 @@ void trim_trailing_newline(const char *file_path) { int seek_res = lseek(fd, -1, SEEK_END); if (seek_res < 0 && errno == EINVAL) { - // empty file + /* It was an empty file */ goto out; } else if (seek_res < 0) { perror("lseek"); goto out; } - // otherwise, seek_res is the new file size - + /* If the seek was successful, seek_res is the + * new file size after trimming the newline. + */ char last_char; int read_res = read(fd, &last_char, 1); if (read_res != 1) { @@ -99,6 +100,7 @@ char *path_for_fd(int fd) { } char *infer_mime_type_from_contents(const char *file_path) { + /* Spawn xdg-mime query filetype */ int pipefd[2]; pipe(pipefd); if (fork() == 0) { @@ -116,14 +118,17 @@ char *infer_mime_type_from_contents(const char *file_path) { int wstatus; wait(&wstatus); + /* See if that worked */ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { close(pipefd[0]); return NULL; } + /* Read the result */ char *res = malloc(256); size_t len = read(pipefd[0], res, 256); - len--; // trim the newline + /* Trim the newline */ + len--; res[len] = 0; close(pipefd[0]); @@ -150,16 +155,16 @@ char *infer_mime_type_from_name(const char *file_path) { } for (char line[200]; fgets(line, sizeof(line), f) != NULL;) { - // skip comments and blank lines + /* Skip comments and black lines */ if (line[0] == '#' || line[0] == '\n') { continue; } - // each line consists of a mime type and a list of extensions + /* Each line consists of a mime type and a list of extensions */ char mime_type[200]; int consumed; if (sscanf(line, "%199s%n", mime_type, &consumed) != 1) { - // malformed line? + /* A malformed line, perhaps? */ continue; } char *lineptr = line + consumed; @@ -175,20 +180,30 @@ char *infer_mime_type_from_name(const char *file_path) { return NULL; } +/* Returns the name of a new file */ char *dump_stdin_into_a_temp_file() { + /* Create a temp directory to host out file */ char dirpath[] = "/tmp/wl-copy-buffer-XXXXXX"; if (mkdtemp(dirpath) != dirpath) { perror("mkdtemp"); exit(1); } + + /* Pick a name for the file we'll be + * creating inside that directory. We + * try to preserve the origial name for + * the mime type inference to work. + */ char *original_path = path_for_fd(STDIN_FILENO); char *name = original_path != NULL ? basename(original_path) : "stdin"; + /* Construct the path */ char *res_path = malloc(strlen(dirpath) + 1 + strlen(name) + 1); memcpy(res_path, dirpath, sizeof(dirpath)); res_path[sizeof(dirpath) - 1] = '/'; strcpy(res_path + sizeof(dirpath), name); + /* Spawn cat to perform the copy */ if (fork() == 0) { int fd = creat(res_path, S_IRUSR | S_IWUSR); if (fd < 0) { diff --git a/src/util/files.h b/src/util/files.h index ec8fa3e..2a2d4b6 100644 --- a/src/util/files.h +++ b/src/util/files.h @@ -23,14 +23,15 @@ int create_anonymous_file(void); void trim_trailing_newline(const char *file_path); -// these functions return owned strings, so make sure -// to free() their return values when done with them +/* These functions return owned strings, so make sure + * to free() their return values when done with them. + */ char *path_for_fd(int fd); char *infer_mime_type_from_contents(const char *file_path); char *infer_mime_type_from_name(const char *file_path); -// returns the name of a new file +/* Returns the name of a new file */ char *dump_stdin_into_a_temp_file(void); #endif /* UTIL_FILES_H */ diff --git a/src/wl-copy.c b/src/wl-copy.c index c4a3e62..6db93a8 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -23,7 +23,7 @@ char *temp_file_to_copy = NULL; int paste_once = 0; void do_cancel() { - // we're done! + /* We're done! */ if (temp_file_to_copy != NULL) { execlp("rm", "rm", "-r", dirname(temp_file_to_copy), NULL); perror("exec rm"); @@ -34,10 +34,10 @@ void do_cancel() { } void do_send(const char *mime_type, int fd) { - // unset O_NONBLOCK + /* Unset O_NONBLOCK */ fcntl(fd, F_SETFL, 0); if (data_to_copy != NULL) { - // copy the specified data, separated by spaces + /* Copy the specified data, separated by spaces */ FILE *f = fdopen(fd, "w"); if (f == NULL) { perror("fdopen"); @@ -52,8 +52,9 @@ void do_send(const char *mime_type, int fd) { } fclose(f); } else { - // copy from the temp file; for that, we delegate to a - // (hopefully) highly optimized implementation of copying + /* Copy from the temp file; for that, we delegate to a + * (hopefully) highly optimized implementation of copying. + */ if (fork() == 0) { dup2(fd, STDOUT_FILENO); execlp("cat", "cat", temp_file_to_copy, NULL); @@ -235,7 +236,7 @@ void do_offer void (*offer_f)(void *source, const char *type) ) { if (mime_type == NULL || mime_type_is_text(mime_type)) { - // offer a few generic plain text formats + /* Offer a few generic plain text formats */ offer_f(source, text_plain); offer_f(source, text_plain_utf8); offer_f(source, "TEXT"); @@ -435,7 +436,7 @@ int main(int argc, char * const argv[]) { requested_seat_name = strdup(optarg); break; default: - // getopt has already printed an error message + /* getopt has already printed an error message */ print_usage(stderr, argv[0]); exit(1); } @@ -449,10 +450,10 @@ int main(int argc, char * const argv[]) { if (!clear) { if (optind < argc) { - // copy our command-line args + /* Copy our command-line arguments */ data_to_copy = &argv[optind]; } else { - // copy stdin + /* Copy data from our stdin */ temp_file_to_copy = dump_stdin_into_a_temp_file(); if (trim_newline) { trim_trailing_newline(temp_file_to_copy); @@ -465,8 +466,11 @@ int main(int argc, char * const argv[]) { if (!stay_in_foreground && !clear) { if (fork() != 0) { - // exit in the parent, but leave the - // child running in the background + /* Move to background. + * We fork our process and leave the + * child running in the background, + * while exiting in the parent. + */ exit(0); } } diff --git a/src/wl-paste.c b/src/wl-paste.c index d3d4022..4038ed2 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -121,7 +121,9 @@ const char *mime_type_to_request() { try_prefixed; } } else { - // no mime type requested explicitly, try to guess + /* No mime type requested explicitly, + * so try to guess. + */ if (options.inferred_type == NULL) { try_text_plain_utf8; try_text_plain; @@ -169,7 +171,7 @@ void do_paste } const char *mime_type = mime_type_to_request(); - // never append a newline character to binary content + /* Never append a newline character to binary content */ if (!mime_type_is_text(mime_type)) { options.no_newline = 1; } @@ -184,6 +186,7 @@ void do_paste wl_display_roundtrip(display); + /* Spawn a cat to perform the copy */ if (fork() == 0) { dup2(pipefd[0], STDIN_FILENO); close(pipefd[0]); @@ -503,7 +506,7 @@ int main(int argc, char * const argv[]) { requested_seat_name = strdup(optarg); break; default: - // getopt has already printed an error message + /* getopt has already printed an error message */ print_usage(stderr, argv[0]); exit(1); } From 9bf0f299c699570f255950ba60c75d5c57a4d876 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Mon, 9 Sep 2019 16:45:20 +0300 Subject: [PATCH 05/36] Unimplement wlr-layer-shell support This largely reverts commits 3eac5897748e6313540477e528e2c94b6b10e6b2 and cd8cb31e611dbb978a1d42f56e29f5de38b36c7c. Now that wlroots-based compositors implement the wlr-data-control protocol, there's no need to use the popup surface hack when running under them, which means wlr-layer-shell does not actually get used. While it is possible that a compositor supports wlr-layer-shell but not wlr-data-control, it is unlikely enough by this point. Furthermore, wl-clipboard is now used widely enough for niche compositors to consider implmenting a protocol to make wl-clipboard work better rather than the other way around. In other words, if your compositor implements wlr-layer-shell but not wlr-data-control and you would like wl-clipboard to work well under your compositor, please implement wlr-data-control in your compositor so that we can avoid relying on hacks. Closes https://github.com/bugaevc/wl-clipboard/issues/24 --- src/boilerplate.c | 67 ----- src/boilerplate.h | 9 - src/meson.build | 8 - src/protocol/wlr-layer-shell-unstable-v1.xml | 285 ------------------- 4 files changed, 369 deletions(-) delete mode 100644 src/protocol/wlr-layer-shell-unstable-v1.xml diff --git a/src/boilerplate.c b/src/boilerplate.c index f285d07..e16c48e 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -73,16 +73,6 @@ void registry_global_handler ); } #endif -#ifdef HAVE_WLR_LAYER_SHELL - else if (strcmp(interface, "zwlr_layer_shell_v1") == 0) { - layer_shell = wl_registry_bind( - registry, - name, - &zwlr_layer_shell_v1_interface, - 1 - ); - } -#endif #ifdef HAVE_GTK_PRIMARY_SELECTION else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) { gtk_primary_selection_device_manager = wl_registry_bind( @@ -341,32 +331,6 @@ const struct xdg_wm_base_listener xdg_wm_base_listener = { #endif -#ifdef HAVE_WLR_LAYER_SHELL - -void layer_surface_configure_handler -( - void *data, - struct zwlr_layer_surface_v1 *layer_surface, - uint32_t serial, - uint32_t width, - uint32_t height -) { - zwlr_layer_surface_v1_ack_configure(layer_surface, serial); -} - -void layer_surface_closed_handler -( - void *data, - struct zwlr_layer_surface_v1 *layer_surface -) {} - -const struct zwlr_layer_surface_v1_listener layer_surface_listener = { - .configure = layer_surface_configure_handler, - .closed = layer_surface_closed_handler -}; - -#endif - void init_wayland_globals() { display = wl_display_connect(NULL); if (display == NULL) { @@ -386,9 +350,6 @@ void init_wayland_globals() { (shell == NULL #ifdef HAVE_XDG_SHELL && xdg_wm_base == NULL -#endif -#ifdef HAVE_WLR_LAYER_SHELL - && layer_shell == NULL #endif ) ) { @@ -476,28 +437,6 @@ void popup_tiny_invisible_surface() { surface = wl_compositor_create_surface(compositor); -#ifdef HAVE_WLR_LAYER_SHELL - if (layer_shell != NULL) { - /* Use wlr-layer-shell */ - layer_surface = zwlr_layer_shell_v1_get_layer_surface( - layer_shell, - surface, - NULL, // output - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - "wl-clipboard" // namespace - ); - zwlr_layer_surface_v1_add_listener( - layer_surface, - &layer_surface_listener, - NULL - ); - zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, 1); - /* Signal that the surface is ready to be configured */ - wl_surface_commit(surface); - /* Wait for the surface to be configured */ - wl_display_roundtrip(display); - } else -#endif if (shell != NULL) { /* Use wl_shell */ shell_surface = wl_shell_get_shell_surface(shell, surface); @@ -553,12 +492,6 @@ void popup_tiny_invisible_surface() { } void destroy_popup_surface() { -#ifdef HAVE_WLR_LAYER_SHELL - if (layer_surface != NULL) { - zwlr_layer_surface_v1_destroy(layer_surface); - layer_surface = NULL; - } -#endif if (shell_surface != NULL) { wl_shell_surface_destroy(shell_surface); shell_surface = NULL; diff --git a/src/boilerplate.h b/src/boilerplate.h index f6f6712..c553897 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -46,10 +46,6 @@ # include "wp-primary-selection.h" #endif -#ifdef HAVE_WLR_LAYER_SHELL -# include "wlr-layer-shell.h" -#endif - #ifdef HAVE_GTK_PRIMARY_SELECTION # include "gtk-primary-selection.h" #endif @@ -73,11 +69,6 @@ struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; #endif -#ifdef HAVE_WLR_LAYER_SHELL -struct zwlr_layer_shell_v1 *layer_shell; -struct zwlr_layer_surface_v1 *layer_surface; -#endif - struct wl_data_device *data_device; #ifdef HAVE_GTK_PRIMARY_SELECTION diff --git a/src/meson.build b/src/meson.build index 3fbc3e7..fa42d57 100644 --- a/src/meson.build +++ b/src/meson.build @@ -7,7 +7,6 @@ if wayland_scanner.found() have_xdg_shell = wayland_protocols.found() have_wp_primary_selection = wayland_protocols.found() and wayland_protocols.version().version_compare('>= 1.17') # these are bundled - have_wlr_layer_shell = have_xdg_shell # depends on xdg_popup have_gtk_primary_selection = true have_wlr_data_control = true @@ -18,7 +17,6 @@ if wayland_scanner.found() endif else have_xdg_shell = false - have_wlr_layer_shell = false have_gtk_primary_selection = false have_wlr_data_control = false endif @@ -32,7 +30,6 @@ conf_data = configuration_data() conf_data.set('PROJECT_VERSION', '"@0@"'.format(meson.project_version())) conf_data.set('HAVE_XDG_SHELL', have_xdg_shell) conf_data.set('HAVE_WP_PRIMARY_SELECTION', have_wp_primary_selection) -conf_data.set('HAVE_WLR_LAYER_SHELL', have_wlr_layer_shell) conf_data.set('HAVE_GTK_PRIMARY_SELECTION', have_gtk_primary_selection) conf_data.set('HAVE_WLR_DATA_CONTROL', have_wlr_data_control) @@ -59,11 +56,6 @@ if have_wp_primary_selection protocols += [['wp-primary-selection', wp_primary_selection_xml]] endif -if have_wlr_layer_shell - wlr_layer_shell_xml = join_paths('protocol', 'wlr-layer-shell-unstable-v1.xml') - protocols += [['wlr-layer-shell', wlr_layer_shell_xml]] -endif - if not have_gtk_primary_selection warning('Building without primary selection support') else diff --git a/src/protocol/wlr-layer-shell-unstable-v1.xml b/src/protocol/wlr-layer-shell-unstable-v1.xml deleted file mode 100644 index 216e0d9..0000000 --- a/src/protocol/wlr-layer-shell-unstable-v1.xml +++ /dev/null @@ -1,285 +0,0 @@ - - - - Copyright © 2017 Drew DeVault - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. - - - - - Clients can use this interface to assign the surface_layer role to - wl_surfaces. Such surfaces are assigned to a "layer" of the output and - rendered with a defined z-depth respective to each other. They may also be - anchored to the edges and corners of a screen and specify input handling - semantics. This interface should be suitable for the implementation of - many desktop shell components, and a broad number of other applications - that interact with the desktop. - - - - - Create a layer surface for an existing surface. This assigns the role of - layer_surface, or raises a protocol error if another role is already - assigned. - - Creating a layer surface from a wl_surface which has a buffer attached - or committed is a client error, and any attempts by a client to attach - or manipulate a buffer prior to the first layer_surface.configure call - must also be treated as errors. - - You may pass NULL for output to allow the compositor to decide which - output to use. Generally this will be the one that the user most - recently interacted with. - - Clients can specify a namespace that defines the purpose of the layer - surface. - - - - - - - - - - - - - - - - - These values indicate which layers a surface can be rendered in. They - are ordered by z depth, bottom-most first. Traditional shell surfaces - will typically be rendered between the bottom and top layers. - Fullscreen shell surfaces are typically rendered at the top layer. - Multiple surfaces can share a single layer, and ordering within a - single layer is undefined. - - - - - - - - - - - - An interface that may be implemented by a wl_surface, for surfaces that - are designed to be rendered as a layer of a stacked desktop-like - environment. - - Layer surface state (size, anchor, exclusive zone, margin, interactivity) - is double-buffered, and will be applied at the time wl_surface.commit of - the corresponding wl_surface is called. - - - - - Sets the size of the surface in surface-local coordinates. The - compositor will display the surface centered with respect to its - anchors. - - If you pass 0 for either value, the compositor will assign it and - inform you of the assignment in the configure event. You must set your - anchor to opposite edges in the dimensions you omit; not doing so is a - protocol error. Both values are 0 by default. - - Size is double-buffered, see wl_surface.commit. - - - - - - - - Requests that the compositor anchor the surface to the specified edges - and corners. If two orthoginal edges are specified (e.g. 'top' and - 'left'), then the anchor point will be the intersection of the edges - (e.g. the top left corner of the output); otherwise the anchor point - will be centered on that edge, or in the center if none is specified. - - Anchor is double-buffered, see wl_surface.commit. - - - - - - - Requests that the compositor avoids occluding an area of the surface - with other surfaces. The compositor's use of this information is - implementation-dependent - do not assume that this region will not - actually be occluded. - - A positive value is only meaningful if the surface is anchored to an - edge, rather than a corner. The zone is the number of surface-local - coordinates from the edge that are considered exclusive. - - Surfaces that do not wish to have an exclusive zone may instead specify - how they should interact with surfaces that do. If set to zero, the - surface indicates that it would like to be moved to avoid occluding - surfaces with a positive excluzive zone. If set to -1, the surface - indicates that it would not like to be moved to accommodate for other - surfaces, and the compositor should extend it all the way to the edges - it is anchored to. - - For example, a panel might set its exclusive zone to 10, so that - maximized shell surfaces are not shown on top of it. A notification - might set its exclusive zone to 0, so that it is moved to avoid - occluding the panel, but shell surfaces are shown underneath it. A - wallpaper or lock screen might set their exclusive zone to -1, so that - they stretch below or over the panel. - - The default value is 0. - - Exclusive zone is double-buffered, see wl_surface.commit. - - - - - - - Requests that the surface be placed some distance away from the anchor - point on the output, in surface-local coordinates. Setting this value - for edges you are not anchored to has no effect. - - The exclusive zone includes the margin. - - Margin is double-buffered, see wl_surface.commit. - - - - - - - - - - Set to 1 to request that the seat send keyboard events to this layer - surface. For layers below the shell surface layer, the seat will use - normal focus semantics. For layers above the shell surface layers, the - seat will always give exclusive keyboard focus to the top-most layer - which has keyboard interactivity set to true. - - Layer surfaces receive pointer, touch, and tablet events normally. If - you do not want to receive them, set the input region on your surface - to an empty region. - - Events is double-buffered, see wl_surface.commit. - - - - - - - This assigns an xdg_popup's parent to this layer_surface. This popup - should have been created via xdg_surface::get_popup with the parent set - to NULL, and this request must be invoked before committing the popup's - initial state. - - See the documentation of xdg_popup for more details about what an - xdg_popup is and how it is used. - - - - - - - When a configure event is received, if a client commits the - surface in response to the configure event, then the client - must make an ack_configure request sometime before the commit - request, passing along the serial of the configure event. - - If the client receives multiple configure events before it - can respond to one, it only has to ack the last configure event. - - A client is not required to commit immediately after sending - an ack_configure request - it may even ack_configure several times - before its next surface commit. - - A client may send multiple ack_configure requests before committing, but - only the last request sent before a commit indicates which configure - event the client really is responding to. - - - - - - - This request destroys the layer surface. - - - - - - The configure event asks the client to resize its surface. - - Clients should arrange their surface for the new states, and then send - an ack_configure request with the serial sent in this configure event at - some point before committing the new surface. - - The client is free to dismiss all but the last configure event it - received. - - The width and height arguments specify the size of the window in - surface-local coordinates. - - The size is a hint, in the sense that the client is free to ignore it if - it doesn't resize, pick a smaller size (to satisfy aspect ratio or - resize in steps of NxM pixels). If the client picks a smaller size and - is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the - surface will be centered on this axis. - - If the width or height arguments are zero, it means the client should - decide its own window dimension. - - - - - - - - - The closed event is sent by the compositor when the surface will no - longer be shown. The output may have been destroyed or the user may - have asked for it to be removed. Further changes to the surface will be - ignored. The client should destroy the resource after receiving this - event, and create a new surface if they so choose. - - - - - - - - - - - - - - - - - From 5c36753c156a30c704ee49c884a35af6ca2ea1d3 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 11 Sep 2019 12:04:55 +0300 Subject: [PATCH 06/36] Workaround id_rsa.pub not being identified as text And extensively docomment why mime_type_is_text() does what it does, including this workaround. --- src/util/string.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/util/string.c b/src/util/string.c index b7b3876..f39db31 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -22,15 +22,35 @@ #include int mime_type_is_text(const char *mime_type) { - return str_has_prefix(mime_type, "text/") + /* A heuristic to detect plain text mime types */ + + /* Types that explicitly declare they're textual */ + int basic + = str_has_prefix(mime_type, "text/") || strcmp(mime_type, "TEXT") == 0 || strcmp(mime_type, "STRING") == 0 - || strcmp(mime_type, "UTF8_STRING") == 0 + || strcmp(mime_type, "UTF8_STRING") == 0; + + /* Common script and markup types */ + int common + = strstr(mime_type, "json") != NULL || str_has_suffix(mime_type, "script") || str_has_suffix(mime_type, "xml") - || str_has_suffix(mime_type, "yaml") - || str_has_suffix(mime_type, "pgp-keys") - || strstr(mime_type, "json") != NULL; + || str_has_suffix(mime_type, "yaml"); + + /* Special-case PGP and SSH keys. + * A public SSH key is typically stored + * in a file that has a name similar to + * id_rsa.pub, which xdg-mime misidentifies + * as being a Publisher file. Note that it + * handles private keys, which do not have + * a .pub extension, correctly. + */ + int special + = strstr(mime_type, "application/vnd.ms-publisher") != NULL + || str_has_suffix(mime_type, "pgp-keys"); + + return basic || common || special; } int str_has_prefix(const char *string, const char *prefix) { From 7f0c644a38d674e8243a0fe4ac6b830d285d91dd Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 11 Sep 2019 12:29:57 +0300 Subject: [PATCH 07/36] Consolidate all options into struct { ... } options This is in preparation for moving option parsing out to explicit separate functions. --- src/wl-copy.c | 51 ++++++++++++++++++++++++++------------------------ src/wl-paste.c | 7 +++---- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/wl-copy.c b/src/wl-copy.c index 6db93a8..64e5397 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -18,9 +18,17 @@ #include "boilerplate.h" +struct { + int stay_in_foreground; + int clear; + char *mime_type; + int trim_newline; + int paste_once; + int primary; +} options; + char * const *data_to_copy = NULL; char *temp_file_to_copy = NULL; -int paste_once = 0; void do_cancel() { /* We're done! */ @@ -65,7 +73,7 @@ void do_send(const char *mime_type, int fd) { wait(NULL); } - if (paste_once) { + if (options.paste_once) { do_cancel(); } } @@ -379,12 +387,6 @@ int main(int argc, char * const argv[]) { bail("Empty argv"); } - int stay_in_foreground = 0; - int clear = 0; - char *mime_type = NULL; - int primary = 0; - int trim_newline = 0; - static struct option long_options[] = { {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, @@ -415,22 +417,22 @@ int main(int argc, char * const argv[]) { print_usage(stdout, argv[0]); exit(0); case 'p': - primary = 1; + options.primary = 1; break; case 'n': - trim_newline = 1; + options.trim_newline = 1; break; case 'o': - paste_once = 1; + options.paste_once = 1; break; case 'f': - stay_in_foreground = 1; + options.stay_in_foreground = 1; break; case 'c': - clear = 1; + options.clear = 1; break; case 't': - mime_type = strdup(optarg); + options.mime_type = strdup(optarg); break; case 's': requested_seat_name = strdup(optarg); @@ -444,27 +446,28 @@ int main(int argc, char * const argv[]) { init_wayland_globals(); - if (primary) { + if (options.primary) { ensure_has_primary_selection(); } - if (!clear) { + if (!options.clear) { if (optind < argc) { /* Copy our command-line arguments */ data_to_copy = &argv[optind]; } else { /* Copy data from our stdin */ temp_file_to_copy = dump_stdin_into_a_temp_file(); - if (trim_newline) { + if (options.trim_newline) { trim_trailing_newline(temp_file_to_copy); } - if (mime_type == NULL) { - mime_type = infer_mime_type_from_contents(temp_file_to_copy); + if (options.mime_type == NULL) { + options.mime_type + = infer_mime_type_from_contents(temp_file_to_copy); } } } - if (!stay_in_foreground && !clear) { + if (!options.stay_in_foreground && !options.clear) { if (fork() != 0) { /* Move to background. * We fork our process and leave the @@ -475,13 +478,13 @@ int main(int argc, char * const argv[]) { } } - if (!primary) { - init_selection(mime_type); + if (!options.primary) { + init_selection(options.mime_type); } else { - init_primary_selection(mime_type); + init_primary_selection(options.mime_type); } - if (clear) { + if (options.clear) { wl_display_roundtrip(display); exit(0); } diff --git a/src/wl-paste.c b/src/wl-paste.c index 4038ed2..1909994 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -23,6 +23,7 @@ struct { char *inferred_type; int no_newline; int list_types; + int primary; } options; struct { @@ -461,8 +462,6 @@ int main(int argc, char * const argv[]) { bail("Empty argv"); } - int primary = 0; - static struct option long_options[] = { {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, @@ -491,7 +490,7 @@ int main(int argc, char * const argv[]) { print_usage(stdout, argv[0]); exit(0); case 'p': - primary = 1; + options.primary = 1; break; case 'n': options.no_newline = 1; @@ -520,7 +519,7 @@ int main(int argc, char * const argv[]) { init_wayland_globals(); - if (!primary) { + if (!options.primary) { init_selection(); } else { init_primary_selection(); From fea8702bdff1d0aba89ce1d36150488bb60fa0cb Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 11 Sep 2019 12:34:42 +0300 Subject: [PATCH 08/36] Separate option parsing into parse_options() Similar to how printing usage is a separate function, print_usage(). --- src/wl-copy.c | 7 +++++-- src/wl-paste.c | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/wl-copy.c b/src/wl-copy.c index 64e5397..38cf9e3 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -381,8 +381,7 @@ void print_usage(FILE *f, const char *argv0) { ); } -int main(int argc, char * const argv[]) { - +void parse_options(int argc, char * const argv[]) { if (argc < 1) { bail("Empty argv"); } @@ -443,6 +442,10 @@ int main(int argc, char * const argv[]) { exit(1); } } +} + +int main(int argc, char * const argv[]) { + parse_options(argc, argv); init_wayland_globals(); diff --git a/src/wl-paste.c b/src/wl-paste.c index 1909994..76767c2 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -456,8 +456,7 @@ void init_primary_selection() { #endif } -int main(int argc, char * const argv[]) { - +void parse_options(int argc, char * const argv[]) { if (argc < 1) { bail("Empty argv"); } @@ -510,6 +509,11 @@ int main(int argc, char * const argv[]) { exit(1); } } +} + +int main(int argc, char * const argv[]) { + parse_options(argc, argv); + char *path = path_for_fd(STDOUT_FILENO); if (path != NULL && options.explicit_type == NULL) { From f5e479dda661bf57a454763e9e137c66f17a1e7e Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 11 Sep 2019 12:59:51 +0300 Subject: [PATCH 09/36] Make most symbols static We used not to care about this and just make everything externally visible. Now we switch to a different convention where everything is static by default unless it needs to be externally visible. --- src/boilerplate.c | 54 +++++++++++++++++++++-------------------- src/boilerplate.h | 1 - src/wl-copy.c | 62 +++++++++++++++++++++++------------------------ src/wl-paste.c | 61 +++++++++++++++++++++++----------------------- 4 files changed, 90 insertions(+), 88 deletions(-) diff --git a/src/boilerplate.c b/src/boilerplate.c index e16c48e..3fd24a3 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -18,7 +18,9 @@ #include "boilerplate.h" -void registry_global_handler +static void process_new_seat(struct wl_seat *new_seat); + +static void registry_global_handler ( void *data, struct wl_registry *registry, @@ -105,19 +107,19 @@ void registry_global_handler #endif } -void registry_global_remove_handler +static void registry_global_remove_handler ( void *data, struct wl_registry *registry, uint32_t name ) {} -const struct wl_registry_listener registry_listener = { +static const struct wl_registry_listener registry_listener = { .global = registry_global_handler, .global_remove = registry_global_remove_handler }; -void keyboard_keymap_handler +static void keyboard_keymap_handler ( void *data, struct wl_keyboard *keyboard, @@ -128,7 +130,7 @@ void keyboard_keymap_handler close(fd); } -void keyboard_enter_handler +static void keyboard_enter_handler ( void *data, struct wl_keyboard *keyboard, @@ -146,7 +148,7 @@ void keyboard_enter_handler } } -void keyboard_leave_handler +static void keyboard_leave_handler ( void *data, struct wl_keyboard *keyboard, @@ -154,7 +156,7 @@ void keyboard_leave_handler struct wl_surface *surface ) {} -void keyboard_key_handler +static void keyboard_key_handler ( void *data, struct wl_keyboard *keyboard, @@ -164,7 +166,7 @@ void keyboard_key_handler uint32_t state ) {} -void keyboard_modifiers_handler +static void keyboard_modifiers_handler ( void *data, struct wl_keyboard *keyboard, @@ -175,7 +177,7 @@ void keyboard_modifiers_handler uint32_t group ) {} -const struct wl_keyboard_listener keayboard_listener = { +static const struct wl_keyboard_listener keayboard_listener = { .keymap = keyboard_keymap_handler, .enter = keyboard_enter_handler, .leave = keyboard_leave_handler, @@ -183,7 +185,7 @@ const struct wl_keyboard_listener keayboard_listener = { .modifiers = keyboard_modifiers_handler, }; -void seat_capabilities_handler +static void seat_capabilities_handler ( void *data, struct wl_seat *this_seat, @@ -199,7 +201,7 @@ void seat_capabilities_handler } } -void seat_name_handler +static void seat_name_handler ( void *data, struct wl_seat *this_seat, @@ -213,14 +215,14 @@ void seat_name_handler } } -const struct wl_seat_listener seat_listener = { +static const struct wl_seat_listener seat_listener = { .capabilities = seat_capabilities_handler, .name = seat_name_handler }; #define UNSET_CAPABILITIES ((void *) (uint32_t) 35) -void process_new_seat(struct wl_seat *new_seat) { +static void process_new_seat(struct wl_seat *new_seat) { if (seat != NULL) { wl_seat_destroy(new_seat); return; @@ -251,7 +253,7 @@ int ensure_seat_has_keyboard() { #undef UNSET_CAPABILITIES -void shell_surface_ping +static void shell_surface_ping ( void *data, struct wl_shell_surface *shell_surface, @@ -260,7 +262,7 @@ void shell_surface_ping wl_shell_surface_pong(shell_surface, serial); } -void shell_surface_configure +static void shell_surface_configure ( void *data, struct wl_shell_surface *shell_surface, @@ -269,13 +271,13 @@ void shell_surface_configure int32_t height ) {} -void shell_surface_popup_done +static void shell_surface_popup_done ( void *data, struct wl_shell_surface *shell_surface ) {} -const struct wl_shell_surface_listener shell_surface_listener = { +static const struct wl_shell_surface_listener shell_surface_listener = { .ping = shell_surface_ping, .configure = shell_surface_configure, .popup_done = shell_surface_popup_done @@ -283,7 +285,7 @@ const struct wl_shell_surface_listener shell_surface_listener = { #ifdef HAVE_XDG_SHELL -void xdg_toplevel_configure_handler +static void xdg_toplevel_configure_handler ( void *data, struct xdg_toplevel *xdg_toplevel, @@ -292,18 +294,18 @@ void xdg_toplevel_configure_handler struct wl_array *states ) {} -void xdg_toplevel_close_handler +static void xdg_toplevel_close_handler ( void *data, struct xdg_toplevel *xdg_toplevel ) {} -const struct xdg_toplevel_listener xdg_toplevel_listener = { +static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure_handler, .close = xdg_toplevel_close_handler }; -void xdg_surface_configure_handler +static void xdg_surface_configure_handler ( void *data, struct xdg_surface *xdg_surface, @@ -312,11 +314,11 @@ void xdg_surface_configure_handler xdg_surface_ack_configure(xdg_surface, serial); } -const struct xdg_surface_listener xdg_surface_listener = { +static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_configure_handler }; -void xdg_wm_base_ping_handler +static void xdg_wm_base_ping_handler ( void *data, struct xdg_wm_base *xdg_wm_base, @@ -325,7 +327,7 @@ void xdg_wm_base_ping_handler xdg_wm_base_pong(xdg_wm_base, serial); } -const struct xdg_wm_base_listener xdg_wm_base_listener = { +static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping_handler }; @@ -514,7 +516,7 @@ void destroy_popup_surface() { static uint32_t global_serial; -void callback_done +static void callback_done ( void *data, struct wl_callback *callback, @@ -523,7 +525,7 @@ void callback_done global_serial = serial; } -const struct wl_callback_listener callback_listener = { +static const struct wl_callback_listener callback_listener = { .done = callback_done }; diff --git a/src/boilerplate.h b/src/boilerplate.h index c553897..d4ea531 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -86,7 +86,6 @@ struct zwlr_data_control_manager_v1 *data_control_manager; struct zwlr_data_control_device_v1 *data_control_device; #endif -void process_new_seat(struct wl_seat *new_seat); const char *requested_seat_name; void init_wayland_globals(void); diff --git a/src/wl-copy.c b/src/wl-copy.c index 38cf9e3..5ad5db3 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -18,7 +18,7 @@ #include "boilerplate.h" -struct { +static struct { int stay_in_foreground; int clear; char *mime_type; @@ -27,10 +27,10 @@ struct { int primary; } options; -char * const *data_to_copy = NULL; -char *temp_file_to_copy = NULL; +static char * const *data_to_copy = NULL; +static char *temp_file_to_copy = NULL; -void do_cancel() { +static void do_cancel() { /* We're done! */ if (temp_file_to_copy != NULL) { execlp("rm", "rm", "-r", dirname(temp_file_to_copy), NULL); @@ -41,7 +41,7 @@ void do_cancel() { } } -void do_send(const char *mime_type, int fd) { +static void do_send(const char *mime_type, int fd) { /* Unset O_NONBLOCK */ fcntl(fd, F_SETFL, 0); if (data_to_copy != NULL) { @@ -78,14 +78,14 @@ void do_send(const char *mime_type, int fd) { } } -void data_source_target_handler +static void data_source_target_handler ( void *data, struct wl_data_source *data_source, const char *mime_type ) {} -void data_source_send_handler +static void data_source_send_handler ( void *data, struct wl_data_source *data_source, @@ -95,7 +95,7 @@ void data_source_send_handler do_send(mime_type, fd); } -void data_source_cancelled_handler +static void data_source_cancelled_handler ( void *data, struct wl_data_source *data_source @@ -103,32 +103,32 @@ void data_source_cancelled_handler do_cancel(); } -const struct wl_data_source_listener data_source_listener = { +static const struct wl_data_source_listener data_source_listener = { .target = data_source_target_handler, .send = data_source_send_handler, .cancelled = data_source_cancelled_handler }; -struct wl_data_source *data_source; +static struct wl_data_source *data_source; -void set_data_selection(uint32_t serial) { +static void set_data_selection(uint32_t serial) { wl_data_device_set_selection(data_device, data_source, serial); wl_display_roundtrip(display); destroy_popup_surface(); } -void try_setting_data_selection_directly() { +static void try_setting_data_selection_directly() { set_data_selection(get_serial()); } -void complain_about_missing_keyboard() { +static void complain_about_missing_keyboard() { bail("Setting primary selection is not supported without a keyboard"); } #ifdef HAVE_GTK_PRIMARY_SELECTION -void gtk_primary_selection_source_send_handler +static void gtk_primary_selection_source_send_handler ( void *data, struct gtk_primary_selection_source *gtk_primary_selection_source, @@ -138,7 +138,7 @@ void gtk_primary_selection_source_send_handler do_send(mime_type, fd); } -void gtk_primary_selection_source_cancelled_handler +static void gtk_primary_selection_source_cancelled_handler ( void *data, struct gtk_primary_selection_source *gtk_primary_selection_source @@ -146,15 +146,15 @@ void gtk_primary_selection_source_cancelled_handler do_cancel(); } -const struct gtk_primary_selection_source_listener +static const struct gtk_primary_selection_source_listener gtk_primary_selection_source_listener = { .send = gtk_primary_selection_source_send_handler, .cancelled = gtk_primary_selection_source_cancelled_handler }; -struct gtk_primary_selection_source *gtk_primary_selection_source; +static struct gtk_primary_selection_source *gtk_primary_selection_source; -void set_gtk_primary_selection(uint32_t serial) { +static void set_gtk_primary_selection(uint32_t serial) { gtk_primary_selection_device_set_selection( gtk_primary_selection_device, @@ -170,7 +170,7 @@ void set_gtk_primary_selection(uint32_t serial) { #ifdef HAVE_WP_PRIMARY_SELECTION -void primary_selection_source_send_handler +static void primary_selection_source_send_handler ( void *data, struct zwp_primary_selection_source_v1 *primary_selection_source, @@ -180,7 +180,7 @@ void primary_selection_source_send_handler do_send(mime_type, fd); } -void primary_selection_source_cancelled_handler +static void primary_selection_source_cancelled_handler ( void *data, struct zwp_primary_selection_source_v1 *primary_selection_source @@ -188,15 +188,15 @@ void primary_selection_source_cancelled_handler do_cancel(); } -const struct zwp_primary_selection_source_v1_listener +static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = { .send = primary_selection_source_send_handler, .cancelled = primary_selection_source_cancelled_handler }; -struct zwp_primary_selection_source_v1 *primary_selection_source; +static struct zwp_primary_selection_source_v1 *primary_selection_source; -void set_primary_selection(uint32_t serial) { +static void set_primary_selection(uint32_t serial) { zwp_primary_selection_device_v1_set_selection( primary_selection_device, @@ -212,7 +212,7 @@ void set_primary_selection(uint32_t serial) { #ifdef HAVE_WLR_DATA_CONTROL -void data_control_source_send_handler +static void data_control_source_send_handler ( void *data, struct zwlr_data_control_source_v1 *data_control_source, @@ -222,7 +222,7 @@ void data_control_source_send_handler do_send(mime_type, fd); } -void data_control_source_cancelled_handler +static void data_control_source_cancelled_handler ( void *data, struct zwlr_data_control_source_v1 *data_source @@ -230,14 +230,14 @@ void data_control_source_cancelled_handler do_cancel(); } -const struct zwlr_data_control_source_v1_listener +static const struct zwlr_data_control_source_v1_listener data_control_source_listener = { .send = data_control_source_send_handler, .cancelled = data_control_source_cancelled_handler }; #endif -void do_offer +static void do_offer ( char *mime_type, void *source, @@ -257,7 +257,7 @@ void do_offer free(mime_type); } -void init_selection(char *mime_type) { +static void init_selection(char *mime_type) { if (use_wlr_data_control) { #ifdef HAVE_WLR_DATA_CONTROL struct zwlr_data_control_source_v1 *data_control_source = @@ -299,7 +299,7 @@ void init_selection(char *mime_type) { } } -void init_primary_selection(char *mime_type) { +static void init_primary_selection(char *mime_type) { ensure_has_primary_selection(); #ifdef HAVE_WP_PRIMARY_SELECTION @@ -354,7 +354,7 @@ void init_primary_selection(char *mime_type) { #endif } -void print_usage(FILE *f, const char *argv0) { +static void print_usage(FILE *f, const char *argv0) { fprintf( f, "Usage:\n" @@ -381,7 +381,7 @@ void print_usage(FILE *f, const char *argv0) { ); } -void parse_options(int argc, char * const argv[]) { +static void parse_options(int argc, char * const argv[]) { if (argc < 1) { bail("Empty argv"); } diff --git a/src/wl-paste.c b/src/wl-paste.c index 76767c2..454a666 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -18,7 +18,7 @@ #include "boilerplate.h" -struct { +static struct { char *explicit_type; char *inferred_type; int no_newline; @@ -26,7 +26,7 @@ struct { int primary; } options; -struct { +static struct { int explicit_available; int inferred_available; int plain_text_utf8_available; @@ -36,7 +36,7 @@ struct { char *any; } available_types; -void do_process_offer(const char *offered_type) { +static void do_process_offer(const char *offered_type) { if (options.list_types) { printf("%s\n", offered_type); } else { @@ -107,7 +107,7 @@ if (available_types.any_text != NULL) \ if (available_types.any != NULL) \ return available_types.any -const char *mime_type_to_request() { +static const char *mime_type_to_request() { if (options.explicit_type != NULL) { if (strcmp(options.explicit_type, "text") == 0) { try_text_plain_utf8; @@ -150,7 +150,7 @@ const char *mime_type_to_request() { #undef try_any_text #undef try_any -void free_types() { +static void free_types() { free(available_types.having_explicit_as_prefix); free(available_types.any_text); free(available_types.any); @@ -158,7 +158,7 @@ void free_types() { free(options.inferred_type); } -void do_paste +static void do_paste ( void *offer, void (*receive_f)(void *offer, const char *mime_type, int fd) @@ -205,7 +205,7 @@ void do_paste exit(0); } -void data_offer_offer +static void data_offer_offer ( void *data, struct wl_data_offer *data_offer, @@ -214,11 +214,11 @@ void data_offer_offer do_process_offer(offered_mime_type); } -const struct wl_data_offer_listener data_offer_listener = { +static const struct wl_data_offer_listener data_offer_listener = { .offer = data_offer_offer }; -void data_device_data_offer +static void data_device_data_offer ( void *data, struct wl_data_device *data_device, @@ -227,7 +227,7 @@ void data_device_data_offer wl_data_offer_add_listener(data_offer, &data_offer_listener, NULL); } -void data_device_selection +static void data_device_selection ( void *data, struct wl_data_device *data_device, @@ -239,14 +239,14 @@ void data_device_selection ); } -const struct wl_data_device_listener data_device_listener = { +static const struct wl_data_device_listener data_device_listener = { .data_offer = data_device_data_offer, .selection = data_device_selection }; #ifdef HAVE_GTK_PRIMARY_SELECTION -void gtk_primary_selection_offer_offer +static void gtk_primary_selection_offer_offer ( void *data, struct gtk_primary_selection_offer *gtk_primary_selection_offer, @@ -255,12 +255,12 @@ void gtk_primary_selection_offer_offer do_process_offer(offered_mime_type); } -const struct gtk_primary_selection_offer_listener +static const struct gtk_primary_selection_offer_listener gtk_primary_selection_offer_listener = { .offer = gtk_primary_selection_offer_offer }; -void gtk_primary_selection_device_data_offer +static void gtk_primary_selection_device_data_offer ( void *data, struct gtk_primary_selection_device *gtk_primary_selection_device, @@ -273,7 +273,7 @@ void gtk_primary_selection_device_data_offer ); } -void gtk_primary_selection_device_selection +static void gtk_primary_selection_device_selection ( void *data, struct gtk_primary_selection_device *gtk_primary_selection_device, @@ -286,7 +286,7 @@ void gtk_primary_selection_device_selection ); } -const struct gtk_primary_selection_device_listener +static const struct gtk_primary_selection_device_listener gtk_primary_selection_device_listener = { .data_offer = gtk_primary_selection_device_data_offer, .selection = gtk_primary_selection_device_selection @@ -296,7 +296,7 @@ gtk_primary_selection_device_listener = { #ifdef HAVE_WP_PRIMARY_SELECTION -void primary_selection_offer_offer +static void primary_selection_offer_offer ( void *data, struct zwp_primary_selection_offer_v1 *primary_selection_offer, @@ -305,12 +305,12 @@ void primary_selection_offer_offer do_process_offer(offered_mime_type); } -const struct zwp_primary_selection_offer_v1_listener +static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = { .offer = primary_selection_offer_offer }; -void primary_selection_device_data_offer +static void primary_selection_device_data_offer ( void *data, struct zwp_primary_selection_device_v1 *primary_selection_device, @@ -323,7 +323,7 @@ void primary_selection_device_data_offer ); } -void primary_selection_device_selection +static void primary_selection_device_selection ( void *data, struct zwp_primary_selection_device_v1 *primary_selection_device, @@ -336,7 +336,7 @@ void primary_selection_device_selection ); } -const struct zwp_primary_selection_device_v1_listener +static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = { .data_offer = primary_selection_device_data_offer, .selection = primary_selection_device_selection @@ -345,7 +345,7 @@ primary_selection_device_listener = { #endif #ifdef HAVE_WLR_DATA_CONTROL -void data_control_offer_offer +static void data_control_offer_offer ( void *data, struct zwlr_data_control_offer_v1 *data_offer, @@ -354,11 +354,12 @@ void data_control_offer_offer do_process_offer(offered_mime_type); } -const struct zwlr_data_control_offer_v1_listener data_control_offer_listener = { +static const struct zwlr_data_control_offer_v1_listener +data_control_offer_listener = { .offer = data_control_offer_offer }; -void data_control_device_data_offer +static void data_control_device_data_offer ( void *data, struct zwlr_data_control_device_v1 *data_control_device, @@ -371,7 +372,7 @@ void data_control_device_data_offer ); } -void data_control_device_selection +static void data_control_device_selection ( void *data, struct zwlr_data_control_device_v1 *data_control_device, @@ -383,14 +384,14 @@ void data_control_device_selection ); } -const struct zwlr_data_control_device_v1_listener +static const struct zwlr_data_control_device_v1_listener data_control_device_listener = { .data_offer = data_control_device_data_offer, .selection = data_control_device_selection }; #endif -void print_usage(FILE *f, const char *argv0) { +static void print_usage(FILE *f, const char *argv0) { fprintf( f, "Usage:\n" @@ -413,7 +414,7 @@ void print_usage(FILE *f, const char *argv0) { ); } -void init_selection() { +static void init_selection() { if (use_wlr_data_control) { #ifdef HAVE_WLR_DATA_CONTROL zwlr_data_control_device_v1_add_listener( @@ -428,7 +429,7 @@ void init_selection() { } } -void init_primary_selection() { +static void init_primary_selection() { ensure_has_primary_selection(); #ifdef HAVE_WP_PRIMARY_SELECTION @@ -456,7 +457,7 @@ void init_primary_selection() { #endif } -void parse_options(int argc, char * const argv[]) { +static void parse_options(int argc, char * const argv[]) { if (argc < 1) { bail("Empty argv"); } From f103563cd74cf4296d8bc9f0b9663fd9e4efb4c1 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 11 Sep 2019 13:08:04 +0300 Subject: [PATCH 10/36] Add a missing shell surface listener This fell through the cracks. The compilers were not issuing an unused symbol warning because of the symbol being public, so now they do. --- src/boilerplate.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/boilerplate.c b/src/boilerplate.c index 3fd24a3..822d13a 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -442,6 +442,11 @@ void popup_tiny_invisible_surface() { if (shell != NULL) { /* Use wl_shell */ shell_surface = wl_shell_get_shell_surface(shell, surface); + wl_shell_surface_add_listener( + shell_surface, + &shell_surface_listener, + NULL + ); wl_shell_surface_set_toplevel(shell_surface); wl_shell_surface_set_title(shell_surface, "wl-clipboard"); } else { From b8e8b9ab5435ab76bbc140dffe809955678be527 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 11 Sep 2019 13:29:56 +0300 Subject: [PATCH 11/36] Make sure to always handle fork() failure --- src/util/files.c | 17 +++++++++++++++-- src/wl-copy.c | 25 ++++++++++++++++++------- src/wl-paste.c | 7 ++++++- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/util/files.c b/src/util/files.c index c96e6b9..591eb4e 100644 --- a/src/util/files.c +++ b/src/util/files.c @@ -103,7 +103,15 @@ char *infer_mime_type_from_contents(const char *file_path) { /* Spawn xdg-mime query filetype */ int pipefd[2]; pipe(pipefd); - if (fork() == 0) { + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + close(pipefd[0]); + close(pipefd[1]); + return NULL; + } + if (pid == 0) { dup2(pipefd[1], STDOUT_FILENO); close(pipefd[0]); close(pipefd[1]); @@ -204,7 +212,12 @@ char *dump_stdin_into_a_temp_file() { strcpy(res_path + sizeof(dirpath), name); /* Spawn cat to perform the copy */ - if (fork() == 0) { + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + if (pid == 0) { int fd = creat(res_path, S_IRUSR | S_IWUSR); if (fd < 0) { perror("creat"); diff --git a/src/wl-copy.c b/src/wl-copy.c index 5ad5db3..ab05bb3 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -63,7 +63,13 @@ static void do_send(const char *mime_type, int fd) { /* Copy from the temp file; for that, we delegate to a * (hopefully) highly optimized implementation of copying. */ - if (fork() == 0) { + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + close(fd); + return; + } + if (pid == 0) { dup2(fd, STDOUT_FILENO); execlp("cat", "cat", temp_file_to_copy, NULL); perror("exec cat"); @@ -471,12 +477,17 @@ int main(int argc, char * const argv[]) { } if (!options.stay_in_foreground && !options.clear) { - if (fork() != 0) { - /* Move to background. - * We fork our process and leave the - * child running in the background, - * while exiting in the parent. - */ + /* Move to background. + * We fork our process and leave the + * child running in the background, + * while exiting in the parent. + */ + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + /* Proceed without forking */ + } + if (pid > 0) { exit(0); } } diff --git a/src/wl-paste.c b/src/wl-paste.c index 454a666..47068c1 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -188,7 +188,12 @@ static void do_paste wl_display_roundtrip(display); /* Spawn a cat to perform the copy */ - if (fork() == 0) { + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + if (pid == 0) { dup2(pipefd[0], STDIN_FILENO); close(pipefd[0]); close(pipefd[1]); From eadb83942052da6685e99e84e38b224f50dd260a Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Wed, 11 Sep 2019 13:33:50 +0300 Subject: [PATCH 12/36] Do not allow any positional arguments to wl-paste They were previously silently ignored. --- src/wl-paste.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wl-paste.c b/src/wl-paste.c index 47068c1..af46b94 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -515,6 +515,12 @@ static void parse_options(int argc, char * const argv[]) { exit(1); } } + + if (optind != argc) { + fprintf(stderr, "Unexpected argument: %s\n", argv[optind]); + print_usage(stderr, argv[0]); + exit(1); + } } int main(int argc, char * const argv[]) { From 2f5954e2fada69df5bd7fc2853d8c185641abfb2 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 12:32:39 +0300 Subject: [PATCH 13/36] Move Wayland protocol includes to their own files --- src/boilerplate.h | 18 ++------------ src/includes/selection-protocols.h | 38 ++++++++++++++++++++++++++++++ src/includes/shell-protocols.h | 30 +++++++++++++++++++++++ src/meson.build | 3 +++ 4 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 src/includes/selection-protocols.h create mode 100644 src/includes/shell-protocols.h diff --git a/src/boilerplate.h b/src/boilerplate.h index d4ea531..b6e0fbb 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -37,22 +37,8 @@ #include #include // PATH_MAX - -#ifdef HAVE_XDG_SHELL -# include "xdg-shell.h" -#endif - -#ifdef HAVE_WP_PRIMARY_SELECTION -# include "wp-primary-selection.h" -#endif - -#ifdef HAVE_GTK_PRIMARY_SELECTION -# include "gtk-primary-selection.h" -#endif - -#ifdef HAVE_WLR_DATA_CONTROL -# include "wlr-data-control.h" -#endif +#include "includes/shell-protocols.h" +#include "includes/selection-protocols.h" struct wl_display *display; struct wl_data_device_manager *data_device_manager; diff --git a/src/includes/selection-protocols.h b/src/includes/selection-protocols.h new file mode 100644 index 0000000..32e74a7 --- /dev/null +++ b/src/includes/selection-protocols.h @@ -0,0 +1,38 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef INCLUDES_SELECTION_PROTOCOLS_H +#define INCLUDES_SELECTION_PROTOCOLS_H + +#include "config.h" + +#include + +#ifdef HAVE_WP_PRIMARY_SELECTION +# include "wp-primary-selection.h" +#endif + +#ifdef HAVE_GTK_PRIMARY_SELECTION +# include "gtk-primary-selection.h" +#endif + +#ifdef HAVE_WLR_DATA_CONTROL +# include "wlr-data-control.h" +#endif + +#endif /* INCLUDES_SELECTION_PROTOCOLS_H */ diff --git a/src/includes/shell-protocols.h b/src/includes/shell-protocols.h new file mode 100644 index 0000000..feb39f5 --- /dev/null +++ b/src/includes/shell-protocols.h @@ -0,0 +1,30 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef INCLUDES_SHELL_PROTOCOLS_H +#define INCLUDES_SHELL_PROTOCOLS_H + +#include "config.h" + +#include + +#ifdef HAVE_XDG_SHELL +# include "xdg-shell.h" +#endif + +#endif /* INCLUDES_SHELL_PROTOCOLS_H */ diff --git a/src/meson.build b/src/meson.build index fa42d57..f517cd7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -96,6 +96,9 @@ boilerplate = static_library( 'util/files.c', 'util/misc.h', 'util/misc.c', + + 'includes/shell-protocols.h', + 'includes/selection-protocols.h', ], dependencies: wayland, link_with: protocol_deps From d981e9d606ad0f999a354a0755bc467a775bed62 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 12:36:51 +0300 Subject: [PATCH 14/36] Rename libwl-clipboard-boilerplate to just libwl-clipboard It's getting increasingly more structured and less boilerplaity, and this trend is only going to continue. It still remains an internal static library, at least for now. --- src/meson.build | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/meson.build b/src/meson.build index f517cd7..aaf5e68 100644 --- a/src/meson.build +++ b/src/meson.build @@ -85,8 +85,8 @@ foreach protocol : protocols protocol_deps += lib endforeach -boilerplate = static_library( - 'wl-clipboard-boilerplate', +lib = static_library( + 'wl-clipboard', [ 'boilerplate.c', @@ -104,5 +104,5 @@ boilerplate = static_library( link_with: protocol_deps ) -executable('wl-copy', 'wl-copy.c', dependencies: wayland, link_with: boilerplate, install: true) -executable('wl-paste', 'wl-paste.c', dependencies: wayland, link_with: boilerplate, install: true) +executable('wl-copy', 'wl-copy.c', dependencies: wayland, link_with: lib, install: true) +executable('wl-paste', 'wl-paste.c', dependencies: wayland, link_with: lib, install: true) From 7aa546e7ffaf44de5a866ccb2394424c19791820 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 13:10:00 +0300 Subject: [PATCH 15/36] Move source handling to types/source This is the first in a series of commits moving parts of logic that was previously implemented in wl-copy/wl-paste into separate files, with separate types (structures) that encapsulate, to a certain degree, the implementation details of a type, somewhat OOP style. --- src/meson.build | 3 + src/types/source.c | 129 +++++++++++++++++++++++++ src/types/source.h | 55 +++++++++++ src/wl-copy.c | 229 +++++++++++---------------------------------- 4 files changed, 241 insertions(+), 175 deletions(-) create mode 100644 src/types/source.c create mode 100644 src/types/source.h diff --git a/src/meson.build b/src/meson.build index aaf5e68..51f8d4b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -99,6 +99,9 @@ lib = static_library( 'includes/shell-protocols.h', 'includes/selection-protocols.h', + + 'types/source.h', + 'types/source.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/source.c b/src/types/source.c new file mode 100644 index 0000000..4c1ae38 --- /dev/null +++ b/src/types/source.c @@ -0,0 +1,129 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include "includes/selection-protocols.h" +#include "types/source.h" +#include "util/string.h" + +#include "unistd.h" + +void source_offer(struct source *self, char *mime_type) { + self->do_offer(self->proxy, mime_type); +} + + +/* Macros to reduce implementation boilerplate */ + +#define SEND_HANDLER(type) \ +static void type ## _send_handler( \ + void *data, \ + struct type *proxy, \ + const char *mime_type, \ + int fd \ +) { \ + struct source *self = data; \ + if (self->send_callback != NULL) { \ + self->send_callback(self, mime_type, fd); \ + } else { \ + close(fd); \ + } \ +} + +#define CANCELLED_HANDLER(type) \ +static void type ## _cancelled_handler( \ + void *data, \ + struct type *proxy \ +) { \ + struct source *self = data; \ + if (self->cancelled_callback != NULL) { \ + self->cancelled_callback(self); \ + } \ +} + +#define LISTENER(type) \ +static const struct type ## _listener type ## _listener = { \ + .send = type ## _send_handler, \ + .cancelled = type ## _cancelled_handler \ +}; + +#define INIT(type) \ +void source_init_ ## type(struct source *self) { \ + self->do_offer = \ + (void (*)(struct wl_proxy *, const char *)) type ## _offer; \ + struct type *proxy = (struct type *) self->proxy; \ + type ## _add_listener(proxy, &type ## _listener, self); \ +} + + +/* Core Wayland implementation */ + +static void wl_data_source_target_handler +( + void *data, + struct wl_data_source *wl_data_source, + const char *mime_type +) {} + +SEND_HANDLER(wl_data_source) +CANCELLED_HANDLER(wl_data_source) + +static const struct wl_data_source_listener wl_data_source_listener = { + .target = wl_data_source_target_handler, + .send = wl_data_source_send_handler, + .cancelled = wl_data_source_cancelled_handler +}; + +INIT(wl_data_source) + + +/* gtk-primary-selection implementation */ + +#ifdef HAVE_GTK_PRIMARY_SELECTION + +SEND_HANDLER(gtk_primary_selection_source) +CANCELLED_HANDLER(gtk_primary_selection_source) +LISTENER(gtk_primary_selection_source) +INIT(gtk_primary_selection_source) + +#endif /* HAVE_GTK_PRIMARY_SELECTION */ + + +/* wp-primary-selection implementation */ + +#ifdef HAVE_WP_PRIMARY_SELECTION + +SEND_HANDLER(zwp_primary_selection_source_v1) +CANCELLED_HANDLER(zwp_primary_selection_source_v1) +LISTENER(zwp_primary_selection_source_v1) +INIT(zwp_primary_selection_source_v1) + +#endif /* HAVE_WP_PRIMARY_SELECTION */ + + +/* wlr-data-control implementation */ + +#ifdef HAVE_WLR_DATA_CONTROL + +SEND_HANDLER(zwlr_data_control_source_v1) +CANCELLED_HANDLER(zwlr_data_control_source_v1) +LISTENER(zwlr_data_control_source_v1) +INIT(zwlr_data_control_source_v1) + +#endif /* HAVE_WLR_DATA_CONTROL */ + diff --git a/src/types/source.h b/src/types/source.h new file mode 100644 index 0000000..b471fe7 --- /dev/null +++ b/src/types/source.h @@ -0,0 +1,55 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_SOURCE_H +#define TYPES_SOURCE_H + +#include "includes/selection-protocols.h" + + +struct source { + /* These fields are initialized by the creator */ + void (*send_callback)(struct source *self, const char *mime_type, int fd); + void (*cancelled_callback)(struct source *self); + void *data; + + struct wl_proxy *proxy; + + /* This field is initialized by the implementation */ + void (*do_offer)(struct wl_proxy *proxy, const char *mime_type); +}; + +void source_offer(struct source *self, char *mime_type); + +/* Initializers */ + +void source_init_wl_data_source(struct source *self); + +#ifdef HAVE_GTK_PRIMARY_SELECTION +void source_init_gtk_primary_selection_source(struct source *self); +#endif + +#ifdef HAVE_WP_PRIMARY_SELECTION +void source_init_zwp_primary_selection_source_v1(struct source *self); +#endif + +#ifdef HAVE_WLR_DATA_CONTROL +void source_init_zwlr_data_control_source_v1(struct source *self); +#endif + +#endif /* TYPES_SOURCE_H */ diff --git a/src/wl-copy.c b/src/wl-copy.c index ab05bb3..0aad31c 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -17,6 +17,7 @@ */ #include "boilerplate.h" +#include "types/source.h" static struct { int stay_in_foreground; @@ -30,7 +31,9 @@ static struct { static char * const *data_to_copy = NULL; static char *temp_file_to_copy = NULL; -static void do_cancel() { +static struct source *source = NULL; + +static void cancelled_callback(struct source *source) { /* We're done! */ if (temp_file_to_copy != NULL) { execlp("rm", "rm", "-r", dirname(temp_file_to_copy), NULL); @@ -41,7 +44,11 @@ static void do_cancel() { } } -static void do_send(const char *mime_type, int fd) { +static void send_callback( + struct source *source, + const char *mime_type, + int fd +) { /* Unset O_NONBLOCK */ fcntl(fd, F_SETFL, 0); if (data_to_copy != NULL) { @@ -80,44 +87,13 @@ static void do_send(const char *mime_type, int fd) { } if (options.paste_once) { - do_cancel(); + cancelled_callback(source); } } -static void data_source_target_handler -( - void *data, - struct wl_data_source *data_source, - const char *mime_type -) {} - -static void data_source_send_handler -( - void *data, - struct wl_data_source *data_source, - const char *mime_type, - int fd -) { - do_send(mime_type, fd); -} - -static void data_source_cancelled_handler -( - void *data, - struct wl_data_source *data_source -) { - do_cancel(); -} - -static const struct wl_data_source_listener data_source_listener = { - .target = data_source_target_handler, - .send = data_source_send_handler, - .cancelled = data_source_cancelled_handler -}; - -static struct wl_data_source *data_source; - static void set_data_selection(uint32_t serial) { + struct wl_data_source *data_source + = (struct wl_data_source *) source->proxy; wl_data_device_set_selection(data_device, data_source, serial); wl_display_roundtrip(display); destroy_popup_surface(); @@ -127,40 +103,15 @@ static void try_setting_data_selection_directly() { set_data_selection(get_serial()); } - static void complain_about_missing_keyboard() { bail("Setting primary selection is not supported without a keyboard"); } #ifdef HAVE_GTK_PRIMARY_SELECTION -static void gtk_primary_selection_source_send_handler -( - void *data, - struct gtk_primary_selection_source *gtk_primary_selection_source, - const char *mime_type, - int fd -) { - do_send(mime_type, fd); -} - -static void gtk_primary_selection_source_cancelled_handler -( - void *data, - struct gtk_primary_selection_source *gtk_primary_selection_source -) { - do_cancel(); -} - -static const struct gtk_primary_selection_source_listener -gtk_primary_selection_source_listener = { - .send = gtk_primary_selection_source_send_handler, - .cancelled = gtk_primary_selection_source_cancelled_handler -}; - -static struct gtk_primary_selection_source *gtk_primary_selection_source; - static void set_gtk_primary_selection(uint32_t serial) { + struct gtk_primary_selection_source *gtk_primary_selection_source + = (struct gtk_primary_selection_source *) source->proxy; gtk_primary_selection_device_set_selection( gtk_primary_selection_device, @@ -176,37 +127,13 @@ static void set_gtk_primary_selection(uint32_t serial) { #ifdef HAVE_WP_PRIMARY_SELECTION -static void primary_selection_source_send_handler -( - void *data, - struct zwp_primary_selection_source_v1 *primary_selection_source, - const char *mime_type, - int fd -) { - do_send(mime_type, fd); -} - -static void primary_selection_source_cancelled_handler -( - void *data, - struct zwp_primary_selection_source_v1 *primary_selection_source -) { - do_cancel(); -} - -static const struct zwp_primary_selection_source_v1_listener -primary_selection_source_listener = { - .send = primary_selection_source_send_handler, - .cancelled = primary_selection_source_cancelled_handler -}; - -static struct zwp_primary_selection_source_v1 *primary_selection_source; - static void set_primary_selection(uint32_t serial) { + struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1 + = (struct zwp_primary_selection_source_v1 *) source->proxy; zwp_primary_selection_device_v1_set_selection( primary_selection_device, - primary_selection_source, + zwp_primary_selection_source_v1, serial ); @@ -216,49 +143,17 @@ static void set_primary_selection(uint32_t serial) { #endif -#ifdef HAVE_WLR_DATA_CONTROL - -static void data_control_source_send_handler -( - void *data, - struct zwlr_data_control_source_v1 *data_control_source, - const char *mime_type, - int fd -) { - do_send(mime_type, fd); -} - -static void data_control_source_cancelled_handler -( - void *data, - struct zwlr_data_control_source_v1 *data_source -) { - do_cancel(); -} - -static const struct zwlr_data_control_source_v1_listener -data_control_source_listener = { - .send = data_control_source_send_handler, - .cancelled = data_control_source_cancelled_handler -}; -#endif - -static void do_offer -( - char *mime_type, - void *source, - void (*offer_f)(void *source, const char *type) -) { +static void do_offer(char *mime_type, struct source *source) { if (mime_type == NULL || mime_type_is_text(mime_type)) { /* Offer a few generic plain text formats */ - offer_f(source, text_plain); - offer_f(source, text_plain_utf8); - offer_f(source, "TEXT"); - offer_f(source, "STRING"); - offer_f(source, "UTF8_STRING"); + source_offer(source, text_plain); + source_offer(source, text_plain_utf8); + source_offer(source, "TEXT"); + source_offer(source, "STRING"); + source_offer(source, "UTF8_STRING"); } if (mime_type != NULL) { - offer_f(source, mime_type); + source_offer(source, mime_type); } free(mime_type); } @@ -266,21 +161,15 @@ static void do_offer static void init_selection(char *mime_type) { if (use_wlr_data_control) { #ifdef HAVE_WLR_DATA_CONTROL - struct zwlr_data_control_source_v1 *data_control_source = - zwlr_data_control_manager_v1_create_data_source( + struct zwlr_data_control_source_v1 *data_control_source + = zwlr_data_control_manager_v1_create_data_source( data_control_manager ); - zwlr_data_control_source_v1_add_listener( - data_control_source, - &data_control_source_listener, - NULL - ); - do_offer( - mime_type, - data_control_source, - (void (*)(void *, const char *)) zwlr_data_control_source_v1_offer - ); + source->proxy = (struct wl_proxy *) data_control_source; + source_init_zwlr_data_control_source_v1(source); + + do_offer(mime_type, source); zwlr_data_control_device_v1_set_selection( data_control_device, @@ -288,16 +177,15 @@ static void init_selection(char *mime_type) { ); #endif } else { - data_source = wl_data_device_manager_create_data_source( - data_device_manager - ); - wl_data_source_add_listener(data_source, &data_source_listener, NULL); + struct wl_data_source *data_source + = wl_data_device_manager_create_data_source( + data_device_manager + ); - do_offer( - mime_type, - data_source, - (void (*)(void *, const char *)) wl_data_source_offer - ); + source->proxy = (struct wl_proxy *) data_source; + source_init_wl_data_source(source); + + do_offer(mime_type, source); action_on_popup_surface_getting_focus = set_data_selection; action_on_no_keyboard = try_setting_data_selection_directly; @@ -310,22 +198,15 @@ static void init_primary_selection(char *mime_type) { #ifdef HAVE_WP_PRIMARY_SELECTION if (primary_selection_device_manager != NULL) { - primary_selection_source = - zwp_primary_selection_device_manager_v1_create_source( + struct zwp_primary_selection_source_v1 *primary_selection_source + = zwp_primary_selection_device_manager_v1_create_source( primary_selection_device_manager ); - zwp_primary_selection_source_v1_add_listener( - primary_selection_source, - &primary_selection_source_listener, - NULL - ); - do_offer( - mime_type, - primary_selection_source, - (void (*)(void *, const char *)) - zwp_primary_selection_source_v1_offer - ); + source->proxy = (struct wl_proxy *) primary_selection_source; + source_init_zwp_primary_selection_source_v1(source); + + do_offer(mime_type, source); action_on_popup_surface_getting_focus = set_primary_selection; action_on_no_keyboard = complain_about_missing_keyboard; @@ -336,21 +217,15 @@ static void init_primary_selection(char *mime_type) { #ifdef HAVE_GTK_PRIMARY_SELECTION if (gtk_primary_selection_device_manager != NULL) { - gtk_primary_selection_source = - gtk_primary_selection_device_manager_create_source( + struct gtk_primary_selection_source* gtk_primary_selection_source + = gtk_primary_selection_device_manager_create_source( gtk_primary_selection_device_manager ); - gtk_primary_selection_source_add_listener( - gtk_primary_selection_source, - >k_primary_selection_source_listener, - NULL - ); - do_offer( - mime_type, - gtk_primary_selection_source, - (void (*)(void *, const char *)) gtk_primary_selection_source_offer - ); + source->proxy = (struct wl_proxy *) gtk_primary_selection_source; + source_init_gtk_primary_selection_source(source); + + do_offer(mime_type, source); action_on_popup_surface_getting_focus = set_gtk_primary_selection; action_on_no_keyboard = complain_about_missing_keyboard; @@ -492,6 +367,10 @@ int main(int argc, char * const argv[]) { } } + source = calloc(1, sizeof(struct source)); + source->send_callback = send_callback; + source->cancelled_callback = cancelled_callback; + if (!options.primary) { init_selection(options.mime_type); } else { From 6a9800d7eb7e871af31427d31fcc50bd882dfdb6 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 13:59:14 +0300 Subject: [PATCH 16/36] Move offer handling to types/offer --- src/meson.build | 2 + src/types/offer.c | 101 ++++++++++++++++++++++++++++ src/types/offer.h | 55 ++++++++++++++++ src/wl-paste.c | 165 +++++++++++++++------------------------------- 4 files changed, 212 insertions(+), 111 deletions(-) create mode 100644 src/types/offer.c create mode 100644 src/types/offer.h diff --git a/src/meson.build b/src/meson.build index 51f8d4b..eee4058 100644 --- a/src/meson.build +++ b/src/meson.build @@ -102,6 +102,8 @@ lib = static_library( 'types/source.h', 'types/source.c', + 'types/offer.h', + 'types/offer.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/offer.c b/src/types/offer.c new file mode 100644 index 0000000..737b358 --- /dev/null +++ b/src/types/offer.c @@ -0,0 +1,101 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/offer.h" +#include "includes/selection-protocols.h" + +#include + +void offer_receive(struct offer *self, const char *mime_type, int fd) { + self->do_receive(self->proxy, mime_type, fd); +} + +void offer_destroy(struct offer *self) { + self->do_destroy(self->proxy); + free(self); +} + + +/* Macros to reduce implementation boilerplate */ + +#define OFFER_HANDLER(type) \ +static void type ## _offer_handler( \ + void *data, \ + struct type *proxy, \ + const char *mime_type \ +) { \ + struct offer *self = data; \ + if (self->offer_callback != NULL) { \ + self->offer_callback(self, mime_type); \ + } \ +} + +#define LISTENER(type) \ +static const struct type ## _listener type ## _listener = { \ + .offer = type ## _offer_handler \ +}; + +#define INIT(type) \ +void offer_init_ ## type(struct offer *self) { \ + self->do_receive = \ + (void (*)(struct wl_proxy *, const char *, int)) type ## _receive; \ + self->do_destroy = (void (*)(struct wl_proxy *)) type ## _destroy; \ + struct type *proxy = (struct type *) self->proxy; \ + type ## _add_listener(proxy, &type ## _listener, self); \ +} + + +/* Core Wayland implementation */ + +OFFER_HANDLER(wl_data_offer) +LISTENER(wl_data_offer) +INIT(wl_data_offer) + + +/* gtk-primary-selection implementation */ + +#ifdef HAVE_GTK_PRIMARY_SELECTION + +OFFER_HANDLER(gtk_primary_selection_offer) +LISTENER(gtk_primary_selection_offer) +INIT(gtk_primary_selection_offer) + +#endif /* HAVE_GTK_PRIMARY_SELECTION */ + + +/* wp-primary-selection implementation */ + +#ifdef HAVE_WP_PRIMARY_SELECTION + +OFFER_HANDLER(zwp_primary_selection_offer_v1) +LISTENER(zwp_primary_selection_offer_v1) +INIT(zwp_primary_selection_offer_v1) + +#endif /* HAVE_WP_PRIMARY_SELECTION */ + + +/* wlr-data-control implementation */ + +#ifdef HAVE_WLR_DATA_CONTROL + +OFFER_HANDLER(zwlr_data_control_offer_v1) +LISTENER(zwlr_data_control_offer_v1) +INIT(zwlr_data_control_offer_v1) + +#endif /* HAVE_WLR_DATA_CONTROL */ + diff --git a/src/types/offer.h b/src/types/offer.h new file mode 100644 index 0000000..59ea053 --- /dev/null +++ b/src/types/offer.h @@ -0,0 +1,55 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_OFFER_H +#define TYPES_OFFER_H + +#include "includes/selection-protocols.h" + +struct offer { + /* These fields are initialized by the creator */ + void (*offer_callback)(struct offer *self, const char *mime_type); + void *data; + + struct wl_proxy *proxy; + + /* These fields are initialized by the implementation */ + void (*do_receive)(struct wl_proxy *proxy, const char *mime_type, int fd); + void (*do_destroy)(struct wl_proxy *proxy); +}; + +void offer_receive(struct offer *self, const char *mime_type, int fd); +void offer_destroy(struct offer *self); + +/* Initializers */ + +void offer_init_wl_data_offer(struct offer *self); + +#ifdef HAVE_GTK_PRIMARY_SELECTION +void offer_init_gtk_primary_selection_offer(struct offer *self); +#endif + +#ifdef HAVE_WP_PRIMARY_SELECTION +void offer_init_zwp_primary_selection_offer_v1(struct offer *self); +#endif + +#ifdef HAVE_WLR_DATA_CONTROL +void offer_init_zwlr_data_control_offer_v1(struct offer *self); +#endif + +#endif /* TYPES_OFFER_H */ diff --git a/src/wl-paste.c b/src/wl-paste.c index af46b94..cf02379 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -17,6 +17,7 @@ */ #include "boilerplate.h" +#include "types/offer.h" static struct { char *explicit_type; @@ -36,7 +37,7 @@ static struct { char *any; } available_types; -static void do_process_offer(const char *offered_type) { +static void offer_callback(struct offer *offer, const char *offered_type) { if (options.list_types) { printf("%s\n", offered_type); } else { @@ -158,11 +159,7 @@ static void free_types() { free(options.inferred_type); } -static void do_paste -( - void *offer, - void (*receive_f)(void *offer, const char *mime_type, int fd) -) { +static void do_paste(struct offer *offer) { if (offer == NULL) { bail("No selection"); } @@ -180,7 +177,7 @@ static void do_paste int pipefd[2]; pipe(pipefd); - receive_f(offer, mime_type, pipefd[1]); + offer_receive(offer, mime_type, pipefd[1]); free_types(); destroy_popup_surface(); @@ -210,38 +207,27 @@ static void do_paste exit(0); } -static void data_offer_offer -( - void *data, - struct wl_data_offer *data_offer, - const char *offered_mime_type -) { - do_process_offer(offered_mime_type); -} - -static const struct wl_data_offer_listener data_offer_listener = { - .offer = data_offer_offer -}; - -static void data_device_data_offer -( +static void data_device_data_offer( void *data, struct wl_data_device *data_device, struct wl_data_offer *data_offer ) { - wl_data_offer_add_listener(data_offer, &data_offer_listener, NULL); + struct offer *offer = calloc(1, sizeof(struct offer)); + offer->proxy = (struct wl_proxy *) data_offer; + offer->offer_callback = offer_callback; + offer_init_wl_data_offer(offer); } -static void data_device_selection -( +static void data_device_selection( void *data, struct wl_data_device *data_device, struct wl_data_offer *data_offer ) { - do_paste( - data_offer, - (void (*)(void *, const char *, int)) wl_data_offer_receive - ); + struct offer *offer = NULL; + if (data_offer != NULL) { + offer = (struct offer *) wl_data_offer_get_user_data(data_offer); + } + do_paste(offer); } static const struct wl_data_device_listener data_device_listener = { @@ -251,44 +237,29 @@ static const struct wl_data_device_listener data_device_listener = { #ifdef HAVE_GTK_PRIMARY_SELECTION -static void gtk_primary_selection_offer_offer -( - void *data, - struct gtk_primary_selection_offer *gtk_primary_selection_offer, - const char *offered_mime_type -) { - do_process_offer(offered_mime_type); -} - -static const struct gtk_primary_selection_offer_listener -gtk_primary_selection_offer_listener = { - .offer = gtk_primary_selection_offer_offer -}; - -static void gtk_primary_selection_device_data_offer -( +static void gtk_primary_selection_device_data_offer( void *data, struct gtk_primary_selection_device *gtk_primary_selection_device, struct gtk_primary_selection_offer *gtk_primary_selection_offer ) { - gtk_primary_selection_offer_add_listener( - gtk_primary_selection_offer, - >k_primary_selection_offer_listener, - NULL - ); + struct offer *offer = calloc(1, sizeof(struct offer)); + offer->proxy = (struct wl_proxy *) gtk_primary_selection_offer; + offer->offer_callback = offer_callback; + offer_init_gtk_primary_selection_offer(offer); } -static void gtk_primary_selection_device_selection -( +static void gtk_primary_selection_device_selection( void *data, struct gtk_primary_selection_device *gtk_primary_selection_device, struct gtk_primary_selection_offer *gtk_primary_selection_offer ) { - do_paste( - gtk_primary_selection_offer, - (void (*)(void *, const char *, int)) - gtk_primary_selection_offer_receive - ); + struct offer *offer = NULL; + if (gtk_primary_selection_offer != NULL) { + offer = (struct offer *) gtk_primary_selection_offer_get_user_data( + gtk_primary_selection_offer + ); + } + do_paste(offer); } static const struct gtk_primary_selection_device_listener @@ -301,44 +272,29 @@ gtk_primary_selection_device_listener = { #ifdef HAVE_WP_PRIMARY_SELECTION -static void primary_selection_offer_offer -( - void *data, - struct zwp_primary_selection_offer_v1 *primary_selection_offer, - const char *offered_mime_type -) { - do_process_offer(offered_mime_type); -} - -static const struct zwp_primary_selection_offer_v1_listener -primary_selection_offer_listener = { - .offer = primary_selection_offer_offer -}; - -static void primary_selection_device_data_offer -( +static void primary_selection_device_data_offer( void *data, struct zwp_primary_selection_device_v1 *primary_selection_device, struct zwp_primary_selection_offer_v1 *primary_selection_offer ) { - zwp_primary_selection_offer_v1_add_listener( - primary_selection_offer, - &primary_selection_offer_listener, - NULL - ); + struct offer *offer = calloc(1, sizeof(struct offer)); + offer->proxy = (struct wl_proxy *) primary_selection_offer; + offer->offer_callback = offer_callback; + offer_init_zwp_primary_selection_offer_v1(offer); } -static void primary_selection_device_selection -( +static void primary_selection_device_selection( void *data, struct zwp_primary_selection_device_v1 *primary_selection_device, struct zwp_primary_selection_offer_v1 *primary_selection_offer ) { - do_paste( - primary_selection_offer, - (void (*)(void *, const char *, int)) - zwp_primary_selection_offer_v1_receive - ); + struct offer *offer = NULL; + if (primary_selection_offer != NULL) { + offer = (struct offer *) zwp_primary_selection_offer_v1_get_user_data( + primary_selection_offer + ); + } + do_paste(offer); } static const struct zwp_primary_selection_device_v1_listener @@ -350,43 +306,30 @@ primary_selection_device_listener = { #endif #ifdef HAVE_WLR_DATA_CONTROL -static void data_control_offer_offer -( - void *data, - struct zwlr_data_control_offer_v1 *data_offer, - const char *offered_mime_type -) { - do_process_offer(offered_mime_type); -} -static const struct zwlr_data_control_offer_v1_listener -data_control_offer_listener = { - .offer = data_control_offer_offer -}; - -static void data_control_device_data_offer -( +static void data_control_device_data_offer( void *data, struct zwlr_data_control_device_v1 *data_control_device, struct zwlr_data_control_offer_v1 *data_control_offer ) { - zwlr_data_control_offer_v1_add_listener( - data_control_offer, - &data_control_offer_listener, - NULL - ); + struct offer *offer = calloc(1, sizeof(struct offer)); + offer->proxy = (struct wl_proxy *) data_control_offer; + offer->offer_callback = offer_callback; + offer_init_zwlr_data_control_offer_v1(offer); } -static void data_control_device_selection -( +static void data_control_device_selection( void *data, struct zwlr_data_control_device_v1 *data_control_device, struct zwlr_data_control_offer_v1 *data_control_offer ) { - do_paste( - data_control_offer, - (void (*)(void *, const char *, int)) zwlr_data_control_offer_v1_receive - ); + struct offer *offer = NULL; + if (data_control_offer != NULL) { + offer = (struct offer *) zwlr_data_control_offer_v1_get_user_data( + data_control_offer + ); + } + do_paste(offer); } static const struct zwlr_data_control_device_v1_listener From 0a7c8fb7471c6f3ac879a962497e3081b7fe233a Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 14:23:09 +0300 Subject: [PATCH 17/36] Move offer mime type handling to struct offer Now, instead of processing offerred mime types as they appear, we will collect them into a single buffer inside struct offer, and then analyze the whole thing when it's ready. This way, in the future it'd be possible to handle the case where we see multiple offers and don't know in adnvance which ones are the ones we need, and which we should ignore. --- src/types/offer.c | 12 ++++-- src/types/offer.h | 18 ++++++-- src/wl-paste.c | 104 ++++++++++++++++++++++------------------------ 3 files changed, 72 insertions(+), 62 deletions(-) diff --git a/src/types/offer.c b/src/types/offer.c index 737b358..c21a562 100644 --- a/src/types/offer.c +++ b/src/types/offer.c @@ -19,7 +19,9 @@ #include "types/offer.h" #include "includes/selection-protocols.h" +#include #include +#include void offer_receive(struct offer *self, const char *mime_type, int fd) { self->do_receive(self->proxy, mime_type, fd); @@ -27,6 +29,7 @@ void offer_receive(struct offer *self, const char *mime_type, int fd) { void offer_destroy(struct offer *self) { self->do_destroy(self->proxy); + wl_array_release(&self->offered_mime_types); free(self); } @@ -40,9 +43,11 @@ static void type ## _offer_handler( \ const char *mime_type \ ) { \ struct offer *self = data; \ - if (self->offer_callback != NULL) { \ - self->offer_callback(self, mime_type); \ - } \ + char *ptr = wl_array_add( \ + &self->offered_mime_types, \ + strlen(mime_type) + 1 \ + ); \ + strcpy(ptr, mime_type); \ } #define LISTENER(type) \ @@ -55,6 +60,7 @@ void offer_init_ ## type(struct offer *self) { \ self->do_receive = \ (void (*)(struct wl_proxy *, const char *, int)) type ## _receive; \ self->do_destroy = (void (*)(struct wl_proxy *)) type ## _destroy; \ + wl_array_init(&self->offered_mime_types); \ struct type *proxy = (struct type *) self->proxy; \ type ## _add_listener(proxy, &type ## _listener, self); \ } diff --git a/src/types/offer.h b/src/types/offer.h index 59ea053..800466b 100644 --- a/src/types/offer.h +++ b/src/types/offer.h @@ -21,18 +21,28 @@ #include "includes/selection-protocols.h" -struct offer { - /* These fields are initialized by the creator */ - void (*offer_callback)(struct offer *self, const char *mime_type); - void *data; +#include +#include +struct offer { + /* This field is initialized by the creator */ struct wl_proxy *proxy; /* These fields are initialized by the implementation */ void (*do_receive)(struct wl_proxy *proxy, const char *mime_type, int fd); void (*do_destroy)(struct wl_proxy *proxy); + struct wl_array offered_mime_types; }; +#define offer_for_each_mime_type(offer, mime_type) \ +for ( \ + const char *mime_type = offer->offered_mime_types.data; \ + mime_type != offer->offered_mime_types.data + \ + offer->offered_mime_types.size; \ + mime_type += strlen(mime_type) + 1 \ +) + + void offer_receive(struct offer *self, const char *mime_type, int fd); void offer_destroy(struct offer *self); diff --git a/src/wl-paste.c b/src/wl-paste.c index cf02379..7936b23 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -27,88 +27,86 @@ static struct { int primary; } options; -static struct { +struct types { int explicit_available; int inferred_available; int plain_text_utf8_available; int plain_text_available; - char *having_explicit_as_prefix; - char *any_text; - char *any; -} available_types; + const char *having_explicit_as_prefix; + const char *any_text; + const char *any; +}; -static void offer_callback(struct offer *offer, const char *offered_type) { - if (options.list_types) { - printf("%s\n", offered_type); - } else { +static struct types classify_offer_types(struct offer *offer) { + struct types types = { 0 }; + offer_for_each_mime_type(offer, mime_type) { if ( options.explicit_type != NULL && - strcmp(offered_type, options.explicit_type) == 0 + strcmp(mime_type, options.explicit_type) == 0 ) { - available_types.explicit_available = 1; + types.explicit_available = 1; } if ( options.inferred_type != NULL && - strcmp(offered_type, options.inferred_type) == 0 + strcmp(mime_type, options.inferred_type) == 0 ) { - available_types.inferred_available = 1; + types.inferred_available = 1; + } + if (strcmp(mime_type, text_plain_utf8) == 0) { + types.plain_text_utf8_available = 1; + } + if (strcmp(mime_type, text_plain) == 0) { + types.plain_text_available = 1; } if ( - strcmp(offered_type, text_plain_utf8) == 0) { - available_types.plain_text_utf8_available = 1; - } - if ( - strcmp(offered_type, text_plain) == 0) { - available_types.plain_text_available = 1; - } - if ( - available_types.any_text == NULL && - mime_type_is_text(offered_type) + types.any_text == NULL && + mime_type_is_text(mime_type) ) { - available_types.any_text = strdup(offered_type); + types.any_text = mime_type; } - if (available_types.any == NULL) { - available_types.any = strdup(offered_type); + if (types.any == NULL) { + types.any = mime_type; } if ( options.explicit_type != NULL && - available_types.having_explicit_as_prefix == NULL && - str_has_prefix(offered_type, options.explicit_type) + types.having_explicit_as_prefix == NULL && + str_has_prefix(mime_type, options.explicit_type) ) { - available_types.having_explicit_as_prefix = strdup(offered_type); + types.having_explicit_as_prefix = mime_type; } } + return types; } #define try_explicit \ -if (available_types.explicit_available) \ +if (types.explicit_available) \ return options.explicit_type #define try_inferred \ -if (available_types.inferred_available) \ +if (types.inferred_available) \ return options.inferred_type #define try_text_plain_utf8 \ -if (available_types.plain_text_utf8_available) \ +if (types.plain_text_utf8_available) \ return text_plain_utf8 #define try_text_plain \ -if (available_types.plain_text_available) \ +if (types.plain_text_available) \ return text_plain #define try_prefixed \ -if (available_types.having_explicit_as_prefix != NULL) \ - return available_types.having_explicit_as_prefix +if (types.having_explicit_as_prefix != NULL) \ + return types.having_explicit_as_prefix #define try_any_text \ -if (available_types.any_text != NULL) \ - return available_types.any_text +if (types.any_text != NULL) \ + return types.any_text #define try_any \ -if (available_types.any != NULL) \ - return available_types.any +if (types.any != NULL) \ + return types.any -static const char *mime_type_to_request() { +static const char *mime_type_to_request(struct types types) { if (options.explicit_type != NULL) { if (strcmp(options.explicit_type, "text") == 0) { try_text_plain_utf8; @@ -151,24 +149,21 @@ static const char *mime_type_to_request() { #undef try_any_text #undef try_any -static void free_types() { - free(available_types.having_explicit_as_prefix); - free(available_types.any_text); - free(available_types.any); - free(options.explicit_type); - free(options.inferred_type); -} - static void do_paste(struct offer *offer) { if (offer == NULL) { bail("No selection"); } if (options.list_types) { + offer_for_each_mime_type(offer, mime_type) { + printf("%s\n", mime_type); + } exit(0); } - const char *mime_type = mime_type_to_request(); + struct types types = classify_offer_types(offer); + const char *mime_type = mime_type_to_request(types); + /* Never append a newline character to binary content */ if (!mime_type_is_text(mime_type)) { options.no_newline = 1; @@ -179,9 +174,7 @@ static void do_paste(struct offer *offer) { offer_receive(offer, mime_type, pipefd[1]); - free_types(); destroy_popup_surface(); - wl_display_roundtrip(display); /* Spawn a cat to perform the copy */ @@ -204,6 +197,11 @@ static void do_paste(struct offer *offer) { if (!options.no_newline) { write(STDOUT_FILENO, "\n", 1); } + + offer_destroy(offer); + + free(options.explicit_type); + free(options.inferred_type); exit(0); } @@ -214,7 +212,6 @@ static void data_device_data_offer( ) { struct offer *offer = calloc(1, sizeof(struct offer)); offer->proxy = (struct wl_proxy *) data_offer; - offer->offer_callback = offer_callback; offer_init_wl_data_offer(offer); } @@ -244,7 +241,6 @@ static void gtk_primary_selection_device_data_offer( ) { struct offer *offer = calloc(1, sizeof(struct offer)); offer->proxy = (struct wl_proxy *) gtk_primary_selection_offer; - offer->offer_callback = offer_callback; offer_init_gtk_primary_selection_offer(offer); } @@ -279,7 +275,6 @@ static void primary_selection_device_data_offer( ) { struct offer *offer = calloc(1, sizeof(struct offer)); offer->proxy = (struct wl_proxy *) primary_selection_offer; - offer->offer_callback = offer_callback; offer_init_zwp_primary_selection_offer_v1(offer); } @@ -314,7 +309,6 @@ static void data_control_device_data_offer( ) { struct offer *offer = calloc(1, sizeof(struct offer)); offer->proxy = (struct wl_proxy *) data_control_offer; - offer->offer_callback = offer_callback; offer_init_zwlr_data_control_offer_v1(offer); } From 8d62b8c956bbfd784dedd40418f5a2c49912b048 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 15:37:53 +0300 Subject: [PATCH 18/36] Move device handling to types/device --- src/boilerplate.c | 33 ------- src/boilerplate.h | 6 -- src/meson.build | 2 + src/types/device.c | 217 +++++++++++++++++++++++++++++++++++++++++++++ src/types/device.h | 73 +++++++++++++++ src/wl-copy.c | 131 ++++++++++++--------------- src/wl-paste.c | 203 +++++++++++------------------------------- 7 files changed, 401 insertions(+), 264 deletions(-) create mode 100644 src/types/device.c create mode 100644 src/types/device.h diff --git a/src/boilerplate.c b/src/boilerplate.c index 822d13a..abbe81a 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -367,39 +367,6 @@ void init_wayland_globals() { } bail("Cannot find the requested seat"); } - - data_device = wl_data_device_manager_get_data_device( - data_device_manager, - seat - ); -#ifdef HAVE_GTK_PRIMARY_SELECTION - if (gtk_primary_selection_device_manager != NULL) { - gtk_primary_selection_device = - gtk_primary_selection_device_manager_get_device( - gtk_primary_selection_device_manager, - seat - ); - } -#endif -#ifdef HAVE_WP_PRIMARY_SELECTION - if (primary_selection_device_manager != NULL) { - primary_selection_device = - zwp_primary_selection_device_manager_v1_get_device( - primary_selection_device_manager, - seat - ); - } -#endif -#ifdef HAVE_WLR_DATA_CONTROL - if (data_control_manager != NULL) { - data_control_device = - zwlr_data_control_manager_v1_get_data_device( - data_control_manager, - seat - ); - use_wlr_data_control = 1; - } -#endif } void ensure_has_primary_selection() { diff --git a/src/boilerplate.h b/src/boilerplate.h index b6e0fbb..fb23c8a 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -55,27 +55,21 @@ struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; #endif -struct wl_data_device *data_device; - #ifdef HAVE_GTK_PRIMARY_SELECTION struct gtk_primary_selection_device_manager *gtk_primary_selection_device_manager; -struct gtk_primary_selection_device *gtk_primary_selection_device; #endif #ifdef HAVE_WP_PRIMARY_SELECTION struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; -struct zwp_primary_selection_device_v1 *primary_selection_device; #endif #ifdef HAVE_WLR_DATA_CONTROL struct zwlr_data_control_manager_v1 *data_control_manager; -struct zwlr_data_control_device_v1 *data_control_device; #endif const char *requested_seat_name; void init_wayland_globals(void); -int use_wlr_data_control; void popup_tiny_invisible_surface(void); void destroy_popup_surface(void); diff --git a/src/meson.build b/src/meson.build index eee4058..8b591cd 100644 --- a/src/meson.build +++ b/src/meson.build @@ -104,6 +104,8 @@ lib = static_library( 'types/source.c', 'types/offer.h', 'types/offer.c', + 'types/device.h', + 'types/device.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/device.c b/src/types/device.c new file mode 100644 index 0000000..534827f --- /dev/null +++ b/src/types/device.c @@ -0,0 +1,217 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include "types/device.h" +#include "types/offer.h" +#include "types/source.h" +#include "includes/selection-protocols.h" + +#include + +int device_supports_selection(struct device *self, int primary) { + return self->supports_selection(self, primary); +} + +void device_set_selection( + struct device *self, + struct source *source, + uint32_t serial, + int primary +) { + self->do_set_selection(self, source, serial, primary); +} + +/* Macros to reduce implementation boilerplate */ + +#define SUPPORTS_SELECTION(type, expr) \ +static int device_supports_selection_on_ ## type(struct device *self, int primary) { \ + return expr; \ +} + +#define SET_SELECTION_IMPL(type, source_type, impl) \ +static void device_set_selection_on_ ## type( \ + struct device *self, \ + struct source *source_wrapper, \ + uint32_t serial, \ + int primary \ +) { \ + struct type *device = (struct type *) self->proxy; \ + struct source_type *source = NULL; \ + if (source_wrapper != NULL) { \ + source = (struct source_type *) source_wrapper->proxy; \ + } \ + impl \ +} + +#define DATA_OFFER_HANDLER(type, offer_type) \ +static void type ## _data_offer_handler( \ + void *data, \ + struct type *device, \ + struct offer_type *offer_proxy \ +) { \ + struct device *self = data; \ + struct offer *offer = calloc(1, sizeof(struct offer)); \ + offer->proxy = (struct wl_proxy *) offer_proxy; \ + offer_init_ ## offer_type(offer); \ + if (self->new_offer_callback) { \ + self->new_offer_callback(offer); \ + } \ +} + +#define SELECTION_HANDLER(type, offer_type, selection_event, primary) \ +static void type ## _ ## selection_event ## _handler( \ + void *data, \ + struct type *device, \ + struct offer_type *offer_proxy \ +) { \ + struct device *self = data; \ + struct offer *offer = NULL; \ + if (offer_proxy != NULL) { \ + offer = wl_proxy_get_user_data((struct wl_proxy *) offer_proxy); \ + } \ + if (self->selection_callback) { \ + self->selection_callback(offer, primary); \ + } \ +} + +#define INIT(type, needs_surface) \ +void device_init_ ## type(struct device *self) { \ + struct type *device = (struct type *) self->proxy; \ + type ## _add_listener(device, &type ## _listener, self); \ + self->supports_selection = device_supports_selection_on_ ## type; \ + self->needs_popup_surface = needs_surface; \ + self->do_set_selection = device_set_selection_on_ ## type; \ +} + + +/* Core Wayland implementation */ + +SUPPORTS_SELECTION(wl_data_device, !primary) + +SET_SELECTION_IMPL(wl_data_device, wl_data_source, { + wl_data_device_set_selection(device, source, serial); +}) + +DATA_OFFER_HANDLER(wl_data_device, wl_data_offer) + +SELECTION_HANDLER(wl_data_device, wl_data_offer, selection, 0) + +static const struct wl_data_device_listener wl_data_device_listener = { + .data_offer = wl_data_device_data_offer_handler, + .selection = wl_data_device_selection_handler +}; + +INIT(wl_data_device, 1) + + +/* gtk-primary-selection implementation */ + +#ifdef HAVE_GTK_PRIMARY_SELECTION + +SUPPORTS_SELECTION(gtk_primary_selection_device, primary) + +SET_SELECTION_IMPL(gtk_primary_selection_device, gtk_primary_selection_source, { + gtk_primary_selection_device_set_selection(device, source, serial); +}) + +DATA_OFFER_HANDLER(gtk_primary_selection_device, gtk_primary_selection_offer) + +SELECTION_HANDLER( + gtk_primary_selection_device, + gtk_primary_selection_offer, + selection, + 1 +) + +static const struct gtk_primary_selection_device_listener +gtk_primary_selection_device_listener = { + .data_offer = gtk_primary_selection_device_data_offer_handler, + .selection = gtk_primary_selection_device_selection_handler +}; + +INIT(gtk_primary_selection_device, 1) + +#endif /* HAVE_GTK_PRIMARY_SELECTION */ + + +/* wp-primary-selection implementation */ + +#ifdef HAVE_WP_PRIMARY_SELECTION + +SUPPORTS_SELECTION(zwp_primary_selection_device_v1, primary) + +SET_SELECTION_IMPL( + zwp_primary_selection_device_v1, + zwp_primary_selection_source_v1, + { + zwp_primary_selection_device_v1_set_selection(device, source, serial); + } +) + +DATA_OFFER_HANDLER( + zwp_primary_selection_device_v1, + zwp_primary_selection_offer_v1 +) + +SELECTION_HANDLER( + zwp_primary_selection_device_v1, + zwp_primary_selection_offer_v1, + selection, + 1 +) + +static const struct zwp_primary_selection_device_v1_listener +zwp_primary_selection_device_v1_listener = { + .data_offer = zwp_primary_selection_device_v1_data_offer_handler, + .selection = zwp_primary_selection_device_v1_selection_handler +}; + +INIT(zwp_primary_selection_device_v1, 1) + +#endif /* HAVE_WP_PRIMARY_SELECTION */ + + +/* wlr-data-control implementation */ + +#ifdef HAVE_WLR_DATA_CONTROL + +SUPPORTS_SELECTION(zwlr_data_control_device_v1, !primary) + +SET_SELECTION_IMPL(zwlr_data_control_device_v1, zwlr_data_control_source_v1, { + zwlr_data_control_device_v1_set_selection(device, source); +}) + +DATA_OFFER_HANDLER(zwlr_data_control_device_v1, zwlr_data_control_offer_v1) + +SELECTION_HANDLER( + zwlr_data_control_device_v1, + zwlr_data_control_offer_v1, + selection, + 0 +) + +static const struct zwlr_data_control_device_v1_listener +zwlr_data_control_device_v1_listener = { + .data_offer = zwlr_data_control_device_v1_data_offer_handler, + .selection = zwlr_data_control_device_v1_selection_handler, +}; + +INIT(zwlr_data_control_device_v1, 0) + +#endif /* HAVE_WLR_DATA_CONTROL */ diff --git a/src/types/device.h b/src/types/device.h new file mode 100644 index 0000000..0867bb2 --- /dev/null +++ b/src/types/device.h @@ -0,0 +1,73 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_DEVICE_H +#define TYPES_DEVICE_H + +#include "includes/selection-protocols.h" + +#include + +struct offer; +struct source; + +struct device { + /* These fields are initialized by the creator */ + void (*new_offer_callback)(struct offer *offer); + void (*selection_callback)(struct offer *offer, int primary); + void *data; + + struct wl_proxy *proxy; + + /* These fields are initialized by the implementation */ + int (*supports_selection)(struct device *self, int primary); + int needs_popup_surface; + void (*do_set_selection)( + struct device *self, + struct source *source, + uint32_t serial, + int primary + ); +}; + +int device_supports_selection(struct device *self, int primary); + +void device_set_selection( + struct device *self, + struct source *source, + uint32_t serial, + int primary +); + +/* Initializers */ + +void device_init_wl_data_device(struct device *self); + +#ifdef HAVE_GTK_PRIMARY_SELECTION +void device_init_gtk_primary_selection_device(struct device *self); +#endif + +#ifdef HAVE_WP_PRIMARY_SELECTION +void device_init_zwp_primary_selection_device_v1(struct device *self); +#endif + +#ifdef HAVE_WLR_DATA_CONTROL +void device_init_zwlr_data_control_device_v1(struct device *self); +#endif + +#endif /* TYPES_DEVICE_H */ diff --git a/src/wl-copy.c b/src/wl-copy.c index 0aad31c..f3900dc 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -18,6 +18,7 @@ #include "boilerplate.h" #include "types/source.h" +#include "types/device.h" static struct { int stay_in_foreground; @@ -31,6 +32,7 @@ static struct { static char * const *data_to_copy = NULL; static char *temp_file_to_copy = NULL; +static struct device *device = NULL; static struct source *source = NULL; static void cancelled_callback(struct source *source) { @@ -91,58 +93,16 @@ static void send_callback( } } -static void set_data_selection(uint32_t serial) { - struct wl_data_source *data_source - = (struct wl_data_source *) source->proxy; - wl_data_device_set_selection(data_device, data_source, serial); +static void set_selection(uint32_t serial) { + device_set_selection(device, source, serial, options.primary); wl_display_roundtrip(display); destroy_popup_surface(); } -static void try_setting_data_selection_directly() { - set_data_selection(get_serial()); -} - static void complain_about_missing_keyboard() { bail("Setting primary selection is not supported without a keyboard"); } -#ifdef HAVE_GTK_PRIMARY_SELECTION - -static void set_gtk_primary_selection(uint32_t serial) { - struct gtk_primary_selection_source *gtk_primary_selection_source - = (struct gtk_primary_selection_source *) source->proxy; - - gtk_primary_selection_device_set_selection( - gtk_primary_selection_device, - gtk_primary_selection_source, - serial - ); - - wl_display_roundtrip(display); - destroy_popup_surface(); -} - -#endif - -#ifdef HAVE_WP_PRIMARY_SELECTION - -static void set_primary_selection(uint32_t serial) { - struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1 - = (struct zwp_primary_selection_source_v1 *) source->proxy; - - zwp_primary_selection_device_v1_set_selection( - primary_selection_device, - zwp_primary_selection_source_v1, - serial - ); - - wl_display_roundtrip(display); - destroy_popup_surface(); -} - -#endif - static void do_offer(char *mime_type, struct source *source) { if (mime_type == NULL || mime_type_is_text(mime_type)) { /* Offer a few generic plain text formats */ @@ -159,8 +119,8 @@ static void do_offer(char *mime_type, struct source *source) { } static void init_selection(char *mime_type) { - if (use_wlr_data_control) { #ifdef HAVE_WLR_DATA_CONTROL + if (data_control_manager != NULL) { struct zwlr_data_control_source_v1 *data_control_source = zwlr_data_control_manager_v1_create_data_source( data_control_manager @@ -171,26 +131,30 @@ static void init_selection(char *mime_type) { do_offer(mime_type, source); - zwlr_data_control_device_v1_set_selection( - data_control_device, - data_control_source - ); -#endif - } else { - struct wl_data_source *data_source - = wl_data_device_manager_create_data_source( - data_device_manager + struct zwlr_data_control_device_v1 *data_control_device + = zwlr_data_control_manager_v1_get_data_device( + data_control_manager, + seat ); - - source->proxy = (struct wl_proxy *) data_source; - source_init_wl_data_source(source); - - do_offer(mime_type, source); - - action_on_popup_surface_getting_focus = set_data_selection; - action_on_no_keyboard = try_setting_data_selection_directly; - popup_tiny_invisible_surface(); + device->proxy = (struct wl_proxy *) data_control_device; + device_init_zwlr_data_control_device_v1(device); } +#endif + + struct wl_data_source *data_source + = wl_data_device_manager_create_data_source( + data_device_manager + ); + + source->proxy = (struct wl_proxy *) data_source; + source_init_wl_data_source(source); + + do_offer(mime_type, source); + + struct wl_data_device *data_device + = wl_data_device_manager_get_data_device(data_device_manager, seat); + device->proxy = (struct wl_proxy *) data_device; + device_init_wl_data_device(device); } static void init_primary_selection(char *mime_type) { @@ -208,16 +172,19 @@ static void init_primary_selection(char *mime_type) { do_offer(mime_type, source); - action_on_popup_surface_getting_focus = set_primary_selection; - action_on_no_keyboard = complain_about_missing_keyboard; - popup_tiny_invisible_surface(); - return; + struct zwp_primary_selection_device_v1 *primary_selection_device + = zwp_primary_selection_device_manager_v1_get_device( + primary_selection_device_manager, + seat + ); + device->proxy = (struct wl_proxy *) primary_selection_device; + device_init_zwp_primary_selection_device_v1(device); } #endif #ifdef HAVE_GTK_PRIMARY_SELECTION if (gtk_primary_selection_device_manager != NULL) { - struct gtk_primary_selection_source* gtk_primary_selection_source + struct gtk_primary_selection_source *gtk_primary_selection_source = gtk_primary_selection_device_manager_create_source( gtk_primary_selection_device_manager ); @@ -227,10 +194,13 @@ static void init_primary_selection(char *mime_type) { do_offer(mime_type, source); - action_on_popup_surface_getting_focus = set_gtk_primary_selection; - action_on_no_keyboard = complain_about_missing_keyboard; - popup_tiny_invisible_surface(); - return; + struct gtk_primary_selection_device *gtk_primary_selection_device + = gtk_primary_selection_device_manager_get_device( + gtk_primary_selection_device_manager, + seat + ); + device->proxy = (struct wl_proxy *) gtk_primary_selection_device; + device_init_gtk_primary_selection_device(device); } #endif } @@ -367,6 +337,8 @@ int main(int argc, char * const argv[]) { } } + device = calloc(1, sizeof(struct device)); + source = calloc(1, sizeof(struct source)); source->send_callback = send_callback; source->cancelled_callback = cancelled_callback; @@ -377,6 +349,21 @@ int main(int argc, char * const argv[]) { init_primary_selection(options.mime_type); } + /* See if we can just set the selection directly */ + if (!device->needs_popup_surface) { + /* If we can, it doesn't actually require + * a serial, so passing zero will do. + */ + device_set_selection(device, source, 0, options.primary); + } else { + /* If we cannot, schedule to do it later, + * when our popup surface gains keyboard focus. + */ + action_on_popup_surface_getting_focus = set_selection; + action_on_no_keyboard = complain_about_missing_keyboard; + popup_tiny_invisible_surface(); + } + if (options.clear) { wl_display_roundtrip(display); exit(0); diff --git a/src/wl-paste.c b/src/wl-paste.c index 7936b23..e79628f 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -18,6 +18,7 @@ #include "boilerplate.h" #include "types/offer.h" +#include "types/device.h" static struct { char *explicit_type; @@ -37,6 +38,8 @@ struct types { const char *any; }; +static struct device *device = NULL; + static struct types classify_offer_types(struct offer *offer) { struct types types = { 0 }; offer_for_each_mime_type(offer, mime_type) { @@ -149,7 +152,15 @@ static const char *mime_type_to_request(struct types types) { #undef try_any_text #undef try_any -static void do_paste(struct offer *offer) { +static void selection_callback(struct offer *offer, int primary) { + /* Ignore events we're not interested in */ + if (primary != options.primary) { + if (offer != NULL) { + offer_destroy(offer); + }; + return; + } + if (offer == NULL) { bail("No selection"); } @@ -205,134 +216,6 @@ static void do_paste(struct offer *offer) { exit(0); } -static void data_device_data_offer( - void *data, - struct wl_data_device *data_device, - struct wl_data_offer *data_offer -) { - struct offer *offer = calloc(1, sizeof(struct offer)); - offer->proxy = (struct wl_proxy *) data_offer; - offer_init_wl_data_offer(offer); -} - -static void data_device_selection( - void *data, - struct wl_data_device *data_device, - struct wl_data_offer *data_offer -) { - struct offer *offer = NULL; - if (data_offer != NULL) { - offer = (struct offer *) wl_data_offer_get_user_data(data_offer); - } - do_paste(offer); -} - -static const struct wl_data_device_listener data_device_listener = { - .data_offer = data_device_data_offer, - .selection = data_device_selection -}; - -#ifdef HAVE_GTK_PRIMARY_SELECTION - -static void gtk_primary_selection_device_data_offer( - void *data, - struct gtk_primary_selection_device *gtk_primary_selection_device, - struct gtk_primary_selection_offer *gtk_primary_selection_offer -) { - struct offer *offer = calloc(1, sizeof(struct offer)); - offer->proxy = (struct wl_proxy *) gtk_primary_selection_offer; - offer_init_gtk_primary_selection_offer(offer); -} - -static void gtk_primary_selection_device_selection( - void *data, - struct gtk_primary_selection_device *gtk_primary_selection_device, - struct gtk_primary_selection_offer *gtk_primary_selection_offer -) { - struct offer *offer = NULL; - if (gtk_primary_selection_offer != NULL) { - offer = (struct offer *) gtk_primary_selection_offer_get_user_data( - gtk_primary_selection_offer - ); - } - do_paste(offer); -} - -static const struct gtk_primary_selection_device_listener -gtk_primary_selection_device_listener = { - .data_offer = gtk_primary_selection_device_data_offer, - .selection = gtk_primary_selection_device_selection -}; - -#endif - -#ifdef HAVE_WP_PRIMARY_SELECTION - -static void primary_selection_device_data_offer( - void *data, - struct zwp_primary_selection_device_v1 *primary_selection_device, - struct zwp_primary_selection_offer_v1 *primary_selection_offer -) { - struct offer *offer = calloc(1, sizeof(struct offer)); - offer->proxy = (struct wl_proxy *) primary_selection_offer; - offer_init_zwp_primary_selection_offer_v1(offer); -} - -static void primary_selection_device_selection( - void *data, - struct zwp_primary_selection_device_v1 *primary_selection_device, - struct zwp_primary_selection_offer_v1 *primary_selection_offer -) { - struct offer *offer = NULL; - if (primary_selection_offer != NULL) { - offer = (struct offer *) zwp_primary_selection_offer_v1_get_user_data( - primary_selection_offer - ); - } - do_paste(offer); -} - -static const struct zwp_primary_selection_device_v1_listener -primary_selection_device_listener = { - .data_offer = primary_selection_device_data_offer, - .selection = primary_selection_device_selection -}; - -#endif - -#ifdef HAVE_WLR_DATA_CONTROL - -static void data_control_device_data_offer( - void *data, - struct zwlr_data_control_device_v1 *data_control_device, - struct zwlr_data_control_offer_v1 *data_control_offer -) { - struct offer *offer = calloc(1, sizeof(struct offer)); - offer->proxy = (struct wl_proxy *) data_control_offer; - offer_init_zwlr_data_control_offer_v1(offer); -} - -static void data_control_device_selection( - void *data, - struct zwlr_data_control_device_v1 *data_control_device, - struct zwlr_data_control_offer_v1 *data_control_offer -) { - struct offer *offer = NULL; - if (data_control_offer != NULL) { - offer = (struct offer *) zwlr_data_control_offer_v1_get_user_data( - data_control_offer - ); - } - do_paste(offer); -} - -static const struct zwlr_data_control_device_v1_listener -data_control_device_listener = { - .data_offer = data_control_device_data_offer, - .selection = data_control_device_selection -}; -#endif - static void print_usage(FILE *f, const char *argv0) { fprintf( f, @@ -357,43 +240,50 @@ static void print_usage(FILE *f, const char *argv0) { } static void init_selection() { - if (use_wlr_data_control) { #ifdef HAVE_WLR_DATA_CONTROL - zwlr_data_control_device_v1_add_listener( - data_control_device, - &data_control_device_listener, - NULL - ); -#endif - } else { - wl_data_device_add_listener(data_device, &data_device_listener, NULL); - popup_tiny_invisible_surface(); + if (data_control_manager != NULL) { + struct zwlr_data_control_device_v1 *data_control_device + = zwlr_data_control_manager_v1_get_data_device( + data_control_manager, + seat + ); + device->proxy = (struct wl_proxy *) data_control_device; + device_init_zwlr_data_control_device_v1(device); + return; } +#endif + + struct wl_data_device *data_device + = wl_data_device_manager_get_data_device(data_device_manager, seat); + device->proxy = (struct wl_proxy *) data_device; + device_init_wl_data_device(device); } static void init_primary_selection() { ensure_has_primary_selection(); #ifdef HAVE_WP_PRIMARY_SELECTION - if (primary_selection_device != NULL) { - zwp_primary_selection_device_v1_add_listener( - primary_selection_device, - &primary_selection_device_listener, - NULL - ); - popup_tiny_invisible_surface(); + if (primary_selection_device_manager != NULL) { + struct zwp_primary_selection_device_v1 *primary_selection_device + = zwp_primary_selection_device_manager_v1_get_device( + primary_selection_device_manager, + seat + ); + device->proxy = (struct wl_proxy *) primary_selection_device; + device_init_zwp_primary_selection_device_v1(device); return; } #endif #ifdef HAVE_GTK_PRIMARY_SELECTION - if (gtk_primary_selection_device != NULL) { - gtk_primary_selection_device_add_listener( - gtk_primary_selection_device, - >k_primary_selection_device_listener, - NULL - ); - popup_tiny_invisible_surface(); + if (gtk_primary_selection_device_manager != NULL) { + struct gtk_primary_selection_device *gtk_primary_selection_device + = gtk_primary_selection_device_manager_get_device( + gtk_primary_selection_device_manager, + seat + ); + device->proxy = (struct wl_proxy *) gtk_primary_selection_device; + device_init_gtk_primary_selection_device(device); return; } #endif @@ -472,12 +362,19 @@ int main(int argc, char * const argv[]) { init_wayland_globals(); + device = calloc(1, sizeof(struct device)); + device->selection_callback = selection_callback; + if (!options.primary) { init_selection(); } else { init_primary_selection(); } + if (device->needs_popup_surface) { + popup_tiny_invisible_surface(); + } + while (wl_display_dispatch(display) >= 0); perror("wl_display_dispatch"); From dc9f8d2b4936b0b564d9bc288bb4d15a5c32b99e Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 16:54:18 +0300 Subject: [PATCH 19/36] Move device manager handling to types/device-manager --- src/meson.build | 2 + src/types/device-manager.c | 137 +++++++++++++++++++++++++++++++++++++ src/types/device-manager.h | 70 +++++++++++++++++++ src/wl-copy.c | 105 +++++++++------------------- src/wl-paste.c | 57 +++++++-------- 5 files changed, 264 insertions(+), 107 deletions(-) create mode 100644 src/types/device-manager.c create mode 100644 src/types/device-manager.h diff --git a/src/meson.build b/src/meson.build index 8b591cd..2911726 100644 --- a/src/meson.build +++ b/src/meson.build @@ -106,6 +106,8 @@ lib = static_library( 'types/offer.c', 'types/device.h', 'types/device.c', + 'types/device-manager.h', + 'types/device-manager.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/device-manager.c b/src/types/device-manager.c new file mode 100644 index 0000000..dff3cb4 --- /dev/null +++ b/src/types/device-manager.c @@ -0,0 +1,137 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/device-manager.h" +#include "types/device.h" +#include "types/source.h" +#include "includes/selection-protocols.h" + +#include + + +struct source *device_manager_create_source(struct device_manager *self) { + return self->do_create_source(self); +} + +struct device *device_manager_get_device( + struct device_manager *self, + struct wl_seat *seat +) { + return self->do_get_device(self, seat); +} + +/* Macros to reduce implementation boilerplate */ + +#define CREATE_SOURCE(type, source_type, method_name) \ +static struct source *device_manager_ ## type ## _do_create_source( \ + struct device_manager *self \ +) { \ + struct type *proxy = (struct type *) self->proxy; \ + struct source *source = calloc(1, sizeof(struct source)); \ + source->proxy = (struct wl_proxy *) type ## _ ## method_name(proxy); \ + source_init_ ## source_type(source); \ + return source; \ +} + +#define GET_DEVICE(type, device_type, method_name) \ +static struct device *device_manager_ ## type ## _do_get_device( \ + struct device_manager *self, \ + struct wl_seat *seat \ +) { \ + struct type *proxy = (struct type *) self->proxy; \ + struct device *device = calloc(1, sizeof(struct device)); \ + device->proxy = (struct wl_proxy *) type ## _ ## method_name(proxy, seat); \ + device_init_ ## device_type(device); \ + return device; \ +} + +#define INIT(type) \ +void device_manager_init_ ## type(struct device_manager *self) { \ + self->do_create_source = device_manager_ ## type ## _do_create_source; \ + self->do_get_device = device_manager_ ## type ## _do_get_device; \ +} + + +/* Core Wayland implementation */ + +CREATE_SOURCE(wl_data_device_manager, wl_data_source, create_data_source) +GET_DEVICE(wl_data_device_manager, wl_data_device, get_data_device) +INIT(wl_data_device_manager) + + +/* gtk-primary-selection implementation */ + +#ifdef HAVE_GTK_PRIMARY_SELECTION + +CREATE_SOURCE( + gtk_primary_selection_device_manager, + gtk_primary_selection_source, + create_source +) + +GET_DEVICE( + gtk_primary_selection_device_manager, + gtk_primary_selection_device, + get_device +) + +INIT(gtk_primary_selection_device_manager) + +#endif /* HAVE_GTK_PRIMARY_SELECTION */ + + +/* wp-primary-selection implementation */ + +#ifdef HAVE_WP_PRIMARY_SELECTION + +CREATE_SOURCE( + zwp_primary_selection_device_manager_v1, + zwp_primary_selection_source_v1, + create_source +) + +GET_DEVICE( + zwp_primary_selection_device_manager_v1, + zwp_primary_selection_device_v1, + get_device +) + +INIT(zwp_primary_selection_device_manager_v1) + +#endif /* HAVE_WP_PRIMARY_SELECTION */ + + +/* wlr-data-control implementation */ + +#ifdef HAVE_WLR_DATA_CONTROL + +CREATE_SOURCE( + zwlr_data_control_manager_v1, + zwlr_data_control_source_v1, + create_data_source +) + +GET_DEVICE( + zwlr_data_control_manager_v1, + zwlr_data_control_device_v1, + get_data_device +) + +INIT(zwlr_data_control_manager_v1) + +#endif /* HAVE_WLR_DATA_CONTROL */ diff --git a/src/types/device-manager.h b/src/types/device-manager.h new file mode 100644 index 0000000..57e1270 --- /dev/null +++ b/src/types/device-manager.h @@ -0,0 +1,70 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_DEVICE_MANAGER_H +#define TYPES_DEVICE_MANAGER_H + +#include "includes/selection-protocols.h" + +#include + +struct device; +struct source; + +struct device_manager { + /* This field is initialized by the creator */ + struct wl_proxy *proxy; + + /* These fields are initialized by the implementation */ + struct source *(*do_create_source)(struct device_manager *self); + struct device *(*do_get_device)( + struct device_manager *self, + struct wl_seat *seat + ); +}; + +struct source *device_manager_create_source(struct device_manager *self); + +struct device *device_manager_get_device( + struct device_manager *self, + struct wl_seat *seat +); + +/* Initializers */ + +void device_manager_init_wl_data_device_manager(struct device_manager *self); + +#ifdef HAVE_GTK_PRIMARY_SELECTION +void device_manager_init_gtk_primary_selection_device_manager( + struct device_manager *self +); +#endif + +#ifdef HAVE_WP_PRIMARY_SELECTION +void device_manager_init_zwp_primary_selection_device_manager_v1( + struct device_manager *self +); +#endif + +#ifdef HAVE_WLR_DATA_CONTROL +void device_manager_init_zwlr_data_control_manager_v1( + struct device_manager *self +); +#endif + +#endif /* TYPES_DEVICE_MANAGER_H */ diff --git a/src/wl-copy.c b/src/wl-copy.c index f3900dc..c8801c7 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -19,6 +19,7 @@ #include "boilerplate.h" #include "types/source.h" #include "types/device.h" +#include "types/device-manager.h" static struct { int stay_in_foreground; @@ -32,6 +33,7 @@ static struct { static char * const *data_to_copy = NULL; static char *temp_file_to_copy = NULL; +static struct device_manager *device_manager = NULL; static struct device *device = NULL; static struct source *source = NULL; @@ -118,89 +120,41 @@ static void do_offer(char *mime_type, struct source *source) { free(mime_type); } -static void init_selection(char *mime_type) { +static void init_device_manager() { #ifdef HAVE_WLR_DATA_CONTROL if (data_control_manager != NULL) { - struct zwlr_data_control_source_v1 *data_control_source - = zwlr_data_control_manager_v1_create_data_source( - data_control_manager - ); - - source->proxy = (struct wl_proxy *) data_control_source; - source_init_zwlr_data_control_source_v1(source); - - do_offer(mime_type, source); - - struct zwlr_data_control_device_v1 *data_control_device - = zwlr_data_control_manager_v1_get_data_device( - data_control_manager, - seat - ); - device->proxy = (struct wl_proxy *) data_control_device; - device_init_zwlr_data_control_device_v1(device); + device_manager->proxy = (struct wl_proxy *) data_control_manager; + device_manager_init_zwlr_data_control_manager_v1(device_manager); + return; } #endif - struct wl_data_source *data_source - = wl_data_device_manager_create_data_source( - data_device_manager - ); - - source->proxy = (struct wl_proxy *) data_source; - source_init_wl_data_source(source); - - do_offer(mime_type, source); - - struct wl_data_device *data_device - = wl_data_device_manager_get_data_device(data_device_manager, seat); - device->proxy = (struct wl_proxy *) data_device; - device_init_wl_data_device(device); + device_manager->proxy = (struct wl_proxy *) data_device_manager; + device_manager_init_wl_data_device_manager(device_manager); } -static void init_primary_selection(char *mime_type) { +static void init_primary_device_manager() { ensure_has_primary_selection(); #ifdef HAVE_WP_PRIMARY_SELECTION if (primary_selection_device_manager != NULL) { - struct zwp_primary_selection_source_v1 *primary_selection_source - = zwp_primary_selection_device_manager_v1_create_source( - primary_selection_device_manager - ); - - source->proxy = (struct wl_proxy *) primary_selection_source; - source_init_zwp_primary_selection_source_v1(source); - - do_offer(mime_type, source); - - struct zwp_primary_selection_device_v1 *primary_selection_device - = zwp_primary_selection_device_manager_v1_get_device( - primary_selection_device_manager, - seat - ); - device->proxy = (struct wl_proxy *) primary_selection_device; - device_init_zwp_primary_selection_device_v1(device); + device_manager->proxy + = (struct wl_proxy *) primary_selection_device_manager; + device_manager_init_zwp_primary_selection_device_manager_v1( + device_manager + ); + return; } #endif #ifdef HAVE_GTK_PRIMARY_SELECTION if (gtk_primary_selection_device_manager != NULL) { - struct gtk_primary_selection_source *gtk_primary_selection_source - = gtk_primary_selection_device_manager_create_source( - gtk_primary_selection_device_manager - ); - - source->proxy = (struct wl_proxy *) gtk_primary_selection_source; - source_init_gtk_primary_selection_source(source); - - do_offer(mime_type, source); - - struct gtk_primary_selection_device *gtk_primary_selection_device - = gtk_primary_selection_device_manager_get_device( - gtk_primary_selection_device_manager, - seat - ); - device->proxy = (struct wl_proxy *) gtk_primary_selection_device; - device_init_gtk_primary_selection_device(device); + device_manager->proxy + = (struct wl_proxy *) gtk_primary_selection_device_manager; + device_manager_init_gtk_primary_selection_device_manager( + device_manager + ); + return; } #endif } @@ -337,17 +291,20 @@ int main(int argc, char * const argv[]) { } } - device = calloc(1, sizeof(struct device)); + device_manager = calloc(1, sizeof(struct device_manager)); + if (!options.primary) { + init_device_manager(); + } else { + init_primary_device_manager(); + } - source = calloc(1, sizeof(struct source)); + device = device_manager_get_device(device_manager, seat); + + source = device_manager_create_source(device_manager); source->send_callback = send_callback; source->cancelled_callback = cancelled_callback; - if (!options.primary) { - init_selection(options.mime_type); - } else { - init_primary_selection(options.mime_type); - } + do_offer(options.mime_type, source); /* See if we can just set the selection directly */ if (!device->needs_popup_surface) { diff --git a/src/wl-paste.c b/src/wl-paste.c index e79628f..f8a89c0 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -19,6 +19,7 @@ #include "boilerplate.h" #include "types/offer.h" #include "types/device.h" +#include "types/device-manager.h" static struct { char *explicit_type; @@ -38,7 +39,7 @@ struct types { const char *any; }; -static struct device *device = NULL; +static struct device_manager *device_manager = NULL; static struct types classify_offer_types(struct offer *offer) { struct types types = { 0 }; @@ -239,51 +240,40 @@ static void print_usage(FILE *f, const char *argv0) { ); } -static void init_selection() { +static void init_device_manager() { #ifdef HAVE_WLR_DATA_CONTROL if (data_control_manager != NULL) { - struct zwlr_data_control_device_v1 *data_control_device - = zwlr_data_control_manager_v1_get_data_device( - data_control_manager, - seat - ); - device->proxy = (struct wl_proxy *) data_control_device; - device_init_zwlr_data_control_device_v1(device); + device_manager->proxy = (struct wl_proxy *) data_control_manager; + device_manager_init_zwlr_data_control_manager_v1(device_manager); return; } #endif - struct wl_data_device *data_device - = wl_data_device_manager_get_data_device(data_device_manager, seat); - device->proxy = (struct wl_proxy *) data_device; - device_init_wl_data_device(device); + device_manager->proxy = (struct wl_proxy *) data_device_manager; + device_manager_init_wl_data_device_manager(device_manager); } -static void init_primary_selection() { +static void init_primary_device_manager() { ensure_has_primary_selection(); #ifdef HAVE_WP_PRIMARY_SELECTION if (primary_selection_device_manager != NULL) { - struct zwp_primary_selection_device_v1 *primary_selection_device - = zwp_primary_selection_device_manager_v1_get_device( - primary_selection_device_manager, - seat - ); - device->proxy = (struct wl_proxy *) primary_selection_device; - device_init_zwp_primary_selection_device_v1(device); + device_manager->proxy + = (struct wl_proxy *) primary_selection_device_manager; + device_manager_init_zwp_primary_selection_device_manager_v1( + device_manager + ); return; } #endif #ifdef HAVE_GTK_PRIMARY_SELECTION if (gtk_primary_selection_device_manager != NULL) { - struct gtk_primary_selection_device *gtk_primary_selection_device - = gtk_primary_selection_device_manager_get_device( - gtk_primary_selection_device_manager, - seat - ); - device->proxy = (struct wl_proxy *) gtk_primary_selection_device; - device_init_gtk_primary_selection_device(device); + device_manager->proxy + = (struct wl_proxy *) gtk_primary_selection_device_manager; + device_manager_init_gtk_primary_selection_device_manager( + device_manager + ); return; } #endif @@ -362,15 +352,16 @@ int main(int argc, char * const argv[]) { init_wayland_globals(); - device = calloc(1, sizeof(struct device)); - device->selection_callback = selection_callback; - + device_manager = calloc(1, sizeof(struct device_manager)); if (!options.primary) { - init_selection(); + init_device_manager(); } else { - init_primary_selection(); + init_primary_device_manager(); } + struct device *device = device_manager_get_device(device_manager, seat); + device->selection_callback = selection_callback; + if (device->needs_popup_surface) { popup_tiny_invisible_surface(); } From ebec7f6f581800703f0daa82fd4537004f5d5cb9 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 17:15:47 +0300 Subject: [PATCH 20/36] Move keyboard handling to types/keyboard --- src/boilerplate.c | 67 +++++------------------------------- src/meson.build | 2 ++ src/types/keyboard.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ src/types/keyboard.h | 34 ++++++++++++++++++ 4 files changed, 126 insertions(+), 59 deletions(-) create mode 100644 src/types/keyboard.c create mode 100644 src/types/keyboard.h diff --git a/src/boilerplate.c b/src/boilerplate.c index abbe81a..33bbf48 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -17,6 +17,7 @@ */ #include "boilerplate.h" +#include "types/keyboard.h" static void process_new_seat(struct wl_seat *new_seat); @@ -119,26 +120,8 @@ static const struct wl_registry_listener registry_listener = { .global_remove = registry_global_remove_handler }; -static void keyboard_keymap_handler -( - void *data, - struct wl_keyboard *keyboard, - uint32_t format, - int fd, - uint32_t size -) { - close(fd); -} - -static void keyboard_enter_handler -( - void *data, - struct wl_keyboard *keyboard, - uint32_t serial, - struct wl_surface *surface, - struct wl_array *keys -) { - struct wl_seat *this_seat = (struct wl_seat *) data; +static void forward_on_focus(struct keyboard *keyboard, uint32_t serial) { + struct wl_seat *this_seat = (struct wl_seat *) keyboard->data; /* When we get to here, global seat is already initialized */ if (this_seat != seat) { return; @@ -148,43 +131,6 @@ static void keyboard_enter_handler } } -static void keyboard_leave_handler -( - void *data, - struct wl_keyboard *keyboard, - uint32_t serial, - struct wl_surface *surface -) {} - -static void keyboard_key_handler -( - void *data, - struct wl_keyboard *keyboard, - uint32_t serial, - uint32_t time, - uint32_t key, - uint32_t state -) {} - -static void keyboard_modifiers_handler -( - void *data, - struct wl_keyboard *keyboard, - uint32_t serial, - uint32_t mods_depressed, - uint32_t mods_latched, - uint32_t mods_locked, - uint32_t group -) {} - -static const struct wl_keyboard_listener keayboard_listener = { - .keymap = keyboard_keymap_handler, - .enter = keyboard_enter_handler, - .leave = keyboard_leave_handler, - .key = keyboard_key_handler, - .modifiers = keyboard_modifiers_handler, -}; - static void seat_capabilities_handler ( void *data, @@ -196,8 +142,11 @@ static void seat_capabilities_handler wl_seat_set_user_data(this_seat, user_data); if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - struct wl_keyboard *keyboard = wl_seat_get_keyboard(this_seat); - wl_keyboard_add_listener(keyboard, &keayboard_listener, this_seat); + struct keyboard *keyboard = calloc(1, sizeof(struct keyboard)); + keyboard->proxy = wl_seat_get_keyboard(this_seat); + keyboard->data = (void *) this_seat; + keyboard->on_focus = forward_on_focus; + keyboard_init(keyboard); } } diff --git a/src/meson.build b/src/meson.build index 2911726..04f5ec2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -108,6 +108,8 @@ lib = static_library( 'types/device.c', 'types/device-manager.h', 'types/device-manager.c', + 'types/keyboard.h', + 'types/keyboard.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/keyboard.c b/src/types/keyboard.c new file mode 100644 index 0000000..99ca793 --- /dev/null +++ b/src/types/keyboard.c @@ -0,0 +1,82 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/keyboard.h" + +#include + +static void wl_keyboard_keymap_handler( + void *data, + struct wl_keyboard *keyboard, + uint32_t format, + int fd, + uint32_t size +) { + close(fd); +} + +static void wl_keyboard_enter_handler( + void *data, + struct wl_keyboard *keyboard, + uint32_t serial, + struct wl_surface *surface, + struct wl_array *keys +) { + struct keyboard *self = (struct keyboard *) data; + if (self->on_focus != NULL) { + self->on_focus(self, serial); + } +} + +static void wl_keyboard_leave_handler( + void *data, + struct wl_keyboard *keyboard, + uint32_t serial, + struct wl_surface *surface +) {} + +static void wl_keyboard_key_handler( + void *data, + struct wl_keyboard *keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state +) {} + +static void wl_keyboard_modifiers_handler( + void *data, + struct wl_keyboard *keyboard, + uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group +) {} + +static const struct wl_keyboard_listener wl_keyboard_listener = { + .keymap = wl_keyboard_keymap_handler, + .enter = wl_keyboard_enter_handler, + .leave = wl_keyboard_leave_handler, + .key = wl_keyboard_key_handler, + .modifiers = wl_keyboard_modifiers_handler, +}; + +void keyboard_init(struct keyboard *self) { + wl_keyboard_add_listener(self->proxy, &wl_keyboard_listener, self); +} diff --git a/src/types/keyboard.h b/src/types/keyboard.h new file mode 100644 index 0000000..e5f02d0 --- /dev/null +++ b/src/types/keyboard.h @@ -0,0 +1,34 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_KEYBOARD_H +#define TYPES_KEYBOARD_H + +#include +#include + +struct keyboard { + /* These fields are initialized by the creator */ + struct wl_keyboard *proxy; + void (*on_focus)(struct keyboard *self, uint32_t serial); + void *data; +}; + +void keyboard_init(struct keyboard *self); + +#endif /* TYPES_KEYBOARD_H */ From 20fa2f039a2c754bdb0cad32ba23a9c1cfac1bb9 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 17:39:02 +0300 Subject: [PATCH 21/36] Move shell surface handling to types/shell-surface --- src/boilerplate.c | 110 ++++++--------------------------- src/boilerplate.h | 3 - src/meson.build | 2 + src/types/shell-surface.c | 125 ++++++++++++++++++++++++++++++++++++++ src/types/shell-surface.h | 44 ++++++++++++++ 5 files changed, 190 insertions(+), 94 deletions(-) create mode 100644 src/types/shell-surface.c create mode 100644 src/types/shell-surface.h diff --git a/src/boilerplate.c b/src/boilerplate.c index 33bbf48..06f1249 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -18,6 +18,9 @@ #include "boilerplate.h" #include "types/keyboard.h" +#include "types/shell-surface.h" + +static struct shell_surface *shell_surface = NULL; static void process_new_seat(struct wl_seat *new_seat); @@ -202,71 +205,8 @@ int ensure_seat_has_keyboard() { #undef UNSET_CAPABILITIES -static void shell_surface_ping -( - void *data, - struct wl_shell_surface *shell_surface, - uint32_t serial -) { - wl_shell_surface_pong(shell_surface, serial); -} - -static void shell_surface_configure -( - void *data, - struct wl_shell_surface *shell_surface, - uint32_t edges, - int32_t width, - int32_t height -) {} - -static void shell_surface_popup_done -( - void *data, - struct wl_shell_surface *shell_surface -) {} - -static const struct wl_shell_surface_listener shell_surface_listener = { - .ping = shell_surface_ping, - .configure = shell_surface_configure, - .popup_done = shell_surface_popup_done -}; - #ifdef HAVE_XDG_SHELL -static void xdg_toplevel_configure_handler -( - void *data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states -) {} - -static void xdg_toplevel_close_handler -( - void *data, - struct xdg_toplevel *xdg_toplevel -) {} - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure_handler, - .close = xdg_toplevel_close_handler -}; - -static void xdg_surface_configure_handler -( - void *data, - struct xdg_surface *xdg_surface, - uint32_t serial -) { - xdg_surface_ack_configure(xdg_surface, serial); -} - -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_configure_handler -}; - static void xdg_wm_base_ping_handler ( void *data, @@ -353,35 +293,33 @@ void popup_tiny_invisible_surface() { */ wl_display_dispatch(display); + shell_surface = calloc(1, sizeof(struct shell_surface)); + surface = wl_compositor_create_surface(compositor); if (shell != NULL) { /* Use wl_shell */ - shell_surface = wl_shell_get_shell_surface(shell, surface); - wl_shell_surface_add_listener( - shell_surface, - &shell_surface_listener, - NULL - ); - wl_shell_surface_set_toplevel(shell_surface); - wl_shell_surface_set_title(shell_surface, "wl-clipboard"); + shell_surface->proxy + = (struct wl_proxy *) wl_shell_get_shell_surface(shell, surface); + shell_surface_init_wl_shell_surface(shell_surface); } else { #ifdef HAVE_XDG_SHELL /* Use xdg-shell */ xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); - xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); - xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); - xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); - xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); - xdg_toplevel_set_title(xdg_toplevel, "wl-clipboard"); - /* Signal that the surface is ready to be configured */ - wl_surface_commit(surface); - /* Wait for the surface to be configured */ - wl_display_roundtrip(display); + shell_surface->proxy + = (struct wl_proxy *) xdg_wm_base_get_xdg_surface( + xdg_wm_base, + surface + ); + shell_surface_init_xdg_surface(shell_surface); #else bail("Unreachable: HAVE_XDG_SHELL undefined and no wl_shell"); #endif } + /* Signal that the surface is ready to be configured */ + wl_surface_commit(surface); + /* Wait for the surface to be configured */ + wl_display_roundtrip(display); if (surface == NULL) { /* It's possible that we've been given focus without us @@ -416,19 +354,9 @@ void popup_tiny_invisible_surface() { void destroy_popup_surface() { if (shell_surface != NULL) { - wl_shell_surface_destroy(shell_surface); + shell_surface_destroy(shell_surface); shell_surface = NULL; } -#ifdef HAVE_XDG_SHELL - if (xdg_toplevel != NULL) { - xdg_toplevel_destroy(xdg_toplevel); - xdg_toplevel = NULL; - } - if (xdg_surface != NULL) { - xdg_surface_destroy(xdg_surface); - xdg_surface = NULL; - } -#endif if (surface != NULL) { wl_surface_destroy(surface); surface = NULL; diff --git a/src/boilerplate.h b/src/boilerplate.h index fb23c8a..2a3e9e8 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -47,12 +47,9 @@ struct wl_compositor *compositor; struct wl_shm *shm; struct wl_shell *shell; struct wl_surface *surface; -struct wl_shell_surface *shell_surface; #ifdef HAVE_XDG_SHELL struct xdg_wm_base *xdg_wm_base; -struct xdg_surface *xdg_surface; -struct xdg_toplevel *xdg_toplevel; #endif #ifdef HAVE_GTK_PRIMARY_SELECTION diff --git a/src/meson.build b/src/meson.build index 04f5ec2..fe842be 100644 --- a/src/meson.build +++ b/src/meson.build @@ -110,6 +110,8 @@ lib = static_library( 'types/device-manager.c', 'types/keyboard.h', 'types/keyboard.c', + 'types/shell-surface.h', + 'types/shell-surface.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/shell-surface.c b/src/types/shell-surface.c new file mode 100644 index 0000000..ba7e61b --- /dev/null +++ b/src/types/shell-surface.c @@ -0,0 +1,125 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/shell-surface.h" +#include "includes/shell-protocols.h" + +#include +#include + +void shell_surface_destroy(struct shell_surface *self) { + self->do_destroy(self); + free(self); +} + + +/* Core Wayland implementation */ + +static void wl_shell_surface_ping( + void *data, + struct wl_shell_surface *wl_shell_surface, + uint32_t serial +) { + wl_shell_surface_pong(wl_shell_surface, serial); +} + +static void wl_shell_surface_configure( + void *data, + struct wl_shell_surface *wl_shell_surface, + uint32_t edges, + int32_t width, + int32_t height +) {} + +static void wl_shell_surface_popup_done( + void *data, + struct wl_shell_surface *wl_shell_surface +) {} + +static const struct wl_shell_surface_listener wl_shell_surface_listener = { + .ping = wl_shell_surface_ping, + .configure = wl_shell_surface_configure, + .popup_done = wl_shell_surface_popup_done +}; + +static void destroy_wl_shell_surface(struct shell_surface *self) { + struct wl_shell_surface *proxy = (struct wl_shell_surface *) self->proxy; + wl_shell_surface_destroy(proxy); +} + +void shell_surface_init_wl_shell_surface(struct shell_surface *self) { + struct wl_shell_surface *proxy = (struct wl_shell_surface *) self->proxy; + wl_shell_surface_add_listener(proxy, &wl_shell_surface_listener, self); + wl_shell_surface_set_toplevel(proxy); + wl_shell_surface_set_title(proxy, "wl-clipboard"); + self->do_destroy = destroy_wl_shell_surface; +} + + +/* xdg-shell implementation */ + +#ifdef HAVE_XDG_SHELL + +static void xdg_toplevel_configure_handler( + void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states +) {} + +static void xdg_toplevel_close_handler( + void *data, + struct xdg_toplevel *xdg_toplevel +) {} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_configure_handler, + .close = xdg_toplevel_close_handler +}; + +static void xdg_surface_configure_handler( + void *data, + struct xdg_surface *xdg_surface, + uint32_t serial +) { + xdg_surface_ack_configure(xdg_surface, serial); +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_configure_handler +}; + +static void destroy_xdg_surface(struct shell_surface *self) { + struct xdg_toplevel *toplevel = (struct xdg_toplevel *) self->proxy2; + struct xdg_surface *proxy = (struct xdg_surface *) self->proxy; + xdg_toplevel_destroy(toplevel); + xdg_surface_destroy(proxy); +} + +void shell_surface_init_xdg_surface(struct shell_surface *self) { + struct xdg_surface *proxy = (struct xdg_surface *) self->proxy; + xdg_surface_add_listener(proxy, &xdg_surface_listener, self); + struct xdg_toplevel *toplevel = xdg_surface_get_toplevel(proxy); + self->proxy2 = (struct wl_proxy *) toplevel; + xdg_toplevel_add_listener(toplevel, &xdg_toplevel_listener, self); + xdg_toplevel_set_title(toplevel, "wl-clipboard"); + self->do_destroy = destroy_xdg_surface; +} + +#endif /* HAVE_XDG_SHELL */ diff --git a/src/types/shell-surface.h b/src/types/shell-surface.h new file mode 100644 index 0000000..d888553 --- /dev/null +++ b/src/types/shell-surface.h @@ -0,0 +1,44 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_SHELL_SURFACE_H +#define TYPES_SHELL_SURFACE_H + +#include "includes/shell-protocols.h" + +struct shell_surface { + /* This field is initialized by the creator */ + struct wl_proxy *proxy; + + /* These fields are initialized by the implementation */ + struct wl_proxy *proxy2; + void (*do_destroy)(struct shell_surface *self); +}; + +void shell_surface_destroy(struct shell_surface *self); + + +/* Initializers */ + +void shell_surface_init_wl_shell_surface(struct shell_surface *self); + +#ifdef HAVE_XDG_SHELL +void shell_surface_init_xdg_surface(struct shell_surface *self); +#endif + +#endif /* TYPES_SHELL_SURFACE_H */ From 235b8c993cc9b35077bd2dd433c07dee2469802f Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 17:53:29 +0300 Subject: [PATCH 22/36] Move shell handling to types/shell --- src/boilerplate.c | 59 ++++++++++-------------------- src/boilerplate.h | 2 +- src/meson.build | 2 + src/types/shell.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++ src/types/shell.h | 53 +++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 41 deletions(-) create mode 100644 src/types/shell.c create mode 100644 src/types/shell.h diff --git a/src/boilerplate.c b/src/boilerplate.c index 06f1249..57b0d43 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -19,8 +19,10 @@ #include "boilerplate.h" #include "types/keyboard.h" #include "types/shell-surface.h" +#include "types/shell.h" static struct shell_surface *shell_surface = NULL; +static struct shell *shell = NULL; static void process_new_seat(struct wl_seat *new_seat); @@ -62,7 +64,7 @@ static void registry_global_handler 1 ); } else if (strcmp(interface, "wl_shell") == 0) { - shell = wl_registry_bind( + wl_shell = wl_registry_bind( registry, name, &wl_shell_interface, @@ -205,23 +207,6 @@ int ensure_seat_has_keyboard() { #undef UNSET_CAPABILITIES -#ifdef HAVE_XDG_SHELL - -static void xdg_wm_base_ping_handler -( - void *data, - struct xdg_wm_base *xdg_wm_base, - uint32_t serial -) { - xdg_wm_base_pong(xdg_wm_base, serial); -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = xdg_wm_base_ping_handler -}; - -#endif - void init_wayland_globals() { display = wl_display_connect(NULL); if (display == NULL) { @@ -247,6 +232,21 @@ void init_wayland_globals() { bail("Missing a required global object"); } + shell = calloc(1, sizeof(struct shell)); + if (wl_shell != NULL) { + /* Use wl_shell */ + shell->proxy = (struct wl_proxy *) wl_shell; + shell_init_wl_shell(shell); + } else { +#ifdef HAVE_XDG_SHELL + /* Use xdg-shell */ + shell->proxy = (struct wl_proxy *) xdg_wm_base; + shell_init_xdg_shell(shell); +#else + bail("Unreachable: HAVE_XDG_SHELL undefined and no wl_shell"); +#endif + } + if (seat == NULL && requested_seat_name != NULL) { wl_display_roundtrip(display); } @@ -293,29 +293,8 @@ void popup_tiny_invisible_surface() { */ wl_display_dispatch(display); - shell_surface = calloc(1, sizeof(struct shell_surface)); - surface = wl_compositor_create_surface(compositor); - - if (shell != NULL) { - /* Use wl_shell */ - shell_surface->proxy - = (struct wl_proxy *) wl_shell_get_shell_surface(shell, surface); - shell_surface_init_wl_shell_surface(shell_surface); - } else { -#ifdef HAVE_XDG_SHELL - /* Use xdg-shell */ - xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); - shell_surface->proxy - = (struct wl_proxy *) xdg_wm_base_get_xdg_surface( - xdg_wm_base, - surface - ); - shell_surface_init_xdg_surface(shell_surface); -#else - bail("Unreachable: HAVE_XDG_SHELL undefined and no wl_shell"); -#endif - } + shell_surface = shell_create_shell_surface(shell, surface); /* Signal that the surface is ready to be configured */ wl_surface_commit(surface); /* Wait for the surface to be configured */ diff --git a/src/boilerplate.h b/src/boilerplate.h index 2a3e9e8..27651f4 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -45,7 +45,7 @@ struct wl_data_device_manager *data_device_manager; struct wl_seat *seat; struct wl_compositor *compositor; struct wl_shm *shm; -struct wl_shell *shell; +struct wl_shell *wl_shell; struct wl_surface *surface; #ifdef HAVE_XDG_SHELL diff --git a/src/meson.build b/src/meson.build index fe842be..8554d0a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -110,6 +110,8 @@ lib = static_library( 'types/device-manager.c', 'types/keyboard.h', 'types/keyboard.c', + 'types/shell.h', + 'types/shell.c', 'types/shell-surface.h', 'types/shell-surface.c', ], diff --git a/src/types/shell.c b/src/types/shell.c new file mode 100644 index 0000000..36d192a --- /dev/null +++ b/src/types/shell.c @@ -0,0 +1,93 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/shell-surface.h" +#include "types/shell.h" +#include "includes/shell-protocols.h" + +#include + +struct shell_surface *shell_create_shell_surface( + struct shell *self, + struct wl_surface *surface +) { + return self->do_create_shell_surface(self, surface); +} + + +/* Core Wayland implementation */ + +static struct shell_surface *wl_shell_create_shell_surface( + struct shell *self, + struct wl_surface *surface +) { + struct wl_shell *shell = (struct wl_shell *) self->proxy; + struct wl_shell_surface *wl_shell_surface = + wl_shell_get_shell_surface(shell, surface); + + struct shell_surface *shell_surface + = calloc(1, sizeof(struct shell_surface)); + shell_surface->proxy = (struct wl_proxy *) wl_shell_surface; + shell_surface_init_wl_shell_surface(shell_surface); + return shell_surface; +} + +void shell_init_wl_shell(struct shell *self) { + self->do_create_shell_surface = wl_shell_create_shell_surface; +} + + +/* xdg-shell implementation */ + +#ifdef HAVE_XDG_SHELL + +static struct shell_surface *xdg_shell_create_shell_surfacce( + struct shell *self, + struct wl_surface *surface +) { + struct xdg_wm_base *wm_base = (struct xdg_wm_base *) self->proxy; + + struct shell_surface *shell_surface + = calloc(1, sizeof(struct shell_surface)); + + struct xdg_surface *xdg_surface = + xdg_wm_base_get_xdg_surface(wm_base, surface); + shell_surface->proxy = (struct wl_proxy *) xdg_surface; + shell_surface_init_xdg_surface(shell_surface); + return shell_surface; +} + +static void xdg_wm_base_ping_handler( + void *data, + struct xdg_wm_base *wm_base, + uint32_t serial +) { + xdg_wm_base_pong(wm_base, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = xdg_wm_base_ping_handler +}; + +void shell_init_xdg_shell(struct shell *self) { + struct xdg_wm_base *wm_base = (struct xdg_wm_base *) self->proxy; + xdg_wm_base_add_listener(wm_base, &xdg_wm_base_listener, self); + self->do_create_shell_surface = xdg_shell_create_shell_surfacce; +} + +#endif /* HAVE_XDG_SHELL */ diff --git a/src/types/shell.h b/src/types/shell.h new file mode 100644 index 0000000..9b866eb --- /dev/null +++ b/src/types/shell.h @@ -0,0 +1,53 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_SHELL_H +#define TYPES_SHELL_H + +#include "includes/shell-protocols.h" + +#include + +struct shell_surface; + +struct shell { + /* This field is initialized by the creator */ + struct wl_proxy *proxy; + + /* This field is initialized by the implementation */ + struct shell_surface *(*do_create_shell_surface)( + struct shell *self, + struct wl_surface *surface + ); +}; + +struct shell_surface *shell_create_shell_surface( + struct shell *self, + struct wl_surface *surface +); + + +/* Initializers */ + +void shell_init_wl_shell(struct shell *self); + +#ifdef HAVE_XDG_SHELL +void shell_init_xdg_shell(struct shell *self); +#endif + +#endif /* TYPES_SHELL_H */ From a1afdc65e5ec644ee83db4d3b85522e0faedc842 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 18:33:15 +0300 Subject: [PATCH 23/36] Move popup surface handling to types/popup-surface --- src/boilerplate.c | 54 +++++----------------- src/boilerplate.h | 1 - src/meson.build | 2 + src/types/popup-surface.c | 95 +++++++++++++++++++++++++++++++++++++++ src/types/popup-surface.h | 43 ++++++++++++++++++ 5 files changed, 151 insertions(+), 44 deletions(-) create mode 100644 src/types/popup-surface.c create mode 100644 src/types/popup-surface.h diff --git a/src/boilerplate.c b/src/boilerplate.c index 57b0d43..7bf77f9 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -20,9 +20,10 @@ #include "types/keyboard.h" #include "types/shell-surface.h" #include "types/shell.h" +#include "types/popup-surface.h" -static struct shell_surface *shell_surface = NULL; static struct shell *shell = NULL; +static struct popup_surface *popup_surface = NULL; static void process_new_seat(struct wl_seat *new_seat); @@ -293,52 +294,19 @@ void popup_tiny_invisible_surface() { */ wl_display_dispatch(display); - surface = wl_compositor_create_surface(compositor); - shell_surface = shell_create_shell_surface(shell, surface); - /* Signal that the surface is ready to be configured */ - wl_surface_commit(surface); - /* Wait for the surface to be configured */ - wl_display_roundtrip(display); + popup_surface = calloc(1, sizeof(struct popup_surface)); + popup_surface->wl_display = display; + popup_surface->wl_compositor = compositor; + popup_surface->wl_shm = shm; + popup_surface->shell = shell; - if (surface == NULL) { - /* It's possible that we've been given focus without us - * ever commiting a buffer, in which case the handlers - * may have already destroyed the surface; there's no - * way or need for us to commit a buffer in that case. - */ - return; - } - - int width = 1; - int height = 1; - int stride = width * 4; - int size = stride * height; // bytes - - /* Open an anonymous file and write some zero bytes to it */ - int fd = create_anonymous_file(); - ftruncate(fd, size); - - /* Turn it into a shared memory pool */ - struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); - - /* Allocate the buffer in that pool */ - struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, - 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); - /* Zeros in ARGB8888 mean fully transparent */ - - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, width, height); - wl_surface_commit(surface); + popup_surface_init(popup_surface); } void destroy_popup_surface() { - if (shell_surface != NULL) { - shell_surface_destroy(shell_surface); - shell_surface = NULL; - } - if (surface != NULL) { - wl_surface_destroy(surface); - surface = NULL; + if (popup_surface != NULL) { + popup_surface_destroy(popup_surface); + popup_surface = NULL; } } diff --git a/src/boilerplate.h b/src/boilerplate.h index 27651f4..c36afa6 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -46,7 +46,6 @@ struct wl_seat *seat; struct wl_compositor *compositor; struct wl_shm *shm; struct wl_shell *wl_shell; -struct wl_surface *surface; #ifdef HAVE_XDG_SHELL struct xdg_wm_base *xdg_wm_base; diff --git a/src/meson.build b/src/meson.build index 8554d0a..55bc901 100644 --- a/src/meson.build +++ b/src/meson.build @@ -114,6 +114,8 @@ lib = static_library( 'types/shell.c', 'types/shell-surface.h', 'types/shell-surface.c', + 'types/popup-surface.h', + 'types/popup-surface.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/popup-surface.c b/src/types/popup-surface.c new file mode 100644 index 0000000..d599077 --- /dev/null +++ b/src/types/popup-surface.c @@ -0,0 +1,95 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/popup-surface.h" +#include "types/shell.h" +#include "types/shell-surface.h" +#include "util/files.h" + +#include +#include +#include + +void popup_surface_init(struct popup_surface *self) { + self->wl_surface = wl_compositor_create_surface(self->wl_compositor); + self->shell_surface = shell_create_shell_surface( + self->shell, + self->wl_surface + ); + + /* Signal that the surface is ready to be configured */ + wl_surface_commit(self->wl_surface); + wl_display_roundtrip(self->wl_display); + + if (self->wl_surface == NULL) { + /* It's possible that we were given focus + * (without ever commiting a buffer) during + * the above roundtrip, in which case the + * handlers may have already destroyed the + * surface. No need to do anything further in + * that case. + */ + free(self); + return; + } + + /* Remember that after this point, we should + * free() self when it gets destroyed. + */ + self->should_free_self = 1; + + int width = 1; + int height = 1; + int stride = width * 4; + int size = stride * height; + + /* Open an anonymous file and write some zero bytes to it */ + int fd = create_anonymous_file(); + ftruncate(fd, size); + + /* Create a shared memory pool */ + struct wl_shm *wl_shm = self->wl_shm; + struct wl_shm_pool *wl_shm_pool = wl_shm_create_pool(wl_shm, fd, size); + + /* Allocate the buffer in that pool */ + struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer( + wl_shm_pool, + 0, + width, + height, + stride, + WL_SHM_FORMAT_ARGB8888 + ); + /* We're using ARGB, so zero bytes mean + * a fully transparent pixel, which happens + * to be exactly what we want. + */ + + wl_surface_attach(self->wl_surface, wl_buffer, 0, 0); + wl_surface_damage(self->wl_surface, 0, 0, width, height); + wl_surface_commit(self->wl_surface); +} + +void popup_surface_destroy(struct popup_surface *self) { + shell_surface_destroy(self->shell_surface); + wl_surface_destroy(self->wl_surface); + + if (self->should_free_self) { + free(self); + } +} diff --git a/src/types/popup-surface.h b/src/types/popup-surface.h new file mode 100644 index 0000000..9193d73 --- /dev/null +++ b/src/types/popup-surface.h @@ -0,0 +1,43 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_POPUP_SURFACE_H +#define TYPES_POPUP_SURFACE_H + +#include + +struct shell; +struct shell_surface; + +struct popup_surface { + /* These fields are initialized by the creator */ + struct wl_display *wl_display; + struct wl_compositor *wl_compositor; + struct wl_shm *wl_shm; + struct shell *shell; + + /* These fields are initialized by the implementation */ + struct shell_surface *shell_surface; + struct wl_surface *wl_surface; + int should_free_self; +}; + +void popup_surface_init(struct popup_surface *self); +void popup_surface_destroy(struct popup_surface *self); + +#endif /* TYPES_POPUP_SURFACE_H */ From 18bd9a195ca0aab21340b41e7675c4fe96c988df Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 13:01:12 +0300 Subject: [PATCH 24/36] Move registry and globals handling to types/registry --- src/boilerplate.c | 162 ++----------------------------------- src/boilerplate.h | 27 +------ src/meson.build | 2 + src/types/popup-surface.c | 21 ++++- src/types/popup-surface.h | 9 +-- src/types/registry.c | 163 ++++++++++++++++++++++++++++++++++++++ src/types/registry.h | 73 +++++++++++++++++ src/util/misc.c | 13 +++ src/util/misc.h | 2 + src/wl-copy.c | 60 +++----------- src/wl-paste.c | 57 +++---------- 11 files changed, 306 insertions(+), 283 deletions(-) create mode 100644 src/types/registry.c create mode 100644 src/types/registry.h diff --git a/src/boilerplate.c b/src/boilerplate.c index 7bf77f9..ece50be 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -21,111 +21,10 @@ #include "types/shell-surface.h" #include "types/shell.h" #include "types/popup-surface.h" +#include "types/registry.h" -static struct shell *shell = NULL; static struct popup_surface *popup_surface = NULL; -static void process_new_seat(struct wl_seat *new_seat); - -static void registry_global_handler -( - void *data, - struct wl_registry *registry, - uint32_t name, - const char *interface, - uint32_t version -) { - if (strcmp(interface, "wl_data_device_manager") == 0) { - data_device_manager = wl_registry_bind( - registry, - name, - &wl_data_device_manager_interface, - 1 - ); - } else if (strcmp(interface, "wl_seat") == 0) { - struct wl_seat *new_seat = wl_registry_bind( - registry, - name, - &wl_seat_interface, - 2 - ); - process_new_seat(new_seat); - } else if (strcmp(interface, "wl_compositor") == 0) { - compositor = wl_registry_bind( - registry, - name, - &wl_compositor_interface, - 3 - ); - } else if (strcmp(interface, "wl_shm") == 0) { - shm = wl_registry_bind( - registry, - name, - &wl_shm_interface, - 1 - ); - } else if (strcmp(interface, "wl_shell") == 0) { - wl_shell = wl_registry_bind( - registry, - name, - &wl_shell_interface, - 1 - ); - } -#ifdef HAVE_XDG_SHELL - else if (strcmp(interface, "xdg_wm_base") == 0) { - xdg_wm_base = wl_registry_bind( - registry, - name, - &xdg_wm_base_interface, - 1 - ); - } -#endif -#ifdef HAVE_GTK_PRIMARY_SELECTION - else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) { - gtk_primary_selection_device_manager = wl_registry_bind( - registry, - name, - >k_primary_selection_device_manager_interface, - 1 - ); - } -#endif -#ifdef HAVE_WP_PRIMARY_SELECTION - else if (strcmp(interface, "zwp_primary_selection_device_manager_v1") == 0) { - primary_selection_device_manager = wl_registry_bind( - registry, - name, - &zwp_primary_selection_device_manager_v1_interface, - 1 - ); - } -#endif -#ifdef HAVE_WLR_DATA_CONTROL - else if (strcmp(interface, "zwlr_data_control_manager_v1") == 0) { - data_control_manager = wl_registry_bind( - registry, - name, - &zwlr_data_control_manager_v1_interface, - 1 - ); - } -#endif -} - -static void registry_global_remove_handler -( - void *data, - struct wl_registry *registry, - uint32_t name -) {} - -static const struct wl_registry_listener registry_listener = { - .global = registry_global_handler, - .global_remove = registry_global_remove_handler -}; - static void forward_on_focus(struct keyboard *keyboard, uint32_t serial) { struct wl_seat *this_seat = (struct wl_seat *) keyboard->data; /* When we get to here, global seat is already initialized */ @@ -177,7 +76,7 @@ static const struct wl_seat_listener seat_listener = { #define UNSET_CAPABILITIES ((void *) (uint32_t) 35) -static void process_new_seat(struct wl_seat *new_seat) { +void process_new_seat(struct wl_seat *new_seat) { if (seat != NULL) { wl_seat_destroy(new_seat); return; @@ -214,40 +113,13 @@ void init_wayland_globals() { bail("Failed to connect to a Wayland server"); } - struct wl_registry *registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, NULL); + registry = calloc(1, sizeof(struct registry)); + registry->wl_display = display; + registry_init(registry); /* Wait for the "initial" set of globals to appear */ wl_display_roundtrip(display); - if ( - data_device_manager == NULL || - compositor == NULL || - shm == NULL || - (shell == NULL -#ifdef HAVE_XDG_SHELL - && xdg_wm_base == NULL -#endif - ) - ) { - bail("Missing a required global object"); - } - - shell = calloc(1, sizeof(struct shell)); - if (wl_shell != NULL) { - /* Use wl_shell */ - shell->proxy = (struct wl_proxy *) wl_shell; - shell_init_wl_shell(shell); - } else { -#ifdef HAVE_XDG_SHELL - /* Use xdg-shell */ - shell->proxy = (struct wl_proxy *) xdg_wm_base; - shell_init_xdg_shell(shell); -#else - bail("Unreachable: HAVE_XDG_SHELL undefined and no wl_shell"); -#endif - } - if (seat == NULL && requested_seat_name != NULL) { wl_display_roundtrip(display); } @@ -259,25 +131,6 @@ void init_wayland_globals() { } } -void ensure_has_primary_selection() { -#ifdef HAVE_GTK_PRIMARY_SELECTION - if (gtk_primary_selection_device_manager != NULL) { - return; - } -#endif -#ifdef HAVE_WP_PRIMARY_SELECTION - if (primary_selection_device_manager != NULL) { - return; - } -#endif - -#if defined(HAVE_GTK_PRIMARY_SELECTION) || defined(HAVE_WP_PRIMARY_SELECTION) - bail("Primary selection is not supported on this compositor"); -#else - bail("wl-clipboard was built without primary selection support"); -#endif -} - void popup_tiny_invisible_surface() { /* HACK: * Pop up a tiny invisible surface to get the keyboard focus, @@ -295,10 +148,7 @@ void popup_tiny_invisible_surface() { wl_display_dispatch(display); popup_surface = calloc(1, sizeof(struct popup_surface)); - popup_surface->wl_display = display; - popup_surface->wl_compositor = compositor; - popup_surface->wl_shm = shm; - popup_surface->shell = shell; + popup_surface->registry = registry; popup_surface_init(popup_surface); } diff --git a/src/boilerplate.h b/src/boilerplate.h index c36afa6..a7928cc 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -40,29 +40,12 @@ #include "includes/shell-protocols.h" #include "includes/selection-protocols.h" +#include "types/registry.h" + struct wl_display *display; -struct wl_data_device_manager *data_device_manager; +struct registry *registry; + struct wl_seat *seat; -struct wl_compositor *compositor; -struct wl_shm *shm; -struct wl_shell *wl_shell; - -#ifdef HAVE_XDG_SHELL -struct xdg_wm_base *xdg_wm_base; -#endif - -#ifdef HAVE_GTK_PRIMARY_SELECTION -struct gtk_primary_selection_device_manager *gtk_primary_selection_device_manager; -#endif - -#ifdef HAVE_WP_PRIMARY_SELECTION -struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; -#endif - -#ifdef HAVE_WLR_DATA_CONTROL -struct zwlr_data_control_manager_v1 *data_control_manager; -#endif - const char *requested_seat_name; void init_wayland_globals(void); @@ -73,6 +56,4 @@ void destroy_popup_surface(void); void (*action_on_popup_surface_getting_focus)(uint32_t serial); void (*action_on_no_keyboard)(void); -void ensure_has_primary_selection(void); - uint32_t get_serial(void); diff --git a/src/meson.build b/src/meson.build index 55bc901..ce3d2b4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -116,6 +116,8 @@ lib = static_library( 'types/shell-surface.c', 'types/popup-surface.h', 'types/popup-surface.c', + 'types/registry.h', + 'types/registry.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/popup-surface.c b/src/types/popup-surface.c index d599077..fe2a59f 100644 --- a/src/types/popup-surface.c +++ b/src/types/popup-surface.c @@ -17,16 +17,27 @@ */ #include "types/popup-surface.h" +#include "types/registry.h" #include "types/shell.h" #include "types/shell-surface.h" #include "util/files.h" +#include "util/misc.h" #include #include #include void popup_surface_init(struct popup_surface *self) { - self->wl_surface = wl_compositor_create_surface(self->wl_compositor); + self->shell = registry_find_shell(self->registry); + if (self->shell == NULL) { + bail("Missing a shell"); + } + + struct wl_compositor *wl_compositor = self->registry->wl_compositor; + if (wl_compositor == NULL) { + bail("Missing the compositor"); + } + self->wl_surface = wl_compositor_create_surface(wl_compositor); self->shell_surface = shell_create_shell_surface( self->shell, self->wl_surface @@ -34,7 +45,7 @@ void popup_surface_init(struct popup_surface *self) { /* Signal that the surface is ready to be configured */ wl_surface_commit(self->wl_surface); - wl_display_roundtrip(self->wl_display); + wl_display_roundtrip(self->registry->wl_display); if (self->wl_surface == NULL) { /* It's possible that we were given focus @@ -63,7 +74,10 @@ void popup_surface_init(struct popup_surface *self) { ftruncate(fd, size); /* Create a shared memory pool */ - struct wl_shm *wl_shm = self->wl_shm; + struct wl_shm *wl_shm = self->registry->wl_shm; + if (wl_shm == NULL) { + bail("Missing the shm"); + } struct wl_shm_pool *wl_shm_pool = wl_shm_create_pool(wl_shm, fd, size); /* Allocate the buffer in that pool */ @@ -88,6 +102,7 @@ void popup_surface_init(struct popup_surface *self) { void popup_surface_destroy(struct popup_surface *self) { shell_surface_destroy(self->shell_surface); wl_surface_destroy(self->wl_surface); + free(self->shell); if (self->should_free_self) { free(self); diff --git a/src/types/popup-surface.h b/src/types/popup-surface.h index 9193d73..bb54f21 100644 --- a/src/types/popup-surface.h +++ b/src/types/popup-surface.h @@ -21,17 +21,16 @@ #include +struct registry; struct shell; struct shell_surface; struct popup_surface { - /* These fields are initialized by the creator */ - struct wl_display *wl_display; - struct wl_compositor *wl_compositor; - struct wl_shm *wl_shm; - struct shell *shell; + /* This field is initialized by the creator */ + struct registry *registry; /* These fields are initialized by the implementation */ + struct shell *shell; struct shell_surface *shell_surface; struct wl_surface *wl_surface; int should_free_self; diff --git a/src/types/registry.c b/src/types/registry.c new file mode 100644 index 0000000..101dbad --- /dev/null +++ b/src/types/registry.c @@ -0,0 +1,163 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/registry.h" +#include "types/shell.h" +#include "types/device-manager.h" +#include "includes/shell-protocols.h" +#include "includes/selection-protocols.h" +#include "util/misc.h" + +#include +#include + +void process_new_seat(struct wl_seat *new_seat); + +#define BIND(interface_name, known_version) \ +if (strcmp(interface, #interface_name) == 0) { \ + self->interface_name = wl_registry_bind( \ + wl_registry, \ + name, \ + &interface_name ## _interface, \ + known_version \ + ); \ +} + +static void wl_registry_global_handler( + void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t version +) { + struct registry *self = (struct registry *) data; + + BIND(wl_compositor, 2) + BIND(wl_shm, 1) + + /* Shells */ + + BIND(wl_shell, 1) + +#ifdef HAVE_XDG_SHELL + BIND(xdg_wm_base, 1) +#endif + + /* Device managers */ + + BIND(wl_data_device_manager, 1) + +#ifdef HAVE_GTK_PRIMARY_SELECTION + BIND(gtk_primary_selection_device_manager, 1) +#endif + +#ifdef HAVE_WP_PRIMARY_SELECTION + BIND(zwp_primary_selection_device_manager_v1, 1) +#endif + +#ifdef HAVE_WLR_DATA_CONTROL + BIND(zwlr_data_control_manager_v1, 1) +#endif + + if (strcmp(interface, "wl_seat") == 0) { + struct wl_seat *seat = wl_registry_bind( + wl_registry, + name, + &wl_seat_interface, + 2 + ); + process_new_seat(seat); + } +} + +static void wl_registry_global_remove_handler( + void *data, + struct wl_registry *wl_registry, + uint32_t name +) {} + +static const struct wl_registry_listener wl_registry_listener = { + .global = wl_registry_global_handler, + .global_remove = wl_registry_global_remove_handler +}; + +void registry_init(struct registry *self) { + self->proxy = wl_display_get_registry(self->wl_display); + wl_registry_add_listener(self->proxy, &wl_registry_listener, self); +} + +struct shell *registry_find_shell(struct registry *self) { + struct shell *shell = calloc(1, sizeof(struct shell)); + + if (self->wl_shell != NULL) { + shell->proxy = (struct wl_proxy *) self->wl_shell; + shell_init_wl_shell(shell); + return shell; + } + +#ifdef HAVE_XDG_SHELL + if (self->xdg_wm_base != NULL) { + shell->proxy = (struct wl_proxy *) self->xdg_wm_base; + shell_init_xdg_shell(shell); + return shell; + } +#endif + + free(shell); + return NULL; +} + +#define TRY(type) \ +if (self->type != NULL) { \ + device_manager->proxy = (struct wl_proxy *) self->type; \ + device_manager_init_ ## type(device_manager); \ + return device_manager; \ +} + +struct device_manager *registry_find_device_manager( + struct registry *self, + int primary +) { + struct device_manager *device_manager + = calloc(1, sizeof(struct device_manager)); + + /* We prefer wlr-data-control, as it doesn't require + * us to use the popup surface hack. + */ + + if (!primary) { +#ifdef HAVE_WLR_DATA_CONTROL + TRY(zwlr_data_control_manager_v1) +#endif + TRY(wl_data_device_manager) + + free(device_manager); + return NULL; + } + +#ifdef HAVE_WP_PRIMARY_SELECTION + TRY(zwp_primary_selection_device_manager_v1) +#endif + +#ifdef HAVE_GTK_PRIMARY_SELECTION + TRY(gtk_primary_selection_device_manager) +#endif + + free(device_manager); + return NULL; +} diff --git a/src/types/registry.h b/src/types/registry.h new file mode 100644 index 0000000..a131d14 --- /dev/null +++ b/src/types/registry.h @@ -0,0 +1,73 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_REGISTRY_H +#define TYPES_REGISTRY_H + +#include "includes/shell-protocols.h" +#include "includes/selection-protocols.h" + +#include + +struct shell; +struct device_manager; + +struct registry { + /* This field is initialized by the creator */ + struct wl_display *wl_display; + + /* These fields are initialized by the implementation */ + + struct wl_registry *proxy; + struct wl_compositor *wl_compositor; + struct wl_shm *wl_shm; + + /* Shells */ + + struct wl_shell *wl_shell; +#ifdef HAVE_XDG_SHELL + struct xdg_wm_base *xdg_wm_base; +#endif + + /* Device managers */ + + struct wl_data_device_manager *wl_data_device_manager; +#ifdef HAVE_GTK_PRIMARY_SELECTION + struct gtk_primary_selection_device_manager + *gtk_primary_selection_device_manager; +#endif +#ifdef HAVE_WP_PRIMARY_SELECTION + struct zwp_primary_selection_device_manager_v1 + *zwp_primary_selection_device_manager_v1; +#endif +#ifdef HAVE_WLR_DATA_CONTROL + struct zwlr_data_control_manager_v1 + *zwlr_data_control_manager_v1; +#endif +}; + +void registry_init(struct registry *self); + +struct shell *registry_find_shell(struct registry *self); + +struct device_manager *registry_find_device_manager( + struct registry *self, + int primary +); + +#endif /* TYPES_REGISTRY_H */ diff --git a/src/util/misc.c b/src/util/misc.c index 934792e..f252a62 100644 --- a/src/util/misc.c +++ b/src/util/misc.c @@ -31,3 +31,16 @@ void print_version_info() { "There is NO WARRANTY, to the extent permitted by law.\n" ); } + +void complain_about_selection_support(int primary) { + if (!primary) { + /* We always expect to find at least wl_data_device_manager */ + bail("Missing a required global object"); + } + +#if !defined(HAVE_WP_PRIMARY_SELECTION) && !defined(HAVE_GTK_PRIMARY_SELECTION) + bail("wl-clipboard was built without primary selection support"); +#endif + + bail("The compositor does not seem to support primary selection"); +} diff --git a/src/util/misc.h b/src/util/misc.h index 54890b9..be5298f 100644 --- a/src/util/misc.h +++ b/src/util/misc.h @@ -26,4 +26,6 @@ void print_version_info(void); +void complain_about_selection_support(int primary); + #endif /* UTIL_MISC_H */ diff --git a/src/wl-copy.c b/src/wl-copy.c index c8801c7..22f5abc 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -20,6 +20,7 @@ #include "types/source.h" #include "types/device.h" #include "types/device-manager.h" +#include "types/registry.h" static struct { int stay_in_foreground; @@ -33,7 +34,6 @@ static struct { static char * const *data_to_copy = NULL; static char *temp_file_to_copy = NULL; -static struct device_manager *device_manager = NULL; static struct device *device = NULL; static struct source *source = NULL; @@ -120,45 +120,6 @@ static void do_offer(char *mime_type, struct source *source) { free(mime_type); } -static void init_device_manager() { -#ifdef HAVE_WLR_DATA_CONTROL - if (data_control_manager != NULL) { - device_manager->proxy = (struct wl_proxy *) data_control_manager; - device_manager_init_zwlr_data_control_manager_v1(device_manager); - return; - } -#endif - - device_manager->proxy = (struct wl_proxy *) data_device_manager; - device_manager_init_wl_data_device_manager(device_manager); -} - -static void init_primary_device_manager() { - ensure_has_primary_selection(); - -#ifdef HAVE_WP_PRIMARY_SELECTION - if (primary_selection_device_manager != NULL) { - device_manager->proxy - = (struct wl_proxy *) primary_selection_device_manager; - device_manager_init_zwp_primary_selection_device_manager_v1( - device_manager - ); - return; - } -#endif - -#ifdef HAVE_GTK_PRIMARY_SELECTION - if (gtk_primary_selection_device_manager != NULL) { - device_manager->proxy - = (struct wl_proxy *) gtk_primary_selection_device_manager; - device_manager_init_gtk_primary_selection_device_manager( - device_manager - ); - return; - } -#endif -} - static void print_usage(FILE *f, const char *argv0) { fprintf( f, @@ -254,10 +215,6 @@ int main(int argc, char * const argv[]) { init_wayland_globals(); - if (options.primary) { - ensure_has_primary_selection(); - } - if (!options.clear) { if (optind < argc) { /* Copy our command-line arguments */ @@ -291,15 +248,20 @@ int main(int argc, char * const argv[]) { } } - device_manager = calloc(1, sizeof(struct device_manager)); - if (!options.primary) { - init_device_manager(); - } else { - init_primary_device_manager(); + /* Create the device */ + struct device_manager *device_manager + = registry_find_device_manager(registry, options.primary); + if (device_manager == NULL) { + complain_about_selection_support(options.primary); } device = device_manager_get_device(device_manager, seat); + if (!device_supports_selection(device, options.primary)) { + complain_about_selection_support(options.primary); + } + + /* Create and initialize the source */ source = device_manager_create_source(device_manager); source->send_callback = send_callback; source->cancelled_callback = cancelled_callback; diff --git a/src/wl-paste.c b/src/wl-paste.c index f8a89c0..c4c8f57 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -20,6 +20,7 @@ #include "types/offer.h" #include "types/device.h" #include "types/device-manager.h" +#include "types/registry.h" static struct { char *explicit_type; @@ -39,8 +40,6 @@ struct types { const char *any; }; -static struct device_manager *device_manager = NULL; - static struct types classify_offer_types(struct offer *offer) { struct types types = { 0 }; offer_for_each_mime_type(offer, mime_type) { @@ -240,45 +239,6 @@ static void print_usage(FILE *f, const char *argv0) { ); } -static void init_device_manager() { -#ifdef HAVE_WLR_DATA_CONTROL - if (data_control_manager != NULL) { - device_manager->proxy = (struct wl_proxy *) data_control_manager; - device_manager_init_zwlr_data_control_manager_v1(device_manager); - return; - } -#endif - - device_manager->proxy = (struct wl_proxy *) data_device_manager; - device_manager_init_wl_data_device_manager(device_manager); -} - -static void init_primary_device_manager() { - ensure_has_primary_selection(); - -#ifdef HAVE_WP_PRIMARY_SELECTION - if (primary_selection_device_manager != NULL) { - device_manager->proxy - = (struct wl_proxy *) primary_selection_device_manager; - device_manager_init_zwp_primary_selection_device_manager_v1( - device_manager - ); - return; - } -#endif - -#ifdef HAVE_GTK_PRIMARY_SELECTION - if (gtk_primary_selection_device_manager != NULL) { - device_manager->proxy - = (struct wl_proxy *) gtk_primary_selection_device_manager; - device_manager_init_gtk_primary_selection_device_manager( - device_manager - ); - return; - } -#endif -} - static void parse_options(int argc, char * const argv[]) { if (argc < 1) { bail("Empty argv"); @@ -343,7 +303,6 @@ static void parse_options(int argc, char * const argv[]) { int main(int argc, char * const argv[]) { parse_options(argc, argv); - char *path = path_for_fd(STDOUT_FILENO); if (path != NULL && options.explicit_type == NULL) { options.inferred_type = infer_mime_type_from_name(path); @@ -352,14 +311,18 @@ int main(int argc, char * const argv[]) { init_wayland_globals(); - device_manager = calloc(1, sizeof(struct device_manager)); - if (!options.primary) { - init_device_manager(); - } else { - init_primary_device_manager(); + /* Create the device */ + struct device_manager *device_manager + = registry_find_device_manager(registry, options.primary); + if (device_manager == NULL) { + complain_about_selection_support(options.primary); } struct device *device = device_manager_get_device(device_manager, seat); + + if (!device_supports_selection(device, options.primary)) { + complain_about_selection_support(options.primary); + } device->selection_callback = selection_callback; if (device->needs_popup_surface) { From 6d20b63147a3327f15b828e50b9d9ca5aa591145 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 13:32:57 +0300 Subject: [PATCH 25/36] Move seat handling to types/seat This type is now responsible for keeping track of the seat's name and capabilities. An array of available seats is stored by the registry, which allows you to find a desired seat by its name. --- src/boilerplate.c | 94 ++++---------------------------------- src/boilerplate.h | 3 +- src/meson.build | 2 + src/types/device-manager.c | 6 ++- src/types/device-manager.h | 5 +- src/types/registry.c | 27 +++++++++-- src/types/registry.h | 8 ++++ src/types/seat.c | 61 +++++++++++++++++++++++++ src/types/seat.h | 39 ++++++++++++++++ 9 files changed, 151 insertions(+), 94 deletions(-) create mode 100644 src/types/seat.c create mode 100644 src/types/seat.h diff --git a/src/boilerplate.c b/src/boilerplate.c index ece50be..58c24db 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -26,87 +26,11 @@ static struct popup_surface *popup_surface = NULL; static void forward_on_focus(struct keyboard *keyboard, uint32_t serial) { - struct wl_seat *this_seat = (struct wl_seat *) keyboard->data; - /* When we get to here, global seat is already initialized */ - if (this_seat != seat) { - return; - } if (action_on_popup_surface_getting_focus != NULL) { action_on_popup_surface_getting_focus(serial); } } -static void seat_capabilities_handler -( - void *data, - struct wl_seat *this_seat, - uint32_t capabilities -) { - /* Stash the capabilities of this seat for later */ - void *user_data = (void *) (unsigned long) capabilities; - wl_seat_set_user_data(this_seat, user_data); - - if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - struct keyboard *keyboard = calloc(1, sizeof(struct keyboard)); - keyboard->proxy = wl_seat_get_keyboard(this_seat); - keyboard->data = (void *) this_seat; - keyboard->on_focus = forward_on_focus; - keyboard_init(keyboard); - } -} - -static void seat_name_handler -( - void *data, - struct wl_seat *this_seat, - const char *name -) { - if (requested_seat_name == NULL) { - return; - } - if (strcmp(name, requested_seat_name) == 0) { - seat = this_seat; - } -} - -static const struct wl_seat_listener seat_listener = { - .capabilities = seat_capabilities_handler, - .name = seat_name_handler -}; - -#define UNSET_CAPABILITIES ((void *) (uint32_t) 35) - -void process_new_seat(struct wl_seat *new_seat) { - if (seat != NULL) { - wl_seat_destroy(new_seat); - return; - } - if (requested_seat_name == NULL) { - seat = new_seat; - } - wl_seat_add_listener(new_seat, &seat_listener, UNSET_CAPABILITIES); -} - -int ensure_seat_has_keyboard() { - void *user_data = wl_seat_get_user_data(seat); - while (user_data == UNSET_CAPABILITIES) { - wl_display_roundtrip(display); - user_data = wl_seat_get_user_data(seat); - } - - uint32_t capabilities = (uint32_t) (unsigned long) user_data; - if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - return 1; - } - - if (action_on_no_keyboard != NULL) { - action_on_no_keyboard(); - } - return 0; -} - -#undef UNSET_CAPABILITIES - void init_wayland_globals() { display = wl_display_connect(NULL); if (display == NULL) { @@ -120,14 +44,13 @@ void init_wayland_globals() { /* Wait for the "initial" set of globals to appear */ wl_display_roundtrip(display); - if (seat == NULL && requested_seat_name != NULL) { - wl_display_roundtrip(display); - } + seat = registry_find_seat(registry, requested_seat_name); if (seat == NULL) { - if (requested_seat_name == NULL) { - bail("No seat available"); + if (requested_seat_name != NULL) { + bail("No such seat"); + } else { + bail("Missing a seat"); } - bail("Cannot find the requested seat"); } } @@ -137,10 +60,11 @@ void popup_tiny_invisible_surface() { * otherwise we won't be notified of the selection. */ - if (!ensure_seat_has_keyboard()) { - return; + struct keyboard *keyboard = seat_get_keyboard(seat); + if (keyboard == NULL) { + bail("This seat has no keyboard"); } - + keyboard->on_focus = forward_on_focus; /* Make sure that we get the keyboard * object before creating the surface, * so that we get the enter event. diff --git a/src/boilerplate.h b/src/boilerplate.h index a7928cc..5ef4431 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -41,11 +41,12 @@ #include "includes/selection-protocols.h" #include "types/registry.h" +#include "types/seat.h" struct wl_display *display; struct registry *registry; -struct wl_seat *seat; +struct seat *seat; const char *requested_seat_name; void init_wayland_globals(void); diff --git a/src/meson.build b/src/meson.build index ce3d2b4..895415f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -110,6 +110,8 @@ lib = static_library( 'types/device-manager.c', 'types/keyboard.h', 'types/keyboard.c', + 'types/seat.h', + 'types/seat.c', 'types/shell.h', 'types/shell.c', 'types/shell-surface.h', diff --git a/src/types/device-manager.c b/src/types/device-manager.c index dff3cb4..54e37ce 100644 --- a/src/types/device-manager.c +++ b/src/types/device-manager.c @@ -19,6 +19,7 @@ #include "types/device-manager.h" #include "types/device.h" #include "types/source.h" +#include "types/seat.h" #include "includes/selection-protocols.h" #include @@ -30,7 +31,7 @@ struct source *device_manager_create_source(struct device_manager *self) { struct device *device_manager_get_device( struct device_manager *self, - struct wl_seat *seat + struct seat *seat ) { return self->do_get_device(self, seat); } @@ -51,9 +52,10 @@ static struct source *device_manager_ ## type ## _do_create_source( \ #define GET_DEVICE(type, device_type, method_name) \ static struct device *device_manager_ ## type ## _do_get_device( \ struct device_manager *self, \ - struct wl_seat *seat \ + struct seat *seat_wrapper \ ) { \ struct type *proxy = (struct type *) self->proxy; \ + struct wl_seat *seat = (struct wl_seat *) seat_wrapper->proxy; \ struct device *device = calloc(1, sizeof(struct device)); \ device->proxy = (struct wl_proxy *) type ## _ ## method_name(proxy, seat); \ device_init_ ## device_type(device); \ diff --git a/src/types/device-manager.h b/src/types/device-manager.h index 57e1270..433546f 100644 --- a/src/types/device-manager.h +++ b/src/types/device-manager.h @@ -23,6 +23,7 @@ #include +struct seat; struct device; struct source; @@ -34,7 +35,7 @@ struct device_manager { struct source *(*do_create_source)(struct device_manager *self); struct device *(*do_get_device)( struct device_manager *self, - struct wl_seat *seat + struct seat *seat ); }; @@ -42,7 +43,7 @@ struct source *device_manager_create_source(struct device_manager *self); struct device *device_manager_get_device( struct device_manager *self, - struct wl_seat *seat + struct seat *seat ); /* Initializers */ diff --git a/src/types/registry.c b/src/types/registry.c index 101dbad..9826541 100644 --- a/src/types/registry.c +++ b/src/types/registry.c @@ -17,6 +17,7 @@ */ #include "types/registry.h" +#include "types/seat.h" #include "types/shell.h" #include "types/device-manager.h" #include "includes/shell-protocols.h" @@ -26,8 +27,6 @@ #include #include -void process_new_seat(struct wl_seat *new_seat); - #define BIND(interface_name, known_version) \ if (strcmp(interface, #interface_name) == 0) { \ self->interface_name = wl_registry_bind( \ @@ -75,13 +74,16 @@ static void wl_registry_global_handler( #endif if (strcmp(interface, "wl_seat") == 0) { - struct wl_seat *seat = wl_registry_bind( + struct seat *seat = calloc(1, sizeof(struct seat)); + seat->proxy = wl_registry_bind( wl_registry, name, &wl_seat_interface, 2 ); - process_new_seat(seat); + seat_init(seat); + struct seat **ptr = wl_array_add(&self->seats, sizeof(struct seat *)); + *ptr = seat; } } @@ -161,3 +163,20 @@ struct device_manager *registry_find_device_manager( free(device_manager); return NULL; } + +struct seat *registry_find_seat( + struct registry *self, + const char *name +) { + /* Ensure we get all the seat info */ + wl_display_roundtrip(self->wl_display); + + struct seat **ptr; + wl_array_for_each(ptr, &self->seats) { + struct seat *seat = *ptr; + if (name == NULL || strcmp(seat->name, name) == 0) { + return seat; + } + } + return NULL; +} diff --git a/src/types/registry.h b/src/types/registry.h index a131d14..e52e427 100644 --- a/src/types/registry.h +++ b/src/types/registry.h @@ -26,6 +26,7 @@ struct shell; struct device_manager; +struct seat; struct registry { /* This field is initialized by the creator */ @@ -34,6 +35,8 @@ struct registry { /* These fields are initialized by the implementation */ struct wl_registry *proxy; + struct wl_array seats; + struct wl_compositor *wl_compositor; struct wl_shm *wl_shm; @@ -70,4 +73,9 @@ struct device_manager *registry_find_device_manager( int primary ); +struct seat *registry_find_seat( + struct registry *self, + const char *name +); + #endif /* TYPES_REGISTRY_H */ diff --git a/src/types/seat.c b/src/types/seat.c new file mode 100644 index 0000000..6e48450 --- /dev/null +++ b/src/types/seat.c @@ -0,0 +1,61 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/seat.h" +#include "types/keyboard.h" + +#include +#include +#include + +static void wl_seat_capabilities_handler( + void *data, + struct wl_seat *seat, + uint32_t capabilities +) { + struct seat *self = (struct seat *) data; + self->capabilities = capabilities; +} + +static void wl_seat_name_handler( + void *data, + struct wl_seat *seat, + const char *name +) { + struct seat *self = (struct seat *) data; + self->name = strdup(name); +} + +static const struct wl_seat_listener wl_seat_listener = { + .capabilities = wl_seat_capabilities_handler, + .name = wl_seat_name_handler +}; + +void seat_init(struct seat *self) { + wl_seat_add_listener(self->proxy, &wl_seat_listener, self); +} + +struct keyboard *seat_get_keyboard(struct seat *self) { + if ((self->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) == 0) { + return NULL; + } + struct keyboard *keyboard = calloc(1, sizeof(struct keyboard)); + keyboard->proxy = wl_seat_get_keyboard(self->proxy); + keyboard_init(keyboard); + return keyboard; +} diff --git a/src/types/seat.h b/src/types/seat.h new file mode 100644 index 0000000..cd92073 --- /dev/null +++ b/src/types/seat.h @@ -0,0 +1,39 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_SEAT_H +#define TYPES_SEAT_H + +#include + +struct keyboard; + +struct seat { + /* This field is initialized by the creator */ + struct wl_seat *proxy; + + /* These fields are initialized by the implementation */ + char *name; + uint32_t capabilities; +}; + +void seat_init(struct seat *self); +struct keyboard *seat_get_keyboard(struct seat *self); + + +#endif /* TYPES_SEAT_H */ From 1c3970b1f9afc03bbc10ab9f1371b144fbedcb8d Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 13:53:39 +0300 Subject: [PATCH 26/36] Make popup surface manage the keyboard and forward its callback --- src/boilerplate.c | 16 +++------------- src/types/popup-surface.c | 34 ++++++++++++++++++++++++++++++---- src/types/popup-surface.h | 8 +++++++- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/boilerplate.c b/src/boilerplate.c index 58c24db..d2a6621 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -17,7 +17,6 @@ */ #include "boilerplate.h" -#include "types/keyboard.h" #include "types/shell-surface.h" #include "types/shell.h" #include "types/popup-surface.h" @@ -25,7 +24,7 @@ static struct popup_surface *popup_surface = NULL; -static void forward_on_focus(struct keyboard *keyboard, uint32_t serial) { +static void forward_on_focus(struct popup_surface *popup_surface, uint32_t serial) { if (action_on_popup_surface_getting_focus != NULL) { action_on_popup_surface_getting_focus(serial); } @@ -60,19 +59,10 @@ void popup_tiny_invisible_surface() { * otherwise we won't be notified of the selection. */ - struct keyboard *keyboard = seat_get_keyboard(seat); - if (keyboard == NULL) { - bail("This seat has no keyboard"); - } - keyboard->on_focus = forward_on_focus; - /* Make sure that we get the keyboard - * object before creating the surface, - * so that we get the enter event. - */ - wl_display_dispatch(display); - popup_surface = calloc(1, sizeof(struct popup_surface)); popup_surface->registry = registry; + popup_surface->seat = seat; + popup_surface->on_focus = forward_on_focus; popup_surface_init(popup_surface); } diff --git a/src/types/popup-surface.c b/src/types/popup-surface.c index fe2a59f..b629a77 100644 --- a/src/types/popup-surface.c +++ b/src/types/popup-surface.c @@ -18,6 +18,8 @@ #include "types/popup-surface.h" #include "types/registry.h" +#include "types/seat.h" +#include "types/keyboard.h" #include "types/shell.h" #include "types/shell-surface.h" #include "util/files.h" @@ -27,12 +29,36 @@ #include #include +static void forward_on_focus( + struct keyboard *keyboard, + uint32_t serial +) { + struct popup_surface *self = (struct popup_surface *) keyboard->data; + if (self->on_focus != NULL) { + self->on_focus(self, serial); + } +} + void popup_surface_init(struct popup_surface *self) { self->shell = registry_find_shell(self->registry); if (self->shell == NULL) { bail("Missing a shell"); } + self->keyboard = seat_get_keyboard(self->seat); + if (self->keyboard == NULL) { + bail("This seat has no keyboard"); + } + self->keyboard->on_focus = forward_on_focus; + self->keyboard->data = self; + + /* Make sure that we get the keyboard + * object before we create the surface, + * so that we get the enter event. + */ + wl_display_dispatch(self->registry->wl_display); + + struct wl_compositor *wl_compositor = self->registry->wl_compositor; if (wl_compositor == NULL) { bail("Missing the compositor"); @@ -50,10 +76,10 @@ void popup_surface_init(struct popup_surface *self) { if (self->wl_surface == NULL) { /* It's possible that we were given focus * (without ever commiting a buffer) during - * the above roundtrip, in which case the - * handlers may have already destroyed the - * surface. No need to do anything further in - * that case. + * the above roundtrip, in which case we have + * already fired the callback and have likely + * already destroyed the surface. No need to + * do anything further in that case. */ free(self); return; diff --git a/src/types/popup-surface.h b/src/types/popup-surface.h index bb54f21..8847519 100644 --- a/src/types/popup-surface.h +++ b/src/types/popup-surface.h @@ -24,15 +24,21 @@ struct registry; struct shell; struct shell_surface; +struct seat; +struct keyboard; struct popup_surface { - /* This field is initialized by the creator */ + /* These fields are initialized by the creator */ struct registry *registry; + struct seat *seat; + void (*on_focus)(struct popup_surface *self, uint32_t serial); + void *data; /* These fields are initialized by the implementation */ struct shell *shell; struct shell_surface *shell_surface; struct wl_surface *wl_surface; + struct keyboard *keyboard; int should_free_self; }; From 7ea4b312168a756d093af7e39a1dd5fef5c746ab Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 14:06:16 +0300 Subject: [PATCH 27/36] Add a typedef for argv_t --- src/util/string.h | 2 ++ src/wl-copy.c | 6 +++--- src/wl-paste.c | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/util/string.h b/src/util/string.h index 4602652..239cf58 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -22,6 +22,8 @@ #define text_plain "text/plain" #define text_plain_utf8 "text/plain;charset=utf-8" +typedef char * const *argv_t; + int mime_type_is_text(const char *mime_type); int str_has_prefix(const char *string, const char *prefix); diff --git a/src/wl-copy.c b/src/wl-copy.c index 22f5abc..bd8a87e 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -31,7 +31,7 @@ static struct { int primary; } options; -static char * const *data_to_copy = NULL; +static argv_t data_to_copy = NULL; static char *temp_file_to_copy = NULL; static struct device *device = NULL; @@ -147,7 +147,7 @@ static void print_usage(FILE *f, const char *argv0) { ); } -static void parse_options(int argc, char * const argv[]) { +static void parse_options(int argc, argv_t argv) { if (argc < 1) { bail("Empty argv"); } @@ -210,7 +210,7 @@ static void parse_options(int argc, char * const argv[]) { } } -int main(int argc, char * const argv[]) { +int main(int argc, argv_t argv) { parse_options(argc, argv); init_wayland_globals(); diff --git a/src/wl-paste.c b/src/wl-paste.c index c4c8f57..ded5179 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -239,7 +239,7 @@ static void print_usage(FILE *f, const char *argv0) { ); } -static void parse_options(int argc, char * const argv[]) { +static void parse_options(int argc, argv_t argv) { if (argc < 1) { bail("Empty argv"); } @@ -300,7 +300,7 @@ static void parse_options(int argc, char * const argv[]) { } } -int main(int argc, char * const argv[]) { +int main(int argc, argv_t argv) { parse_options(argc, argv); char *path = path_for_fd(STDOUT_FILENO); From ca45611d2530d965a2bb9d6e7a5235792ecf8477 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 14:21:26 +0300 Subject: [PATCH 28/36] Make wl-copy and wl-paste use popup surface directly This lets us finally completely get rid of the infrastructure that boilerplate.h and boilerplate.c used to have for managing the popup surface and its callbacks. --- src/boilerplate.c | 55 ----------------------------------------------- src/boilerplate.h | 8 ------- src/wl-copy.c | 24 +++++++++++++-------- src/wl-paste.c | 18 ++++++++++++++-- 4 files changed, 31 insertions(+), 74 deletions(-) diff --git a/src/boilerplate.c b/src/boilerplate.c index d2a6621..a5ed4ae 100644 --- a/src/boilerplate.c +++ b/src/boilerplate.c @@ -22,14 +22,6 @@ #include "types/popup-surface.h" #include "types/registry.h" -static struct popup_surface *popup_surface = NULL; - -static void forward_on_focus(struct popup_surface *popup_surface, uint32_t serial) { - if (action_on_popup_surface_getting_focus != NULL) { - action_on_popup_surface_getting_focus(serial); - } -} - void init_wayland_globals() { display = wl_display_connect(NULL); if (display == NULL) { @@ -52,50 +44,3 @@ void init_wayland_globals() { } } } - -void popup_tiny_invisible_surface() { - /* HACK: - * Pop up a tiny invisible surface to get the keyboard focus, - * otherwise we won't be notified of the selection. - */ - - popup_surface = calloc(1, sizeof(struct popup_surface)); - popup_surface->registry = registry; - popup_surface->seat = seat; - popup_surface->on_focus = forward_on_focus; - - popup_surface_init(popup_surface); -} - -void destroy_popup_surface() { - if (popup_surface != NULL) { - popup_surface_destroy(popup_surface); - popup_surface = NULL; - } -} - -static uint32_t global_serial; - -static void callback_done -( - void *data, - struct wl_callback *callback, - uint32_t serial -) { - global_serial = serial; -} - -static const struct wl_callback_listener callback_listener = { - .done = callback_done -}; - -uint32_t get_serial() { - struct wl_callback *callback = wl_display_sync(display); - wl_callback_add_listener(callback, &callback_listener, NULL); - - while (global_serial == 0) { - wl_display_dispatch(display); - } - - return global_serial; -} diff --git a/src/boilerplate.h b/src/boilerplate.h index 5ef4431..9f99d6b 100644 --- a/src/boilerplate.h +++ b/src/boilerplate.h @@ -50,11 +50,3 @@ struct seat *seat; const char *requested_seat_name; void init_wayland_globals(void); - -void popup_tiny_invisible_surface(void); -void destroy_popup_surface(void); - -void (*action_on_popup_surface_getting_focus)(uint32_t serial); -void (*action_on_no_keyboard)(void); - -uint32_t get_serial(void); diff --git a/src/wl-copy.c b/src/wl-copy.c index bd8a87e..97a1ccd 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -21,6 +21,7 @@ #include "types/device.h" #include "types/device-manager.h" #include "types/registry.h" +#include "types/popup-surface.h" static struct { int stay_in_foreground; @@ -36,6 +37,7 @@ static char *temp_file_to_copy = NULL; static struct device *device = NULL; static struct source *source = NULL; +static struct popup_surface *popup_surface = NULL; static void cancelled_callback(struct source *source) { /* We're done! */ @@ -95,14 +97,16 @@ static void send_callback( } } -static void set_selection(uint32_t serial) { +static void set_selection( + struct popup_surface *popup_surface, + uint32_t serial +) { device_set_selection(device, source, serial, options.primary); wl_display_roundtrip(display); - destroy_popup_surface(); -} - -static void complain_about_missing_keyboard() { - bail("Setting primary selection is not supported without a keyboard"); + if (popup_surface != NULL) { + popup_surface_destroy(popup_surface); + popup_surface = NULL; + } } static void do_offer(char *mime_type, struct source *source) { @@ -278,9 +282,11 @@ int main(int argc, argv_t argv) { /* If we cannot, schedule to do it later, * when our popup surface gains keyboard focus. */ - action_on_popup_surface_getting_focus = set_selection; - action_on_no_keyboard = complain_about_missing_keyboard; - popup_tiny_invisible_surface(); + popup_surface = calloc(1, sizeof(struct popup_surface)); + popup_surface->registry = registry; + popup_surface->seat = seat; + popup_surface->on_focus = set_selection; + popup_surface_init(popup_surface); } if (options.clear) { diff --git a/src/wl-paste.c b/src/wl-paste.c index ded5179..70c41d1 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -21,6 +21,7 @@ #include "types/device.h" #include "types/device-manager.h" #include "types/registry.h" +#include "types/popup-surface.h" static struct { char *explicit_type; @@ -40,6 +41,8 @@ struct types { const char *any; }; +static struct popup_surface *popup_surface = NULL; + static struct types classify_offer_types(struct offer *offer) { struct types types = { 0 }; offer_for_each_mime_type(offer, mime_type) { @@ -185,7 +188,10 @@ static void selection_callback(struct offer *offer, int primary) { offer_receive(offer, mime_type, pipefd[1]); - destroy_popup_surface(); + if (popup_surface != NULL) { + popup_surface_destroy(popup_surface); + popup_surface = NULL; + } wl_display_roundtrip(display); /* Spawn a cat to perform the copy */ @@ -326,7 +332,15 @@ int main(int argc, argv_t argv) { device->selection_callback = selection_callback; if (device->needs_popup_surface) { - popup_tiny_invisible_surface(); + /* If we cannot get the selection directly, pop up + * a surface. When it gets focus, we'll immediately + * get the selection events, se we don't need to do + * anything special on the surface getting focus. + */ + popup_surface = calloc(1, sizeof(struct popup_surface)); + popup_surface->registry = registry; + popup_surface->seat = seat; + popup_surface_init(popup_surface); } while (wl_display_dispatch(display) >= 0); From e15813320b8ae1cd692951b170a48ee40a4f465a Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 14:51:56 +0300 Subject: [PATCH 29/36] Move Wayland initialization into wl-copy and wl-paste This was the last thing left in boilerplate.c, so now boilerplate.c and boilerplate.h are finally completely gone. --- src/boilerplate.c | 46 ----------------------------------------- src/boilerplate.h | 52 ----------------------------------------------- src/meson.build | 2 -- src/wl-copy.c | 44 +++++++++++++++++++++++++++++++++------ src/wl-paste.c | 43 ++++++++++++++++++++++++++++++++++----- 5 files changed, 76 insertions(+), 111 deletions(-) delete mode 100644 src/boilerplate.c delete mode 100644 src/boilerplate.h diff --git a/src/boilerplate.c b/src/boilerplate.c deleted file mode 100644 index a5ed4ae..0000000 --- a/src/boilerplate.c +++ /dev/null @@ -1,46 +0,0 @@ -/* wl-clipboard - * - * Copyright © 2019 Sergey Bugaev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "boilerplate.h" -#include "types/shell-surface.h" -#include "types/shell.h" -#include "types/popup-surface.h" -#include "types/registry.h" - -void init_wayland_globals() { - display = wl_display_connect(NULL); - if (display == NULL) { - bail("Failed to connect to a Wayland server"); - } - - registry = calloc(1, sizeof(struct registry)); - registry->wl_display = display; - registry_init(registry); - - /* Wait for the "initial" set of globals to appear */ - wl_display_roundtrip(display); - - seat = registry_find_seat(registry, requested_seat_name); - if (seat == NULL) { - if (requested_seat_name != NULL) { - bail("No such seat"); - } else { - bail("Missing a seat"); - } - } -} diff --git a/src/boilerplate.h b/src/boilerplate.h deleted file mode 100644 index 9f99d6b..0000000 --- a/src/boilerplate.h +++ /dev/null @@ -1,52 +0,0 @@ -/* wl-clipboard - * - * Copyright © 2019 Sergey Bugaev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "config.h" - -#include "util/string.h" -#include "util/files.h" -#include "util/misc.h" - -#include -#include -#include -#include -#include -#include -#include // isupper -#include // open -#include // open -#include // open -#include // exit -#include // basename -#include -#include // PATH_MAX - -#include "includes/shell-protocols.h" -#include "includes/selection-protocols.h" - -#include "types/registry.h" -#include "types/seat.h" - -struct wl_display *display; -struct registry *registry; - -struct seat *seat; -const char *requested_seat_name; - -void init_wayland_globals(void); diff --git a/src/meson.build b/src/meson.build index 895415f..ab85a56 100644 --- a/src/meson.build +++ b/src/meson.build @@ -88,8 +88,6 @@ endforeach lib = static_library( 'wl-clipboard', [ - 'boilerplate.c', - 'util/string.h', 'util/string.c', 'util/files.h', diff --git a/src/wl-copy.c b/src/wl-copy.c index 97a1ccd..7e5fa48 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -16,13 +16,24 @@ * along with this program. If not, see . */ -#include "boilerplate.h" #include "types/source.h" #include "types/device.h" #include "types/device-manager.h" #include "types/registry.h" #include "types/popup-surface.h" +#include "util/files.h" +#include "util/string.h" +#include "util/misc.h" + +#include +#include +#include +#include +#include +#include +#include + static struct { int stay_in_foreground; int clear; @@ -30,11 +41,13 @@ static struct { int trim_newline; int paste_once; int primary; + const char *seat_name; } options; static argv_t data_to_copy = NULL; static char *temp_file_to_copy = NULL; +static struct wl_display *wl_display = NULL; static struct device *device = NULL; static struct source *source = NULL; static struct popup_surface *popup_surface = NULL; @@ -102,7 +115,7 @@ static void set_selection( uint32_t serial ) { device_set_selection(device, source, serial, options.primary); - wl_display_roundtrip(display); + wl_display_roundtrip(wl_display); if (popup_surface != NULL) { popup_surface_destroy(popup_surface); popup_surface = NULL; @@ -204,7 +217,7 @@ static void parse_options(int argc, argv_t argv) { options.mime_type = strdup(optarg); break; case 's': - requested_seat_name = strdup(optarg); + options.seat_name = strdup(optarg); break; default: /* getopt has already printed an error message */ @@ -217,7 +230,26 @@ static void parse_options(int argc, argv_t argv) { int main(int argc, argv_t argv) { parse_options(argc, argv); - init_wayland_globals(); + wl_display = wl_display_connect(NULL); + if (wl_display == NULL) { + bail("Failed to connect to a Wayland server"); + } + + struct registry *registry = calloc(1, sizeof(struct registry)); + registry->wl_display = wl_display; + registry_init(registry); + + /* Wait for the initial set of globals to appear */ + wl_display_roundtrip(wl_display); + + struct seat *seat = registry_find_seat(registry, options.seat_name); + if (seat == NULL) { + if (options.seat_name != NULL) { + bail("No such seat"); + } else { + bail("Missing a seat"); + } + } if (!options.clear) { if (optind < argc) { @@ -290,11 +322,11 @@ int main(int argc, argv_t argv) { } if (options.clear) { - wl_display_roundtrip(display); + wl_display_roundtrip(wl_display); exit(0); } - while (wl_display_dispatch(display) >= 0); + while (wl_display_dispatch(wl_display) >= 0); perror("wl_display_dispatch"); return 1; diff --git a/src/wl-paste.c b/src/wl-paste.c index 70c41d1..5d1241b 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -16,19 +16,32 @@ * along with this program. If not, see . */ -#include "boilerplate.h" #include "types/offer.h" #include "types/device.h" #include "types/device-manager.h" #include "types/registry.h" #include "types/popup-surface.h" +#include "util/files.h" +#include "util/string.h" +#include "util/misc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + static struct { char *explicit_type; char *inferred_type; int no_newline; int list_types; int primary; + const char *seat_name; } options; struct types { @@ -41,6 +54,7 @@ struct types { const char *any; }; +static struct wl_display *wl_display = NULL; static struct popup_surface *popup_surface = NULL; static struct types classify_offer_types(struct offer *offer) { @@ -192,7 +206,7 @@ static void selection_callback(struct offer *offer, int primary) { popup_surface_destroy(popup_surface); popup_surface = NULL; } - wl_display_roundtrip(display); + wl_display_roundtrip(wl_display); /* Spawn a cat to perform the copy */ pid_t pid = fork(); @@ -290,7 +304,7 @@ static void parse_options(int argc, argv_t argv) { options.explicit_type = strdup(optarg); break; case 's': - requested_seat_name = strdup(optarg); + options.seat_name = strdup(optarg); break; default: /* getopt has already printed an error message */ @@ -315,7 +329,26 @@ int main(int argc, argv_t argv) { } free(path); - init_wayland_globals(); + wl_display = wl_display_connect(NULL); + if (wl_display == NULL) { + bail("Failed to connect to a Wayland server"); + } + + struct registry *registry = calloc(1, sizeof(struct registry)); + registry->wl_display = wl_display; + registry_init(registry); + + /* Wait for the initial set of globals to appear */ + wl_display_roundtrip(wl_display); + + struct seat *seat = registry_find_seat(registry, options.seat_name); + if (seat == NULL) { + if (options.seat_name != NULL) { + bail("No such seat"); + } else { + bail("Missing a seat"); + } + } /* Create the device */ struct device_manager *device_manager @@ -343,7 +376,7 @@ int main(int argc, argv_t argv) { popup_surface_init(popup_surface); } - while (wl_display_dispatch(display) >= 0); + while (wl_display_dispatch(wl_display) >= 0); perror("wl_display_dispatch"); return 1; From 27e65aa7051d085dceb259d2781e1bc2d3e1bdb9 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 17:10:54 +0300 Subject: [PATCH 30/36] Reimplement clearing selection We now properly set a nil/NULL source instead of setting a dummy source and exiting. It's actually more straigtforawrd this way, and the Mutter bug that caused it to crash on nil sources [1] has been fixed. Fixes https://github.com/bugaevc/wl-clipboard/issues/47 [1]: https://gitlab.gnome.org/GNOME/mutter/issues/335 --- src/wl-copy.c | 98 ++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/src/wl-copy.c b/src/wl-copy.c index 7e5fa48..ff469ae 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -120,21 +120,9 @@ static void set_selection( popup_surface_destroy(popup_surface); popup_surface = NULL; } -} - -static void do_offer(char *mime_type, struct source *source) { - if (mime_type == NULL || mime_type_is_text(mime_type)) { - /* Offer a few generic plain text formats */ - source_offer(source, text_plain); - source_offer(source, text_plain_utf8); - source_offer(source, "TEXT"); - source_offer(source, "STRING"); - source_offer(source, "UTF8_STRING"); + if (options.clear) { + exit(0); } - if (mime_type != NULL) { - source_offer(source, mime_type); - } - free(mime_type); } static void print_usage(FILE *f, const char *argv0) { @@ -251,6 +239,19 @@ int main(int argc, argv_t argv) { } } + /* Create the device */ + struct device_manager *device_manager + = registry_find_device_manager(registry, options.primary); + if (device_manager == NULL) { + complain_about_selection_support(options.primary); + } + + device = device_manager_get_device(device_manager, seat); + + if (!device_supports_selection(device, options.primary)) { + complain_about_selection_support(options.primary); + } + if (!options.clear) { if (optind < argc) { /* Copy our command-line arguments */ @@ -266,44 +267,42 @@ int main(int argc, argv_t argv) { = infer_mime_type_from_contents(temp_file_to_copy); } } - } - if (!options.stay_in_foreground && !options.clear) { - /* Move to background. - * We fork our process and leave the - * child running in the background, - * while exiting in the parent. - */ - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - /* Proceed without forking */ + if (!options.stay_in_foreground) { + /* Move to background. + * We fork our process and leave the + * child running in the background, + * while exiting in the parent. + */ + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + /* Proceed without forking */ + } + if (pid > 0) { + exit(0); + } } - if (pid > 0) { - exit(0); + + /* Create the source */ + source = device_manager_create_source(device_manager); + source->send_callback = send_callback; + source->cancelled_callback = cancelled_callback; + if (options.mime_type != NULL) { + source_offer(source, options.mime_type); } + if (options.mime_type == NULL || mime_type_is_text(options.mime_type)) { + /* Offer a few generic plain text formats */ + source_offer(source, text_plain); + source_offer(source, text_plain_utf8); + source_offer(source, "TEXT"); + source_offer(source, "STRING"); + source_offer(source, "UTF8_STRING"); + } + free(options.mime_type); + options.mime_type = NULL; } - /* Create the device */ - struct device_manager *device_manager - = registry_find_device_manager(registry, options.primary); - if (device_manager == NULL) { - complain_about_selection_support(options.primary); - } - - device = device_manager_get_device(device_manager, seat); - - if (!device_supports_selection(device, options.primary)) { - complain_about_selection_support(options.primary); - } - - /* Create and initialize the source */ - source = device_manager_create_source(device_manager); - source->send_callback = send_callback; - source->cancelled_callback = cancelled_callback; - - do_offer(options.mime_type, source); - /* See if we can just set the selection directly */ if (!device->needs_popup_surface) { /* If we can, it doesn't actually require @@ -321,11 +320,6 @@ int main(int argc, argv_t argv) { popup_surface_init(popup_surface); } - if (options.clear) { - wl_display_roundtrip(wl_display); - exit(0); - } - while (wl_display_dispatch(wl_display) >= 0); perror("wl_display_dispatch"); From c4919ee748baa33865c2cc2cc9d1b4105bf1e36e Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Thu, 12 Sep 2019 14:49:52 +0300 Subject: [PATCH 31/36] Update the bundled version of wlr-data-control-unstable-v1 --- src/protocol/wlr-data-control-unstable-v1.xml | 80 ++++++++++++++++--- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/src/protocol/wlr-data-control-unstable-v1.xml b/src/protocol/wlr-data-control-unstable-v1.xml index a588755..75e8671 100644 --- a/src/protocol/wlr-data-control-unstable-v1.xml +++ b/src/protocol/wlr-data-control-unstable-v1.xml @@ -2,6 +2,7 @@ Copyright © 2018 Simon Ser + Copyright © 2019 Ivan Molodetskikh Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted @@ -40,7 +41,7 @@ interface version number is reset. - + This interface is a manager that allows creating per-seat data device controls. @@ -70,7 +71,7 @@ - + This interface allows a client to manage a seat's selection. @@ -79,8 +80,14 @@ - All objects created by the device will still remain valid, until their - appropriate destroy request has been called. + This request asks the compositor to set the selection to the data from + the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the selection, set the source to NULL. @@ -95,20 +102,19 @@ The data_offer event introduces a new wlr_data_control_offer object, - which will subsequently be used in the wlr_data_control_device.selection - event. Immediately following the wlr_data_control_device.data_offer - event, the new data_offer object will send out - wlr_data_control_offer.offer events to describe the MIME types it - offers. - - This event replaces the previous data offer, which should be destroyed - by the client. + which will subsequently be used in either the + wlr_data_control_device.selection event (for the regular clipboard + selections) or the wlr_data_control_device.primary_selection event (for + the primary clipboard selections). Immediately following the + wlr_data_control_device.data_offer event, the new data_offer object + will send out wlr_data_control_offer.offer events to describe the MIME + types it offers. - + The selection event is sent out to notify the client of a new wlr_data_control_offer for the selection for this device. The wlr_data_control_device.data_offer and the wlr_data_control_offer.offer @@ -118,6 +124,9 @@ wlr_data_control_offer or NULL is received. The client must destroy the previous selection wlr_data_control_offer, if any, upon receiving this event. + + The first selection event is sent upon binding the + wlr_data_control_device object. @@ -129,6 +138,51 @@ the client. + + + + + + The primary_selection event is sent out to notify the client of a new + wlr_data_control_offer for the primary selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The primary_selection event is sent to a client when a + new primary selection is set. The wlr_data_control_offer is valid until + a new wlr_data_control_offer or NULL is received. The client must + destroy the previous primary selection wlr_data_control_offer, if any, + upon receiving this event. + + If the compositor supports primary selection, the first + primary_selection event is sent upon binding the + wlr_data_control_device object. + + + + + + + This request asks the compositor to set the primary selection to the + data from the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the primary selection, set the source to NULL. + + The compositor will ignore this request if it does not support primary + selection. + + + + + + + From 6afabd3b59f37578020c6d1046503726100d0e4d Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 17:25:43 +0300 Subject: [PATCH 32/36] Support acessing primary selection using wlr-data-control See https://github.com/swaywm/wlr-protocols/pull/34 --- src/types/device-manager.c | 1 + src/types/device-manager.h | 3 +- src/types/device.c | 58 ++++++++++++++++++++++++++++++++++++-- src/types/device.h | 1 + src/types/registry.c | 28 ++++++++++++++++-- src/wl-paste.c | 6 +++- 6 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/types/device-manager.c b/src/types/device-manager.c index 54e37ce..bd6e04e 100644 --- a/src/types/device-manager.c +++ b/src/types/device-manager.c @@ -58,6 +58,7 @@ static struct device *device_manager_ ## type ## _do_get_device( \ struct wl_seat *seat = (struct wl_seat *) seat_wrapper->proxy; \ struct device *device = calloc(1, sizeof(struct device)); \ device->proxy = (struct wl_proxy *) type ## _ ## method_name(proxy, seat); \ + device->wl_display = self->wl_display; \ device_init_ ## device_type(device); \ return device; \ } diff --git a/src/types/device-manager.h b/src/types/device-manager.h index 433546f..bb7e88d 100644 --- a/src/types/device-manager.h +++ b/src/types/device-manager.h @@ -28,8 +28,9 @@ struct device; struct source; struct device_manager { - /* This field is initialized by the creator */ + /* These fields are initialized by the creator */ struct wl_proxy *proxy; + struct wl_display *wl_display; /* These fields are initialized by the implementation */ struct source *(*do_create_source)(struct device_manager *self); diff --git a/src/types/device.c b/src/types/device.c index 534827f..16525a6 100644 --- a/src/types/device.c +++ b/src/types/device.c @@ -22,6 +22,7 @@ #include "types/source.h" #include "includes/selection-protocols.h" +#include #include int device_supports_selection(struct device *self, int primary) { @@ -191,10 +192,46 @@ INIT(zwp_primary_selection_device_v1, 1) #ifdef HAVE_WLR_DATA_CONTROL -SUPPORTS_SELECTION(zwlr_data_control_device_v1, !primary) +enum TriState { + Unknown, + Yes, + No +}; + +/* Whether wlr-data-control supports primary selection */ +static enum TriState device_wlr_supports_primary_selection = Unknown; +static int device_get_wlr_supports_selection(struct device *self, int primary) { + if (!primary) { + return 1; + } + + if (device_wlr_supports_primary_selection == Yes) { + return 1; + } else if (device_wlr_supports_primary_selection == No) { + return 0; + } + + wl_display_roundtrip(self->wl_display); + + if (device_wlr_supports_primary_selection == Yes) { + return 1; + } else { + device_wlr_supports_primary_selection = No; + return 0; + } +} + +SUPPORTS_SELECTION( + zwlr_data_control_device_v1, + device_get_wlr_supports_selection(self, primary) +) SET_SELECTION_IMPL(zwlr_data_control_device_v1, zwlr_data_control_source_v1, { - zwlr_data_control_device_v1_set_selection(device, source); + if (!primary) { + zwlr_data_control_device_v1_set_selection(device, source); + } else { + zwlr_data_control_device_v1_set_primary_selection(device, source); + } }) DATA_OFFER_HANDLER(zwlr_data_control_device_v1, zwlr_data_control_offer_v1) @@ -206,10 +243,27 @@ SELECTION_HANDLER( 0 ) +static void zwlr_data_control_device_v1_primary_selection_handler( + void *data, + struct zwlr_data_control_device_v1 *device, + struct zwlr_data_control_offer_v1 *offer_proxy +) { + device_wlr_supports_primary_selection = Yes; + struct device *self = data; + struct offer *offer = NULL; + if (offer_proxy != NULL) { + offer = wl_proxy_get_user_data((struct wl_proxy *) offer_proxy); + } + if (self->selection_callback != NULL) { + self->selection_callback(offer, 1); + } +} + static const struct zwlr_data_control_device_v1_listener zwlr_data_control_device_v1_listener = { .data_offer = zwlr_data_control_device_v1_data_offer_handler, .selection = zwlr_data_control_device_v1_selection_handler, + .primary_selection = zwlr_data_control_device_v1_primary_selection_handler }; INIT(zwlr_data_control_device_v1, 0) diff --git a/src/types/device.h b/src/types/device.h index 0867bb2..5f5e3fe 100644 --- a/src/types/device.h +++ b/src/types/device.h @@ -33,6 +33,7 @@ struct device { void *data; struct wl_proxy *proxy; + struct wl_display *wl_display; /* These fields are initialized by the implementation */ int (*supports_selection)(struct device *self, int primary); diff --git a/src/types/registry.c b/src/types/registry.c index 9826541..67c7fde 100644 --- a/src/types/registry.c +++ b/src/types/registry.c @@ -70,7 +70,7 @@ static void wl_registry_global_handler( #endif #ifdef HAVE_WLR_DATA_CONTROL - BIND(zwlr_data_control_manager_v1, 1) + BIND(zwlr_data_control_manager_v1, version > 2 ? 2 : version) #endif if (strcmp(interface, "wl_seat") == 0) { @@ -137,8 +137,10 @@ struct device_manager *registry_find_device_manager( ) { struct device_manager *device_manager = calloc(1, sizeof(struct device_manager)); + device_manager->wl_display = self->wl_display; - /* We prefer wlr-data-control, as it doesn't require + /* For regular selection, we just look at the two supported + * protocols. We prefer wlr-data-control, as it doesn't require * us to use the popup surface hack. */ @@ -152,6 +154,28 @@ struct device_manager *registry_find_device_manager( return NULL; } + /* For primary selection, it's a bit more complicated. We also + * prefer wlr-data-control, but we don't know in advance whether + * the compositor supports primary selection, as unlike with + * other protocols here, the mere presence of wlr-data-control + * does not imply primary selection support. However, we assume + * that if a compositor supports primary selection at all, then + * if it supports wlr-data-control v2 it also supports primary + * selection over wlr-data-control; which is only reasonable. + */ + +#ifdef HAVE_WLR_DATA_CONTROL + if (self->zwlr_data_control_manager_v1 != NULL) { + struct wl_proxy *proxy + = (struct wl_proxy *) self->zwlr_data_control_manager_v1; + if (wl_proxy_get_version(proxy) >= 2) { + device_manager->proxy = proxy; + device_manager_init_zwlr_data_control_manager_v1(device_manager); + return device_manager; + } + } +#endif + #ifdef HAVE_WP_PRIMARY_SELECTION TRY(zwp_primary_selection_device_manager_v1) #endif diff --git a/src/wl-paste.c b/src/wl-paste.c index 5d1241b..b255610 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -358,11 +358,15 @@ int main(int argc, argv_t argv) { } struct device *device = device_manager_get_device(device_manager, seat); + /* Set up the callback before checking whether the device + * actually supports the kind of selection we need, because + * checking for the support might roundtrip. + */ + device->selection_callback = selection_callback; if (!device_supports_selection(device, options.primary)) { complain_about_selection_support(options.primary); } - device->selection_callback = selection_callback; if (device->needs_popup_surface) { /* If we cannot get the selection directly, pop up From 8b83791ad6a935baef3ef904e79e858a47cce1b4 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 17:45:38 +0300 Subject: [PATCH 33/36] Move copy action logic into types/copy-action --- src/meson.build | 2 + src/types/copy-action.c | 158 ++++++++++++++++++++++++++++++++++++++++ src/types/copy-action.h | 54 ++++++++++++++ src/wl-copy.c | 148 ++++++++++++------------------------- 4 files changed, 261 insertions(+), 101 deletions(-) create mode 100644 src/types/copy-action.c create mode 100644 src/types/copy-action.h diff --git a/src/meson.build b/src/meson.build index ab85a56..9837e9a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -118,6 +118,8 @@ lib = static_library( 'types/popup-surface.c', 'types/registry.h', 'types/registry.c', + 'types/copy-action.h', + 'types/copy-action.c', ], dependencies: wayland, link_with: protocol_deps diff --git a/src/types/copy-action.c b/src/types/copy-action.c new file mode 100644 index 0000000..5ef9ea1 --- /dev/null +++ b/src/types/copy-action.c @@ -0,0 +1,158 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "types/copy-action.h" +#include "types/device.h" +#include "types/source.h" +#include "types/popup-surface.h" + +#include "util/misc.h" + +#include +#include +#include +#include +#include +#include +#include + +static void do_set_selection(struct copy_action *self, uint32_t serial) { + /* Set the selection and make sure it reaches + * the display before we do anything else, + * such as destroying the surface or exiting. + */ + device_set_selection(self->device, self->source, serial, self->primary); + wl_display_roundtrip(self->device->wl_display); + + /* Now, if we have used a popup surface, destroy it */ + if (self->device->needs_popup_surface) { + popup_surface_destroy(self->popup_surface); + self->popup_surface = NULL; + } + + /* And invoke the callback */ + if (self->did_set_selection_callback != NULL) { + self->did_set_selection_callback(self); + } +} + +static void on_focus( + struct popup_surface *popup_surface, + uint32_t serial +) { + struct copy_action *self = (struct copy_action *) popup_surface->data; + do_set_selection(self, serial); +} + +static void do_send(struct source *source, const char *mime_type, int fd) { + struct copy_action *self = source->data; + + /* Unset O_NONBLOCK */ + fcntl(fd, F_SETFL, 0); + + if (self->file_to_copy != NULL) { + /* Copy the file to the given file descriptor + * by spawning an appropriate cat process. + */ + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + close(fd); + return; + } + if (pid == 0) { + dup2(fd, STDOUT_FILENO); + close(fd); + execlp("cat", "cat", self->file_to_copy, NULL); + perror("exec cat"); + exit(1); + } + close(fd); + /* Wait for the cat process to exit. This effectively + * means waiting for the other side to read the whole + * file. In theory, a malicious client could perform a + * denial-of-serivice attack against us. Perhaps we + * should switch to an asynchronous child waiting scheme + * instead. + */ + wait(NULL); + } else { + /* We'll perform the copy ourselves */ + FILE *f = fdopen(fd, "w"); + if (f == NULL) { + perror("fdopen"); + close(fd); + return; + } + + if (self->data_to_copy.ptr != NULL) { + /* Just copy the given chunk of data */ + fwrite(self->data_to_copy.ptr, 1, self->data_to_copy.len, f); + } else if (self->argv_to_copy != NULL) { + /* Copy an argv-style string array, + * inserting spaces between items. + */ + int is_first = 1; + for (argv_t word = self->argv_to_copy; *word != NULL; word++) { + if (!is_first) { + fwrite(" ", 1, 1, f); + } + is_first = 0; + fwrite(*word, 1, strlen(*word), f); + } + } else { + bail("Unreachable: nothing to copy"); + } + + fclose(f); + } + + + if (self->pasted_callback != NULL) { + self->pasted_callback(self); + } +} + +static void forward_cancel(struct source *source) { + struct copy_action *self = source->data; + if (self->cancelled_callback != NULL) { + self->cancelled_callback(self); + } +} + +void copy_action_init(struct copy_action *self) { + if (self->source != NULL) { + self->source->send_callback = do_send; + self->source->cancelled_callback = forward_cancel; + self->source->data = self; + } + /* See if we can just set the selection directly */ + if (!self->device->needs_popup_surface) { + /* If we can, it doesn't actually require + * a serial, so passing zero will do. + */ + do_set_selection(self, 0); + } else { + /* If we cannot, schedule to do it later, + * when our popup surface gains keyboard focus. + */ + self->popup_surface->on_focus = on_focus; + self->popup_surface->data = self; + popup_surface_init(self->popup_surface); + } +} diff --git a/src/types/copy-action.h b/src/types/copy-action.h new file mode 100644 index 0000000..c0a1290 --- /dev/null +++ b/src/types/copy-action.h @@ -0,0 +1,54 @@ +/* wl-clipboard + * + * Copyright © 2019 Sergey Bugaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TYPES_COPY_ACTION_H +#define TYPES_COPY_ACTION_H + +#include "util/string.h" + +#include + +struct device; +struct source; +struct popup_surface; + +struct copy_action { + /* These fields are initialized by the creator */ + struct device *device; + struct source *source; + struct popup_surface *popup_surface; + int primary; + + void (*did_set_selection_callback)(struct copy_action *self); + void (*pasted_callback)(struct copy_action *self); + void (*cancelled_callback)(struct copy_action *self); + + /* Exactly one of these fields must be non-null if the source + * is non-null, otherwise all these fields must be null. + */ + const char *file_to_copy; + argv_t argv_to_copy; + struct { + const char *ptr; + size_t len; + } data_to_copy; +}; + +void copy_action_init(struct copy_action *self); + +#endif /* TYPES_COPY_ACTION_H */ diff --git a/src/wl-copy.c b/src/wl-copy.c index ff469ae..f21fef6 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include "types/copy-action.h" #include "types/source.h" #include "types/device.h" #include "types/device-manager.h" @@ -28,11 +29,9 @@ #include #include -#include #include #include #include -#include static struct { int stay_in_foreground; @@ -44,18 +43,20 @@ static struct { const char *seat_name; } options; -static argv_t data_to_copy = NULL; -static char *temp_file_to_copy = NULL; +static void did_set_selection_callback(struct copy_action *copy_action) { + if (options.clear) { + exit(0); + } +} -static struct wl_display *wl_display = NULL; -static struct device *device = NULL; -static struct source *source = NULL; -static struct popup_surface *popup_surface = NULL; - -static void cancelled_callback(struct source *source) { - /* We're done! */ - if (temp_file_to_copy != NULL) { - execlp("rm", "rm", "-r", dirname(temp_file_to_copy), NULL); +static void cleanup_and_exit(struct copy_action *copy_action) { + /* We're done copying! + * All that's left to do now is to + * clean up after ourselves and exit.*/ + char *temp_file = (char *) copy_action->file_to_copy; + if (temp_file != NULL) { + /* Clean up our temporary file */ + execlp("rm", "rm", "-r", dirname(temp_file), NULL); perror("exec rm"); exit(1); } else { @@ -63,65 +64,13 @@ static void cancelled_callback(struct source *source) { } } -static void send_callback( - struct source *source, - const char *mime_type, - int fd -) { - /* Unset O_NONBLOCK */ - fcntl(fd, F_SETFL, 0); - if (data_to_copy != NULL) { - /* Copy the specified data, separated by spaces */ - FILE *f = fdopen(fd, "w"); - if (f == NULL) { - perror("fdopen"); - exit(1); - } - char * const *dataptr = data_to_copy; - for (int is_first = 1; *dataptr != NULL; dataptr++, is_first = 0) { - if (!is_first) { - fwrite(" ", 1, 1, f); - } - fwrite(*dataptr, 1, strlen(*dataptr), f); - } - fclose(f); - } else { - /* Copy from the temp file; for that, we delegate to a - * (hopefully) highly optimized implementation of copying. - */ - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - close(fd); - return; - } - if (pid == 0) { - dup2(fd, STDOUT_FILENO); - execlp("cat", "cat", temp_file_to_copy, NULL); - perror("exec cat"); - exit(1); - } - close(fd); - wait(NULL); - } - - if (options.paste_once) { - cancelled_callback(source); - } +static void cancelled_callback(struct copy_action *copy_action) { + cleanup_and_exit(copy_action); } -static void set_selection( - struct popup_surface *popup_surface, - uint32_t serial -) { - device_set_selection(device, source, serial, options.primary); - wl_display_roundtrip(wl_display); - if (popup_surface != NULL) { - popup_surface_destroy(popup_surface); - popup_surface = NULL; - } - if (options.clear) { - exit(0); +static void pasted_callback(struct copy_action *copy_action) { + if (options.paste_once) { + cleanup_and_exit(copy_action); } } @@ -218,7 +167,7 @@ static void parse_options(int argc, argv_t argv) { int main(int argc, argv_t argv) { parse_options(argc, argv); - wl_display = wl_display_connect(NULL); + struct wl_display *wl_display = wl_display_connect(NULL); if (wl_display == NULL) { bail("Failed to connect to a Wayland server"); } @@ -246,26 +195,31 @@ int main(int argc, argv_t argv) { complain_about_selection_support(options.primary); } - device = device_manager_get_device(device_manager, seat); + struct device *device = device_manager_get_device(device_manager, seat); if (!device_supports_selection(device, options.primary)) { complain_about_selection_support(options.primary); } + /* Create and initialize the copy action */ + struct copy_action *copy_action = calloc(1, sizeof(struct copy_action)); + copy_action->device = device; + copy_action->primary = options.primary; + if (!options.clear) { if (optind < argc) { /* Copy our command-line arguments */ - data_to_copy = &argv[optind]; + copy_action->argv_to_copy = &argv[optind]; } else { /* Copy data from our stdin */ - temp_file_to_copy = dump_stdin_into_a_temp_file(); + char *temp_file = dump_stdin_into_a_temp_file(); if (options.trim_newline) { - trim_trailing_newline(temp_file_to_copy); + trim_trailing_newline(temp_file); } if (options.mime_type == NULL) { - options.mime_type - = infer_mime_type_from_contents(temp_file_to_copy); + options.mime_type = infer_mime_type_from_contents(temp_file); } + copy_action->file_to_copy = temp_file; } if (!options.stay_in_foreground) { @@ -285,41 +239,33 @@ int main(int argc, argv_t argv) { } /* Create the source */ - source = device_manager_create_source(device_manager); - source->send_callback = send_callback; - source->cancelled_callback = cancelled_callback; + copy_action->source = device_manager_create_source(device_manager); if (options.mime_type != NULL) { - source_offer(source, options.mime_type); + source_offer(copy_action->source, options.mime_type); } if (options.mime_type == NULL || mime_type_is_text(options.mime_type)) { /* Offer a few generic plain text formats */ - source_offer(source, text_plain); - source_offer(source, text_plain_utf8); - source_offer(source, "TEXT"); - source_offer(source, "STRING"); - source_offer(source, "UTF8_STRING"); + source_offer(copy_action->source, text_plain); + source_offer(copy_action->source, text_plain_utf8); + source_offer(copy_action->source, "TEXT"); + source_offer(copy_action->source, "STRING"); + source_offer(copy_action->source, "UTF8_STRING"); } free(options.mime_type); options.mime_type = NULL; } - /* See if we can just set the selection directly */ - if (!device->needs_popup_surface) { - /* If we can, it doesn't actually require - * a serial, so passing zero will do. - */ - device_set_selection(device, source, 0, options.primary); - } else { - /* If we cannot, schedule to do it later, - * when our popup surface gains keyboard focus. - */ - popup_surface = calloc(1, sizeof(struct popup_surface)); - popup_surface->registry = registry; - popup_surface->seat = seat; - popup_surface->on_focus = set_selection; - popup_surface_init(popup_surface); + if (device->needs_popup_surface) { + copy_action->popup_surface = calloc(1, sizeof(struct popup_surface)); + copy_action->popup_surface->registry = registry; + copy_action->popup_surface->seat = seat; } + copy_action->did_set_selection_callback = did_set_selection_callback; + copy_action->pasted_callback = pasted_callback; + copy_action->cancelled_callback = cancelled_callback; + copy_action_init(copy_action); + while (wl_display_dispatch(wl_display) >= 0); perror("wl_display_dispatch"); From 771a6c63ceda9c0a7c5950f2a0bb73500318f8d3 Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 17:47:36 +0300 Subject: [PATCH 34/36] Move forking to background to happen after setting selection This way, we'll actually return our error code to the parent process if anything goes wrong during the initial stages, instead of successfully exiting in the parent. --- src/wl-copy.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/wl-copy.c b/src/wl-copy.c index f21fef6..612e895 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -47,6 +47,22 @@ static void did_set_selection_callback(struct copy_action *copy_action) { if (options.clear) { exit(0); } + + if (!options.stay_in_foreground) { + /* Move to background. + * We fork our process and leave the + * child running in the background, + * while exiting in the parent. + */ + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + /* Proceed without forking */ + } + if (pid > 0) { + exit(0); + } + } } static void cleanup_and_exit(struct copy_action *copy_action) { @@ -222,22 +238,6 @@ int main(int argc, argv_t argv) { copy_action->file_to_copy = temp_file; } - if (!options.stay_in_foreground) { - /* Move to background. - * We fork our process and leave the - * child running in the background, - * while exiting in the parent. - */ - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - /* Proceed without forking */ - } - if (pid > 0) { - exit(0); - } - } - /* Create the source */ copy_action->source = device_manager_create_source(device_manager); if (options.mime_type != NULL) { From 87dcd09eba8f3d6bfdce4371c8617af50876793e Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 15 Sep 2019 19:21:54 +0300 Subject: [PATCH 35/36] Implement wl-paste --watch Closes https://github.com/bugaevc/wl-clipboard/issues/39 --- README.md | 1 + completions/bash/wl-paste | 13 ++++++-- data/wl-clipboard.1 | 9 ++++- src/wl-paste.c | 70 ++++++++++++++++++++++++++++++++++----- 4 files changed, 81 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 925cd02..66231f9 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ For `wl-paste`: * `-n`, `--no-newline` Do not append a newline character after the pasted clipboard content. This option is automatically enabled for non-text content types. * `-l`, `--list-types` Instead of pasting the selection, output the list of MIME types it is offered in. +* `-w command...`, `--watch command...` Instead of pasting once and exiting, continuously watch the clipboard for changes, and run the specified command each time a new selection appears. The spawned process can read the clipboard contents from its standard input. This mode requires a compositor that supports the [wlroots data-control protocol](https://github.com/swaywm/wlr-protocols/blob/master/unstable/wlr-data-control-unstable-v1.xml). For both: diff --git a/completions/bash/wl-paste b/completions/bash/wl-paste index 85eb7bf..91003fe 100644 --- a/completions/bash/wl-paste +++ b/completions/bash/wl-paste @@ -49,10 +49,19 @@ _wl_clipboard_complete_paste_types() { _wl_paste_completion() { compopt +o default - local cur prev opts types seats + local cur prev opts types seats offset + + for (( offset=1; offset < COMP_CWORD; offset++ )); do + cur="${COMP_WORDS[offset]}" + if [ \( "x${cur:0:1}" = "x-" -a "x${cur:1:2}" != "x-" -a "${cur: -1}" = "w" \) -o "$cur" = "--watch" ]; then + _command_offset $(($offset+1)) + return + fi + done + cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="-n --no-newline -l --list-types -p --primary -t --type -s --seat -v --version -h --help" + opts="-n --no-newline -l --list-types -w --watch -p --primary -t --type -s --seat -v --version -h --help" if [ "$prev" = ">" ]; then compopt -o default COMPREPLY=() diff --git a/data/wl-clipboard.1 b/data/wl-clipboard.1 index c3fc327..035697d 100644 --- a/data/wl-clipboard.1 +++ b/data/wl-clipboard.1 @@ -1,4 +1,4 @@ -.TH WL-CLIPBOARD 1 2019-04-15 wl-clipboard +.TH WL-CLIPBOARD 1 2019-09-15 wl-clipboard .SH NAME wl-clipboard \- Wayland copy and paste command line utilities .SH SYNOPSIS @@ -16,6 +16,7 @@ wl-clipboard \- Wayland copy and paste command line utilities [\fB--primary\fR] [\fB--no-newline\fR] [\fB--list-types\fR] +[\fB--watch \fIcommand...\fR] [\fB--type \fImime/type\fR] [\fB--seat \fIseat-name\fR] .SH DESCRIPTION @@ -85,6 +86,12 @@ system, there is little reason to use this option. Instead of pasting the selection, output the list of MIME types it is offered in. .TP +\fB-w\fI command...\fR, \fB--watch \fIcommand... +Instead of pasting once and exiting, continuously watch the clipboard for +changes, and run the specified \fIcommand\fR each time a new selection appears. +The spawned process can read the clipboard contents from its standard input. +This mode requires a compositor that supports the wlroots data-control protocol. +.TP \fB-v\fR, \fB--version Display the version of wl-clipboard and some short info about its license. .TP diff --git a/src/wl-paste.c b/src/wl-paste.c index b255610..36a1142 100644 --- a/src/wl-paste.c +++ b/src/wl-paste.c @@ -41,6 +41,8 @@ static struct { int no_newline; int list_types; int primary; + int watch; + argv_t watch_command; const char *seat_name; } options; @@ -158,7 +160,7 @@ static const char *mime_type_to_request(struct types types) { try_inferred; } } - bail("No suitable type of content copied"); + return NULL; } #undef try_explicit @@ -179,6 +181,9 @@ static void selection_callback(struct offer *offer, int primary) { } if (offer == NULL) { + if (options.watch) { + return; + } bail("No selection"); } @@ -192,6 +197,14 @@ static void selection_callback(struct offer *offer, int primary) { struct types types = classify_offer_types(offer); const char *mime_type = mime_type_to_request(types); + if (mime_type == NULL) { + if (options.watch) { + offer_destroy(offer); + return; + } + bail("No suitable type of content copied"); + } + /* Never append a newline character to binary content */ if (!mime_type_is_text(mime_type)) { options.no_newline = 1; @@ -208,32 +221,54 @@ static void selection_callback(struct offer *offer, int primary) { } wl_display_roundtrip(wl_display); - /* Spawn a cat to perform the copy */ + /* Spawn a cat to perform the copy. + * If watch mode is active, we spawn + * a custom command instead. + */ pid_t pid = fork(); if (pid < 0) { perror("fork"); + if (options.watch) { + /* Try to cope without exiting completely */ + close(pipefd[0]); + close(pipefd[1]); + offer_destroy(offer); + return; + } exit(1); } if (pid == 0) { dup2(pipefd[0], STDIN_FILENO); close(pipefd[0]); close(pipefd[1]); - execlp("cat", "cat", NULL); - perror("exec cat"); + if (options.watch) { + execvp(options.watch_command[0], options.watch_command); + fprintf( + stderr, + "Failed to spawn %s: %s", + options.watch_command[0], + strerror(errno) + ); + } else { + execlp("cat", "cat", NULL); + perror("exec cat"); + } exit(1); } close(pipefd[0]); close(pipefd[1]); wait(NULL); - if (!options.no_newline) { + if (!options.no_newline && !options.watch) { write(STDOUT_FILENO, "\n", 1); } offer_destroy(offer); - free(options.explicit_type); - free(options.inferred_type); - exit(0); + if (!options.watch) { + free(options.explicit_type); + free(options.inferred_type); + exit(0); + } } static void print_usage(FILE *f, const char *argv0) { @@ -246,6 +281,8 @@ static void print_usage(FILE *f, const char *argv0) { "\t-n, --no-newline\tDo not append a newline character.\n" "\t-l, --list-types\tInstead of pasting, list the offered types.\n" "\t-p, --primary\t\tUse the \"primary\" clipboard.\n" + "\t-w, --watch command\t" + "Run a command each time the selection changes.\n" "\t-t, --type mime/type\t" "Override the inferred MIME type for the content.\n" "\t-s, --seat seat-name\t" @@ -270,13 +307,14 @@ static void parse_options(int argc, argv_t argv) { {"primary", no_argument, 0, 'p'}, {"no-newline", no_argument, 0, 'n'}, {"list-types", no_argument, 0, 'l'}, + {"watch", required_argument, 0, 'w'}, {"type", required_argument, 0, 't'}, {"seat", required_argument, 0, 's'}, {0, 0, 0, 0} }; while (1) { int option_index; - const char *opts = "vhpnlt:s:"; + const char *opts = "vhpnlw:t:s:"; int c = getopt_long(argc, argv, opts, long_options, &option_index); if (c == -1) { break; @@ -300,6 +338,14 @@ static void parse_options(int argc, argv_t argv) { case 'l': options.list_types = 1; break; + case 'w': + options.watch = 1; + options.watch_command = (argv_t) &argv[optind - 1]; + /* We're going to forward the rest of our + * arguments to the command we spawn, so stop + * trying to process further options. + */ + return; case 't': options.explicit_type = strdup(optarg); break; @@ -369,6 +415,12 @@ int main(int argc, argv_t argv) { } if (device->needs_popup_surface) { + if (options.watch) { + bail( + "Watch mode requires a compositor that supports " + "wlroots data-control protocol" + ); + } /* If we cannot get the selection directly, pop up * a surface. When it gets focus, we'll immediately * get the selection events, se we don't need to do From 7f562d9ef9698227ac58b7bd0be4385b9db047ec Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Mon, 16 Sep 2019 00:19:16 +0300 Subject: [PATCH 36/36] WIP: Implement wl-copy --regular and multiseat --- src/types/copy-action.c | 118 ++++++++++++++++------- src/types/copy-action.h | 22 +++-- src/types/source.c | 9 +- src/types/source.h | 4 +- src/wl-copy.c | 207 ++++++++++++++++++++++++++++------------ 5 files changed, 252 insertions(+), 108 deletions(-) diff --git a/src/types/copy-action.c b/src/types/copy-action.c index 5ef9ea1..7ddce62 100644 --- a/src/types/copy-action.c +++ b/src/types/copy-action.c @@ -31,32 +31,42 @@ #include #include -static void do_set_selection(struct copy_action *self, uint32_t serial) { - /* Set the selection and make sure it reaches - * the display before we do anything else, - * such as destroying the surface or exiting. - */ - device_set_selection(self->device, self->source, serial, self->primary); - wl_display_roundtrip(self->device->wl_display); - - /* Now, if we have used a popup surface, destroy it */ - if (self->device->needs_popup_surface) { - popup_surface_destroy(self->popup_surface); - self->popup_surface = NULL; - } - - /* And invoke the callback */ - if (self->did_set_selection_callback != NULL) { - self->did_set_selection_callback(self); - } -} +static struct popup_surface *current_popup_surface = NULL; +static struct wl_array pending_selections; static void on_focus( struct popup_surface *popup_surface, uint32_t serial ) { - struct copy_action *self = (struct copy_action *) popup_surface->data; - do_set_selection(self, serial); + /* Set the pending selections */ + struct wl_display *wl_display = NULL; + struct copy_action **ptr; + wl_array_for_each(ptr, &pending_selections) { + struct copy_action *self = *ptr; + wl_display = self->device->wl_display; + device_set_selection(self->device, self->source, serial, self->primary); + } + + /* Make sure they reach the display + * before we do anything else, such + * as destroying the surface or exiting. + */ + wl_display_roundtrip(wl_display); + + /* Destroy the surface */ + popup_surface_destroy(current_popup_surface); + current_popup_surface = NULL; + + /* Invoke the callbacks */ + wl_array_for_each(ptr, &pending_selections) { + struct copy_action *self = *ptr; + if (self->did_set_selection_callback != NULL) { + self->did_set_selection_callback(self); + } + } + + /* Finally, destroy the array */ + wl_array_release(&pending_selections); } static void do_send(struct source *source, const char *mime_type, int fd) { @@ -65,7 +75,7 @@ static void do_send(struct source *source, const char *mime_type, int fd) { /* Unset O_NONBLOCK */ fcntl(fd, F_SETFL, 0); - if (self->file_to_copy != NULL) { + if (self->copy_source.file_path != NULL) { /* Copy the file to the given file descriptor * by spawning an appropriate cat process. */ @@ -78,7 +88,7 @@ static void do_send(struct source *source, const char *mime_type, int fd) { if (pid == 0) { dup2(fd, STDOUT_FILENO); close(fd); - execlp("cat", "cat", self->file_to_copy, NULL); + execlp("cat", "cat", self->copy_source.file_path, NULL); perror("exec cat"); exit(1); } @@ -100,15 +110,20 @@ static void do_send(struct source *source, const char *mime_type, int fd) { return; } - if (self->data_to_copy.ptr != NULL) { + if (self->copy_source.data.ptr != NULL) { /* Just copy the given chunk of data */ - fwrite(self->data_to_copy.ptr, 1, self->data_to_copy.len, f); - } else if (self->argv_to_copy != NULL) { + fwrite( + self->copy_source.data.ptr, + 1, + self->copy_source.data.len, + f + ); + } else if (self->copy_source.argv != NULL) { /* Copy an argv-style string array, * inserting spaces between items. */ int is_first = 1; - for (argv_t word = self->argv_to_copy; *word != NULL; word++) { + for (argv_t word = self->copy_source.argv; *word != NULL; word++) { if (!is_first) { fwrite(" ", 1, 1, f); } @@ -141,18 +156,49 @@ void copy_action_init(struct copy_action *self) { self->source->cancelled_callback = forward_cancel; self->source->data = self; } + /* See if we can just set the selection directly */ if (!self->device->needs_popup_surface) { - /* If we can, it doesn't actually require - * a serial, so passing zero will do. + /* The simple case. + * In this case, setting the selection + * doesn't actually require a serial, + * so passing zero will do. */ - do_set_selection(self, 0); - } else { - /* If we cannot, schedule to do it later, - * when our popup surface gains keyboard focus. + device_set_selection(self->device, self->source, 0, self->primary); + /* Make sure it reaches the display + * before we do anything else. */ - self->popup_surface->on_focus = on_focus; - self->popup_surface->data = self; - popup_surface_init(self->popup_surface); + wl_display_roundtrip(self->device->wl_display); + /* And invoke the callback */ + if (self->did_set_selection_callback != NULL) { + self->did_set_selection_callback(self); + } + return; + } + + /* The complicated case. + * Add our action to the list of the pending + * actions to be done when the popup surface + * gets foces, and initialize both the surface + * and the array if needed. + */ + if (current_popup_surface == NULL) { + wl_array_init(&pending_selections); + } + struct copy_action **ptr = (struct copy_action **) wl_array_add( + &pending_selections, + sizeof(struct copy_action *) + ); + *ptr = self; + + /* Only initialize the surface after + * we've initialized the array, because + * it might get keyboard focus immediately. + */ + if (current_popup_surface == NULL) { + current_popup_surface = self->popup_surface; + current_popup_surface->on_focus = on_focus; + current_popup_surface->data = self; + popup_surface_init(current_popup_surface); } } diff --git a/src/types/copy-action.h b/src/types/copy-action.h index c0a1290..5d46f20 100644 --- a/src/types/copy-action.h +++ b/src/types/copy-action.h @@ -27,6 +27,18 @@ struct device; struct source; struct popup_surface; +struct copy_source { + /* Exactly one of these fields must be non-null if the source + * is non-null, otherwise all these fields must be null. + */ + const char *file_path; + argv_t argv; + struct { + const char *ptr; + size_t len; + } data; +}; + struct copy_action { /* These fields are initialized by the creator */ struct device *device; @@ -38,15 +50,7 @@ struct copy_action { void (*pasted_callback)(struct copy_action *self); void (*cancelled_callback)(struct copy_action *self); - /* Exactly one of these fields must be non-null if the source - * is non-null, otherwise all these fields must be null. - */ - const char *file_to_copy; - argv_t argv_to_copy; - struct { - const char *ptr; - size_t len; - } data_to_copy; + struct copy_source copy_source; }; void copy_action_init(struct copy_action *self); diff --git a/src/types/source.c b/src/types/source.c index 4c1ae38..94d57ce 100644 --- a/src/types/source.c +++ b/src/types/source.c @@ -21,12 +21,18 @@ #include "types/source.h" #include "util/string.h" -#include "unistd.h" +#include +#include void source_offer(struct source *self, char *mime_type) { self->do_offer(self->proxy, mime_type); } +void source_destroy(struct source *self) { + self->do_destroy(self->proxy); + free(self); +} + /* Macros to reduce implementation boilerplate */ @@ -66,6 +72,7 @@ static const struct type ## _listener type ## _listener = { \ void source_init_ ## type(struct source *self) { \ self->do_offer = \ (void (*)(struct wl_proxy *, const char *)) type ## _offer; \ + self->do_destroy = (void (*)(struct wl_proxy *)) type ## _destroy; \ struct type *proxy = (struct type *) self->proxy; \ type ## _add_listener(proxy, &type ## _listener, self); \ } diff --git a/src/types/source.h b/src/types/source.h index b471fe7..43c0675 100644 --- a/src/types/source.h +++ b/src/types/source.h @@ -30,11 +30,13 @@ struct source { struct wl_proxy *proxy; - /* This field is initialized by the implementation */ + /* These fields are initialized by the implementation */ void (*do_offer)(struct wl_proxy *proxy, const char *mime_type); + void (*do_destroy)(struct wl_proxy *proxy); }; void source_offer(struct source *self, char *mime_type); +void source_destroy(struct source *self); /* Initializers */ diff --git a/src/wl-copy.c b/src/wl-copy.c index 612e895..89948bc 100644 --- a/src/wl-copy.c +++ b/src/wl-copy.c @@ -28,6 +28,7 @@ #include "util/misc.h" #include +#include #include #include #include @@ -40,15 +41,26 @@ static struct { int trim_newline; int paste_once; int primary; - const char *seat_name; + int regular; + struct wl_array seat_names; } options; +static struct { + int unset; + int live; +} copy_actions_cnt; + static void did_set_selection_callback(struct copy_action *copy_action) { + copy_actions_cnt.unset--; + if (options.clear) { - exit(0); + copy_actions_cnt.live--; + if (copy_actions_cnt.live == 0) { + exit(0); + } } - if (!options.stay_in_foreground) { + if (!options.stay_in_foreground && copy_actions_cnt.unset == 0) { /* Move to background. * We fork our process and leave the * child running in the background, @@ -69,7 +81,7 @@ static void cleanup_and_exit(struct copy_action *copy_action) { /* We're done copying! * All that's left to do now is to * clean up after ourselves and exit.*/ - char *temp_file = (char *) copy_action->file_to_copy; + char *temp_file = (char *) copy_action->copy_source.file_path; if (temp_file != NULL) { /* Clean up our temporary file */ execlp("rm", "rm", "-r", dirname(temp_file), NULL); @@ -81,7 +93,10 @@ static void cleanup_and_exit(struct copy_action *copy_action) { } static void cancelled_callback(struct copy_action *copy_action) { - cleanup_and_exit(copy_action); + copy_actions_cnt.live--; + if (copy_actions_cnt.live == 0) { + cleanup_and_exit(copy_action); + } } static void pasted_callback(struct copy_action *copy_action) { @@ -90,6 +105,109 @@ static void pasted_callback(struct copy_action *copy_action) { } } +static void set_up_selection_for_seat( + struct wl_display *wl_display, + struct registry *registry, + struct seat *seat, + struct copy_source copy_source, + int primary +) { + /* Create the device */ + struct device_manager *device_manager + = registry_find_device_manager(registry, primary); + if (device_manager == NULL) { + complain_about_selection_support(primary); + } + + struct device *device = device_manager_get_device(device_manager, seat); + + if (!device_supports_selection(device, primary)) { + complain_about_selection_support(primary); + } + + copy_actions_cnt.live++; + copy_actions_cnt.unset++; + + /* Create and initialize the copy action */ + struct copy_action *copy_action = calloc(1, sizeof(struct copy_action)); + copy_action->device = device; + copy_action->primary = primary; + copy_action->copy_source = copy_source; + + /* Create the source */ + if (!options.clear) { + copy_action->source = device_manager_create_source(device_manager); + if (options.mime_type != NULL) { + source_offer(copy_action->source, options.mime_type); + } + if (options.mime_type == NULL || mime_type_is_text(options.mime_type)) { + /* Offer a few generic plain text formats */ + source_offer(copy_action->source, text_plain); + source_offer(copy_action->source, text_plain_utf8); + source_offer(copy_action->source, "TEXT"); + source_offer(copy_action->source, "STRING"); + source_offer(copy_action->source, "UTF8_STRING"); + } + } + + if (device->needs_popup_surface) { + copy_action->popup_surface = calloc(1, sizeof(struct popup_surface)); + copy_action->popup_surface->registry = registry; + copy_action->popup_surface->seat = seat; + } + + copy_action->did_set_selection_callback = did_set_selection_callback; + copy_action->pasted_callback = pasted_callback; + copy_action->cancelled_callback = cancelled_callback; + copy_action_init(copy_action); +} + +static void set_up_selection( + struct wl_display *wl_display, + struct registry *registry, + struct copy_source copy_source, + int primary +) { + struct seat *seat = NULL; + + /* Go over the requested seat names */ + for ( + const char *seat_name = options.seat_names.data; + seat_name != options.seat_names.data + options.seat_names.size; + seat_name += strlen(seat_name) + 1 + ) { + seat = registry_find_seat(registry, seat_name); + if (seat == NULL) { + bail("No such seat"); + } + set_up_selection_for_seat( + wl_display, + registry, + seat, + copy_source, + primary + ); + } + + /* See if any seat was requested at all */ + if (seat == NULL) { + /* No seat was explicitly requested. + * Try to find any seat. + */ + seat = registry_find_seat(registry, NULL); + if (seat == NULL) { + bail("Missing a seat"); + } + set_up_selection_for_seat( + wl_display, + registry, + seat, + copy_source, + primary + ); + } +} + static void print_usage(FILE *f, const char *argv0) { fprintf( f, @@ -101,6 +219,7 @@ static void print_usage(FILE *f, const char *argv0) { "\t-o, --paste-once\tOnly serve one paste request and then exit.\n" "\t-f, --foreground\tStay in the foreground instead of forking.\n" "\t-c, --clear\t\tInstead of copying anything, clear the clipboard.\n" + "\t-r, --regular\t\tUse the regular clipboard.\n" "\t-p, --primary\t\tUse the \"primary\" clipboard.\n" "\t-n, --trim-newline\tDo not copy the trailing newline character.\n" "\t-t, --type mime/type\t" @@ -122,6 +241,8 @@ static void parse_options(int argc, argv_t argv) { bail("Empty argv"); } + wl_array_init(&options.seat_names); + static struct option long_options[] = { {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, @@ -130,13 +251,14 @@ static void parse_options(int argc, argv_t argv) { {"paste-once", no_argument, 0, 'o'}, {"foreground", no_argument, 0, 'f'}, {"clear", no_argument, 0, 'c'}, + {"regular", no_argument, 0, 'r'}, {"type", required_argument, 0, 't'}, {"seat", required_argument, 0, 's'}, {0, 0, 0, 0} }; while (1) { int option_index; - const char *opts = "vhpnofct:s:"; + const char *opts = "vhpnofcrt:s:"; int c = getopt_long(argc, argv, opts, long_options, &option_index); if (c == -1) { break; @@ -166,11 +288,15 @@ static void parse_options(int argc, argv_t argv) { case 'c': options.clear = 1; break; + case 'r': + options.regular = 1; + break; case 't': options.mime_type = strdup(optarg); break; - case 's': - options.seat_name = strdup(optarg); + case 's':; + char *ptr = wl_array_add(&options.seat_names, strlen(optarg) + 1); + strcpy(ptr, optarg); break; default: /* getopt has already printed an error message */ @@ -195,37 +321,12 @@ int main(int argc, argv_t argv) { /* Wait for the initial set of globals to appear */ wl_display_roundtrip(wl_display); - struct seat *seat = registry_find_seat(registry, options.seat_name); - if (seat == NULL) { - if (options.seat_name != NULL) { - bail("No such seat"); - } else { - bail("Missing a seat"); - } - } - - /* Create the device */ - struct device_manager *device_manager - = registry_find_device_manager(registry, options.primary); - if (device_manager == NULL) { - complain_about_selection_support(options.primary); - } - - struct device *device = device_manager_get_device(device_manager, seat); - - if (!device_supports_selection(device, options.primary)) { - complain_about_selection_support(options.primary); - } - - /* Create and initialize the copy action */ - struct copy_action *copy_action = calloc(1, sizeof(struct copy_action)); - copy_action->device = device; - copy_action->primary = options.primary; + struct copy_source copy_source = { 0 }; if (!options.clear) { if (optind < argc) { /* Copy our command-line arguments */ - copy_action->argv_to_copy = &argv[optind]; + copy_source.argv = &argv[optind]; } else { /* Copy data from our stdin */ char *temp_file = dump_stdin_into_a_temp_file(); @@ -235,36 +336,20 @@ int main(int argc, argv_t argv) { if (options.mime_type == NULL) { options.mime_type = infer_mime_type_from_contents(temp_file); } - copy_action->file_to_copy = temp_file; + copy_source.file_path = temp_file; } - - /* Create the source */ - copy_action->source = device_manager_create_source(device_manager); - if (options.mime_type != NULL) { - source_offer(copy_action->source, options.mime_type); - } - if (options.mime_type == NULL || mime_type_is_text(options.mime_type)) { - /* Offer a few generic plain text formats */ - source_offer(copy_action->source, text_plain); - source_offer(copy_action->source, text_plain_utf8); - source_offer(copy_action->source, "TEXT"); - source_offer(copy_action->source, "STRING"); - source_offer(copy_action->source, "UTF8_STRING"); - } - free(options.mime_type); - options.mime_type = NULL; } - if (device->needs_popup_surface) { - copy_action->popup_surface = calloc(1, sizeof(struct popup_surface)); - copy_action->popup_surface->registry = registry; - copy_action->popup_surface->seat = seat; + /* By default, or if the regular clipboard + * is explicitly requested, use the regular + * clipboard. + */ + if (!options.primary || options.regular) { + set_up_selection(wl_display, registry, copy_source, 0); + } + if (options.primary) { + set_up_selection(wl_display, registry, copy_source, 1); } - - copy_action->did_set_selection_callback = did_set_selection_callback; - copy_action->pasted_callback = pasted_callback; - copy_action->cancelled_callback = cancelled_callback; - copy_action_init(copy_action); while (wl_display_dispatch(wl_display) >= 0);