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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
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.')
}
|