187 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			V
		
	
	
| module clipboard
 | |
| 
 | |
| import time
 | |
| 
 | |
| #include <windows.h>
 | |
| #flag -luser32
 | |
| 
 | |
| struct WndClassEx {
 | |
| 	cb_size         u32
 | |
| 	style           u32
 | |
| 	lpfn_wnd_proc   voidptr
 | |
| 	cb_cls_extra    int
 | |
| 	cb_wnd_extra    int
 | |
| 	h_instance      C.HINSTANCE
 | |
| 	h_icon          C.HICON
 | |
| 	h_cursor        C.HCURSOR
 | |
| 	hbr_background  C.HBRUSH
 | |
| 	lpsz_menu_name  &u16 // LPCWSTR
 | |
| 	lpsz_class_name &u16
 | |
| 	h_icon_sm       &u16
 | |
| }
 | |
| 
 | |
| fn C.RegisterClassEx(class &WndClassEx) int
 | |
| 
 | |
| fn C.GetClipboardOwner() &C.HWND
 | |
| 
 | |
| fn C.CreateWindowEx(dwExStyle i64, lpClassName &u16, lpWindowName &u16, dwStyle i64, x int, y int, nWidth int, nHeight int, hWndParent i64, hMenu voidptr, h_instance voidptr, lpParam voidptr) &C.HWND
 | |
| 
 | |
| // fn C.MultiByteToWideChar(CodePage u32, dw_flags u16, lpMultiByteStr byteptr, cbMultiByte int, lpWideCharStr u16, cchWideChar int) int
 | |
| fn C.EmptyClipboard()
 | |
| 
 | |
| fn C.CloseClipboard()
 | |
| 
 | |
| fn C.GlobalAlloc(uFlag u32, size i64) C.HGLOBAL
 | |
| 
 | |
| fn C.GlobalFree(buf C.HGLOBAL)
 | |
| 
 | |
| fn C.GlobalLock(buf C.HGLOBAL) voidptr
 | |
| 
 | |
| fn C.GlobalUnlock(buf C.HGLOBAL) bool
 | |
| 
 | |
| fn C.SetClipboardData(uFormat u32, data voidptr) C.HANDLE
 | |
| 
 | |
| fn C.GetClipboardData(uFormat u32) C.HANDLE
 | |
| 
 | |
| fn C.DefWindowProc(hwnd C.HWND, msg u32, wParam C.WPARAM, lParam C.LPARAM) C.LRESULT
 | |
| 
 | |
| fn C.SetLastError(error i64)
 | |
| 
 | |
| fn C.OpenClipboard(hwnd C.HWND) int
 | |
| 
 | |
| fn C.DestroyWindow(hwnd C.HWND)
 | |
| 
 | |
| struct Clipboard {
 | |
| 	max_retries int
 | |
| 	retry_delay int
 | |
| mut:
 | |
| 	hwnd C.HWND
 | |
| 	foo  int // TODO remove
 | |
| }
 | |
| 
 | |
