#!/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()