Skip to content

Instantly share code, notes, and snippets.

@kiko
Last active December 24, 2015 01:39
Show Gist options
  • Save kiko/6724792 to your computer and use it in GitHub Desktop.
Save kiko/6724792 to your computer and use it in GitHub Desktop.
diff --git a/chroot-bin/croutonclip b/chroot-bin/croutonclip
index a280419..226f26d 100755
--- a/chroot-bin/croutonclip
+++ b/chroot-bin/croutonclip
@@ -5,6 +5,7 @@
#
# Synchronizes clipboard between X displays, making use of crouton's WebSocket
# server and Chromium extension to synchronize the clipboard with Chromium OS
+# Define XMETHOD variable (x11, xephyr or cros) in prior to run this script.
VERBOSE=''
@@ -47,7 +48,7 @@ copyclip() {
# Copy clipboard content from the current display
{
- if [ "$current" = ':0' ]; then
+ if [ "$current" = 'aura' ]; then
echo -n 'R' | websocketcommand
else
# Check if display is still running
@@ -68,7 +69,7 @@ copyclip() {
fi
# Paste clipboard content to the next display
- if [ "$next" = ':0' ]; then
+ if [ "$next" = 'aura' ]; then
STATUS="`(echo -n 'W'; cat) | websocketcommand`"
if [ "$STATUS" != 'WOK' ]; then
# Write failed, skip Chromium OS (do not update $current)
@@ -125,20 +126,19 @@ waitwebsocket() {
croutonwebsocket &
addtrap "kill $! 2>/dev/null"
-xmethod="`readlink -f '/etc/X11/xinit/xserverrc'`"
-xmethod="${xmethod##*-}"
-
# For both x11/xephyr, the code works as follow:
# - A command outputs one line per event (window/tty change).
# - In a second subshell, read these events, one by one, and transfer the
# clipboard content.
# This makes sure we do not miss any events and that we copy the clipboard
# around in the right sequence.
+# For cros, guest OS shares X display with Chrome OS (:0).
-if [ "$xmethod" = 'xephyr' ]; then
+case "$XMETHOD" in
+'xephyr' | 'cros')
# Assume current display is Chromium OS, as the awk script does not detect
# the current window
- current=':0'
+ current='aura'
# Wait for ratpoison to come up
while ! host-x11 xprop -root _NET_WM_NAME | grep -q 'ratpoison'; do
@@ -175,7 +175,9 @@ if [ "$xmethod" = 'xephyr' ]; then
# Translate window id to display number
id="${line%,}"
if [ "$id" = "$aura" ]; then
- display=":0"
+ display="aura"
+ elif [ "$XMETHOD" = 'cros' ]; then
+ display=':0'
else
# This relies on WM_NAME looking like "Xephyr on :1.0 (...)"
display="`host-x11 xprop -format WM_NAME 8s '\t$0' \
@@ -185,8 +187,8 @@ if [ "$xmethod" = 'xephyr' ]; then
copyclip "$display"
done
- }
-elif [ "$xmethod" = 'x11' ]; then
+ };;
+'x11')
# No need to set $current:
# croutonvtmonitor outputs the current tty right after it is started
@@ -204,7 +206,7 @@ elif [ "$xmethod" = 'x11' ]; then
while read tty; do
display=''
if [ "$tty" = "tty1" ]; then
- display=":0"
+ display="aura"
else
# Find which process owns the tty
process="`ps -o pid= -t "$tty" | sed 's/ //g'`"
@@ -222,9 +224,8 @@ elif [ "$xmethod" = 'x11' ]; then
copyclip "$display"
done
- }
-else
- echo "Invalid xmethod='$xmethod'." >&2
-fi
-
+ };;
+*)
+ echo "Invalid xmethod='$XMETHOD'." >&2;;
+esac
exit 1
#!/bin/sh -e
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Synchronizes clipboard between X displays, making use of crouton's WebSocket
# server and Chromium extension to synchronize the clipboard with Chromium OS
# Define XMETHOD variable (x11, xephyr or cros) in prior to run this script.
VERBOSE=''
. "`dirname "$0"`/../installer/functions"
PIPEDIR='/tmp/crouton-ext'
PIPEIN="$PIPEDIR/in"
PIPEOUT="$PIPEDIR/out"
PIPELOCK="$PIPEDIR/lock"
# Write a command to croutonwebsocket, and read back response
websocketcommand() {
# Check that $PIPEDIR and the FIFO pipes exist
if ! [ -d "$PIPEDIR" -a -p "$PIPEIN" -a -p "$PIPEOUT" ]; then
echo "EError $PIPEIN or $PIPEOUT are not pipes."
exit 0
fi
(
flock 5
cat > "$PIPEIN"
cat "$PIPEOUT"
) 5>"$PIPELOCK"
}
current=''
copyclip() {
next="$1"
# Do not copy if next is empty (display cannot be detected), or
# if current == next. Also set current=$next if $current is empty
if [ -z "$next" -o "${current:="$next"}" = "$next" ]; then
return 0
fi
if [ -n "$VERBOSE" ]; then
echo ">>Current: $current>>"
fi
# Copy clipboard content from the current display
{
if [ "$current" = 'aura' ]; then
echo -n 'R' | websocketcommand
else
# Check if display is still running
if DISPLAY="$current" xdpyinfo >/dev/null 2>&1; then
echo -n 'R'
DISPLAY="$current" xclip -o -sel clip
else
echo -n "EUnable to open display '$current'."
fi
fi
} | (
STATUS="`head -c 1`"
if [ "$STATUS" != 'R' ]; then
echo -n "croutonwebsocket error: " >&2
cat >&2
# Stop here (the clipboard content is lost in this case)
exit 0
fi
# Paste clipboard content to the next display
if [ "$next" = 'aura' ]; then
STATUS="`(echo -n 'W'; cat) | websocketcommand`"
if [ "$STATUS" != 'WOK' ]; then
# Write failed, skip Chromium OS (do not update $current)
echo -n "croutonwebsocket error: $STATUS" >&2
exit 1
fi
else
# Do not override content if it "looks" the same
# (we might have rich text or other content in the clipboard)
cliptmp="`mktemp "croutonclip.XXX" --tmpdir=/tmp`"
trap "rm -f '$cliptmp'" 0
cat > $cliptmp
if ! DISPLAY="$next" xclip -o -sel clip | diff -q - "$cliptmp" > /dev/null; then
cat "$cliptmp" | DISPLAY="$next" xclip -i -sel clip
fi
fi
) && current="$next"
if [ -n "$VERBOSE" ]; then
echo "<<Next: $current<<"
fi
}
# Wait for the websocket server to get connected to the extension
# Timeout after 10 seconds (twice crouton extension retry period)
waitwebsocket() {
timeout=10
while [ $timeout -gt 0 ]; do
if [ -n "$VERBOSE" ]; then
echo "Ping..."
fi
# Prepare and send a ping message
MSG="PING$$$timeout"
STATUS="`echo -n "$MSG" | websocketcommand`"
if [ "$STATUS" = "$MSG" ]; then
if [ -n "$VERBOSE" ]; then
echo "OK!"
fi
return 0
fi
if [ -n "$VERBOSE" ]; then
echo "$STATUS"
fi
sleep 1
timeout=$(($timeout-1))
done
echo "Timeout waiting for extension to connect." >&2
}
croutonwebsocket &
addtrap "kill $! 2>/dev/null"
# For both x11/xephyr, the code works as follow:
# - A command outputs one line per event (window/tty change).
# - In a second subshell, read these events, one by one, and transfer the
# clipboard content.
# This makes sure we do not miss any events and that we copy the clipboard
# around in the right sequence.
# For cros, guest OS shares X display with Chrome OS (:0).
case "$XMETHOD" in
'xephyr' | 'cros')
# Assume current display is Chromium OS, as the awk script does not detect
# the current window
current='aura'
# Wait for ratpoison to come up
while ! host-x11 xprop -root _NET_WM_NAME | grep -q 'ratpoison'; do
sleep .1
done 2>&-
# Get the aura root window number (hex)
aura="`host-x11 xwininfo -root -children | mawk '/aura_root/ {print $1}'`"
# Detect window change using MapNotify events that do not have the
# override-redirect flag ("override NO" in xev events)
{
host-x11 xev -root &
cpid=$!
addtrap "kill $cpid 2>/dev/null"
if ! wait $cpid; then
echo "croutonclip: xev error ($?)"
fi
} | mawk -W interactive '
m {
if ($6 == "NO") {
print $4 # Window id
}
m = 0
}
/^MapNotify/ {
m = 1
}
' | {
# Wait for extension to connect (do not fail even if it times out)
waitwebsocket
while read line; do
# Translate window id to display number
id="${line%,}"
if [ "$id" = "$aura" ]; then
display="aura"
elif [ "$XMETHOD" = 'cros' ]; then
display=':0'
else
# This relies on WM_NAME looking like "Xephyr on :1.0 (...)"
display="`host-x11 xprop -format WM_NAME 8s '\t$0' \
-id "$id" WM_NAME | cut -f 2 | \
sed -n -e 's/^\"Xephyr on \(:[0-9]\{1,\}\).*$/\1/p'`"
fi
copyclip "$display"
done
};;
'x11')
# No need to set $current:
# croutonvtmonitor outputs the current tty right after it is started
{
croutonvtmonitor &
cpid=$!
addtrap "kill $cpid 2>/dev/null"
if ! wait $cpid; then
echo "croutonclip: croutonvtmonitor error ($?)"
fi
} | {
# Wait for extension to connect (do not fail even if it times out)
waitwebsocket
while read tty; do
display=''
if [ "$tty" = "tty1" ]; then
display="aura"
else
# Find which process owns the tty
process="`ps -o pid= -t "$tty" | sed 's/ //g'`"
if [ -n "$process" ]; then
# Find which display the process owns (if any)
for lock in /tmp/.X*-lock; do
if grep -q "^ *$process$" "$lock"; then
display="${lock#/tmp/.X}"
display=":${display%-lock}"
fi
done
fi
fi
copyclip "$display"
done
};;
*)
echo "Invalid xmethod='$XMETHOD'." >&2;;
esac
exit 1
@drinkcat
Copy link

