aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/clipboard
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/old/vlib/clipboard
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/old/vlib/clipboard')
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard.v37
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard_android.c.v15
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v70
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard_darwin.m23
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard_default.c.v15
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v15
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard_test.v29
-rw-r--r--v_windows/v/old/vlib/clipboard/clipboard_windows.c.v186
-rw-r--r--v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v49
-rw-r--r--v_windows/v/old/vlib/clipboard/x11/clipboard.c.v501
10 files changed, 940 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/clipboard/clipboard.v b/v_windows/v/old/vlib/clipboard/clipboard.v
new file mode 100644
index 0000000..d82289b
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard.v
@@ -0,0 +1,37 @@
+module clipboard
+
+// new returns a new `Clipboard` instance allocated on the heap.
+// The `Clipboard` resources can be released with `free()`
+pub fn new() &Clipboard {
+ return new_clipboard()
+}
+
+// copy copies `text` into the clipboard.
+pub fn (mut cb Clipboard) copy(text string) bool {
+ return cb.set_text(text)
+}
+
+// paste returns current entry as a `string` from the clipboard.
+pub fn (mut cb Clipboard) paste() string {
+ return cb.get_text()
+}
+
+// clear_all clears the clipboard.
+pub fn (mut cb Clipboard) clear_all() {
+ cb.clear()
+}
+
+// destroy destroys the clipboard and free it's resources.
+pub fn (mut cb Clipboard) destroy() {
+ cb.free()
+}
+
+// check_ownership returns `true` if the `Clipboard` has the content ownership.
+pub fn (cb Clipboard) check_ownership() bool {
+ return cb.has_ownership()
+}
+
+// is_available returns `true` if the clipboard is available for use.
+pub fn (cb &Clipboard) is_available() bool {
+ return cb.check_availability()
+}
diff --git a/v_windows/v/old/vlib/clipboard/clipboard_android.c.v b/v_windows/v/old/vlib/clipboard/clipboard_android.c.v
new file mode 100644
index 0000000..eb1e6b8
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard_android.c.v
@@ -0,0 +1,15 @@
+module clipboard
+
+import clipboard.dummy
+
+pub type Clipboard = dummy.Clipboard
+
+fn new_clipboard() &Clipboard {
+ return dummy.new_clipboard()
+}
+
+// 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 {
+ return dummy.new_primary()
+}
diff --git a/v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v b/v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v
new file mode 100644
index 0000000..eff5d3b
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v
@@ -0,0 +1,70 @@
+module clipboard
+
+#include <libkern/OSAtomic.h>
+#include <Cocoa/Cocoa.h>
+#flag -framework Cocoa
+#include "@VEXEROOT/vlib/clipboard/clipboard_darwin.m"
+
+pub struct Clipboard {
+ pb voidptr
+ last_cb_serial i64
+mut:
+ foo int // TODO remove, for mut hack
+}
+
+fn C.darwin_new_pasteboard() voidptr
+
+fn C.darwin_get_pasteboard_text(voidptr) &byte
+
+fn C.darwin_set_pasteboard_text(voidptr, string) bool
+
+fn new_clipboard() &Clipboard {
+ cb := &Clipboard{
+ pb: C.darwin_new_pasteboard() // pb
+ }
+ return cb
+}
+
+pub fn (cb &Clipboard) check_availability() bool {
+ return cb.pb != C.NULL
+}
+
+pub fn (mut cb Clipboard) clear() {
+ cb.foo = 0
+ cb.set_text('')
+ //#[cb->pb clearContents];
+}
+
+pub fn (mut cb Clipboard) free() {
+ cb.foo = 0
+ // nothing to free
+}
+
+pub fn (cb &Clipboard) has_ownership() bool {
+ if cb.last_cb_serial == 0 {
+ return false
+ }
+ //#return [cb->pb changeCount] == cb->last_cb_serial;
+ return false
+}
+
+fn C.OSAtomicCompareAndSwapLong()
+
+pub fn (mut cb Clipboard) set_text(text string) bool {
+ return C.darwin_set_pasteboard_text(cb.pb, text)
+}
+
+pub fn (mut cb Clipboard) get_text() string {
+ cb.foo = 0
+ if isnil(cb.pb) {
+ return ''
+ }
+ utf8_clip := C.darwin_get_pasteboard_text(cb.pb)
+ return unsafe { utf8_clip.vstring() }
+}
+
+// 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.')
+}
diff --git a/v_windows/v/old/vlib/clipboard/clipboard_darwin.m b/v_windows/v/old/vlib/clipboard/clipboard_darwin.m
new file mode 100644
index 0000000..cabc744
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard_darwin.m
@@ -0,0 +1,23 @@
+//NSPasteboard* darwin_new_pasteboard() {
+void* darwin_new_pasteboard() {
+ return (__bridge void*) [NSPasteboard generalPasteboard];
+}
+
+char* darwin_get_pasteboard_text(void* pb) {
+ NSString *ns_clip = [((__bridge NSPasteboard*)pb) stringForType:NSStringPboardType]; //NSPasteboardTypeString
+ if (ns_clip == nil) {
+ return "";
+ }
+ return [ns_clip UTF8String];
+}
+
+bool darwin_set_pasteboard_text(void* _pb, string text) {
+ NSPasteboard* pb = (__bridge NSPasteboard*) _pb;
+ NSString *ns_clip = [[ NSString alloc ] initWithBytesNoCopy:text.str length:text.len encoding:NSUTF8StringEncoding freeWhenDone: false];
+ [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
+ bool ret = [pb setString:ns_clip forType:NSStringPboardType];
+ //[ns_clip release];
+ int serial = [pb changeCount];
+ //OSAtomicCompareAndSwapLong(cb.last_cb_serial, serial, &cb.last_cb_serial);
+ return ret;
+}
diff --git a/v_windows/v/old/vlib/clipboard/clipboard_default.c.v b/v_windows/v/old/vlib/clipboard/clipboard_default.c.v
new file mode 100644
index 0000000..d8f65ce
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard_default.c.v
@@ -0,0 +1,15 @@
+module clipboard
+
+import clipboard.x11
+
+pub type Clipboard = x11.Clipboard
+
+fn new_clipboard() &Clipboard {
+ return x11.new_clipboard()
+}
+
+// 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 {
+ return x11.new_primary()
+}
diff --git a/v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v b/v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v
new file mode 100644
index 0000000..eb1e6b8
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v
@@ -0,0 +1,15 @@
+module clipboard
+
+import clipboard.dummy
+
+pub type Clipboard = dummy.Clipboard
+
+fn new_clipboard() &Clipboard {
+ return dummy.new_clipboard()
+}
+
+// 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 {
+ return dummy.new_primary()
+}
diff --git a/v_windows/v/old/vlib/clipboard/clipboard_test.v b/v_windows/v/old/vlib/clipboard/clipboard_test.v
new file mode 100644
index 0000000..6c97f44
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard_test.v
@@ -0,0 +1,29 @@
+import clipboard
+
+fn run_test(is_primary bool) {
+ mut cb := if is_primary { clipboard.new_primary() } else { clipboard.new() }
+ if !cb.is_available() {
+ return
+ }
+ assert cb.check_ownership() == false
+ assert cb.copy('I am a good boy!') == true
+ // assert cb.check_ownership() == true TODO
+ assert cb.paste() == 'I am a good boy!'
+ cb.clear_all()
+ assert cb.paste().len <= 0
+ cb.destroy()
+}
+
+fn test_primary() {
+ $if linux || freebsd {
+ // run_test(true)
+ return
+ }
+}
+
+fn test_clipboard() {
+ $if linux || freebsd {
+ return
+ }
+ run_test(false)
+}
diff --git a/v_windows/v/old/vlib/clipboard/clipboard_windows.c.v b/v_windows/v/old/vlib/clipboard/clipboard_windows.c.v
new file mode 100644
index 0000000..9ae42d1
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/clipboard_windows.c.v
@@ -0,0 +1,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.')
+}
diff --git a/v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v b/v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v
new file mode 100644
index 0000000..a3f4f35
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v
@@ -0,0 +1,49 @@
+module dummy
+
+pub struct Clipboard {
+mut:
+ text string // text data sent or received
+ got_text bool // used to confirm that we have got the text
+ is_owner bool // to save selection owner state
+}
+
+// new_clipboard returns a new `Clipboard` instance allocated on the heap.
+// The `Clipboard` resources can be released with `free()`
+pub fn new_clipboard() &Clipboard {
+ return &Clipboard{}
+}
+
+// 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 {
+ return &Clipboard{}
+}
+
+pub fn (mut cb Clipboard) set_text(text string) bool {
+ cb.text = text
+ cb.is_owner = true
+ cb.got_text = true
+ return true
+}
+
+pub fn (mut cb Clipboard) get_text() string {
+ return cb.text
+}
+
+pub fn (mut cb Clipboard) clear() {
+ cb.text = ''
+ cb.is_owner = false
+}
+
+pub fn (mut cb Clipboard) free() {
+}
+
+pub fn (cb &Clipboard) has_ownership() bool {
+ return cb.is_owner
+}
+
+pub fn (cb &Clipboard) check_availability() bool {
+ // This is a dummy clipboard implementation,
+ // which can be always used, although it does not do much...
+ return true
+}
diff --git a/v_windows/v/old/vlib/clipboard/x11/clipboard.c.v b/v_windows/v/old/vlib/clipboard/x11/clipboard.c.v
new file mode 100644
index 0000000..d5a4656
--- /dev/null
+++ b/v_windows/v/old/vlib/clipboard/x11/clipboard.c.v
@@ -0,0 +1,501 @@
+// Currently there is only X11 Selections support and no way to handle Wayland
+// but since Wayland isn't extremely adopted, we are covering almost all Linux distros.
+module x11
+
+import time
+import sync
+import math
+
+$if freebsd {
+ #flag -I/usr/local/include
+ #flag -L/usr/local/lib
+} $else $if openbsd {
+ #flag -I/usr/X11R6/include
+ #flag -L/usr/X11R6/lib
+}
+#flag -lX11
+
+#include <X11/Xlib.h> # Please install a package with the X11 development headers, for example: `apt-get install libx11-dev`
+// X11
+[typedef]
+struct C.Display {
+}
+
+type Window = u64
+type Atom = u64
+
+fn C.XInitThreads() int
+
+fn C.XCloseDisplay(d &C.Display)
+
+fn C.XFlush(d &C.Display)
+
+fn C.XDestroyWindow(d &C.Display, w Window)
+
+fn C.XNextEvent(d &C.Display, e &C.XEvent)
+
+fn C.XSetSelectionOwner(d &C.Display, a Atom, w Window, time int)
+
+fn C.XGetSelectionOwner(d &C.Display, a Atom) Window
+
+fn C.XChangeProperty(d &C.Display, requestor Window, property Atom, typ Atom, format int, mode int, data voidptr, nelements int) int
+
+fn C.XSendEvent(d &C.Display, requestor Window, propogate int, mask i64, event &C.XEvent)
+
+fn C.XInternAtom(d &C.Display, typ &byte, only_if_exists int) Atom
+
+fn C.XCreateSimpleWindow(d &C.Display, root Window, x int, y int, width u32, height u32, border_width u32, border u64, background u64) Window
+
+fn C.XOpenDisplay(name &byte) &C.Display
+
+fn C.XConvertSelection(d &C.Display, selection Atom, target Atom, property Atom, requestor Window, time int) int
+
+fn C.XSync(d &C.Display, discard int) int
+
+fn C.XGetWindowProperty(d &C.Display, w Window, property Atom, offset i64, length i64, delete int, req_type Atom, actual_type_return &Atom, actual_format_return &int, nitems &u64, bytes_after_return &u64, prop_return &&byte) int
+
+fn C.XDeleteProperty(d &C.Display, w Window, property Atom) int
+
+fn C.DefaultScreen(display &C.Display) int
+
+fn C.RootWindow(display &C.Display, screen_number int) Window
+
+fn C.BlackPixel(display &C.Display, screen_number int) u32
+
+fn C.WhitePixel(display &C.Display, screen_number int) u32
+
+fn C.XFree(data voidptr)
+
+fn todo_del() {}
+
+[typedef]
+struct C.XSelectionRequestEvent {
+mut:
+ display &C.Display // Display the event was read from
+ owner Window
+ requestor Window
+ selection Atom
+ target Atom
+ property Atom
+ time int
+}
+
+[typedef]
+struct C.XSelectionEvent {
+mut:
+ @type int
+ display &C.Display // Display the event was read from
+ requestor Window
+ selection Atom
+ target Atom
+ property Atom
+ time int
+}
+
+[typedef]
+struct C.XSelectionClearEvent {
+mut:
+ window Window
+ selection Atom
+}
+
+[typedef]
+struct C.XDestroyWindowEvent {
+mut:
+ window Window
+}
+
+[typedef]
+union C.XEvent {
+mut:
+ @type int
+ xdestroywindow C.XDestroyWindowEvent
+ xselectionclear C.XSelectionClearEvent
+ xselectionrequest C.XSelectionRequestEvent
+ xselection C.XSelectionEvent
+}
+
+const (
+ atom_names = ['TARGETS', 'CLIPBOARD', 'PRIMARY', 'SECONDARY', 'TEXT', 'UTF8_STRING', 'text/plain',
+ 'text/html',
+ ]
+)
+
+// UNSUPPORTED TYPES: MULTIPLE, INCR, TIMESTAMP, image/bmp, image/jpeg, image/tiff, image/png
+// all the atom types we need
+// currently we only support text
+// in the future, maybe we can extend this
+// to support other mime types
+enum AtomType {
+ xa_atom = 0 // value 4
+ xa_string = 1 // value 31
+ targets = 2
+ clipboard = 3
+ primary = 4
+ secondary = 5
+ text = 6
+ utf8_string = 7
+ text_plain = 8
+ text_html = 9
+}
+
+pub struct Clipboard {
+ display &C.Display
+mut:
+ selection Atom // the selection atom
+ window Window
+ atoms []Atom
+ mutex &sync.Mutex
+ text string // text data sent or received
+ got_text bool // used to confirm that we have got the text
+ is_owner bool // to save selection owner state
+}
+
+struct Property {
+ actual_type Atom
+ actual_format int
+ nitems u64
+ data &byte
+}
+
+// new_clipboard returns a new `Clipboard` instance allocated on the heap.
+// The `Clipboard` resources can be released with `free()`
+pub fn new_clipboard() &Clipboard {
+ return new_x11_clipboard(.clipboard)
+}
+
+// new_x11_clipboard initializes a new clipboard of the given selection type.
+// Multiple clipboard instance types can be initialized and used separately.
+fn new_x11_clipboard(selection AtomType) &Clipboard {
+ if selection !in [.clipboard, .primary, .secondary] {
+ panic('Wrong AtomType. Must be one of .primary, .secondary or .clipboard.')
+ }
+ // init x11 thread support
+ status := C.XInitThreads()
+ if status == 0 {
+ println('WARN: this system does not support threads; clipboard will cause the program to lock.')
+ }
+
+ display := new_display()
+
+ if display == C.NULL {
+ println('ERROR: No X Server running. Clipboard cannot be used.')
+ return &Clipboard{
+ display: 0
+ mutex: sync.new_mutex()
+ }
+ }
+
+ mut cb := &Clipboard{
+ display: display
+ window: create_xwindow(display)
+ mutex: sync.new_mutex()
+ }
+ cb.intern_atoms()
+ cb.selection = cb.get_atom(selection)
+ // start the listener on another thread or
+ // we will be locked and will have to hard exit
+ go cb.start_listener()
+ return cb
+}
+
+pub fn (cb &Clipboard) check_availability() bool {
+ return cb.display != C.NULL
+}
+
+pub fn (mut cb Clipboard) free() {
+ C.XDestroyWindow(cb.display, cb.window)
+ cb.window = Window(0)
+ // FIX ME: program hangs when closing display
+ // XCloseDisplay(cb.display)
+}
+
+pub fn (mut cb Clipboard) clear() {
+ cb.mutex.@lock()
+ C.XSetSelectionOwner(cb.display, cb.selection, Window(0), C.CurrentTime)
+ C.XFlush(cb.display)
+ cb.is_owner = false
+ cb.text = ''
+ cb.mutex.unlock()
+}
+
+pub fn (cb &Clipboard) has_ownership() bool {
+ return cb.is_owner
+}
+
+fn (cb &Clipboard) take_ownership() {
+ C.XSetSelectionOwner(cb.display, cb.selection, cb.window, C.CurrentTime)
+ C.XFlush(cb.display)
+}
+
+// set_text stores `text` in the system clipboard.
+pub fn (mut cb Clipboard) set_text(text string) bool {
+ if cb.window == Window(0) {
+ return false
+ }
+ cb.mutex.@lock()
+ cb.text = text
+ cb.is_owner = true
+ cb.take_ownership()
+ C.XFlush(cb.display)
+ cb.mutex.unlock()
+ // sleep a little bit
+ time.sleep(1 * time.millisecond)
+ return cb.is_owner
+}
+
+pub fn (mut cb Clipboard) get_text() string {
+ if cb.window == Window(0) {
+ return ''
+ }
+ if cb.is_owner {
+ return cb.text
+ }
+ cb.got_text = false
+
+ // Request a list of possible conversions, if we're pasting.
+ C.XConvertSelection(cb.display, cb.selection, cb.get_atom(.targets), cb.selection,
+ cb.window, C.CurrentTime)
+
+ // wait for the text to arrive
+ mut retries := 5
+ for {
+ if cb.got_text || retries == 0 {
+ break
+ }
+ time.sleep(50 * time.millisecond)
+ retries--
+ }
+ return cb.text
+}
+
+// transmit_selection is crucial to handling all the different data types.
+// If we ever support other mimetypes they should be handled here.
+fn (mut cb Clipboard) transmit_selection(xse &C.XSelectionEvent) bool {
+ if xse.target == cb.get_atom(.targets) {
+ targets := cb.get_supported_targets()
+ C.XChangeProperty(xse.display, xse.requestor, xse.property, cb.get_atom(.xa_atom),
+ 32, C.PropModeReplace, targets.data, targets.len)
+ } else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != '' {
+ cb.mutex.@lock()
+ C.XChangeProperty(xse.display, xse.requestor, xse.property, xse.target, 8, C.PropModeReplace,
+ cb.text.str, cb.text.len)
+ cb.mutex.unlock()
+ } else {
+ return false
+ }
+ return true
+}
+
+fn (mut cb Clipboard) start_listener() {
+ event := C.XEvent{}
+ mut sent_request := false
+ mut to_be_requested := Atom(0)
+ for {
+ C.XNextEvent(cb.display, &event)
+ if unsafe { event.@type == 0 } {
+ println('error')
+ continue
+ }
+ match unsafe { event.@type } {
+ C.DestroyNotify {
+ if unsafe { event.xdestroywindow.window == cb.window } {
+ // we are done
+ return
+ }
+ }
+ C.SelectionClear {
+ if unsafe { event.xselectionclear.window == cb.window } && unsafe {
+ event.xselectionclear.selection == cb.selection
+ } {
+ cb.mutex.@lock()
+ cb.is_owner = false
+ cb.text = ''
+ cb.mutex.unlock()
+ }
+ }
+ C.SelectionRequest {
+ if unsafe { event.xselectionrequest.selection == cb.selection } {
+ mut xsre := &C.XSelectionRequestEvent{
+ display: 0
+ }
+ xsre = unsafe { &event.xselectionrequest }
+
+ mut xse := C.XSelectionEvent{
+ @type: C.SelectionNotify // 31
+ display: xsre.display
+ requestor: xsre.requestor
+ selection: xsre.selection
+ time: xsre.time
+ target: xsre.target
+ property: xsre.property
+ }
+ if !cb.transmit_selection(&xse) {
+ xse.property = new_atom(0)
+ }
+ C.XSendEvent(cb.display, xse.requestor, 0, C.PropertyChangeMask, voidptr(&xse))
+ C.XFlush(cb.display)
+ }
+ }
+ C.SelectionNotify {
+ if unsafe {
+ event.xselection.selection == cb.selection
+ && event.xselection.property != Atom(0)
+ } {
+ if unsafe { event.xselection.target == cb.get_atom(.targets) && !sent_request } {
+ sent_request = true
+ prop := read_property(cb.display, cb.window, cb.selection)
+ to_be_requested = cb.pick_target(prop)
+ if to_be_requested != Atom(0) {
+ C.XConvertSelection(cb.display, cb.selection, to_be_requested,
+ cb.selection, cb.window, C.CurrentTime)
+ }
+ } else if unsafe { event.xselection.target == to_be_requested } {
+ sent_request = false
+ to_be_requested = Atom(0)
+ cb.mutex.@lock()
+ prop := unsafe {
+ read_property(event.xselection.display, event.xselection.requestor,
+ event.xselection.property)
+ }
+ unsafe {
+ C.XDeleteProperty(event.xselection.display, event.xselection.requestor,
+ event.xselection.property)
+ }
+ if cb.is_supported_target(prop.actual_type) {
+ cb.got_text = true
+ unsafe {
+ cb.text = prop.data.vstring() // TODO: return byteptr to support other mimetypes
+ }
+ }
+ cb.mutex.unlock()
+ }
+ }
+ }
+ C.PropertyNotify {}
+ else {}
+ }
+ }
+}
+
+/*
+* Helpers
+*/
+// intern_atoms initializes all the atoms we need.
+fn (mut cb Clipboard) intern_atoms() {
+ cb.atoms << Atom(4) // XA_ATOM
+ cb.atoms << Atom(31) // XA_STRING
+ for i, name in x11.atom_names {
+ only_if_exists := if i == int(AtomType.utf8_string) { 1 } else { 0 }
+ cb.atoms << C.XInternAtom(cb.display, &char(name.str), only_if_exists)
+ if i == int(AtomType.utf8_string) && cb.atoms[i] == Atom(0) {
+ cb.atoms[i] = cb.get_atom(.xa_string)
+ }
+ }
+}
+
+fn read_property(d &C.Display, w Window, p Atom) Property {
+ actual_type := Atom(0)
+ actual_format := 0
+ nitems := u64(0)
+ bytes_after := u64(0)
+ ret := &byte(0)
+ mut read_bytes := 1024
+ for {
+ if ret != 0 {
+ C.XFree(ret)
+ }
+ C.XGetWindowProperty(d, w, p, 0, read_bytes, 0, 0, &actual_type, &actual_format,
+ &nitems, &bytes_after, &ret)
+ read_bytes *= 2
+ if bytes_after == 0 {
+ break
+ }
+ }
+ return Property{actual_type, actual_format, nitems, ret}
+}
+
+// pick_target finds the best target given a local copy of a property.
+fn (cb &Clipboard) pick_target(prop Property) Atom {
+ // The list of targets is a list of atoms, so it should have type XA_ATOM
+ // but it may have the type TARGETS instead.
+ if (prop.actual_type != cb.get_atom(.xa_atom) && prop.actual_type != cb.get_atom(.targets))
+ || prop.actual_format != 32 {
+ // This would be really broken. Targets have to be an atom list
+ // and applications should support this. Nevertheless, some
+ // seem broken (MATLAB 7, for instance), so ask for STRING
+ // next instead as the lowest common denominator
+ return cb.get_atom(.xa_string)
+ } else {
+ atom_list := &Atom(voidptr(prop.data))
+
+ mut to_be_requested := Atom(0)
+
+ // This is higher than the maximum priority.
+ mut priority := math.max_i32
+
+ for i in 0 .. prop.nitems {
+ // See if this data type is allowed and of higher priority (closer to zero)
+ // than the present one.
+
+ target := unsafe { atom_list[i] }
+ if cb.is_supported_target(target) {
+ index := cb.get_target_index(target)
+ if priority > index && index >= 0 {
+ priority = index
+ to_be_requested = target
+ }
+ }
+ }
+ return to_be_requested
+ }
+}
+
+fn (cb &Clipboard) get_atoms(types ...AtomType) []Atom {
+ mut atoms := []Atom{}
+ for typ in types {
+ atoms << cb.atoms[typ]
+ }
+ return atoms
+}
+
+fn (cb &Clipboard) get_atom(typ AtomType) Atom {
+ return cb.atoms[typ]
+}
+
+fn (cb &Clipboard) is_supported_target(target Atom) bool {
+ return cb.get_target_index(target) >= 0
+}
+
+fn (cb &Clipboard) get_target_index(target Atom) int {
+ for i, atom in cb.get_supported_targets() {
+ if atom == target {
+ return i
+ }
+ }
+ return -1
+}
+
+fn (cb &Clipboard) get_supported_targets() []Atom {
+ return cb.get_atoms(AtomType.utf8_string, .xa_string, .text, .text_plain, .text_html)
+}
+
+fn new_atom(value int) &Atom {
+ return unsafe { &Atom(&u64(u64(value))) }
+}
+
+fn create_xwindow(display &C.Display) Window {
+ n := C.DefaultScreen(display)
+ return C.XCreateSimpleWindow(display, C.RootWindow(display, n), 0, 0, 1, 1, 0, C.BlackPixel(display,
+ n), C.WhitePixel(display, n))
+}
+
+fn new_display() &C.Display {
+ return C.XOpenDisplay(C.NULL)
+}
+
+// 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 {
+ return new_x11_clipboard(.primary)
+}