clipboard: make the Linux version compile

pull/4539/head
Alexander Medvednikov 2020-04-21 07:24:23 +00:00
parent 9d107007a1
commit 4471314291
1 changed files with 76 additions and 76 deletions

View File

@ -12,25 +12,25 @@ import (
#include <X11/Xlib.h> #include <X11/Xlib.h>
// X11 // X11
struct C.Display struct C.Display{}
struct C.Atom struct C.Atom{}
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 Window) fn C.XDestroyWindow(d &Display, w Window)
fn C.XNextEvent(d Display, e &XEvent) fn C.XNextEvent(d Display, e &XEvent)
fn C.XSetSelectionOwner(d &Display, a Atom, w Window, time int) fn C.XSetSelectionOwner(d &Display, a C.Atom, w Window, time int)
fn C.XGetSelectionOwner(d &Display, a Atom) Window fn C.XGetSelectionOwner(d &Display, a C.Atom) Window
fn C.XChangeProperty(d &Display, requestor Window, property Atom, typ Atom, format int, mode int, data voidptr, nelements int) int fn C.XChangeProperty(d &Display, requestor Window, property C.Atom, typ C.Atom, format int, mode int, data voidptr, nelements int) int
fn C.XSendEvent(d &Display, requestor Window, propogate int, mask i64, event &XEvent) fn C.XSendEvent(d &Display, requestor Window, propogate int, mask i64, event &XEvent)
fn C.XInternAtom(d &Display, typ byteptr, only_if_exists int) Atom fn C.XInternAtom(d &Display, typ byteptr, only_if_exists int) C.Atom
fn C.XCreateSimpleWindow(d &Display, root Window, x int, y int, width u32, height u32, border_width u32, border u64, background u64) Window fn C.XCreateSimpleWindow(d &Display, root Window, x int, y int, width u32, height u32, border_width u32, border u64, background u64) Window
fn C.XOpenDisplay(name byteptr) &Display fn C.XOpenDisplay(name byteptr) &Display
fn C.XConvertSelection(d &Display, selection Atom, target Atom, property 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 Atom, offset i64, length i64, delete int, req_type Atom, actual_type_return &Atom, actual_format_return &int, nitems &i64, bytes_after_return &i64, 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 &i64, bytes_after_return &i64, prop_return &byteptr) int
fn C.XDeleteProperty(d &Display, w Window, property 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
@ -39,28 +39,28 @@ fn C.XFree()
struct C.XSelectionRequestEvent{ struct C.XSelectionRequestEvent{
mut: mut:
selection Atom selection C.Atom
display &Display /* Display the event was read from */ display &Display /* Display the event was read from */
owner Window owner Window
requestor Window requestor Window
target Atom target C.Atom
property Atom property C.Atom
time int time int
} }
struct C.XSelectionEvent{ struct C.XSelectionEvent{
mut: mut:
@type int @type int
selection Atom selection C.Atom
display &Display /* Display the event was read from */ display &Display /* Display the event was read from */
requestor Window requestor Window
target Atom target C.Atom
property Atom property C.Atom
time int time int
} }
struct C.XSelectionClearEvent{ struct C.XSelectionClearEvent{
mut: mut:
window Window window Window
selection Atom selection C.Atom
} }
struct C.XDestroyWindowEvent { struct C.XDestroyWindowEvent {
mut: mut:
@ -69,10 +69,10 @@ struct C.XDestroyWindowEvent {
struct C.XEvent{ struct C.XEvent{
mut: mut:
@type int @type int
xselectionrequest XSelectionRequestEvent xselectionrequest C.XSelectionRequestEvent
xselection XSelectionEvent xselection C.XSelectionEvent
xselectionclear XSelectionClearEvent xselectionclear C.XSelectionClearEvent
xdestroywindow XDestroyWindowEvent xdestroywindow C.XDestroyWindowEvent
} }
const ( const (
@ -84,7 +84,7 @@ const (
// 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
// to support other mime types // to support other mime types
enum atom_type { enum AtomType {
xa_atom = 0, //value 4 xa_atom = 0, //value 4
xa_string = 1, //value 31 xa_string = 1, //value 31
targets = 2, targets = 2,
@ -100,9 +100,9 @@ enum atom_type {
pub struct Clipboard { pub struct Clipboard {
display &Display display &Display
mut: mut:
selection Atom //the selection atom selection C.Atom //the selection atom
window Window window Window
atoms []Atom atoms []C.Atom
mutex &sync.Mutex mutex &sync.Mutex
text string // text data sent or received text string // text data sent or received
got_text bool // used to confirm that we have got the text got_text bool // used to confirm that we have got the text
@ -110,7 +110,7 @@ pub struct Clipboard {
} }
struct Property{ struct Property{
actual_type Atom actual_type C.Atom
actual_format int actual_format int
nitems int nitems int
data byteptr data byteptr
@ -122,13 +122,13 @@ fn new_clipboard() &Clipboard {
// Initialize a new clipboard of the given selection type. // Initialize a new clipboard of the given selection type.
// 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 atom_type) &Clipboard { fn new_x11_clipboard(selection AtomType) &Clipboard {
if !(selection in [.clipboard, .primary, .secondary]) { if !(selection in [.clipboard, .primary, .secondary]) {
panic("Wrong atom_type. 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 := 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.")
} }
@ -158,7 +158,7 @@ fn (cb &Clipboard) check_availability() bool {
} }
fn (cb mut Clipboard) free() { fn (cb mut Clipboard) free() {
XDestroyWindow(cb.display, cb.window) C.XDestroyWindow(cb.display, cb.window)
cb.window = Window(C.None) cb.window = Window(C.None)
//FIX ME: program hangs when closing display //FIX ME: program hangs when closing display
//XCloseDisplay(cb.display) //XCloseDisplay(cb.display)
@ -166,8 +166,8 @@ fn (cb mut Clipboard) free() {
fn (cb mut Clipboard) clear(){ fn (cb mut Clipboard) clear(){
cb.mutex.lock() cb.mutex.lock()
XSetSelectionOwner(cb.display, cb.selection, Window(C.None), C.CurrentTime) C.XSetSelectionOwner(cb.display, cb.selection, Window(C.None), C.CurrentTime)
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()
@ -178,8 +178,8 @@ fn (cb &Clipboard) has_ownership() bool {
} }
fn (cb &Clipboard) take_ownership(){ fn (cb &Clipboard) take_ownership(){
XSetSelectionOwner(cb.display, cb.selection, cb.window, C.CurrentTime) C.XSetSelectionOwner(cb.display, cb.selection, cb.window, C.CurrentTime)
XFlush(cb.display) C.XFlush(cb.display)
} }
fn (cb mut Clipboard) set_text(text string) bool { fn (cb mut Clipboard) set_text(text string) bool {
@ -188,7 +188,7 @@ fn (cb mut Clipboard) set_text(text string) bool {
cb.text = text cb.text = text
cb.is_owner = true cb.is_owner = true
cb.take_ownership() cb.take_ownership()
XFlush(cb.display) C.XFlush(cb.display)
cb.mutex.unlock() cb.mutex.unlock()
// sleep a little bit // sleep a little bit
time.sleep(1) time.sleep(1)
@ -203,7 +203,7 @@ fn (cb mut Clipboard) get_text() string {
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.
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
@ -217,13 +217,13 @@ fn (cb mut Clipboard) get_text() string {
// this function is crucial to handling all the different data types // this function is crucial to handling all the different data types
// if we ever support other mimetypes they should be handled here // if we ever support other mimetypes they should be handled here
fn (cb mut Clipboard) transmit_selection(xse &XSelectionEvent) bool { fn (cb mut 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()
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), 32, C.PropModeReplace, targets.data, targets.len)
} else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != "" { } else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != "" {
cb.mutex.lock() cb.mutex.lock()
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
@ -232,11 +232,11 @@ fn (cb mut Clipboard) transmit_selection(xse &XSelectionEvent) bool {
} }
fn (cb mut Clipboard) start_listener(){ fn (cb mut Clipboard) start_listener(){
event := XEvent{} event := C.XEvent{}
mut sent_request := false mut sent_request := false
mut to_be_requested := Atom(0) mut to_be_requested := C.Atom(0)
for { for {
XNextEvent(cb.display, &event) C.XNextEvent(cb.display, &event)
if event.@type == 0 { if event.@type == 0 {
println("error") println("error")
continue continue
@ -257,10 +257,10 @@ fn (cb mut Clipboard) start_listener(){
} }
C.SelectionRequest { C.SelectionRequest {
if event.xselectionrequest.selection == cb.selection { if event.xselectionrequest.selection == cb.selection {
mut xsre := &XSelectionRequestEvent{ display: 0 } mut xsre := &C.XSelectionRequestEvent{ display: 0 }
xsre = &event.xselectionrequest xsre = &event.xselectionrequest
mut xse := XSelectionEvent{ mut xse := C.XSelectionEvent{
@type: C.SelectionNotify // 31 @type: C.SelectionNotify // 31
display: xsre.display display: xsre.display
requestor: xsre.requestor requestor: xsre.requestor
@ -272,25 +272,25 @@ fn (cb mut Clipboard) start_listener(){
if !cb.transmit_selection(&xse) { if !cb.transmit_selection(&xse) {
xse.property = new_atom(C.None) xse.property = new_atom(C.None)
} }
XSendEvent(cb.display, xse.requestor, 0, C.PropertyChangeMask, &xse) C.XSendEvent(cb.display, xse.requestor, 0, C.PropertyChangeMask, &xse)
XFlush(cb.display) C.XFlush(cb.display)
} }
} }
C.SelectionNotify { C.SelectionNotify {
if event.xselection.selection == cb.selection && event.xselection.property != Atom(C.None) { if event.xselection.selection == cb.selection && event.xselection.property != C.Atom(C.None) {
if event.xselection.target == cb.get_atom(.targets) && !sent_request { if 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 != Atom(0) { if to_be_requested != C.Atom(0) {
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 event.xselection.target == to_be_requested { } else if event.xselection.target == to_be_requested {
sent_request = false sent_request = false
to_be_requested = Atom(0) to_be_requested = C.Atom(0)
cb.mutex.lock() cb.mutex.lock()
prop := read_property(event.xselection.display, event.xselection.requestor, event.xselection.property) prop := read_property(event.xselection.display, event.xselection.requestor, event.xselection.property)
XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property) 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
cb.text = string(prop.data) //TODO: return byteptr to support other mimetypes cb.text = string(prop.data) //TODO: return byteptr to support other mimetypes
@ -311,19 +311,19 @@ fn (cb mut Clipboard) start_listener(){
// Initialize all the atoms we need // Initialize all the atoms we need
fn (cb mut Clipboard) intern_atoms(){ fn (cb mut Clipboard) intern_atoms(){
cb.atoms << Atom(4) //XA_ATOM cb.atoms << C.Atom(4) //XA_ATOM
cb.atoms << Atom(31) //XA_STRING cb.atoms << C.Atom(31) //XA_STRING
for i, name in atom_names{ for i, name in atom_names{
only_if_exists := if i == int(atom_type.utf8_string) {1} else {0} only_if_exists := if i == int(AtomType.utf8_string) {1} else {0}
cb.atoms << XInternAtom(cb.display, name.str, only_if_exists) cb.atoms << C.XInternAtom(cb.display, name.str, only_if_exists)
if i == int(atom_type.utf8_string) && cb.atoms[i] == Atom(C.None) { if i == int(AtomType.utf8_string) && cb.atoms[i] == C.Atom(C.None) {
cb.atoms[i] = cb.get_atom(.xa_string) cb.atoms[i] = cb.get_atom(.xa_string)
} }
} }
} }
fn read_property(d &Display, w Window, p Atom) Property { fn read_property(d &Display, w Window, p C.Atom) Property {
actual_type := Atom(0) actual_type := C.Atom(0)
actual_format := 0 actual_format := 0
nitems := 0 nitems := 0
bytes_after := 0 bytes_after := 0
@ -333,7 +333,7 @@ fn read_property(d &Display, w Window, p Atom) Property {
if ret != 0 { if ret != 0 {
C.XFree(ret) C.XFree(ret)
} }
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}
} }
@ -341,10 +341,10 @@ fn read_property(d &Display, w Window, p Atom) Property {
} }
// Finds the best target given a local copy of a property. // Finds the best target given a local copy of a property.
fn (cb &Clipboard) pick_target(prop Property) 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
@ -354,9 +354,9 @@ fn (cb &Clipboard) pick_target(prop Property) Atom {
} }
else else
{ {
atom_list := &Atom(prop.data) atom_list := &C.Atom(prop.data)
mut to_be_requested := Atom(0) mut to_be_requested := C.Atom(0)
//This is higher than the maximum priority. //This is higher than the maximum priority.
mut priority := math.max_i32 mut priority := math.max_i32
@ -367,7 +367,7 @@ fn (cb &Clipboard) pick_target(prop Property) Atom {
if cb.is_supported_target(atom_list[i]) { if cb.is_supported_target(atom_list[i]) {
index := cb.get_target_index(atom_list[i]) index := cb.get_target_index(atom_list[i])
if(priority > index && index >= 0) if priority > index && index >= 0
{ {
priority = index priority = index
to_be_requested = atom_list[i] to_be_requested = atom_list[i]
@ -378,47 +378,47 @@ fn (cb &Clipboard) pick_target(prop Property) Atom {
} }
} }
fn (cb &Clipboard) get_atoms(types ...atom_type) []Atom { fn (cb &Clipboard) get_atoms(types ...AtomType) []C.Atom {
mut atoms := []Atom mut atoms := []C.Atom
for typ in types { for typ in types {
atoms << cb.atoms[typ] atoms << cb.atoms[typ]
} }
return atoms return atoms
} }
fn (cb &Clipboard) get_atom(typ atom_type) Atom { fn (cb &Clipboard) get_atom(typ AtomType) C.Atom {
return cb.atoms[typ] return cb.atoms[typ]
} }
fn (cb &Clipboard) is_supported_target(target Atom) bool { fn (cb &Clipboard) is_supported_target(target C.Atom) bool {
return cb.get_target_index(target) >= 0 return cb.get_target_index(target) >= 0
} }
fn (cb &Clipboard) get_target_index(target 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
} }
fn (cb &Clipboard) get_supported_targets() []Atom { fn (cb &Clipboard) get_supported_targets() []C.Atom {
return cb.get_atoms(atom_type.utf8_string, .xa_string, .text, .text_plain, .text_html) return cb.get_atoms(AtomType.utf8_string, .xa_string, .text, .text_plain, .text_html)
} }
fn new_atom(value int) &Atom { fn new_atom(value int) &C.Atom {
mut atom := &Atom{} mut atom := &C.Atom{}
atom = value atom = value
return atom return atom
} }
fn create_xwindow(display &Display) Window { fn create_xwindow(display &Display) Window {
N := C.DefaultScreen(display) n := C.DefaultScreen(display)
return 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, N), C.WhitePixel(display, N)) 0, C.BlackPixel(display, n), C.WhitePixel(display, n))
} }
fn new_display() &Display { fn new_display() &Display {
return XOpenDisplay(C.NULL) return C.XOpenDisplay(C.NULL)
} }
// create a new PRIMARY clipboard (only supported on Linux) // create a new PRIMARY clipboard (only supported on Linux)