Nice! Thanks for the contribution.

A few things that can be improved:

  • The existing code uses :0 as meaning that we want to copy the clipboard from Chrome. Since you actually want to update the clipboard on display :0, you could change that, and use display="aura" instead of display=":0" in both xephyr and x11 methods (and change the original values to aura, as well as the tests in copyclip)
  • xmethods cros and xephyr share a lot of code. You could merge them together, and skip the while loop waiting for ratpoison (but don't you need ratpoison anyway?), and assign display to :0 in the cros case, without to trying to parse the Xephyr window name.
  • I'm not sure to understand what purpose chroot-etc/xserverrc-cros serves (it looks identical to what host-x11 does).

@kiko
Copy link
Author

kiko commented Sep 27, 2013

Hi, Thanks a lot for your feedback!

  • Nice, I didn't like :1 hack but didn't want to pollute your code. I'll try to rearrange as you suggested.
  • I don't know why crouton hard code ratpoison here and there. I think we can use virtually any window manager we want, can't we?
  • I added xserverrc-cros because croutonclip uses xserverrc-* suffix to determine $xmethod
    https://github.com/drinkcat/chroagh/blob/master/chroot-bin/croutonclip#L128

@drinkcat
Copy link

Huh, I don't know why I didn't get notification of your comment.

Yes, you can use another window manager (at long as it allows Chrome to be in full screen, I guess that'd be weird otherwise). ratpoison is what we install by default in the Xephyr setup, that's why it's hardcoded.

Good point about xserverrc-cros, can't remember what my own code does ,-)

@drinkcat
Copy link

drinkcat commented Oct 1, 2013

Line 183: Can't just just set this to display=':0'? That'll remove the hacks in copyclip.

Apart from that, looks good!

@drinkcat
Copy link

drinkcat commented Oct 1, 2013

One more thing... Trying to figure out how to integrate this neatly in the crouton framework.

You could remove lines 130-131, and instead rely on an environment variable XMETHOD that needs to be passed to the script. That removes the need for the xserverr-cros hack, and make it possible to use the script in both cros or x11/xephyr context.

@kiko
Copy link
Author

kiko commented Oct 2, 2013

You genius, all hacks are removed now. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment