Uses shared network file to share a clipboard across machines (e.g., virtual machines).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | #!/usr/bin/python2.5
# -*- coding: utf-8 -*-
""" A shared clipboard using a network accessible file. Inspired by [1] but
supports Linux, unicode, and interrupting (if paused virtual machine).
[1]:http://www.devx.com/opensource/Article/37233/1954
"""
import codecs
import os
try: # use killable process because xsel/xclip sometimes stalls
from killableprocess import Popen # http://svn.smedbergs.us/python-processes/trunk/
except ImportError:
from subprocess import Popen
from subprocess import PIPE
import signal
import sys
import time
#### Clipboard files and functions per system ####
# Mac stuff removed since I can't test, see [1] above.
if sys.platform == 'win32':
clipboard_fn = r'w:\apps\clipboard\clipboard.txt'
log_fn = r'w:\apps\clipboard\log_clipboard.txt'
import win32clipboard
def openClipboard():
win32clipboard.OpenClipboard()
def closeClipboard():
try:
win32clipboard.CloseClipboard()
except Exception, e:
print e
pass
def getClipboardData():
from win32clipboard import CF_UNICODETEXT
if win32clipboard.IsClipboardFormatAvailable(CF_UNICODETEXT):
return win32clipboard.GetClipboardData().decode('cp1252')
else:
return None
def setClipboardData(data): # “ Václav
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT,
data)
elif sys.platform == 'linux2':
clipboard_fn = r'/home/reagle/e/win/apps/clipboard/clipboard.txt'
log_fn = r'/home/reagle/e/win/apps/clipboard/log_clipboard.txt'
def openClipboard():
pass
def closeClipboard():
pass
def getClipboardData(): # xclip needs iso-8859-1 for ver < 0.11 #failed
my_logger.debug(' %s: getClipboardData' % sys.platform)
p = Popen(['/usr/bin/xsel', '-t', '200', '-o'], stdout=PIPE)
p.wait(2)
result = p.communicate()[0].decode('utf-8')
my_logger.debug(' %s: gotClipboardData "%s"' % (sys.platform, result))
return result
def setClipboardData(data):
my_logger.debug(' %s: setClipboardData data = "%s"' % (sys.platform, data))
p = Popen(['/usr/bin/xsel', '-t', '200', '-i'], stdin=PIPE)
result = p.communicate(input=data.encode('utf-8'))
p.wait(2)
my_logger.debug(' %s: setClipboardData done' % sys.platform)
return result
else:
print "Unknown system"
sys.exit()
#### Logging ####
#import logging
#import logging.handlers
#my_logger = logging.getLogger('MyLogger')
#my_logger.setLevel(logging.DEBUG) # CRITICAL
#handler = logging.StreamHandler(sys.stderr)
##handler = logging.handlers.RotatingFileHandler(
##log_fn, maxBytes=2000, backupCount=0) # log_fn
#my_logger.addHandler(handler)
class Logger:
def debug(self, msg):
pass
#now = time.localtime(time.time())
#print time.strftime("%H%M%S", now) + msg.encode('utf-8')
my_logger = Logger()
#### File Object ####
class File:
def __init__(self, clipboard_fn):
self.prev_mtime = self.mtime = None
self.clipboard_fn = clipboard_fn
def hasUpdated(self):
'''Checking file attribute lessens file blocking of read'''
self.prev_mtime = self.mtime
self.mtime = os.stat(self.clipboard_fn).st_mtime # last time clipboard_fn modified
return self.mtime != self.prev_mtime
def update(self):
self.mtime = os.stat(self.clipboard_fn).st_mtime
my_logger.debug(' %s: updated mtime "%s"' % (sys.platform, self.mtime))
def write(self, data):
my_logger.debug(' %s: opening clipboard_fn for write' % sys.platform)
fd = codecs.open(self.clipboard_fn, 'w', 'utf-8')
fd.write(data)
fd.close()
my_logger.debug(' %s: closed clipboard_fn for write' % sys.platform)
my_logger.debug(' %s: wrote "%s"' % (sys.platform, data))
def read(self):
my_logger.debug('%s: clipboard_fn updated, opening for read' % sys.platform)
fd = codecs.open(self.clipboard_fn, 'r', 'utf-8')
data = fd.read()
my_logger.debug(" %s: getting data = '%s'" % (sys.platform, data))
fd.close()
my_logger.debug(' %s: closed clipboard_fn for read' % sys.platform)
return data
clipboard_file = File(clipboard_fn)
#### Poll clipboard loop #####
wait_no = 0
def monitorClipboard(clipboard_fn):
prev_data = u''
while (True):
time.sleep(1)
try:
openClipboard()
except:
my_logger.debug('OpenClipboard() failed')
continue
try:
data = getClipboardData()
if data and data != prev_data: # write to clipboard_fn
my_logger.debug('\n\n%s: update clipboard update: "%s" != "%s"'
% (sys.platform, data[0:10], prev_data[0:10]))
clipboard_file.write(data)
prev_data = data
clipboard_file.update()
else: # local clipboard hasn't changed, did remote clipboard change?
if clipboard_file.hasUpdated():
data = clipboard_file.read()
if data != prev_data:
setClipboardData(data)
prev_data = data
sys.stdout.flush()
except Exception, e:
my_logger.debug(str(e))
time.sleep(5) # for network upon resume: wait 5 seconds, 3 times
wait_no += 1
if wait_no == 3:
sys.exit()
else:
wait_no = 0 # if suceeded, reset wait
closeClipboard()
def main():
monitorClipboard(clipboard_fn)
if __name__=='__main__':
main()
|