Xclip Does Not Terminate When Tracing It

xclip does not terminate when tracing it

XClip forks a child when launched without -verbose. The only difference with -verbose is that there is no child forked and the same original process handles ConvertSelection events.

Usually in X Window toolkits copy/paste is implemented via X Selections:

Selections are global server resources named by an atom and owned by a
particular client. The number of selections is not limited by the
protocol; as many selections as atoms may exist. Selections are
designed to provide the basis for building communication mechanisms
between clients. The official definition is found in the glosary of the
X Protocol:

"...an indirect property with dynamic type; that is,
rather than having the property stored in the server, it is maintained
by some client (the ‘‘owner’’).
A selection is global in nature and is
thought of as belonging to the user (although maintained by clients),
rather than as being private to a particular window subhierarchy or a
particular set of clients."

From the applications perspective,
selections provide a mechanism for transmitting information between X
clients. As X is a networking protocol, the existance of a separate
channel for data transmission between the various clients cannot be
assumed to exist. Selections are intended only for data transfer which
directly relates the the user-interface aspects of the application,
although there isn’t any enforcement of this policy.

Content of selection is stored in application itself and requested with
ConvertSelection event ("convert" here because there is a way for client
to ask for particular mimetype (or "view", or format) of selected data.
Conversion, again, happens in the application which owns selected buffer.

Because of this architecture, there is no way to "copy text to system
buffer and exit" - because you are a system buffer. XClip simulates "copy and exit"
by forking and daemonizing.

Avoid enter when pasting xsel / xclip

You could use tr, for example

date +%Y-%m-%d_%H:%M | tr -d '\n' | xclip -selection c 

See this question for different ways to achieve it: Bash: Strip trailing linebreak from output

passthru hangs if the executed command writes to the clipboard

"xclip -i -selection clipboard > /dev/null"
should fix it

xclip doesn't close the STDOUT.

related to: this questions answer

Why `xclip .bashrc` takes much longer than system(xclip .bashrc) in ruby?

I doubt that xclip just takes a long time to terminate when you use backticks to shell out. This has to do with the selection. Without any selection provided through -sel it will default to XA_PRIMARY which is conventionally used to implement copying and pasting via the middle mouse button.

When you run

$ xclip text.txt

the content becomes available through XA_PRIMARY which means you can paste it through your middle mouse button or $ xclip -o. It starts to get weird when you execute it thorugh a shell out in Ruby:

ruby -e '`xclip text.txt`

It never really terminates if you do nothing. It terminates when you select someting in your X11 system, for instance in the console or everywhere else. Just selection, marking something with your mouse. If you do not, it will hang and/or timeout at some point.

The same behavior can be observed when you use the verbose mode:

$ xclip -verbose text.txt

Connected to X server.
Using UTF8_STRING.
Reading text.txt...
Waiting for selection requests, Control-C to quit
Waiting for selection request number 1

The selection request, again, is served when you select something.

A good analysis tool for this is strace (the -f option is for tracking forks, too)

$ strace -f ruby -e '`xclip text.txt`'

...
poll([{fd=3, events=POLLIN|POLLOUT}], 1, 4294967295) = 1 ([{fd=3, revents=POLLOUT}])
writev(3, [{"\20\0\3\0\4\0\200\2INCR", 12}, {NULL, 0}, {"", 0}], 3) = 12
poll([{fd=3, events=POLLIN}], 1, 4294967295) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "\1\0\f\0\0\0\0\0\235\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 4096, 0, NULL, NULL) = 32
recvfrom(3, 0x165e964, 4096, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvfrom(3, 0x165e964, 4096, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=3, events=POLLIN}], 1, 4294967295

In the last line it hangs until a selection is made. poll() is used to wait for a file event at the file descriptor. It will terminate at some point, but 4,294,967,295 ms is rather long. It can be traced the same with with just strace -f xclip text.txt.

You can take a look at the file descriptors through ls -l /proc/PID/fd. The one with number 3 is the file descriptor where xclip waits for your selection.

What makes it so hard to debug is that if terminates instantly with strace xclip text.txt, but not with strace -f text.txt. The moment where you want to trace the fork it does not work anymore. That is the same problem you have with Ruby. It tries to trace the output, because Kernel#` wants to return the output. This has probably also to do with the ticket #9 Not closing stdout when setting clipboard from stdin.

This is my theory. The moment you shell out and want to follow the output of xclip, be it with Ruby to read from standard out, or strace to trace the forks, the standard output does not get closed until you make a selection.

This does not explain it very well, but it demonstrates that is does not have to do anything with Ruby. I will create a question which focuses on xclip only and not in the context of Ruby.

Git pre-push hook hangs after piping to xclip

xsel, as well as xclip, waits until another program explicitly fetches the selected data.

This behavior is a design-conditional of X11, since there "is no X selection buffer. The selection mechanism in X11 is an interclient communication mediated by the X server each time any program wishes to know the selection contents [...]. In order to implement modification of the selection(s) (in input, keep and exchange modes) [these programs detach] from the terminal, spawning a child process to supply the new selection(s) on demand. This child exits immediately when any other program takes over the selection(s)" -- from the xsel man-page

With other words your Gits pre-push commit is executed until you start providing your text-selection to the clipboard. The process is then halted until you are making use of this text-snippet by invoking any command or executing any program which "fetches" the clipboard text.

Forking the xclip-command does not work

My first idea was to detach this selection-providing process to let it live parallel to the (then) ongoing execution of your hook-script. Unfortunately this does not work, either the main-process ist halting anyways, until the subprocess returns, or the text-selection of the forked command is not available to the current X11-server.

Possible Solutions

Due to the behavior of the "clipboard" in X11 you must evade providing text-selections in the time-relevant processing of git-hooks.

  1. Use a clipboard-manager - The most clipboard-managers (like Klipper [for KDE] or Glipper [for Gnome]) provide mechanisms to decouple the supply of data from its usage - which emulates the clipboard behavior of Windows and Mac OS.

  2. Alias the git-push-command - if you're operating your git-repository mostly in the shell you might wrap the git-push-command in an alias or small shell-script which first invokes the push and afterwards provides the text-snippet to the clipboard. The disadvantage of this approach is, (besides of the cli-dependency) that the command will "hang" at the end, until you fetched the clipboard-content.

Unless you're not able to install additional software to your system i'd recommend the use of a clipboard-manager.

Use Klipper to put Text from the CLI into the X11-clipboard

You can access Klipper through the shell by using qdbus - a command-line-interface to Qt-applications. An example took from Milian Wolff's Blog Post, adapted to your script, may look like the following:

#!/bin/sh

PBI=\`git symbolic-ref --short HEAD\`
echo "**Backlog Item [$PBI]:**\n" > pr_messages/$PBI.md
git log develop..HEAD --format=" - [x] %B" >> pr_messages/$PBI.md

sed -r -i 's|\[([0-9]{4,})\]|[[\1](http://tracker/_workitems/\1)]|g' pr_messages/$PBI.md

PR_MESSAGE=$(cat pr_messages/$PBI.md)
qdbus org.kde.klipper /klipper setClipboardContents "$PR_MESSAGE"

Another interesting article about the interaction of qdbus and Klipper: https://askubuntu.com/questions/393601/a-command-for-pasting-the-same-as-ctrl-v



Related Topics



Leave a reply



Submit