[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'''''. * The server script, called '''teleclip''', runs on the Linux machine as a background wish process. (To start it, type 'teleclip &'.) It has a hidden (withdrawn) window with a text widget which holds any text sent to it by the client. The text is selected as soon as it is received, so that it can be pasted into any other window with a simple click of the middle mouse button. Selecting text in any window on the Linux machine immediately makes it available for the client to receive upon request. * The client script, called '''teleclip_client.tcl''', runs under wish on the Windows machine. (To start it, double-click on the file. Alternatively, for convenient startup, I put a shortcut to it in the 'Quick Launch' area of my Windows taskbar.) It creates a little window with 'Send' and 'Receive' buttons. To send some text, I select it, Copy it to the Windows clipboard, and then click on the 'Send' button. To get whatever text is selected on the Linux machine, I click on the 'Receive' button and then Paste it where I want it. 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. '''The server script (for the Linux machine) : teleclip''' #!/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 '''The client script (for the Windows machine) : teleclip_client.tcl''' # 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 ---- [Category Application] [Category Desktop]