2019-11-17 03:40:03 +01:00
|
|
|
module clipboard
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
2020-06-19 12:54:56 +02:00
|
|
|
#flag -lUser32
|
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
struct WndClassEx {
|
2020-05-16 16:12:23 +02:00
|
|
|
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
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
2019-11-24 11:22:57 +01:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.RegisterClassEx(class WndClassEx) int
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.GetClipboardOwner() &C.HWND
|
2020-05-16 16:12:23 +02:00
|
|
|
|
|
|
|
fn C.CreateWindowEx(dwExStyle i64, lpClassName, lpWindowName &u16, dwStyle i64, x, y, nWidth, nHeight int, hWndParent i64, hMenu, h_instance, lpParam voidptr) &C.HWND
|
|
|
|
|
|
|
|
// fn C.MultiByteToWideChar(CodePage u32, dw_flags u16, lpMultiByteStr byteptr, cbMultiByte int, lpWideCharStr u16, cchWideChar int) int
|
2019-11-17 03:40:03 +01:00
|
|
|
fn C.EmptyClipboard()
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2019-11-17 03:40:03 +01:00
|
|
|
fn C.CloseClipboard()
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.GlobalAlloc(uFlag u32, size i64) C.HGLOBAL
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.GlobalFree(buf C.HGLOBAL)
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.GlobalLock(buf C.HGLOBAL)
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.GlobalUnlock(buf C.HGLOBAL)
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2019-11-17 03:40:03 +01:00
|
|
|
fn C.SetClipboardData(uFormat u32, data voidptr) C.HANDLE
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2019-11-17 03:40:03 +01:00
|
|
|
fn C.GetClipboardData(uFormat u32) C.HANDLE
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.DefWindowProc(hwnd C.HWND, msg u32, wParam C.WPARAM, lParam C.LPARAM) C.LRESULT
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2019-11-17 03:40:03 +01:00
|
|
|
fn C.SetLastError(error i64)
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.OpenClipboard(hwnd C.HWND) int
|
2020-05-16 16:12:23 +02:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
fn C.DestroyWindow(hwnd C.HWND)
|
2019-11-17 03:40:03 +01:00
|
|
|
|
2020-04-04 15:37:13 +02:00
|
|
|
struct Clipboard {
|
2019-11-17 03:40:03 +01:00
|
|
|
max_retries int
|
|
|
|
retry_delay int
|
|
|
|
mut:
|
2020-05-16 16:12:23 +02:00
|
|
|
hwnd C.HWND
|
|
|
|
foo int // TODO remove
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn (cb &Clipboard) get_clipboard_lock() bool {
|
|
|
|
mut retries := cb.max_retries
|
|
|
|
mut last_error := u32(0)
|
|
|
|
for {
|
|
|
|
retries--
|
|
|
|
if retries < 0 {
|
|
|
|
break
|
|
|
|
}
|
2020-04-04 15:37:13 +02:00
|
|
|
last_error = C.GetLastError()
|
|
|
|
if C.OpenClipboard(cb.hwnd) > 0 {
|
2019-11-17 03:40:03 +01:00
|
|
|
return true
|
|
|
|
} else if last_error != u32(C.ERROR_ACCESS_DENIED) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
time.sleep(cb.retry_delay)
|
|
|
|
}
|
2020-04-04 15:37:13 +02:00
|
|
|
C.SetLastError(last_error)
|
2019-11-17 03:40:03 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_clipboard() &Clipboard {
|
2020-05-16 16:12:23 +02:00
|
|
|
mut cb := &Clipboard{
|
2019-11-17 03:40:03 +01:00
|
|
|
max_retries: 5
|
|
|
|
retry_delay: 5
|
|
|
|
}
|
2020-05-16 16:12:23 +02:00
|
|
|
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
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
2020-04-04 15:37:13 +02:00
|
|
|
if C.RegisterClassEx(&wndclass) == 0 && C.GetLastError() != u32(C.ERROR_CLASS_ALREADY_EXISTS) {
|
2020-05-16 16:12:23 +02:00
|
|
|
println('Failed registering class.')
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
2020-05-16 16:12:23 +02:00
|
|
|
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)
|
2019-11-17 03:40:03 +01:00
|
|
|
if hwnd == C.NULL {
|
2020-05-16 16:12:23 +02:00
|
|
|
println('Error creating window!')
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
|
|
|
cb.hwnd = hwnd
|
|
|
|
return cb
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (cb &Clipboard) check_availability() bool {
|
2020-04-04 15:37:13 +02:00
|
|
|
return cb.hwnd != C.HWND(C.NULL)
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn (cb &Clipboard) has_ownership() bool {
|
2020-04-04 15:37:13 +02:00
|
|
|
return C.GetClipboardOwner() == cb.hwnd
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 16:12:23 +02:00
|
|
|
fn (mut cb Clipboard) clear() {
|
|
|
|
if !cb.get_clipboard_lock() {
|
|
|
|
return
|
|
|
|
}
|
2020-04-04 15:37:13 +02:00
|
|
|
C.EmptyClipboard()
|
|
|
|
C.CloseClipboard()
|
2019-12-07 13:51:00 +01:00
|
|
|
cb.foo = 0
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 16:12:23 +02:00
|
|
|
fn (mut cb Clipboard) free() {
|
2020-04-04 15:37:13 +02:00
|
|
|
C.DestroyWindow(cb.hwnd)
|
2019-12-07 13:51:00 +01:00
|
|
|
cb.foo = 0
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// the string.to_wide doesn't work with SetClipboardData, don't know why
|
2020-04-04 15:37:13 +02:00
|
|
|
fn to_wide(text string) &C.HGLOBAL {
|
2020-05-16 16:12:23 +02:00
|
|
|
len_required := C.MultiByteToWideChar(C.CP_UTF8, C.MB_ERR_INVALID_CHARS, text.str, text.len +
|
|
|
|
1, C.NULL, 0)
|
2020-05-27 05:42:48 +02:00
|
|
|
buf := C.GlobalAlloc(C.GMEM_MOVEABLE, i64(sizeof(u16)) * len_required)
|
2020-04-04 15:37:13 +02:00
|
|
|
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)
|
2020-08-20 10:33:12 +02:00
|
|
|
unsafe {
|
|
|
|
locked[len_required - 1] = u16(0)
|
|
|
|
}
|
2020-04-04 15:37:13 +02:00
|
|
|
C.GlobalUnlock(buf)
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
|
|
|
return buf
|
|
|
|
}
|
|
|
|
|
2020-05-16 16:12:23 +02:00
|
|
|
fn (mut cb Clipboard) set_text(text string) bool {
|
2019-12-07 13:51:00 +01:00
|
|
|
cb.foo = 0
|
2019-11-17 03:40:03 +01:00
|
|
|
buf := to_wide(text)
|
|
|
|
if !cb.get_clipboard_lock() {
|
2020-04-04 15:37:13 +02:00
|
|
|
C.GlobalFree(buf)
|
2019-11-17 03:40:03 +01:00
|
|
|
return false
|
|
|
|
} else {
|
2019-11-24 11:22:57 +01:00
|
|
|
// EmptyClipboard must be called to properly update clipboard ownership
|
2020-04-04 15:37:13 +02:00
|
|
|
C.EmptyClipboard()
|
|
|
|
if C.SetClipboardData(C.CF_UNICODETEXT, buf) == C.HANDLE(C.NULL) {
|
2020-05-16 16:12:23 +02:00
|
|
|
println('SetClipboardData: Failed.')
|
2020-04-04 15:37:13 +02:00
|
|
|
C.CloseClipboard()
|
|
|
|
C.GlobalFree(buf)
|
2019-11-17 03:40:03 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2019-11-24 11:22:57 +01:00
|
|
|
// CloseClipboard appears to change the sequence number...
|
2020-04-04 15:37:13 +02:00
|
|
|
C.CloseClipboard()
|
2019-11-17 03:40:03 +01:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-05-16 16:12:23 +02:00
|
|
|
fn (mut cb Clipboard) get_text() string {
|
2019-12-07 13:51:00 +01:00
|
|
|
cb.foo = 0
|
2019-11-17 03:40:03 +01:00
|
|
|
if !cb.get_clipboard_lock() {
|
2020-05-16 16:12:23 +02:00
|
|
|
return ''
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
2020-04-04 15:37:13 +02:00
|
|
|
h_data := C.GetClipboardData(C.CF_UNICODETEXT)
|
|
|
|
if h_data == C.HANDLE(C.NULL) {
|
|
|
|
C.CloseClipboard()
|
2020-05-16 16:12:23 +02:00
|
|
|
return ''
|
2019-11-17 03:40:03 +01:00
|
|
|
}
|
2020-04-04 15:37:13 +02:00
|
|
|
str := string_from_wide(&u16(C.GlobalLock(h_data)))
|
|
|
|
C.GlobalUnlock(h_data)
|
2019-11-17 03:40:03 +01:00
|
|
|
return str
|
2019-11-18 11:10:31 +01:00
|
|
|
}
|
2019-12-27 17:59:04 +01:00
|
|
|
|
|
|
|
pub fn new_primary() &Clipboard {
|
|
|
|
panic('Primary clipboard is not supported on non-Linux systems.')
|
|
|
|
}
|