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