Peter Shawhan - July 23, 2004
I am fortunate to have two computers to use at work: a Linux desktop machine and a lightweight Windows laptop. I use the Linux machine to write and run programs, and the laptop for email, web browsing, writing documents, and working remotely from home. During the workday, they sit next to each other on my desk, and I switch back and forth between them frequently. The only problem with this arrangement is that I often will get an email message or view a web page containing commands to be executed on the Linux command line; or, I will have some program output text on the Linux machine that I want to send to someone in an email message. In that case, the half-meter distance between the two machines on my desk stands in the way of a simple cut-and-paste.
To get around this problem, I invested half a day in writing a pair of Tcl scripts which let me easily copy the contents of the Windows clipboard to the X (PRIMARY) selection on the Linux machine, and vice versa. I call this client-server system teleclip.
This system works well for me as long as the selected text is not too long; I have had some trouble with selections longer than a few thousand characters (?), which is presumably related to the way the selection mechanism divides larger amounts of data into chunks, but I have not bothered to track this down. Note that there is no security in this system other than the obscurity of the server host name and port number (which I have changed in the scripts below to protect the innocent) and the fact that the server ignores input which does not match its very limited protocol.
#!/usr/bin/wish # teleclip server for unix # Written by Peter Shawhan, 21 Sept 2002 #-- Hard-code the port number set serverPort 33333 ##========================================================================= proc Main {} { #-- Withdraw the main window wm withdraw . #-- Open a listening socket global serverPort if [catch {socket -server Connect $serverPort} lsock] { puts stderr "Error opening listening socket: $lsock" exit 1 } #-- Create a text widget, but do not pack it; it's just for holding #-- clipboard data that we receive text .text return } ##========================================================================= proc Connect { sock addr port } { #-- Handle a client connection fconfigure $sock -buffering full -blocking 0 fileevent $sock readable "Input $sock" set ::buf($sock) "" return } ##========================================================================= proc Input { sock } { #-- Handle input from the client upvar #0 buf($sock) buf if { [eof $sock] || [catch {read $sock} data] } { #-- Socket was closed by client, or there was an error reading from it catch { close $sock } unset ::buf($sock) return } append buf $data #-- If input is complete, act on it if { [info complete $buf] } { switch -exact -- [lindex $buf 0] { "cput" { if { [llength $buf] >= 2 } { #-- Stick the data into the hidden text widget .text delete 1.0 end .text insert end [lindex $buf 1] .text tag add sel 1.0 end-1char #-- Delete the buffer contents that we've dealt with #-- (typically this deletes the entire buffer) set buf [lrange $buf 2 end] } } "cget" { #-- Send contents of PRIMARY selection (since this is unix) puts $sock [list [selection get] ] flush $sock set buf [lrange $buf 1 end] } default { #-- Just ignore this input from the client set buf [lrange $buf 1 end] } } } return } ##========================================================================= #-- Now start the program and enter the event loop Main
# teleclip client for Windows/Macintosh # Written by Peter Shawhan, 21 Sept 2002 #-- Hard-code server's Internet address and port number set serverIP teleclip.ligo.caltech.edu set serverPort 33333 ##========================================================================= proc Main {} { global serverIP serverPort buf #-- Open a socket connection to the server if [catch {socket $serverIP $serverPort} sock] { #-- Display an error message message .message -aspect 10000 -justify center \ -text "Error connecting to teleclip server\nat $serverIP:\n$sock" pack .message return } fconfigure $sock -buffering full -blocking 0 #-- Set up a handler for receiving clipboard data over the socket fileevent $sock readable "Input $sock" set ::buf "" #-- Create "Send" and "Receive" buttons, and pack them in the window button .send -text Send -command "Send $sock" button .receive -text Receive -command "Receive $sock" pack .send .receive -side left return } ##========================================================================= proc Send { sock } { #-- Send the contents of the clipboard; ignore errors catch { puts $sock [list "cput" [selection get -selection CLIPBOARD] ] flush $sock } return } ##========================================================================= proc Receive { sock } { #-- Send a request for clipboard data; the data coming back will be #-- handled by the fileevent puts $sock "cget" flush $sock return } ##========================================================================= proc Input { sock } { global serverIP buf if { [eof $sock] || [catch {read $sock} data] } { catch { close $sock } #-- Replace the buttons with an error message destroy .send .receive message .message -aspect 10000 -justify center\ -text "teleclip server at\n$serverIP disconnected" pack .message return } append buf $data #-- If input is complete, stick it into the clipboard if { [info complete $buf] } { clipboard clear clipboard append -- [lindex $buf 0] set buf "" } return } ##========================================================================= #-- Now start the program and enter the event loop Main