| fn (cb &Clipboard) get_clipboard_lock() bool {
 | |
| 	mut retries := cb.max_retries
 | |
| 	mut last_error := u32(0)
 | |
| 	for {
 | |
| 		retries--
 | |
| 		if retries < 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		last_error = C.GetLastError()
 | |
| 		if C.OpenClipboard(cb.hwnd) > 0 {
 | |
| 			return true
 | |
| 		} else if last_error != u32(C.ERROR_ACCESS_DENIED) {
 | |
| 			return false
 | |
| 		}
 | |
| 		time.sleep(cb.retry_delay * time.second)
 | |
| 	}
 | |
| 	C.SetLastError(last_error)
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| fn new_clipboard() &Clipboard {
 | |
| 	mut cb := &Clipboard{
 | |
| 		max_retries: 5
 | |
| 		retry_delay: 5
 | |
| 	}
 | |
| 	class_name := 'clipboard'
 | |
| 	wndclass := WndClassEx{
 | |
| 		cb_size: sizeof(WndClassEx)
 | |
| 		lpfn_wnd_proc: voidptr(&C.DefWindowProc)
 | |
| 		lpsz_class_name: class_name.to_wide()
 | |
| 		lpsz_menu_name: 0
 | |
| 		h_icon_sm: 0
 | |
| 	}
 | |
| 	if C.RegisterClassEx(&wndclass) == 0 && C.GetLastError() != u32(C.ERROR_CLASS_ALREADY_EXISTS) {
 | |
| 		println('Failed registering class.')
 | |
| 	}
 | |
| 	hwnd := C.CreateWindowEx(0, wndclass.lpsz_class_name, wndclass.lpsz_class_name, 0,
 | |
| 		0, 0, 0, 0, C.HWND_MESSAGE, C.NULL, C.NULL, C.NULL)
 | |
| 	if hwnd == C.NULL {
 | |
| 		println('Error creating window!')
 | |
| 	}
 | |
| 	cb.hwnd = hwnd
 | |
| 	return cb
 | |
| }
 | |
| 
 | |
| pub fn (cb &Clipboard) check_availability() bool {
 | |
| 	return cb.hwnd != C.HWND(C.NULL)
 | |
| }
 | |
| 
 | |
| pub fn (cb &Clipboard) has_ownership() bool {
 | |
| 	return C.GetClipboardOwner() == cb.hwnd
 | |
| }
 | |
| 
 | |
| pub fn (mut cb Clipboard) clear() {
 | |
| 	if !cb.get_clipboard_lock() {
 | |
| 		return
 | |
| 	}
 | |
| 	C.EmptyClipboard()
 | |
| 	C.CloseClipboard()
 | |
| 	cb.foo = 0
 | |
| }
 | |
| 
 | |
| pub fn (mut cb Clipboard) free() {
 | |
| 	C.DestroyWindow(cb.hwnd)
 | |
| 	cb.foo = 0
 | |
| }
 | |
| 
 | |
| // the string.to_wide doesn't work with SetClipboardData, don't know why
 | |
| fn to_wide(text string) C.HGLOBAL {
 | |
| 	len_required := C.MultiByteToWideChar(C.CP_UTF8, C.MB_ERR_INVALID_CHARS, text.str,
 | |
| 		text.len + 1, C.NULL, 0)
 | |
| 	buf := C.GlobalAlloc(C.GMEM_MOVEABLE, i64(sizeof(u16)) * len_required)
 | |
| 	if buf != C.HGLOBAL(C.NULL) {
 | |
| 		mut locked := &u16(C.GlobalLock(buf))
 | |
| 		C.MultiByteToWideChar(C.CP_UTF8, C.MB_ERR_INVALID_CHARS, text.str, text.len + 1,
 | |
| 			locked, len_required)
 | |
| 		unsafe {
 | |
| 			locked[len_required - 1] = u16(0)
 | |
| 		}
 | |
| 		C.GlobalUnlock(buf)
 | |
| 	}
 | |
| 	return buf
 | |
| }
 | |
| 
 | |
| pub fn (mut cb Clipboard) set_text(text string) bool {
 | |
| 	cb.foo = 0
 | |
| 	buf := to_wide(text)
 | |
| 	if !cb.get_clipboard_lock() {
 | |
| 		C.GlobalFree(buf)
 | |
| 		return false
 | |
| 	} else {
 | |
| 		// EmptyClipboard must be called to properly update clipboard ownership
 | |
| 		C.EmptyClipboard()
 | |
| 		if C.SetClipboardData(C.CF_UNICODETEXT, buf) == C.HANDLE(C.NULL) {
 | |
| 			println('SetClipboardData: Failed.')
 | |
| 			C.CloseClipboard()
 | |
| 			C.GlobalFree(buf)
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	// CloseClipboard appears to change the sequence number...
 | |
| 	C.CloseClipboard()
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| pub fn (mut cb Clipboard) get_text() string {
 | |
| 	cb.foo = 0
 | |
| 	if !cb.get_clipboard_lock() {
 | |
| 		return ''
 | |
| 	}
 | |
| 	h_data := C.GetClipboardData(C.CF_UNICODETEXT)
 | |
| 	if h_data == C.HANDLE(C.NULL) {
 | |
| 		C.CloseClipboard()
 | |
| 		return ''
 | |
| 	}
 | |
| 	str := unsafe { string_from_wide(&u16(C.GlobalLock(C.HGLOBAL(h_data)))) }
 | |
| 	C.GlobalUnlock(C.HGLOBAL(h_data))
 | |
| 	return str
 | |
| }
 | |
| 
 | |
| // new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap.
 | |
| // Please note: new_primary only works on X11 based systems.
 | |
| pub fn new_primary() &Clipboard {
 | |
| 	panic('Primary clipboard is not supported on non-Linux systems.')
 | |
| }
 |