forked from alexflint/gallium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.go
257 lines (220 loc) · 6.48 KB
/
app.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
package gallium
/*
#cgo CFLAGS: -mmacosx-version-min=10.8
#cgo CFLAGS: -DGALLIUM_DIR=${SRCDIR}
#cgo CFLAGS: -Idist/include
#cgo LDFLAGS: -F${SRCDIR}/dist
#cgo LDFLAGS: -framework Gallium
#cgo LDFLAGS: -Wl,-rpath,@executable_path/../Frameworks
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/dist
#cgo LDFLAGS: -mmacosx-version-min=10.8
#include <stdlib.h>
#include "gallium/browser.h"
#include "gallium/cocoa.h"
// It does not seem that we can import "_cgo_export.h" from here
extern void cgo_onReady(int);
// This is a wrapper around GalliumLoop that adds the function pointer
// argument, since this does not seem to be possible from Go directly.
static inline void helper_GalliumLoop(int app_id, const char* arg0, struct gallium_error** err) {
GalliumLoop(app_id, arg0, &cgo_onReady, err);
}
*/
import "C"
import (
"errors"
"fmt"
"log"
"time"
"unsafe"
)
// cerr holds a C-allocated error, which must be freed explicitly.
type cerr struct {
c *C.struct_gallium_error
}
// newCerr allocates a new error struct. It must be freed explicitly.
func newCerr() cerr {
return cerr{
c: (*C.struct_gallium_error)(C.malloc(C.sizeof_struct_gallium_error)),
}
}
func (e cerr) free() {
C.free(unsafe.Pointer(e.c))
}
func (e *cerr) err() error {
// TODO
return fmt.Errorf("C error")
}
// Loop starts the browser loop and does not return unless there is an initialization error
func Loop(args []string, onReady func(*App)) error {
log.Println("\n\n=== gallium.Loop ===")
cerr := newCerr()
defer cerr.free()
app := App{
ready: make(chan struct{}),
}
go func() {
select {
case <-app.ready:
onReady(&app)
case <-time.After(3 * time.Second):
log.Fatal("Waited for 3 seconds without ready signal")
}
}()
appId := apps.add(&app)
C.helper_GalliumLoop(C.int(appId), C.CString(args[0]), &cerr.c)
return cerr.err()
}
// appManager is the singleton for managing app instances
type appManager []*App
func (m *appManager) add(app *App) int {
id := len(*m)
*m = append(*m, app)
return id
}
func (m *appManager) get(id int) *App {
return (*m)[id]
}
var apps appManager
// App is the handle that allows you to create windows and menus
type App struct {
// ready is how the cgo onready callback indicates to the Loop goroutine that
// chromium is initialized
ready chan struct{}
}
// Rect represents a rectangular region on the screen
type Rect struct {
Width int // Width in pixels
Height int // Height in pixels
Left int // Left is offset from left in pixel
Top int // Left is offset from top in pixels
}
// WindowOptions contains options for creating windows
type WindowOptions struct {
Title string // String to display in title bar
Shape Rect // Initial size and position of window
TitleBar bool // Whether the window title bar
Frame bool // Whether the window has a frame
Resizable bool // Whether the window border can be dragged to change its shape
CloseButton bool // Whether the window has a close button
MinButton bool // Whether the window has a miniaturize button
FullScreenButton bool // Whether the window has a full screen button
Menu []MenuEntry
}
// FramedWindow contains options for an "ordinary" window with title bar,
// frame, and min/max/close buttons.
var FramedWindow = WindowOptions{
Shape: Rect{
Width: 800,
Height: 600,
Left: 100,
Top: 100,
},
TitleBar: true,
Frame: true,
Resizable: true,
CloseButton: true,
MinButton: true,
FullScreenButton: true,
Title: "Gallium",
}
// FramelessWindow contains options for a window with no frame or border, but that
// is still resizable.
var FramelessWindow = WindowOptions{
Shape: Rect{
Width: 800,
Height: 600,
Left: 100,
Top: 100,
},
Resizable: true,
}
// Window represents a window registered with the native UI toolkit (e.g. NSWindow on macOS)
type Window struct {
c *C.gallium_window_t
}
var (
errZeroWidth = errors.New("window width was zero")
errZeroHeight = errors.New("window height was zero")
)
// OpenWindow creates a window that will load the given URL.
func (app *App) OpenWindow(url string, opt WindowOptions) (*Window, error) {
if opt.Shape.Width == 0 {
return nil, errZeroWidth
}
if opt.Shape.Height == 0 {
return nil, errZeroHeight
}
// Create the Cocoa window
cwin := C.GalliumOpenWindow(
C.CString(url),
C.CString(opt.Title),
C.int(opt.Shape.Width),
C.int(opt.Shape.Height),
C.int(opt.Shape.Left),
C.int(opt.Shape.Top),
C.bool(opt.TitleBar),
C.bool(opt.Frame),
C.bool(opt.Resizable),
C.bool(opt.CloseButton),
C.bool(opt.MinButton),
C.bool(opt.FullScreenButton))
// TODO: associate menu
return &Window{
c: cwin,
}, nil
}
// Shape gets the current shape of the window.
func (w *Window) Shape() Rect {
return Rect{
Width: int(C.GalliumWindowGetWidth(w.c)),
Height: int(C.GalliumWindowGetHeight(w.c)),
Left: int(C.GalliumWindowGetLeft(w.c)),
Top: int(C.GalliumWindowGetTop(w.c)),
}
}
// Shape gets the current shape of the window.
func (w *Window) SetShape(r Rect) {
C.GalliumWindowSetShape(w.c, C.int(r.Width), C.int(r.Height), C.int(r.Left), C.int(r.Top))
}
// URL gets the URL that the window is currently at.
func (w *Window) URL() string {
return C.GoString(C.GalliumWindowGetURL(w.c))
}
// LoadURL causes the window to load the given URL
func (w *Window) LoadURL(url string) {
C.GalliumWindowLoadURL(w.c, C.CString(url))
}
// Reload reloads the current URL
func (w *Window) Reload() {
C.GalliumWindowReload(w.c)
}
// Reload reloads the current URL, ignoring cached versions of resources.
func (w *Window) ReloadNoCache() {
C.GalliumWindowReloadNoCache(w.c)
}
// Open opens the window. This is the default state for a window created
// via OpenWindow, so you only need to call this if you manually closed
// the window.
func (w *Window) Open() {
C.GalliumWindowOpen(w.c)
}
// Close closes the window, as if the close button had been clicked.
func (w *Window) Close() {
C.GalliumWindowClose(w.c)
}
// Miniaturize miniaturizes the window, as if the min button had been clicked.
func (w *Window) Miniaturize() {
C.GalliumWindowMiniaturize(w.c)
}
// OpenDevTools opens the developer tools for this window.
func (w *Window) OpenDevTools() {
C.GalliumWindowOpenDevTools(w.c)
}
// CloseDevTools closes the developer tools.
func (w *Window) CloseDevTools() {
C.GalliumWindowCloseDevTools(w.c)
}
// DevToolsVisible returns whether the developer tools are showing
func (w *Window) DevToolsAreOpen() bool {
return bool(C.GalliumWindowDevToolsAreOpen(w.c))
}