/
io.go
166 lines (150 loc) · 4.25 KB
/
io.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package cairo
//#cgo pkg-config: cairo
//#include <stdlib.h>
//#include <cairo/cairo.h>
//
//extern cairo_status_t go_write_callback(void*, unsigned char*, unsigned int);
//
//extern void go_write_callback_reaper(void*);
//
//extern cairo_status_t go_read_callback(void*, unsigned char*, unsigned int);
//
//extern void go_read_callback_reaper(void*);
//
//static cairo_status_t c_write_callback(void* w, unsigned char* data, unsigned int length) {
// return go_write_callback(w, data, length);
//}
//
//typedef cairo_status_t (*wcallback_pass_back)(void*, unsigned char*, unsigned int);
//
///*This is required to expose the c callback wrapping the go callback back to Go*/
//static wcallback_pass_back callback_getter() {
// return &c_write_callback;
//}
//
//static void c_write_callback_reaper(void *data) {
// go_write_callback_reaper(data);
//}
//
//typedef void (*wreaper_pass_back)(void*);
//
//static wreaper_pass_back wreaper_getter() {
// return &c_write_callback_reaper;
//}
import "C"
import (
"io"
"sync"
"unsafe"
)
var (
wmap = map[id]*writer{}
mux = new(sync.Mutex)
//there will only ever be one writer per object.
wkey = &C.cairo_user_data_key_t{}
)
type writer struct {
w io.Writer
err error
id id
}
func (w *writer) write(p []byte) error {
if w.err != nil {
return w.err
}
n, err := w.w.Write(p)
if err != nil {
w.err = err
}
if n == len(p) {
return w.err
}
w.err = io.ErrShortWrite
return w.err
}
//XtensionCairoWriteFuncT is a cairo_write_func_t that expects as its closure
//argument the result of calling XtensionWrapWriter on a Writer.
//The surface or device created with this pair must be used to register
//the wrapped Writer with that objects XtensionRegisterWriter method.
//
//Anything less will cause at best memory leaks and at worst random errors.
//
//See XtensionWrapWriter for more information.
var XtensionCairoWriteFuncT = C.callback_getter()
//export go_write_callback
func go_write_callback(w unsafe.Pointer, data *C.uchar, length C.uint) C.cairo_status_t {
W := (*writer)(w)
bs := C.GoBytes(unsafe.Pointer(data), C.int(length))
if err := W.write(bs); err == nil {
return errSuccess
}
return errWriteError
}
//export go_write_callback_reaper
func go_write_callback_reaper(w unsafe.Pointer) {
W := (*writer)(w)
mux.Lock()
defer mux.Unlock()
delete(wmap, W.id)
W.w = nil
W.err = nil
}
func storeWriter(W *writer) {
mux.Lock()
defer mux.Unlock()
wmap[W.id] = W
}
//XtensionRegisterWriter registers the writer wrapped by XtensionWrapWriter
//with the surface so that it does not get garbage collected until libcairo
//releases the surface.
//
//See XtensionWrapWriter for more information.
func (s *XtensionSurface) XtensionRegisterWriter(w unsafe.Pointer) {
if err := s.Err(); err != nil {
go_write_callback_reaper(w)
}
W := (*writer)(w)
W.id = s.id()
C.cairo_surface_set_user_data(s.s, wkey, w, C.wreaper_getter())
storeWriter(W)
}
//XtensionRegisterWriter registers the writer wrapped by XtensionWrapWriter
//with the surface so that it does not get garbage collected until libcairo
//releases the device.
//
//See XtensionWrapWriter for more information.
func (d *XtensionDevice) XtensionRegisterWriter(w unsafe.Pointer) {
if err := d.Err(); err != nil {
go_write_callback_reaper(w)
}
W := (*writer)(w)
W.id = d.id()
C.cairo_device_set_user_data(d.d, wkey, w, C.wreaper_getter())
storeWriter(W)
}
//XtensionWrapWriter wraps a writer in a special container to communicate
//with libcairo.
//
//It also stores the returned value so that it is not garbage collected.
//
//You must use this along with XtensionCairoWriteFuncT when wrapping any
//of libcairo's _create_for_stream factories.
//
//After the surface or device is created the returned pointer must
//be registered with the surface or device using its XtensionRegisterWriter
//method.
//
//Example
//
//Say you wanted to wrap an X surface created with
//cairo_X_surface_create_for_stream.
//
//In the factory for your Go surface, you need code like the following:
// wrapped := cairo.XtensionWrapWriter(iowriter)
// s := C.cairo_X_surface_create_for_stream(cairo.XtensionCairoWriteFuncT, wrapped)
// S := cairo.NewXtensionSurface(s)
// S.XtensionRegisterWriter(wrapped)
func XtensionWrapWriter(w io.Writer) (closure unsafe.Pointer) {
W := &writer{w: w}
return unsafe.Pointer(W)
}