v / vlib / clipboard
Raw file | 201 loc (172 sloc) | 5.17 KB | Latest commit hash 8b962f844
1module clipboard
2
3import time
4
5#include <windows.h>
6#flag -luser32
7
8struct WndClassEx {
9 cb_size u32
10 style u32
11 lpfn_wnd_proc voidptr
12 cb_cls_extra int
13 cb_wnd_extra int
14 h_instance C.HINSTANCE
15 h_icon C.HICON
16 h_cursor C.HCURSOR
17 hbr_background C.HBRUSH
18 lpsz_menu_name &u16 = unsafe { nil } // LPCWSTR
19 lpsz_class_name &u16 = unsafe { nil }
20 h_icon_sm &u16 = unsafe { nil }
21}
22
23fn C.RegisterClassEx(class &WndClassEx) int
24
25fn C.GetClipboardOwner() &C.HWND
26
27fn 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
28
29// fn C.MultiByteToWideChar(CodePage u32, dw_flags u16, lpMultiByteStr byteptr, cbMultiByte int, lpWideCharStr u16, cchWideChar int) int
30fn C.EmptyClipboard()
31
32fn C.CloseClipboard()
33
34fn C.GlobalAlloc(uFlag u32, size i64) C.HGLOBAL
35
36fn C.GlobalFree(buf C.HGLOBAL)
37
38fn C.GlobalLock(buf C.HGLOBAL) voidptr
39
40fn C.GlobalUnlock(buf C.HGLOBAL) bool
41
42fn C.SetClipboardData(uFormat u32, data voidptr) C.HANDLE
43
44fn C.GetClipboardData(uFormat u32) C.HANDLE
45
46fn C.DefWindowProc(hwnd C.HWND, msg u32, wParam C.WPARAM, lParam C.LPARAM) C.LRESULT
47
48fn C.SetLastError(error i64)
49
50fn C.OpenClipboard(hwnd C.HWND) int
51
52fn C.DestroyWindow(hwnd C.HWND)
53
54// Clipboard represents a system clipboard.
55//
56// System "copy" and "paste" actions utilize the clipboard for temporary storage.
57[heap]
58pub struct Clipboard {
59 max_retries int
60 retry_delay int
61mut:
62 hwnd C.HWND
63 foo int // TODO remove
64}
65
66fn (cb &Clipboard) get_clipboard_lock() bool {
67 mut retries := cb.max_retries
68 mut last_error := u32(0)
69 for {
70 retries--
71 if retries < 0 {
72 break
73 }
74 last_error = C.GetLastError()
75 if C.OpenClipboard(cb.hwnd) > 0 {
76 return true
77 } else if last_error != u32(C.ERROR_ACCESS_DENIED) {
78 return false
79 }
80 time.sleep(cb.retry_delay * time.second)
81 }
82 C.SetLastError(last_error)
83 return false
84}
85
86fn new_clipboard() &Clipboard {
87 mut cb := &Clipboard{
88 max_retries: 5
89 retry_delay: 5
90 }
91 class_name := 'clipboard'
92 wndclass := WndClassEx{
93 cb_size: sizeof(WndClassEx)
94 lpfn_wnd_proc: voidptr(&C.DefWindowProc)
95 lpsz_class_name: class_name.to_wide()
96 lpsz_menu_name: 0
97 h_icon_sm: 0
98 }
99 if C.RegisterClassEx(&wndclass) == 0 && C.GetLastError() != u32(C.ERROR_CLASS_ALREADY_EXISTS) {
100 println('Failed registering class.')
101 }
102 hwnd := C.CreateWindowEx(0, wndclass.lpsz_class_name, wndclass.lpsz_class_name, 0,
103 0, 0, 0, 0, C.HWND_MESSAGE, C.NULL, C.NULL, C.NULL)
104 if hwnd == C.NULL {
105 println('Error creating window!')
106 }
107 cb.hwnd = hwnd
108 return cb
109}
110
111// check_availability returns true if the clipboard is ready to be used.
112pub fn (cb &Clipboard) check_availability() bool {
113 return cb.hwnd != C.HWND(C.NULL)
114}
115
116// has_ownership returns true if the contents of
117// the clipboard were created by this clipboard instance.
118pub fn (cb &Clipboard) has_ownership() bool {
119 return C.GetClipboardOwner() == cb.hwnd
120}
121
122// clear empties the clipboard contents.
123pub fn (mut cb Clipboard) clear() {
124 if !cb.get_clipboard_lock() {
125 return
126 }
127 C.EmptyClipboard()
128 C.CloseClipboard()
129 cb.foo = 0
130}
131
132// free releases all memory associated with the clipboard
133// instance.
134pub fn (mut cb Clipboard) free() {
135 C.DestroyWindow(cb.hwnd)
136 cb.foo = 0
137}
138
139// the string.to_wide doesn't work with SetClipboardData, don't know why
140fn to_wide(text string) C.HGLOBAL {
141 len_required := C.MultiByteToWideChar(C.CP_UTF8, C.MB_ERR_INVALID_CHARS, text.str,
142 text.len + 1, C.NULL, 0)
143 buf := C.GlobalAlloc(C.GMEM_MOVEABLE, i64(sizeof(u16)) * len_required)
144 if buf != C.HGLOBAL(C.NULL) {
145 mut locked := &u16(C.GlobalLock(buf))
146 C.MultiByteToWideChar(C.CP_UTF8, C.MB_ERR_INVALID_CHARS, text.str, text.len + 1,
147 locked, len_required)
148 unsafe {
149 locked[len_required - 1] = u16(0)
150 }
151 C.GlobalUnlock(buf)
152 }
153 return buf
154}
155
156// set_text transfers `text` to the system clipboard.
157// This is often associated with a *copy* action (`Ctrl` + `C`).
158pub fn (mut cb Clipboard) set_text(text string) bool {
159 cb.foo = 0
160 buf := to_wide(text)
161 if !cb.get_clipboard_lock() {
162 C.GlobalFree(buf)
163 return false
164 } else {
165 // EmptyClipboard must be called to properly update clipboard ownership
166 C.EmptyClipboard()
167 if C.SetClipboardData(C.CF_UNICODETEXT, buf) == C.HANDLE(C.NULL) {
168 println('SetClipboardData: Failed.')
169 C.CloseClipboard()
170 C.GlobalFree(buf)
171 return false
172 }
173 }
174 // CloseClipboard appears to change the sequence number...
175 C.CloseClipboard()
176 return true
177}
178
179// get_text retrieves the contents of the system clipboard
180// as a `string`.
181// This is often associated with a *paste* action (`Ctrl` + `V`).
182pub fn (mut cb Clipboard) get_text() string {
183 cb.foo = 0
184 if !cb.get_clipboard_lock() {
185 return ''
186 }
187 h_data := C.GetClipboardData(C.CF_UNICODETEXT)
188 if h_data == C.HANDLE(C.NULL) {
189 C.CloseClipboard()
190 return ''
191 }
192 str := unsafe { string_from_wide(&u16(C.GlobalLock(C.HGLOBAL(h_data)))) }
193 C.GlobalUnlock(C.HGLOBAL(h_data))
194 return str
195}
196
197// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap.
198// Please note: new_primary only works on X11 based systems.
199pub fn new_primary() &Clipboard {
200 panic('Primary clipboard is not supported on non-Linux systems.')
201}