summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Sateler <fsateler@debian.org>2023-04-30 09:48:22 -0400
committerFelipe Sateler <fsateler@debian.org>2023-04-30 09:48:22 -0400
commitf4ea24f7e793aac436242dc8168cb40b7b9a007f (patch)
tree802c3017db57b75b06dd0b92075d1897d315307b
parentbc9c7aa80e149c1792a4a8c81972464cd61544e9 (diff)
parent55498ede94720e2db45ab8d70b4f5526eb1f0fbe (diff)
Update upstream source from tag 'upstream/2.2.0_rc1'
Update to upstream version '2.2.0~rc1' with Debian dir dcd35e4e034a07e3dbe7b40ee83ca7dbaa2e691b
-rw-r--r--BUILDING.md42
-rw-r--r--README.md96
-rw-r--r--completions/fish/meson.build6
-rw-r--r--data/wl-clipboard.1175
-rw-r--r--meson.build2
-rw-r--r--src/includes/selection-protocols.h2
-rw-r--r--src/includes/shell-protocols.h11
-rw-r--r--src/meson.build78
-rw-r--r--src/protocol/gtk-shell.xml107
-rw-r--r--src/protocol/meson.build97
-rw-r--r--src/types/copy-action.c15
-rw-r--r--src/types/copy-action.h5
-rw-r--r--src/types/device-manager.c2
-rw-r--r--src/types/device-manager.h2
-rw-r--r--src/types/device.c7
-rw-r--r--src/types/device.h2
-rw-r--r--src/types/keyboard.c2
-rw-r--r--src/types/keyboard.h2
-rw-r--r--src/types/offer.c2
-rw-r--r--src/types/offer.h2
-rw-r--r--src/types/popup-surface.c57
-rw-r--r--src/types/popup-surface.h8
-rw-r--r--src/types/registry.c10
-rw-r--r--src/types/registry.h9
-rw-r--r--src/types/seat.c2
-rw-r--r--src/types/seat.h2
-rw-r--r--src/types/shell-surface.c2
-rw-r--r--src/types/shell-surface.h2
-rw-r--r--src/types/shell.c2
-rw-r--r--src/types/shell.h2
-rw-r--r--src/types/source.c2
-rw-r--r--src/types/source.h2
-rw-r--r--src/util/files.c6
-rw-r--r--src/util/files.h2
-rw-r--r--src/util/misc.c81
-rw-r--r--src/util/misc.h6
-rw-r--r--src/util/string.c6
-rw-r--r--src/util/string.h2
-rw-r--r--src/wl-copy.c71
-rw-r--r--src/wl-paste.c132
40 files changed, 757 insertions, 306 deletions
diff --git a/BUILDING.md b/BUILDING.md
new file mode 100644
index 0000000..82af29e
--- /dev/null
+++ b/BUILDING.md
@@ -0,0 +1,42 @@
+# Building
+
+wl-clipboard is a simple Meson project, so building it is just:
+
+```bash
+# Clone:
+$ git clone https://github.com/bugaevc/wl-clipboard.git
+$ cd wl-clipboard
+
+# Build:
+$ meson setup build
+$ cd build
+$ ninja
+
+# Install
+$ sudo meson install
+```
+
+wl-clipboard supports Linux and BSD systems, and is also known to work on
+Mac OS X and GNU Hurd. The only mandatory dependency is the `wayland-client`
+library (try package named `wayland-devel` or `libwayland-dev`).
+
+Optional (but highly recommended) dependencies for building:
+* `wayland-scanner`
+* `wayland-protocols` (version 1.12 or later)
+
+If these are found during configuration, wl-clipboard gets built with
+additional protocols support, which enables features such as primary selection
+support and `--watch` mode. Note that many compositors have dropped support for
+the `wl_shell` interface, which means wl-clipboard will not work under them
+unless built with both `wayland-scanner` and `wayland-protocols`.
+
+Optional dependencies for running:
+* `xdg-mime` for content type inference in `wl-copy` (try package named
+ `xdg-utils`)
+* `/etc/mime.types` file for type inference in `wl-paste` (try package named
+ `mime-support` or `mailcap`)
+
+If you're packaging wl-clipboard for a distribution, please consider making
+packages providing `xdg-mime` and `/etc/mime.types` *weak* dependencies of the
+package providing wl-clipboard, meaning ones that get installed along with
+wl-clipboard by default, but are not strictly required by it.
diff --git a/README.md b/README.md
index 915db62..b0ebd16 100644
--- a/README.md
+++ b/README.md
@@ -4,96 +4,35 @@ This project implements two command-line Wayland clipboard utilities, `wl-copy`
and `wl-paste`, that let you easily copy data between the clipboard and Unix
pipes, sockets, files and so on.
-Usage is as simple as:
-
```bash
-# copy a simple text message
+# Copy a simple text message:
$ wl-copy Hello world!
-# copy the list of files in Downloads
+# Copy the list of files in ~/Downloads:
$ ls ~/Downloads | wl-copy
-# copy an image file
+# Copy an image:
$ wl-copy < ~/Pictures/photo.png
-# paste to a file
-$ wl-paste > clipboard.txt
-
-# grep each pasted word in file source.c
-$ for word in $(wl-paste); do grep $word source.c; done
-
-# copy the previous command
+# Copy the previous command:
$ wl-copy "!!"
-# replace the current selection with the list of types it's offered in
-$ wl-paste --list-types | wl-copy
-```
-
-Although `wl-copy` and `wl-paste` are particularly optimized for plain text and
-other textual content formats, they fully support content of arbitrary MIME
-types. `wl-copy` automatically infers the type of the copied content by running
-`xdg-mime(1)` on it. `wl-paste` tries its best to pick a type to paste based on
-the list of offered MIME types and the extension of the file it's pasting into.
-If you're not satisfied with the type they pick or don't want to rely on this
-implicit type inference, you can explicitly specify the type to use with the
-`--type` option.
-
-# Options
-
-For `wl-copy`:
-
-* `-n`, `--trim-newline` Do not copy the trailing newline character if it is present in the input file.
-* `-o`, `--paste-once` Only serve one paste request and then exit. Unless a clipboard manager specifically designed to prevent this is in use, this has the effect of clearing the clipboard after the first paste, which is useful for copying sensitive data such as passwords. Note that this may break pasting into some clients, in particular pasting into XWayland windows is known to break when this option is used.
-* `-f`, `--foreground` By default, `wl-copy` forks and serves data requests in the background; this option overrides that behavior, causing `wl-copy` to run in the foreground.
-* `-c`, `--clear` Instead of copying anything, clear the clipboard so that nothing is copied.
-
-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 and when using the `--watch` mode.
-* `-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:
-
-* `-p`, `--primary` Use the "primary" clipboard instead of the regular clipboard.
-* `-t mime/type`, `--type mime/type` Override the inferred MIME type for the content. For `wl-copy` this option controls which type `wl-copy` will offer the content as. For `wl-paste` it controls which of the offered types `wl-paste` will request the content in. In addition to specific MIME types such as _image/png_, `wl-paste` also accepts generic type names such as _text_ and _image_ which make it automatically pick some offered MIME type that matches the given generic name.
-* `-s seat-name`, `--seat seat-name` Specify which seat `wl-copy` and `wl-paste` should work with. Wayland natively supports multi-seat configurations where each seat gets its own mouse pointer, keyboard focus, and among other things its own separate clipboard. The name of the default seat is likely _default_ or _seat0_, and additional seat names normally come form `udev(7)` property `ENV{WL_SEAT}`. You can view the list of the currently available seats as advertised by the compositor using the `weston-info(1)` tool. If you don't specify the seat name explicitly, `wl-copy` and `wl-paste` will pick a seat arbitrarily. If you are using a single-seat system, there is little reason to use this option.
-* `-v`, `--version` Display the version of wl-clipboard and some short info about its license.
-* `-h`, `--help` Display a short help message listing the available options.
-
-# Building
-
-wl-clipboard is a simple Meson project, so building it is just:
-
-```bash
-# clone
-$ git clone https://github.com/bugaevc/wl-clipboard.git
-$ cd wl-clipboard
+# Paste to a file:
+$ wl-paste > clipboard.txt
-# build
-$ meson build
-$ cd build
-$ ninja
+# Sort clipboard contents:
+$ wl-paste | sort | wl-copy
-# install
-$ sudo ninja install
+# Upload clipboard contents to a pastebin on each change:
+$ wl-paste --watch nc paste.example.org 5555
```
-wl-clipboard supports Linux and BSD systems, and is also known to work on
-Mac OS X and GNU Hurd. The only mandatory dependency is the `wayland-client`
-library (try package named `wayland-devel` or `libwayland-dev`).
-
-Optional dependencies for building:
-* `wayland-scanner`
-* `wayland-protocols` (version 1.12 or later)
+Please see the wl-clipboard(1) man page for more details.
-If these are found during configuration, wl-clipboard gets built with
-additional protocols support, which enables features such as primary selection
-support and `--watch` mode.
+# Installing
-Optional dependencies for running:
-* `xdg-mime` for content type inference in `wl-copy` (try package named `xdg-utils`)
-* `/etc/mime.types` file for type inference in `wl-paste` (try package named `mime-support` or `mailcap`)
+wl-clipboard is likely available in your favorite Linux or BSD distro. For
+building from source, see [BUILDING.md](BUILDING.md).
# License
@@ -102,5 +41,8 @@ version 3 or later.
# Related projects
-* [wl-clipboard-x11](https://github.com/brunelli/wl-clipboard-x11): A wrapper to use wl-clipboard as a drop-in replacement to X11 clipboard tools.
-* [wl-clipboard-rs](https://github.com/YaLTeR/wl-clipboard-rs): A Rust crate (library) for working with the Wayland clipboard which includes a reimplementation of `wl-copy` and `wl-paste`.
+* [wl-clipboard-x11](https://github.com/brunelli/wl-clipboard-x11): A wrapper to
+ use wl-clipboard as a drop-in replacement to X11 clipboard tools.
+* [wl-clipboard-rs](https://github.com/YaLTeR/wl-clipboard-rs): A Rust crate
+ (library) for working with the Wayland clipboard which includes a
+ reimplementation of `wl-copy` and `wl-paste`.
diff --git a/completions/fish/meson.build b/completions/fish/meson.build
index b0bc411..cb1280c 100644
--- a/completions/fish/meson.build
+++ b/completions/fish/meson.build
@@ -2,7 +2,11 @@ fish_completion_dir = get_option('fishcompletiondir')
fish = dependency('fish', required : false)
if fish_completion_dir == '' and fish.found()
- fish_completion_dir = fish.get_pkgconfig_variable('completionsdir')
+ if meson.version().version_compare('>= 0.58')
+ fish_completion_dir = fish.get_variable('completionsdir')
+ else
+ fish_completion_dir = fish.get_pkgconfig_variable('completionsdir')
+ endif
elif fish_completion_dir == ''
# Fish does not look in /usr/local/, which is the default prefix,
# so we cannot use get_option("datadir"). Instead, they recommend
diff --git a/data/wl-clipboard.1 b/data/wl-clipboard.1
index 93feed9..b6f7de5 100644
--- a/data/wl-clipboard.1
+++ b/data/wl-clipboard.1
@@ -1,24 +1,17 @@
-.TH WL-CLIPBOARD 1 2019-09-16 wl-clipboard
+.TH WL-CLIPBOARD 1 2023-04-22 wl-clipboard
.SH NAME
wl-clipboard \- Wayland copy and paste command line utilities
.SH SYNOPSIS
.B wl-copy
-[\fB--primary\fR]
-[\fB--trim-newline\fR]
-[\fB--paste-once\fR]
-[\fB--foreground\fR]
-[\fB--clear\fR]
-[\fB--type \fImime/type\fR]
-[\fB--seat \fIseat-name\fR]
+[\fB\-\-primary\fR]
+[\fB\-\-type \fImime/type\fR]
[\fItext\fR...]
.PP
.B wl-paste
-[\fB--primary\fR]
-[\fB--no-newline\fR]
-[\fB--list-types\fR]
-[\fB--type \fImime/type\fR]
-[\fB--seat \fIseat-name\fR]
-[\fB--watch \fIcommand\fR...\]
+[\fB\-\-primary\fR]
+[\fB\-\-type \fImime/type\fR]
+.PP
+Only the most useful options are listed here; see below for the full list.
.SH DESCRIPTION
\fBwl-copy\fR copies the given \fItext\fR to the Wayland clipboard.
If no \fItext\fR is given, \fBwl-copy\fR copies data from its standard input.
@@ -32,36 +25,36 @@ running \fBxdg-mime\fR(1) on it. \fBwl-paste\fR tries its best to pick a type to
paste based on the list of offered MIME types and the extension of the file it's
pasting into. If you're not satisfied with the type they pick or don't want to
rely on this implicit type inference, you can explicitly specify the type to use
-with the \fB--type\fR option.
+with the \fB\-\-type\fR option.
.SH OPTIONS
.TP
-\fB-p\fR, \fB--primary
+\fB\-p\fR, \fB\-\-primary
Use the "primary" clipboard instead of the regular clipboard.
.TP
-\fB-o\fR, \fB--paste-once
+\fB\-o\fR, \fB\-\-paste-once\fR (for \fBwl-copy\fR)
Only serve one paste request and then exit. Unless a clipboard manager
specifically designed to prevent this is in use, this has the effect of clearing
the clipboard after the first paste, which is useful for copying sensitive data
-such as passwords. Note that this may break pasting into some clients, in
-particular pasting into XWayland windows is known to break when this option is
-used.
+such as passwords. Note that this may break pasting into some clients that
+expect to be able to paste multiple times, in particular pasting into XWayland
+windows is known to break when this option is used.
.TP
-\fB-f\fR, \fB--foreground
+\fB\-f\fR, \fB\-\-foreground\fR (for \fBwl-copy\fR)
By default, \fBwl-copy\fR forks and serves data requests in the background; this
option overrides that behavior, causing \fBwl-copy\fR to run in the foreground.
.TP
-\fB-c\fR, \fB--clear
+\fB\-c\fR, \fB\-\-clear\fR (for \fBwl-copy\fR)
Instead of copying anything, clear the clipboard so that nothing is copied.
.TP
-\fB-n\fR, \fB--trim-newline
+\fB\-n\fR, \fB\-\-trim-newline\fR (for \fBwl-copy\fR)
Do not copy the trailing newline character if it is present in the input file.
.TP
-\fB-n\fR, \fB--no-newline
+\fB\-n\fR, \fB\-\-no-newline\fR (for \fBwl-paste\fR)
Do not append a newline character after the pasted clipboard content. This
option is automatically enabled for non-text content types and when using the
-\fB--watch\fR mode.
+\fB\-\-watch\fR mode.
.TP
-\fB-t\fI mime/type\fR, \fB--type\fI mime/type
+\fB\-t\fI mime/type\fR, \fB\-\-type\fI mime/type
Override the automatically selected MIME type. For \fBwl-copy\fR this option
controls which type \fBwl-copy\fR will offer the content as. For \fBwl-paste\fR
it controls which of the offered types \fBwl-paste\fR will request the content
@@ -69,7 +62,7 @@ in. In addition to specific MIME types such as \fIimage/png\fR, \fBwl-paste\fR
also accepts generic type names such as \fItext\fR and \fIimage\fR which make it
automatically pick some offered MIME type that matches the given generic name.
.TP
-\fB-s\fI seat-name\fR, \fB--seat\fI seat-name
+\fB\-s\fI seat-name\fR, \fB\-\-seat\fI seat-name
Specify which seat \fBwl-copy\fR and \fBwl-paste\fR should work with. Wayland
natively supports multi-seat configurations where each seat gets its own mouse
pointer, keyboard focus, and among other things its own separate clipboard. The
@@ -83,24 +76,27 @@ tool. If you don't specify the seat name explicitly, \fBwl-copy\fR and
\fBwl-paste\fR will pick a seat arbitrarily. If you are using a single-seat
system, there is little reason to use this option.
.TP
-\fB-l\fR, \fB--list-types
+\fB\-l\fR, \fB\-\-list-types\fR (for \fBwl-paste\fR)
Instead of pasting the selection, output the list of MIME types it is offered
in.
.TP
-\fB-w\fI command\fR..., \fB--watch \fIcommand\fR...
+\fB\-w\fI command\fR..., \fB\-\-watch \fIcommand\fR... (for \fBwl-paste\fR)
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.
+\fBwl-paste\fR also sets the \fBCLIPBOARD_STATE\fR variable in the environment
+of the spawned processes (see below).
+.IP
This mode requires a compositor that supports the wlroots data-control protocol.
.TP
-\fB-v\fR, \fB--version
+\fB\-v\fR, \fB\-\-version
Display the version of wl-clipboard and some short info about its license.
.TP
-\fB-h\fR, \fB--help
+\fB\-h\fR, \fB\-\-help
Display a short help message listing the available options.
.SH ENVIRONMENT
.TP
-WAYLAND_DISPLAY
+.B WAYLAND_DISPLAY
Specifies what Wayland server \fBwl-copy\fR and \fBwl-paste\fR should connect
to. This is the same environment variable that you pass to other Wayland
clients, such as graphical applications, that connect to this Wayland server. It
@@ -109,34 +105,131 @@ compositor. See
.BR wl_display_connect (3)
for more details.
.TP
-WAYLAND_DEBUG
+.B WAYLAND_DEBUG
When set to \fB1\fR, causes the \fBwayland-client\fR(7) library to log every
interaction \fBwl-copy\fR and \fBwl-paste\fR make with the Wayland compositor to
stderr.
+.TP
+.B CLIPBOARD_STATE
+Set by \fBwl-paste\fR for the spawned command in \fB\-\-watch\fR mode. Currently
+the following possible values are \fIdefined\fR:
+.RS
+.TP
+CLIPBOARD_STATE=\fBdata
+Indicates that the clipboard contains data that the spawned command can read
+from its standard input. This is the most common case.
+.TP
+CLIPBOARD_STATE=\fBnil
+Indicates that the clipboard is empty. In this case the spawned command's
+standard input will be attached to \fI/dev/null\fR. Note that this is subtly
+different from the clipboard containing zero-sized data (which can be achieved,
+for instance, by running \fBwl-copy < /dev/null\fR).
+.TP
+CLIPBOARD_STATE=\fBclear
+Indicates that the clipboard is empty because of an explicit clear request, such
+as after running \fBwl-copy --clear\fR. As for \fBnil\fR, the command's standard
+input will be attached to \fI/dev/null\fR.
+.TP
+CLIPBOARD_STATE=\fBsensitive
+Indicates that the clipboard contains sensitive data such as a password or a
+key. It is probably best to avoid visibly displaying or persistently saving
+clipboard contents.
+.RE
+.IP
+Any client programs implementing the \fBCLIPBOARD_STATE\fR protocol are
+encouraged to implement proper support for all the values listed above, as well
+as to fall back to some sensible behavior if \fBCLIPBOARD_STATE\fR is unset or
+set to some unrecognized value (this is to leave the design space open for
+future extensions). However, the currently existing Wayland clipboard protocols
+don't let wl-clipboard identify the cases where \fBclear\fR and \fBsensitive\fR
+values should be set, so currently wl-clipboard only ever sets
+\fBCLIPBOARD_STATE\fR to \fBdata\fR or \fBnil\fR.
+.IP
+The \fBCLIPBOARD_STATE\fR protocol was intentionally designed to not be specific
+to either wl-clipboard or Wayland; in fact, other clipboard tools are encouraged
+to implement the same protocol. Currently, the SerenityOS
+.BR paste (1)
+utility is known to implement the same \fBCLIPBOARD_STATE\fR protocol.
+.SH FILES
+.TP
+.I /etc/mime.types
+If present, read by \fBwl-paste\fR to infer the MIME type to paste in based on
+the file name extension of its standard output.
+.SH BUGS
+Unless the Wayland compositor implements the wlroots data-control protocol,
+wl-clipboard has to resort to using a hack to access the clipboard: it will
+briefly pop up a tiny transparent surface (window). On some desktop
+environments (in particular when using tiling window managers), this can cause
+visual issues such as brief flashing. In some cases the Wayland compositor
+doesn't give focus to the popup surface, which prevents wl-clipboard from
+accessing the clipboard and manifests as a hang.
+.PP
+There is currently no way to copy data in multiple MIME types, such as multiple
+image formats, at the same time.
+.br
+See
+.nh
+<https://github.com/bugaevc/wl-clipboard/issues/71>.
+.hy
+.PP
+wl-clipboard is not always able to detect that a MIME type is textual, which may
+break pasting into clients that expect textual formats, not
+\fIapplication/something\fR. The workaround, same as for all format inference
+issues, is to specify the desired MIME type explicitly, such as
+\fBwl-copy \-\-type\fI text/plain\fR.
+.PP
+\fBwl-copy \-\-clear\fR and \fBwl-copy \-\-paste-once\fR don't always interact
+well with clipboard managers that are overeager to preserve clipboard contents.
+.PP
+Applications written using the GTK 3 toolkit copy text with \(dq\er\en\(dq (also
+known as CR LF, 13 10 in ASCII) line endings, which takes most other software by
+surprise. wl-cipboard does nothing to rectify this. The recommended workaround
+is piping \fBwl-paste\fR output through
+.BR dos2unix (1)
+when pasting from a GTK 3 application.
+.br
+See
+.nh
+<https://gitlab.gnome.org/GNOME/gtk/-/issues/2307>.
+.hy
.SH EXAMPLES
+.TP
+Copy a simple text message:
$
.BI wl-copy " Hello world!"
-.PP
+.TP
+Copy the list of files in \fI~/Downloads\fR:
$
.IB "ls ~/Downloads" " | wl-copy"
-.PP
+.TP
+Copy an image:
$
.BI "wl-copy < " ~/Pictures/photo.png
-.PP
+.TP
+Copy the previous command:
$
.B wl-copy \(dq!!\(dq
-.PP
+.TP
+Paste to a file, without appending a newline:
$
-.BI "wl-paste -n > " clipboard.txt
-.PP
+.BI "wl-paste \-n > " clipboard.txt
+.TP
+Sort clipboard contents:
$
-.B wl-paste --list-types | wl-copy
+.B wl-paste | sort | wl-copy
+.TP
+Upload clipboard contents to a pastebin on each change:
+$
+.BI "wl-paste --watch nc " "paste.example.org 5555
.SH AUTHOR
Written by Sergey Bugaev.
.SH REPORTING BUGS
Report wl-clipboard bugs to <https://github.com/bugaevc/wl-clipboard/issues>
+.br
+Please make sure to mention which Wayland compositor you are using, and attach
+\fBWAYLAND_DEBUG=1\fR debugging logs of wl-clipboard.
.SH COPYRIGHT
-Copyright \(co 2019 Sergey Bugaev.
+Copyright \(co 2018-2023 Sergey Bugaev.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
.br
This is free software: you are free to change and redistribute it.
diff --git a/meson.build b/meson.build
index 81ad51f..1879fba 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
project('wl-clipboard', 'c',
- version: '2.1.0',
+ version: '2.2.0-rc1',
license: 'GPL3+',
meson_version: '>= 0.44.0',
default_options: 'c_std=gnu99'
diff --git a/src/includes/selection-protocols.h b/src/includes/selection-protocols.h
index 32e74a7..768cdf1 100644
--- a/src/includes/selection-protocols.h
+++ b/src/includes/selection-protocols.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/includes/shell-protocols.h b/src/includes/shell-protocols.h
index feb39f5..a71bddc 100644
--- a/src/includes/shell-protocols.h
+++ b/src/includes/shell-protocols.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -27,4 +27,13 @@
# include "xdg-shell.h"
#endif
+#ifdef HAVE_GTK_SHELL
+# include "gtk-shell.h"
+#endif
+
+/* Not strictly speaking a shell */
+#ifdef HAVE_XDG_ACTIVATION
+# include "xdg-activation.h"
+#endif
+
#endif /* INCLUDES_SHELL_PROTOCOLS_H */
diff --git a/src/meson.build b/src/meson.build
index 45c2f67..37a638c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,91 +1,17 @@
wayland = dependency('wayland-client')
-wayland_scanner = find_program('wayland-scanner', required: false, native: true)
-wayland_protocols = dependency('wayland-protocols', version: '>= 1.12', required: false)
-
-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_gtk_primary_selection = true
- have_wlr_data_control = true
-
- if wayland.version().version_compare('>= 1.15')
- scanner_code_command = 'private-code'
- else
- scanner_code_command = 'code'
- endif
-else
- have_xdg_shell = false
- have_gtk_primary_selection = false
- have_wlr_data_control = false
-endif
-
cc = meson.get_compiler('c')
have_memfd = cc.has_header_symbol('sys/syscall.h', 'SYS_memfd_create')
have_shm_anon = cc.has_header_symbol('sys/mman.h', 'SHM_ANON')
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_GTK_PRIMARY_SELECTION', have_gtk_primary_selection)
-conf_data.set('HAVE_WLR_DATA_CONTROL', have_wlr_data_control)
-
conf_data.set('HAVE_MEMFD', have_memfd)
conf_data.set('HAVE_SHM_ANON', have_shm_anon)
-configure_file(output: 'config.h', configuration: conf_data)
-
-if wayland_protocols.found()
- protocols_path = wayland_protocols.get_pkgconfig_variable('pkgdatadir')
-endif
-
-protocols = []
+subdir('protocol')
-if not have_xdg_shell
- warning('Building without xdg-shell support')
-else
- xdg_shell_xml = join_paths(protocols_path, 'stable', 'xdg-shell', 'xdg-shell.xml')
- protocols += [['xdg-shell', xdg_shell_xml]]
-endif
-
-if have_wp_primary_selection
- wp_primary_selection_xml = join_paths(protocols_path, 'unstable', 'primary-selection', 'primary-selection-unstable-v1.xml')
- protocols += [['wp-primary-selection', wp_primary_selection_xml]]
-endif
-
-if not have_gtk_primary_selection
- warning('Building without primary selection support')
-else
- gtk_primary_selection_xml = join_paths('protocol', 'gtk-primary-selection.xml')
- protocols += [['gtk-primary-selection', gtk_primary_selection_xml]]
-endif
-
-if have_wlr_data_control
- wlr_data_control_xml = join_paths('protocol', 'wlr-data-control-unstable-v1.xml')
- protocols += [['wlr-data-control', wlr_data_control_xml]]
-endif
-
-protocol_deps = []
-protocol_headers = []
-
-foreach protocol : protocols
- name = protocol[0]
- xml = protocol[1]
- header = custom_target(name + ' client header',
- input: xml, output: name + '.h',
- command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@']
- )
- impl = custom_target(name + ' code',
- input: xml, output: name + '.c',
- command: [wayland_scanner, scanner_code_command, '@INPUT@', '@OUTPUT@']
- )
- protocol_headers += header
- lib = static_library(name, impl, header, dependencies: wayland)
- protocol_deps += lib
-endforeach
+configure_file(output: 'config.h', configuration: conf_data)
lib = static_library(
'wl-clipboard',
diff --git a/src/protocol/gtk-shell.xml b/src/protocol/gtk-shell.xml
new file mode 100644
index 0000000..a8d51c6
--- /dev/null
+++ b/src/protocol/gtk-shell.xml
@@ -0,0 +1,107 @@
+<protocol name="gtk">
+
+ <interface name="gtk_shell1" version="5">
+ <description summary="gtk specific extensions">
+ gtk_shell is a protocol extension providing additional features for
+ clients implementing it.
+ </description>
+
+ <enum name="capability">
+ <entry name="global_app_menu" value="1"/>
+ <entry name="global_menu_bar" value="2"/>
+ <entry name="desktop_icons" value="3"/>
+ </enum>
+
+ <event name="capabilities">
+ <arg name="capabilities" type="uint"/>
+ </event>
+
+ <request name="get_gtk_surface">
+ <arg name="gtk_surface" type="new_id" interface="gtk_surface1"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="set_startup_id">
+ <arg name="startup_id" type="string" allow-null="true"/>
+ </request>
+
+ <request name="system_bell">
+ <arg name="surface" type="object" interface="gtk_surface1" allow-null="true"/>
+ </request>
+
+ <!-- Version 3 additions -->
+ <request name="notify_launch" since="3">
+ <arg name="startup_id" type="string"/>
+ </request>
+ </interface>
+
+ <interface name="gtk_surface1" version="5">
+ <request name="set_dbus_properties">
+ <arg name="application_id" type="string" allow-null="true"/>
+ <arg name="app_menu_path" type="string" allow-null="true"/>
+ <arg name="menubar_path" type="string" allow-null="true"/>
+ <arg name="window_object_path" type="string" allow-null="true"/>
+ <arg name="application_object_path" type="string" allow-null="true"/>
+ <arg name="unique_bus_name" type="string" allow-null="true"/>
+ </request>
+
+ <request name="set_modal"/>
+ <request name="unset_modal"/>
+
+ <request name="present">
+ <arg name="time" type="uint"/>
+ </request>
+
+ <!-- Version 2 additions -->
+
+ <enum name="state">
+ <entry name="tiled" value="1"/>
+
+ <entry name="tiled_top" value="2" since="2" />
+ <entry name="tiled_right" value="3" since="2" />
+ <entry name="tiled_bottom" value="4" since="2" />
+ <entry name="tiled_left" value="5" since="2" />
+ </enum>
+
+ <enum name="edge_constraint" since="2">
+ <entry name="resizable_top" value="1"/>
+ <entry name="resizable_right" value="2"/>
+ <entry name="resizable_bottom" value="3"/>
+ <entry name="resizable_left" value="4"/>
+ </enum>
+
+ <event name="configure">
+ <arg name="states" type="array"/>
+ </event>
+
+ <event name="configure_edges" since="2">
+ <arg name="constraints" type="array"/>
+ </event>
+
+ <!-- Version 3 additions -->
+ <request name="request_focus" since="3">
+ <arg name="startup_id" type="string" allow-null="true"/>
+ </request>
+
+ <!-- Version 4 additions -->
+ <request name="release" type="destructor" since="4"/>
+
+ <!-- Version 5 additions -->
+ <enum name="gesture" since="5">
+ <entry name="double_click" value="1"/>
+ <entry name="right_click" value="2"/>
+ <entry name="middle_click" value="3"/>
+ </enum>
+
+ <enum name="error" since="5">
+ <entry name="invalid_gesture" value="0"/>
+ </enum>
+
+ <request name="titlebar_gesture" since="5">
+ <arg name="serial" type="uint"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ <arg name="gesture" type="uint" enum="gesture"/>
+ </request>
+ </interface>
+
+</protocol>
diff --git a/src/protocol/meson.build b/src/protocol/meson.build
new file mode 100644
index 0000000..0d1a47e
--- /dev/null
+++ b/src/protocol/meson.build
@@ -0,0 +1,97 @@
+wayland_scanner = find_program('wayland-scanner', required: false, native: true)
+wayland_protocols = dependency('wayland-protocols', version: '>= 1.12', required: false)
+
+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')
+ have_xdg_activation = wayland_protocols.found() and wayland_protocols.version().version_compare('>= 1.21')
+ # these are bundled
+ have_gtk_primary_selection = true
+ have_wlr_data_control = true
+ have_gtk_shell = true
+
+ if wayland.version().version_compare('>= 1.15')
+ scanner_code_command = 'private-code'
+ else
+ scanner_code_command = 'code'
+ endif
+else
+ have_xdg_shell = false
+ have_wp_primary_selection = false
+ have_xdg_activation = false
+ have_gtk_primary_selection = false
+ have_wlr_data_control = false
+ have_gtk_shell = false
+endif
+
+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_GTK_PRIMARY_SELECTION', have_gtk_primary_selection)
+conf_data.set('HAVE_WLR_DATA_CONTROL', have_wlr_data_control)
+conf_data.set('HAVE_XDG_ACTIVATION', have_xdg_activation)
+conf_data.set('HAVE_GTK_SHELL', have_gtk_shell)
+
+if wayland_protocols.found()
+ if meson.version().version_compare('>= 0.58')
+ protocols_path = wayland_protocols.get_variable('pkgdatadir')
+ else
+ protocols_path = wayland_protocols.get_pkgconfig_variable('pkgdatadir')
+ endif
+endif
+
+protocols = []
+
+if not have_xdg_shell
+ warning('Building without xdg-shell support')
+else
+ xdg_shell_xml = join_paths(protocols_path, 'stable', 'xdg-shell', 'xdg-shell.xml')
+ protocols += [['xdg-shell', xdg_shell_xml]]
+endif
+
+if have_wp_primary_selection
+ wp_primary_selection_xml = join_paths(protocols_path, 'unstable', 'primary-selection', 'primary-selection-unstable-v1.xml')
+ protocols += [['wp-primary-selection', wp_primary_selection_xml]]
+endif
+
+if not have_gtk_primary_selection
+ warning('Building without primary selection support')
+else
+ gtk_primary_selection_xml = 'gtk-primary-selection.xml'
+ protocols += [['gtk-primary-selection', gtk_primary_selection_xml]]
+endif
+
+if have_wlr_data_control
+ wlr_data_control_xml = 'wlr-data-control-unstable-v1.xml'
+ protocols += [['wlr-data-control', wlr_data_control_xml]]
+endif
+
+if have_xdg_activation
+ xdg_activation_xml = join_paths(protocols_path, 'staging', 'xdg-activation', 'xdg-activation-v1.xml')
+ protocols += [['xdg-activation', xdg_activation_xml]]
+endif
+
+if have_gtk_shell
+ gtk_shell_xml = 'gtk-shell.xml'
+ protocols += [['gtk-shell', gtk_shell_xml]]
+endif
+
+
+protocol_deps = []
+protocol_headers = []
+
+foreach protocol : protocols
+ name = protocol[0]
+ xml = protocol[1]
+ header = custom_target(name + ' client header',
+ input: xml, output: name + '.h',
+ command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@']
+ )
+ impl = custom_target(name + ' code',
+ input: xml, output: name + '.c',
+ command: [wayland_scanner, scanner_code_command, '@INPUT@', '@OUTPUT@']
+ )
+ protocol_headers += header
+ lib = static_library(name, impl, header, dependencies: wayland)
+ protocol_deps += lib
+endforeach
diff --git a/src/types/copy-action.c b/src/types/copy-action.c
index f661fc3..b3f8829 100644
--- a/src/types/copy-action.c
+++ b/src/types/copy-action.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -71,7 +71,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->fd_to_copy_from != -1) {
/* Copy the file to the given file descriptor
* by spawning an appropriate cat process.
*/
@@ -82,9 +82,11 @@ static void do_send(struct source *source, const char *mime_type, int fd) {
return;
}
if (pid == 0) {
+ dup2(self->fd_to_copy_from, STDIN_FILENO);
+ close(self->fd_to_copy_from);
dup2(fd, STDOUT_FILENO);
close(fd);
- execlp("cat", "cat", self->file_to_copy, NULL);
+ execlp("cat", "cat", NULL);
perror("exec cat");
exit(1);
}
@@ -96,7 +98,12 @@ static void do_send(struct source *source, const char *mime_type, int fd) {
* should switch to an asynchronous child waiting scheme
* instead.
*/
- wait(NULL);
+ waitpid(pid, NULL, 0);
+ /* Seek back to the beginning of the file */
+ off_t rc = lseek(self->fd_to_copy_from, 0, SEEK_SET);
+ if (rc < 0) {
+ perror("lseek");
+ }
} else {
/* We'll perform the copy ourselves */
FILE *f = fdopen(fd, "w");
diff --git a/src/types/copy-action.h b/src/types/copy-action.h
index c0a1290..babe179 100644
--- a/src/types/copy-action.h
+++ b/src/types/copy-action.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -40,8 +40,9 @@ struct copy_action {
/* Exactly one of these fields must be non-null if the source
* is non-null, otherwise all these fields must be null.
+ * The null value for fd_to_copy_from is -1.
*/
- const char *file_to_copy;
+ int fd_to_copy_from;
argv_t argv_to_copy;
struct {
const char *ptr;
diff --git a/src/types/device-manager.c b/src/types/device-manager.c
index bd6e04e..5e88189 100644
--- a/src/types/device-manager.c
+++ b/src/types/device-manager.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/device-manager.h b/src/types/device-manager.h
index bb7e88d..f6de074 100644
--- a/src/types/device-manager.h
+++ b/src/types/device-manager.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/device.c b/src/types/device.c
index 16525a6..4d81b94 100644
--- a/src/types/device.c
+++ b/src/types/device.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -41,7 +41,10 @@ void device_set_selection(
/* Macros to reduce implementation boilerplate */
#define SUPPORTS_SELECTION(type, expr) \
-static int device_supports_selection_on_ ## type(struct device *self, int primary) { \
+static int device_supports_selection_on_ ## type( \
+ struct device *self, \
+ int primary \
+) { \
return expr; \
}
diff --git a/src/types/device.h b/src/types/device.h
index 5f5e3fe..041a9e8 100644
--- a/src/types/device.h
+++ b/src/types/device.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/keyboard.c b/src/types/keyboard.c
index 99ca793..6090b5f 100644
--- a/src/types/keyboard.c
+++ b/src/types/keyboard.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/keyboard.h b/src/types/keyboard.h
index e5f02d0..fe0b27f 100644
--- a/src/types/keyboard.h
+++ b/src/types/keyboard.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/offer.c b/src/types/offer.c
index c21a562..f5be19b 100644
--- a/src/types/offer.c
+++ b/src/types/offer.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/offer.h b/src/types/offer.h
index 21dd567..a12d786 100644
--- a/src/types/offer.h
+++ b/src/types/offer.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/popup-surface.c b/src/types/popup-surface.c
index 1aad115..8af3755 100644
--- a/src/types/popup-surface.c
+++ b/src/types/popup-surface.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -42,7 +42,7 @@ static void forward_on_focus(
void popup_surface_init(struct popup_surface *self) {
self->shell = registry_find_shell(self->registry);
if (self->shell == NULL) {
- bail("Missing a shell");
+ complain_about_missing_shell();
}
self->keyboard = seat_get_keyboard(self->seat);
@@ -61,7 +61,7 @@ void popup_surface_init(struct popup_surface *self) {
struct wl_compositor *wl_compositor = self->registry->wl_compositor;
if (wl_compositor == NULL) {
- bail("Missing the compositor");
+ complain_about_missing_global("wl_compositor");
}
self->wl_surface = wl_compositor_create_surface(wl_compositor);
self->shell_surface = shell_create_shell_surface(
@@ -69,6 +69,15 @@ void popup_surface_init(struct popup_surface *self) {
self->wl_surface
);
+#ifdef HAVE_GTK_SHELL
+ if (self->registry->gtk_shell1 != NULL) {
+ self->gtk_surface = gtk_shell1_get_gtk_surface(
+ self->registry->gtk_shell1,
+ self->wl_surface
+ );
+ }
+#endif
+
/* Signal that the surface is ready to be configured */
wl_surface_commit(self->wl_surface);
wl_display_roundtrip(self->registry->wl_display);
@@ -102,7 +111,7 @@ void popup_surface_init(struct popup_surface *self) {
/* Create a shared memory pool */
struct wl_shm *wl_shm = self->registry->wl_shm;
if (wl_shm == NULL) {
- bail("Missing the shm");
+ complain_about_missing_global("wl_shm");
}
struct wl_shm_pool *wl_shm_pool = wl_shm_create_pool(wl_shm, fd, size);
@@ -122,6 +131,32 @@ void popup_surface_init(struct popup_surface *self) {
wl_surface_attach(self->wl_surface, wl_buffer, 0, 0);
wl_surface_damage(self->wl_surface, 0, 0, width, height);
+
+ /* Ask the compositor nicely to give us focus */
+#ifdef HAVE_GTK_SHELL
+ if (self->gtk_surface != NULL) {
+ gtk_surface1_present(self->gtk_surface, 0);
+ }
+#endif
+#ifdef HAVE_XDG_ACTIVATION
+ if (self->registry->xdg_activation_v1 != NULL) {
+ /* See if someone was kind enough to leave
+ * some tokens for us in the environment.
+ */
+ const char *token = getenv("XDG_ACTIVATION_TOKEN");
+ if (token == NULL) {
+ token = getenv("DESKTOP_STARTUP_ID");
+ }
+ if (token != NULL) {
+ xdg_activation_v1_activate(
+ self->registry->xdg_activation_v1,
+ token,
+ self->wl_surface
+ );
+ }
+ }
+#endif
+
wl_surface_commit(self->wl_surface);
}
@@ -135,7 +170,21 @@ void popup_surface_destroy(struct popup_surface *self) {
self->keyboard->data = NULL;
shell_surface_destroy(self->shell_surface);
+#ifdef HAVE_GTK_SHELL
+ if (self->gtk_surface != NULL) {
+ if (
+ gtk_surface1_get_version(self->gtk_surface) >=
+ GTK_SURFACE1_RELEASE_SINCE_VERSION
+ ) {
+ gtk_surface1_release(self->gtk_surface);
+ } else {
+ gtk_surface1_destroy(self->gtk_surface);
+ }
+ }
+#endif
wl_surface_destroy(self->wl_surface);
+ /* Let popup_surface_init() know we're already done */
+ self->wl_surface = NULL;
free(self->shell);
if (self->should_free_self) {
diff --git a/src/types/popup-surface.h b/src/types/popup-surface.h
index 8847519..d81fe37 100644
--- a/src/types/popup-surface.h
+++ b/src/types/popup-surface.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -19,6 +19,8 @@
#ifndef TYPES_POPUP_SURFACE_H
#define TYPES_POPUP_SURFACE_H
+#include "includes/shell-protocols.h"
+
#include <wayland-client.h>
struct registry;
@@ -40,6 +42,10 @@ struct popup_surface {
struct wl_surface *wl_surface;
struct keyboard *keyboard;
int should_free_self;
+
+#ifdef HAVE_GTK_SHELL
+ struct gtk_surface1 *gtk_surface;
+#endif
};
void popup_surface_init(struct popup_surface *self);
diff --git a/src/types/registry.c b/src/types/registry.c
index 67c7fde..b33c4d1 100644
--- a/src/types/registry.c
+++ b/src/types/registry.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -49,6 +49,10 @@ static void wl_registry_global_handler(
BIND(wl_compositor, 2)
BIND(wl_shm, 1)
+#ifdef HAVE_XDG_ACTIVATION
+ BIND(xdg_activation_v1, 1)
+#endif
+
/* Shells */
BIND(wl_shell, 1)
@@ -57,6 +61,10 @@ static void wl_registry_global_handler(
BIND(xdg_wm_base, 1)
#endif
+#ifdef HAVE_GTK_SHELL
+ BIND(gtk_shell1, 4)
+#endif
+
/* Device managers */
BIND(wl_data_device_manager, 1)
diff --git a/src/types/registry.h b/src/types/registry.h
index e52e427..d6c64ce 100644
--- a/src/types/registry.h
+++ b/src/types/registry.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -40,12 +40,19 @@ struct registry {
struct wl_compositor *wl_compositor;
struct wl_shm *wl_shm;
+#ifdef HAVE_XDG_ACTIVATION
+ struct xdg_activation_v1 *xdg_activation_v1;
+#endif
+
/* Shells */
struct wl_shell *wl_shell;
#ifdef HAVE_XDG_SHELL
struct xdg_wm_base *xdg_wm_base;
#endif
+#ifdef HAVE_GTK_SHELL
+ struct gtk_shell1 *gtk_shell1;
+#endif
/* Device managers */
diff --git a/src/types/seat.c b/src/types/seat.c
index 6e48450..59e7df8 100644
--- a/src/types/seat.c
+++ b/src/types/seat.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/seat.h b/src/types/seat.h
index cd92073..8ffade7 100644
--- a/src/types/seat.h
+++ b/src/types/seat.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/shell-surface.c b/src/types/shell-surface.c
index ba7e61b..1940d4d 100644
--- a/src/types/shell-surface.c
+++ b/src/types/shell-surface.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/shell-surface.h b/src/types/shell-surface.h
index d888553..f8cef96 100644
--- a/src/types/shell-surface.h
+++ b/src/types/shell-surface.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/shell.c b/src/types/shell.c
index 36d192a..12ef997 100644
--- a/src/types/shell.c
+++ b/src/types/shell.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/shell.h b/src/types/shell.h
index 9b866eb..99369ce 100644
--- a/src/types/shell.h
+++ b/src/types/shell.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/source.c b/src/types/source.c
index a59110d..45b0f14 100644
--- a/src/types/source.c
+++ b/src/types/source.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/types/source.h b/src/types/source.h
index b471fe7..a0cd28b 100644
--- a/src/types/source.h
+++ b/src/types/source.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/util/files.c b/src/util/files.c
index 750aa0b..bc2b06b 100644
--- a/src/util/files.c
+++ b/src/util/files.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -136,7 +136,7 @@ char *infer_mime_type_from_contents(const char *file_path) {
close(pipefd[1]);
int wstatus;
- wait(&wstatus);
+ waitpid(pid, &wstatus, 0);
/* See if that worked */
if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
@@ -243,7 +243,7 @@ char *dump_stdin_into_a_temp_file() {
}
int wstatus;
- wait(&wstatus);
+ waitpid(pid, &wstatus, 0);
if (original_path != NULL) {
free(original_path);
}
diff --git a/src/util/files.h b/src/util/files.h
index 2a2d4b6..b34f852 100644
--- a/src/util/files.h
+++ b/src/util/files.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/util/misc.c b/src/util/misc.c
index e9f7671..797d2b6 100644
--- a/src/util/misc.c
+++ b/src/util/misc.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -20,11 +20,14 @@
#include "util/misc.h"
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
void print_version_info() {
printf(
"wl-clipboard " PROJECT_VERSION "\n"
- "Copyright (C) 2019 Sergey Bugaev\n"
+ "Copyright (C) 2018-2023 Sergey Bugaev\n"
"License GPLv3+: GNU GPL version 3 or later"
" <https://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n"
@@ -35,7 +38,7 @@ void print_version_info() {
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");
+ complain_about_missing_global("wl_data_device_manager");
}
#if !defined(HAVE_WP_PRIMARY_SELECTION) && !defined(HAVE_GTK_PRIMARY_SELECTION)
@@ -57,3 +60,75 @@ void complain_about_watch_mode_support() {
);
#endif
}
+
+void complain_about_wayland_connection() {
+ int saved_errno = errno;
+ fprintf(stderr, "Failed to connect to a Wayland server");
+ if (saved_errno != 0) {
+ fprintf(stderr, ": %s", strerror(saved_errno));
+ }
+ fputc('\n', stderr);
+
+ const char *display = getenv("WAYLAND_DISPLAY");
+ const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
+ if (display != NULL) {
+ fprintf(stderr, "Note: WAYLAND_DISPLAY is set to %s\n", display);
+ } else {
+ fprintf(
+ stderr,
+ "Note: WAYLAND_DISPLAY is unset"
+ " (falling back to wayland-0)\n"
+ );
+ display = "wayland-0";
+ }
+ if (runtime_dir != NULL) {
+ fprintf(stderr, "Note: XDG_RUNTIME_DIR is set to %s\n", runtime_dir);
+ } else {
+ fprintf(stderr, "Note: XDG_RUNTIME_DIR is unset\n");
+ }
+ if (display[0] != '/' && runtime_dir != NULL) {
+ fprintf(
+ stderr,
+ "Please check whether %s/%s socket exists and is accessible.\n",
+ runtime_dir,
+ display
+ );
+ }
+ exit(1);
+}
+
+void complain_about_missing_seat(const char *seat_name) {
+ if (seat_name != NULL) {
+ fprintf(stderr, "No such seat: %s\n", seat_name);
+ } else {
+ complain_about_missing_global("seat");
+ }
+ exit(1);
+}
+
+void complain_about_missing_global(const char *global) {
+ fprintf(
+ stderr,
+ "The compositor does not seem to implement %s,"
+ " which is required for wl-clipboard to work\n",
+ global
+ );
+ exit(1);
+}
+
+void complain_about_missing_shell() {
+ fprintf(
+ stderr,
+ "The compositor does not seem to implement a Wayland shell,"
+ " which is required for wl-clipboard to work\n"
+ );
+
+#ifndef HAVE_XDG_SHELL
+ fprintf(
+ stderr,
+ "Note: wl-clipboard was built without xdg-shell support\n"
+ );
+#endif
+
+ exit(1);
+}
diff --git a/src/util/misc.h b/src/util/misc.h
index 02c9984..1bf81a8 100644
--- a/src/util/misc.h
+++ b/src/util/misc.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -28,5 +28,9 @@ void print_version_info(void);
void complain_about_selection_support(int primary);
void complain_about_watch_mode_support(void);
+void complain_about_wayland_connection(void);
+void complain_about_missing_seat(const char *seat_name);
+void complain_about_missing_global(const char *global);
+void complain_about_missing_shell(void);
#endif /* UTIL_MISC_H */
diff --git a/src/util/string.c b/src/util/string.c
index f39db31..bfea1de 100644
--- a/src/util/string.c
+++ b/src/util/string.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -36,7 +36,9 @@ int mime_type_is_text(const char *mime_type) {
= 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, "yaml")
+ || str_has_suffix(mime_type, "csv")
+ || str_has_suffix(mime_type, "ini");
/* Special-case PGP and SSH keys.
* A public SSH key is typically stored
diff --git a/src/util/string.h b/src/util/string.h
index 239cf58..1477944 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
diff --git a/src/wl-copy.c b/src/wl-copy.c
index f08cf17..d911875 100644
--- a/src/wl-copy.c
+++ b/src/wl-copy.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -22,6 +22,7 @@
#include "types/device-manager.h"
#include "types/registry.h"
#include "types/popup-surface.h"
+#include "types/offer.h"
#include "util/files.h"
#include "util/string.h"
@@ -81,28 +82,20 @@ static void did_set_selection_callback(struct copy_action *copy_action) {
}
}
-static void cleanup_and_exit(struct copy_action *copy_action, int code) {
- /* 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 {
- exit(code);
- }
-}
-
static void cancelled_callback(struct copy_action *copy_action) {
- cleanup_and_exit(copy_action, 0);
+ exit(0);
}
static void pasted_callback(struct copy_action *copy_action) {
if (options.paste_once) {
- cleanup_and_exit(copy_action, 0);
+ exit(0);
+ }
+}
+
+static void selection_callback(struct offer *offer, int primary) {
+ /* We're not interested */
+ if (offer != NULL) {
+ offer_destroy(offer);
}
}
@@ -199,9 +192,17 @@ static void parse_options(int argc, argv_t argv) {
int main(int argc, argv_t argv) {
parse_options(argc, argv);
+ /* Ignore SIGPIPE.
+ * We don't really output anything
+ * to our stdout, yet we don't want
+ * to get killed when writing clipboard
+ * contents to a closed pipe.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
struct wl_display *wl_display = wl_display_connect(NULL);
if (wl_display == NULL) {
- bail("Failed to connect to a Wayland server");
+ complain_about_wayland_connection();
}
struct registry *registry = calloc(1, sizeof(struct registry));
@@ -213,11 +214,7 @@ int main(int argc, argv_t argv) {
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");
- }
+ complain_about_missing_seat(options.seat_name);
}
/* Create the device */
@@ -228,6 +225,7 @@ int main(int argc, argv_t argv) {
}
struct device *device = device_manager_get_device(device_manager, seat);
+ device->selection_callback = selection_callback;
if (!device_supports_selection(device, options.primary)) {
complain_about_selection_support(options.primary);
@@ -235,6 +233,7 @@ int main(int argc, argv_t argv) {
/* Create and initialize the copy action */
struct copy_action *copy_action = calloc(1, sizeof(struct copy_action));
+ copy_action->fd_to_copy_from = -1;
copy_action->device = device;
copy_action->primary = options.primary;
@@ -257,7 +256,28 @@ 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_action->fd_to_copy_from = open(
+ temp_file,
+ O_RDONLY | O_CLOEXEC
+ );
+ if (copy_action->fd_to_copy_from < 0) {
+ perror("Failed to open temp file");
+ return 1;
+ }
+ /* Now, remove the temp file and its
+ * containing directory. We still keep
+ * access to the file through our open
+ * file descriptor.
+ */
+ int rc = unlink(temp_file);
+ if (rc < 0) {
+ perror("Failed to unlink temp file");
+ }
+ rc = rmdir(dirname(temp_file));
+ if (rc < 0) {
+ perror("Failed to remove temp file directory");
+ }
+ free(temp_file);
}
/* Create the source */
@@ -291,6 +311,5 @@ int main(int argc, argv_t argv) {
while (wl_display_dispatch(wl_display) >= 0);
perror("wl_display_dispatch");
- cleanup_and_exit(copy_action, 1);
return 1;
}
diff --git a/src/wl-paste.c b/src/wl-paste.c
index 1c61974..b608ea9 100644
--- a/src/wl-paste.c
+++ b/src/wl-paste.c
@@ -1,6 +1,6 @@
/* wl-clipboard
*
- * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
+ * Copyright © 2018-2023 Sergey Bugaev <bugaevc@gmail.com>
*
* 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
@@ -28,6 +28,8 @@
#include <wayland-client.h>
#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
@@ -58,6 +60,7 @@ struct types {
static struct wl_display *wl_display = NULL;
static struct popup_surface *popup_surface = NULL;
+static int offer_received = 0;
static struct types classify_offer_types(struct offer *offer) {
struct types types = { 0 };
@@ -171,7 +174,71 @@ static const char *mime_type_to_request(struct types types) {
#undef try_any_text
#undef try_any
+static int run_paste_command(int stdin_fd, const char *clipboard_state) {
+ /* 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");
+ close(stdin_fd);
+ return 0;
+ }
+ if (pid == 0) {
+ dup2(stdin_fd, STDIN_FILENO);
+ close(stdin_fd);
+ if (options.watch) {
+ if (clipboard_state != NULL) {
+ setenv("CLIPBOARD_STATE", clipboard_state, 1);
+ }
+ 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(stdin_fd);
+ waitpid(pid, NULL, 0);
+ return 1;
+}
+
+static void complain_no_suitable_type() {
+ fprintf(stderr, "Clipboard content is not available as ");
+ if (options.explicit_type != NULL) {
+ fprintf(stderr, "requested type \"%s\"\n", options.explicit_type);
+ } else {
+ assert(options.inferred_type);
+ fprintf(
+ stderr,
+ "inferred output type \"%s\"\n",
+ options.inferred_type
+ );
+ }
+ fprintf(stderr, "Use \"wl-paste --list-types\" to view available types.");
+ if (options.explicit_type == NULL) {
+ fprintf(stderr, " Use \"--type\" to explicitly specify a type.");
+ }
+ fputc('\n', stderr);
+ exit(1);
+}
+
static void selection_callback(struct offer *offer, int primary) {
+ /* Ignore all but the first non-NULL offer.
+ * This could happen due to reentrancy, though
+ * we try to prevent it in other ways.
+ */
+ if (offer_received && !options.watch) {
+ return;
+ }
+
/* Ignore events we're not interested in */
if (primary != options.primary) {
if (offer != NULL) {
@@ -181,12 +248,20 @@ static void selection_callback(struct offer *offer, int primary) {
}
if (offer == NULL) {
- if (options.watch) {
+ if (!options.watch) {
+ bail("Nothing is copied");
+ }
+ int devnull = open("/dev/null", O_RDONLY | O_CLOEXEC);
+ if (devnull < 0) {
+ perror("open /dev/null");
return;
}
- bail("No selection");
+ run_paste_command(devnull, "nil");
+ return;
}
+ offer_received = 1;
+
if (options.list_types) {
offer_for_each_mime_type(offer, mime_type) {
printf("%s\n", mime_type);
@@ -202,7 +277,7 @@ static void selection_callback(struct offer *offer, int primary) {
offer_destroy(offer);
return;
}
- bail("No suitable type of content copied");
+ complain_no_suitable_type();
}
/* Never append a newline character to binary content */
@@ -230,45 +305,24 @@ static void selection_callback(struct offer *offer, int primary) {
popup_surface_destroy(popup_surface);
popup_surface = NULL;
}
- wl_display_roundtrip(wl_display);
-
- /* Spawn a cat to perform the copy.
- * If watch mode is active, we spawn
- * a custom command instead.
+ /* Make sure the receive request reaches
+ * the compositor before we block on reading.
+ * We call flush() instead of dispatch() to
+ * prevent reentrancy.
*/
- pid_t pid = fork();
- if (pid < 0) {
- perror("fork");
+ wl_display_flush(wl_display);
+
+ close(pipefd[1]);
+ rc = run_paste_command(pipefd[0], "data");
+ if (!rc) {
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]);
- 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 && !options.watch) {
rc = write(STDOUT_FILENO, "\n", 1);
if (rc != 1) {
@@ -418,7 +472,7 @@ int main(int argc, argv_t argv) {
wl_display = wl_display_connect(NULL);
if (wl_display == NULL) {
- bail("Failed to connect to a Wayland server");
+ complain_about_wayland_connection();
}
struct registry *registry = calloc(1, sizeof(struct registry));
@@ -430,11 +484,7 @@ int main(int argc, argv_t argv) {
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");
- }
+ complain_about_missing_seat(options.seat_name);
}
/* Create the device */