clipboard: use a nicer error when X11/Xlib.h is missing
parent
985ef52872
commit
b3a4f746a2
vlib/clipboard/x11
|
@ -9,39 +9,60 @@ import math
|
||||||
#flag -lX11
|
#flag -lX11
|
||||||
#flag freebsd -I/usr/local/include
|
#flag freebsd -I/usr/local/include
|
||||||
#flag freebsd -L/usr/local/lib -lX11
|
#flag freebsd -L/usr/local/lib -lX11
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h> # Please install a package with the X11 development headers, for example: `apt-get install libx11-dev`
|
||||||
|
|
||||||
// X11
|
// X11
|
||||||
[typedef]
|
[typedef]
|
||||||
struct C.Display
|
struct C.Display {
|
||||||
|
}
|
||||||
|
|
||||||
[typedef]
|
[typedef]
|
||||||
struct C.Atom
|
struct C.Atom {
|
||||||
|
}
|
||||||
|
|
||||||
[typedef]
|
[typedef]
|
||||||
struct C.Window
|
struct C.Window {
|
||||||
|
}
|
||||||
|
|
||||||
fn C.XInitThreads() int
|
fn C.XInitThreads() int
|
||||||
|
|
||||||
fn C.XCloseDisplay(d &Display)
|
fn C.XCloseDisplay(d &Display)
|
||||||
|
|
||||||
fn C.XFlush(d &Display)
|
fn C.XFlush(d &Display)
|
||||||
|
|
||||||
fn C.XDestroyWindow(d &Display, w C.Window)
|
fn C.XDestroyWindow(d &Display, w C.Window)
|
||||||
|
|
||||||
fn C.XNextEvent(d C.Display, e &XEvent)
|
fn C.XNextEvent(d C.Display, e &XEvent)
|
||||||
|
|
||||||
fn C.XSetSelectionOwner(d &Display, a C.Atom, w C.Window, time int)
|
fn C.XSetSelectionOwner(d &Display, a C.Atom, w C.Window, time int)
|
||||||
|
|
||||||
fn C.XGetSelectionOwner(d &Display, a C.Atom) C.Window
|
fn C.XGetSelectionOwner(d &Display, a C.Atom) C.Window
|
||||||
|
|
||||||
fn C.XChangeProperty(d &Display, requestor C.Window, property C.Atom, typ C.Atom, format int, mode int, data voidptr, nelements int) int
|
fn C.XChangeProperty(d &Display, requestor C.Window, property C.Atom, typ C.Atom, format int, mode int, data voidptr, nelements int) int
|
||||||
|
|
||||||
fn C.XSendEvent(d &Display, requestor C.Window, propogate int, mask i64, event &XEvent)
|
fn C.XSendEvent(d &Display, requestor C.Window, propogate int, mask i64, event &XEvent)
|
||||||
|
|
||||||
fn C.XInternAtom(d &Display, typ byteptr, only_if_exists int) C.Atom
|
fn C.XInternAtom(d &Display, typ byteptr, only_if_exists int) C.Atom
|
||||||
fn C.XCreateSimpleWindow(d &Display, root C.Window, x int, y int, width u32, height u32, border_width u32,
|
|
||||||
border u64, background u64) C.Window
|
fn C.XCreateSimpleWindow(d &Display, root C.Window, x int, y int, width u32, height u32, border_width u32, border u64, background u64) C.Window
|
||||||
|
|
||||||
fn C.XOpenDisplay(name byteptr) &C.Display
|
fn C.XOpenDisplay(name byteptr) &C.Display
|
||||||
|
|
||||||
fn C.XConvertSelection(d &Display, selection C.Atom, target C.Atom, property C.Atom, requestor Window, time int) int
|
fn C.XConvertSelection(d &Display, selection C.Atom, target C.Atom, property C.Atom, requestor Window, time int) int
|
||||||
|
|
||||||
fn C.XSync(d &Display, discard int) int
|
fn C.XSync(d &Display, discard int) int
|
||||||
|
|
||||||
fn C.XGetWindowProperty(d &Display, w Window, property C.Atom, offset i64, length i64, delete int, req_type C.Atom, actual_type_return &C.Atom, actual_format_return &int, nitems &u64, bytes_after_return &u64, prop_return &byteptr) int
|
fn C.XGetWindowProperty(d &Display, w Window, property C.Atom, offset i64, length i64, delete int, req_type C.Atom, actual_type_return &C.Atom, actual_format_return &int, nitems &u64, bytes_after_return &u64, prop_return &byteptr) int
|
||||||
|
|
||||||
fn C.XDeleteProperty(d &Display, w Window, property C.Atom) int
|
fn C.XDeleteProperty(d &Display, w Window, property C.Atom) int
|
||||||
|
|
||||||
fn C.DefaultScreen() int
|
fn C.DefaultScreen() int
|
||||||
|
|
||||||
fn C.RootWindow() voidptr
|
fn C.RootWindow() voidptr
|
||||||
|
|
||||||
fn C.BlackPixel() voidptr
|
fn C.BlackPixel() voidptr
|
||||||
|
|
||||||
fn C.WhitePixel() voidptr
|
fn C.WhitePixel() voidptr
|
||||||
|
|
||||||
fn C.XFree()
|
fn C.XFree()
|
||||||
|
|
||||||
fn todo_del() {}
|
fn todo_del() {}
|
||||||
|
@ -49,7 +70,7 @@ fn todo_del(){}
|
||||||
[typedef]
|
[typedef]
|
||||||
struct C.XSelectionRequestEvent {
|
struct C.XSelectionRequestEvent {
|
||||||
mut:
|
mut:
|
||||||
display &C.Display /* Display the event was read from */
|
display &C.Display // Display the event was read from
|
||||||
owner C.Window
|
owner C.Window
|
||||||
requestor C.Window
|
requestor C.Window
|
||||||
selection C.Atom
|
selection C.Atom
|
||||||
|
@ -62,7 +83,7 @@ struct C.XSelectionRequestEvent{
|
||||||
struct C.XSelectionEvent {
|
struct C.XSelectionEvent {
|
||||||
mut:
|
mut:
|
||||||
@type int
|
@type int
|
||||||
display &C.Display /* Display the event was read from */
|
display &C.Display // Display the event was read from
|
||||||
requestor C.Window
|
requestor C.Window
|
||||||
selection C.Atom
|
selection C.Atom
|
||||||
target C.Atom
|
target C.Atom
|
||||||
|
@ -94,10 +115,12 @@ union C.XEvent{
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
atom_names = ["TARGETS", "CLIPBOARD", "PRIMARY", "SECONDARY", "TEXT", "UTF8_STRING", "text/plain", "text/html"]
|
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
|
|
||||||
|
|
||||||
|
// UNSUPPORTED TYPES: MULTIPLE, INCR, TIMESTAMP, image/bmp, image/jpeg, image/tiff, image/png
|
||||||
// all the atom types we need
|
// all the atom types we need
|
||||||
// currently we only support text
|
// currently we only support text
|
||||||
// in the future, maybe we can extend this
|
// in the future, maybe we can extend this
|
||||||
|
@ -142,20 +165,22 @@ pub fn new_clipboard() &Clipboard {
|
||||||
// We can initialize multiple clipboard instances and use them separately
|
// We can initialize multiple clipboard instances and use them separately
|
||||||
fn new_x11_clipboard(selection AtomType) &Clipboard {
|
fn new_x11_clipboard(selection AtomType) &Clipboard {
|
||||||
if selection !in [.clipboard, .primary, .secondary] {
|
if selection !in [.clipboard, .primary, .secondary] {
|
||||||
panic("Wrong AtomType. Must be one of .primary, .secondary or .clipboard.")
|
panic('Wrong AtomType. Must be one of .primary, .secondary or .clipboard.')
|
||||||
}
|
}
|
||||||
|
|
||||||
// init x11 thread support
|
// init x11 thread support
|
||||||
status := C.XInitThreads()
|
status := C.XInitThreads()
|
||||||
if status == 0 {
|
if status == 0 {
|
||||||
println("WARN: this system does not support threads; clipboard will cause the program to lock.")
|
println('WARN: this system does not support threads; clipboard will cause the program to lock.')
|
||||||
}
|
}
|
||||||
|
|
||||||
display := new_display()
|
display := new_display()
|
||||||
|
|
||||||
if display == C.NULL {
|
if display == C.NULL {
|
||||||
println("ERROR: No X Server running. Clipboard cannot be used.")
|
println('ERROR: No X Server running. Clipboard cannot be used.')
|
||||||
return &Clipboard{ display: 0 mutex: sync.new_mutex() }
|
return &Clipboard{
|
||||||
|
display: 0
|
||||||
|
mutex: sync.new_mutex()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mut cb := &Clipboard{
|
mut cb := &Clipboard{
|
||||||
|
@ -187,7 +212,7 @@ fn (mut cb Clipboard) clear(){
|
||||||
C.XSetSelectionOwner(cb.display, cb.selection, C.Window(C.None), C.CurrentTime)
|
C.XSetSelectionOwner(cb.display, cb.selection, C.Window(C.None), C.CurrentTime)
|
||||||
C.XFlush(cb.display)
|
C.XFlush(cb.display)
|
||||||
cb.is_owner = false
|
cb.is_owner = false
|
||||||
cb.text = ""
|
cb.text = ''
|
||||||
cb.mutex.unlock()
|
cb.mutex.unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +226,9 @@ fn (cb &Clipboard) take_ownership(){
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut cb Clipboard) set_text(text string) bool {
|
pub fn (mut cb Clipboard) set_text(text string) bool {
|
||||||
if cb.window == C.Window(C.None) {return false}
|
if cb.window == C.Window(C.None) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
cb.mutex.m_lock()
|
cb.mutex.m_lock()
|
||||||
cb.text = text
|
cb.text = text
|
||||||
cb.is_owner = true
|
cb.is_owner = true
|
||||||
|
@ -214,19 +241,24 @@ pub fn (mut cb Clipboard) set_text(text string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut cb Clipboard) get_text() string {
|
fn (mut cb Clipboard) get_text() string {
|
||||||
if cb.window == C.Window(C.None) {return ""}
|
if cb.window == C.Window(C.None) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
if cb.is_owner {
|
if cb.is_owner {
|
||||||
return cb.text
|
return cb.text
|
||||||
}
|
}
|
||||||
cb.got_text = false
|
cb.got_text = false
|
||||||
|
|
||||||
// Request a list of possible conversions, if we're pasting.
|
// 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)
|
C.XConvertSelection(cb.display, cb.selection, cb.get_atom(.targets), cb.selection,
|
||||||
|
cb.window, C.CurrentTime)
|
||||||
|
|
||||||
// wait for the text to arrive
|
// wait for the text to arrive
|
||||||
mut retries := 5
|
mut retries := 5
|
||||||
for {
|
for {
|
||||||
if cb.got_text || retries == 0 {break}
|
if cb.got_text || retries == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
time.usleep(50000)
|
time.usleep(50000)
|
||||||
retries--
|
retries--
|
||||||
}
|
}
|
||||||
|
@ -238,10 +270,12 @@ fn (mut cb Clipboard) get_text() string {
|
||||||
fn (mut cb Clipboard) transmit_selection(xse &C.XSelectionEvent) bool {
|
fn (mut cb Clipboard) transmit_selection(xse &C.XSelectionEvent) bool {
|
||||||
if xse.target == cb.get_atom(.targets) {
|
if xse.target == cb.get_atom(.targets) {
|
||||||
targets := cb.get_supported_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)
|
C.XChangeProperty(xse.display, xse.requestor, xse.property, cb.get_atom(.xa_atom),
|
||||||
} else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != "" {
|
32, C.PropModeReplace, targets.data, targets.len)
|
||||||
|
} else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != '' {
|
||||||
cb.mutex.m_lock()
|
cb.mutex.m_lock()
|
||||||
C.XChangeProperty(xse.display, xse.requestor, xse.property, xse.target, 8, C.PropModeReplace, cb.text.str, cb.text.len)
|
C.XChangeProperty(xse.display, xse.requestor, xse.property, xse.target, 8, C.PropModeReplace,
|
||||||
|
cb.text.str, cb.text.len)
|
||||||
cb.mutex.unlock()
|
cb.mutex.unlock()
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -256,7 +290,7 @@ fn (mut cb Clipboard) start_listener(){
|
||||||
for {
|
for {
|
||||||
C.XNextEvent(cb.display, &event)
|
C.XNextEvent(cb.display, &event)
|
||||||
if unsafe { event.@type == 0 } {
|
if unsafe { event.@type == 0 } {
|
||||||
println("error")
|
println('error')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
match unsafe { event.@type } {
|
match unsafe { event.@type } {
|
||||||
|
@ -267,16 +301,20 @@ fn (mut cb Clipboard) start_listener(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
C.SelectionClear {
|
C.SelectionClear {
|
||||||
if unsafe { event.xselectionclear.window == cb.window } && unsafe { event.xselectionclear.selection == cb.selection } {
|
if unsafe { event.xselectionclear.window == cb.window } && unsafe { event.xselectionclear.selection ==
|
||||||
|
cb.selection }
|
||||||
|
{
|
||||||
cb.mutex.m_lock()
|
cb.mutex.m_lock()
|
||||||
cb.is_owner = false
|
cb.is_owner = false
|
||||||
cb.text = ""
|
cb.text = ''
|
||||||
cb.mutex.unlock()
|
cb.mutex.unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
C.SelectionRequest {
|
C.SelectionRequest {
|
||||||
if unsafe { event.xselectionrequest.selection == cb.selection } {
|
if unsafe { event.xselectionrequest.selection == cb.selection } {
|
||||||
mut xsre := &C.XSelectionRequestEvent{ display: 0 }
|
mut xsre := &C.XSelectionRequestEvent{
|
||||||
|
display: 0
|
||||||
|
}
|
||||||
xsre = unsafe { &event.xselectionrequest }
|
xsre = unsafe { &event.xselectionrequest }
|
||||||
|
|
||||||
mut xse := C.XSelectionEvent{
|
mut xse := C.XSelectionEvent{
|
||||||
|
@ -296,20 +334,25 @@ fn (mut cb Clipboard) start_listener(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
C.SelectionNotify {
|
C.SelectionNotify {
|
||||||
if unsafe { event.xselection.selection == cb.selection && event.xselection.property != C.Atom(C.None) } {
|
if unsafe { event.xselection.selection == cb.selection &&
|
||||||
|
event.xselection.property != C.Atom(C.None) }
|
||||||
|
{
|
||||||
if unsafe { event.xselection.target == cb.get_atom(.targets) && !sent_request } {
|
if unsafe { event.xselection.target == cb.get_atom(.targets) && !sent_request } {
|
||||||
sent_request = true
|
sent_request = true
|
||||||
prop := read_property(cb.display, cb.window, cb.selection)
|
prop := read_property(cb.display, cb.window, cb.selection)
|
||||||
to_be_requested = cb.pick_target(prop)
|
to_be_requested = cb.pick_target(prop)
|
||||||
if to_be_requested != C.Atom(0) {
|
if to_be_requested != C.Atom(0) {
|
||||||
C.XConvertSelection(cb.display, cb.selection, to_be_requested, cb.selection, cb.window, C.CurrentTime)
|
C.XConvertSelection(cb.display, cb.selection, to_be_requested,
|
||||||
|
cb.selection, cb.window, C.CurrentTime)
|
||||||
}
|
}
|
||||||
} else if unsafe { event.xselection.target == to_be_requested } {
|
} else if unsafe { event.xselection.target == to_be_requested } {
|
||||||
sent_request = false
|
sent_request = false
|
||||||
to_be_requested = C.Atom(0)
|
to_be_requested = C.Atom(0)
|
||||||
cb.mutex.m_lock()
|
cb.mutex.m_lock()
|
||||||
prop := unsafe{ read_property(event.xselection.display, event.xselection.requestor, event.xselection.property) }
|
prop := unsafe { read_property(event.xselection.display, event.xselection.requestor,
|
||||||
unsafe{ C.XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property) }
|
event.xselection.property) }
|
||||||
|
unsafe { C.XDeleteProperty(event.xselection.display, event.xselection.requestor,
|
||||||
|
event.xselection.property) }
|
||||||
if cb.is_supported_target(prop.actual_type) {
|
if cb.is_supported_target(prop.actual_type) {
|
||||||
cb.got_text = true
|
cb.got_text = true
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -326,10 +369,7 @@ fn (mut cb Clipboard) start_listener(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
// Initialize all the atoms we need
|
// Initialize all the atoms we need
|
||||||
fn (mut cb Clipboard) intern_atoms() {
|
fn (mut cb Clipboard) intern_atoms() {
|
||||||
cb.atoms << C.Atom(4) // XA_ATOM
|
cb.atoms << C.Atom(4) // XA_ATOM
|
||||||
|
@ -354,9 +394,12 @@ fn read_property(d &C.Display, w C.Window, p C.Atom) Property {
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
C.XFree(ret)
|
C.XFree(ret)
|
||||||
}
|
}
|
||||||
C.XGetWindowProperty(d, w, p, 0, read_bytes, 0, C.AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &ret)
|
C.XGetWindowProperty(d, w, p, 0, read_bytes, 0, C.AnyPropertyType, &actual_type,
|
||||||
|
&actual_format, &nitems, &bytes_after, &ret)
|
||||||
read_bytes *= 2
|
read_bytes *= 2
|
||||||
if bytes_after == 0 {break}
|
if bytes_after == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Property{actual_type, actual_format, nitems, ret}
|
return Property{actual_type, actual_format, nitems, ret}
|
||||||
}
|
}
|
||||||
|
@ -365,16 +408,16 @@ fn read_property(d &C.Display, w C.Window, p C.Atom) Property {
|
||||||
fn (cb &Clipboard) pick_target(prop Property) C.Atom {
|
fn (cb &Clipboard) pick_target(prop Property) C.Atom {
|
||||||
// The list of targets is a list of atoms, so it should have type XA_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.
|
// 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
|
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
|
// This would be really broken. Targets have to be an atom list
|
||||||
// and applications should support this. Nevertheless, some
|
// and applications should support this. Nevertheless, some
|
||||||
// seem broken (MATLAB 7, for instance), so ask for STRING
|
// seem broken (MATLAB 7, for instance), so ask for STRING
|
||||||
// next instead as the lowest common denominator
|
// next instead as the lowest common denominator
|
||||||
return cb.get_atom(.xa_string)
|
return cb.get_atom(.xa_string)
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
atom_list := &C.Atom(prop.data)
|
atom_list := &C.Atom(prop.data)
|
||||||
|
|
||||||
mut to_be_requested := C.Atom(0)
|
mut to_be_requested := C.Atom(0)
|
||||||
|
@ -417,7 +460,9 @@ fn (cb &Clipboard) is_supported_target(target C.Atom) bool {
|
||||||
|
|
||||||
fn (cb &Clipboard) get_target_index(target C.Atom) int {
|
fn (cb &Clipboard) get_target_index(target C.Atom) int {
|
||||||
for i, atom in cb.get_supported_targets() {
|
for i, atom in cb.get_supported_targets() {
|
||||||
if atom == target {return i}
|
if atom == target {
|
||||||
|
return i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
@ -432,8 +477,8 @@ fn new_atom(value int) &C.Atom {
|
||||||
|
|
||||||
fn create_xwindow(display &C.Display) C.Window {
|
fn create_xwindow(display &C.Display) C.Window {
|
||||||
n := C.DefaultScreen(display)
|
n := C.DefaultScreen(display)
|
||||||
return C.XCreateSimpleWindow(display, C.RootWindow(display, n), 0, 0, 1, 1,
|
return C.XCreateSimpleWindow(display, C.RootWindow(display, n), 0, 0, 1, 1, 0, C.BlackPixel(display,
|
||||||
0, C.BlackPixel(display, n), C.WhitePixel(display, n))
|
n), C.WhitePixel(display, n))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_display() &C.Display {
|
fn new_display() &C.Display {
|
||||||
|
|
Loading…
Reference in New Issue