from __future__ import with_statement
from contextlib import contextmanager
import ctypes

GMEM_DDESHARE = 0x2000
GMEM_MOVEABLE = 0x0002
CF_TEXT = 1

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

@contextmanager
def _GlobalLock(h):
	p = kernel32.GlobalLock(h)
	if not p:
		raise WindowsError("GlobalLock() failed")
	try:
		yield p
	finally:
		kernel32.GlobalUnlock(h)

@contextmanager
def _Clipboard():
	if not user32.OpenClipboard(None):
		raise WindowsError("OpenClipboard() failed")
	try:
		yield
	finally:
		user32.CloseClipboard()

def set(s):
	s = s.encode("mbcs")
	# memory block
	h = kernel32.GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len(s) + 1)
	if not h:
		raise WindowsError("GlobalAlloc() failed")
	try:
		with _GlobalLock(h) as p:
			p = (ctypes.c_char * (len(s) + 1)).from_address(p)
			for i, c in enumerate(s + "\0"):
				p[i] = c
		# clipboard
		with _Clipboard():
			user32.EmptyClipboard()
			user32.SetClipboardData(CF_TEXT, h)
	except:
		kernel32.GlobalFree(h)
		raise

def get():
	with _Clipboard():
		h = user32.GetClipboardData(CF_TEXT)
		if h:
			with _GlobalLock(h) as p:
				return ctypes.c_char_p(p).value.decode("mbcs")

def clear():
	with _Clipboard():
		user32.EmptyClipboard()

