-
Notifications
You must be signed in to change notification settings - Fork 135
/
app.go
314 lines (266 loc) · 7.74 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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
package gallium
/*
#cgo CFLAGS: -mmacosx-version-min=10.8
#cgo CFLAGS: -DGALLIUM_DIR=${SRCDIR}
#cgo CFLAGS: -Idist/include
#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"
"os"
"path/filepath"
"time"
"unsafe"
)
var (
errZeroWidth = errors.New("window width was zero")
errZeroHeight = errors.New("window height was zero")
)
// 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{}
}
// 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,
Bottom: 100,
},
TitleBar: true,
Frame: true,
Resizable: true,
CloseButton: true,
MinButton: true,
FullScreenButton: true,
Title: defaultWindowTitle(),
}
func defaultWindowTitle() string {
// try the display name first
if name := BundleInfo("CFBundleDisplayName"); name != "" {
return name
}
// then fall back to the short name
if name := BundleInfo("CFBundleName"); name != "" {
return name
}
// then fall back to the executable name
if len(os.Args) > 0 {
filepath.Base(os.Args[0])
}
return ""
}
// 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,
Bottom: 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
}
// 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.Bottom),
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 rectFromC(C.GalliumWindowGetShape(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.Bottom))
}
// 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)
}
// Undo undoes the last text editing action
func (w *Window) Undo() {
C.GalliumWindowUndo(w.c)
}
// Redo redoes the last text editing action
func (w *Window) Redo() {
C.GalliumWindowRedo(w.c)
}
// Cut cuts the current text selection to the pastboard
func (w *Window) Cut() {
C.GalliumWindowCut(w.c)
}
// Copy copies the current text selection to the pasteboard
func (w *Window) Copy() {
C.GalliumWindowCopy(w.c)
}
// Paste pastes from the pasteboard
func (w *Window) Paste() {
C.GalliumWindowPaste(w.c)
}
// PasteAndMatchStyle pastes from the pasteboard, matching style to the current element
func (w *Window) PasteAndMatchStyle() {
C.GalliumWindowPasteAndMatchStyle(w.c)
}
// Delete deletes the current text selection
func (w *Window) Delete() {
C.GalliumWindowDelete(w.c)
}
// SelectAll selects all text in the current element
func (w *Window) SelectAll() {
C.GalliumWindowSelectAll(w.c)
}
// Unselect unselects any text selection
func (w *Window) Unselect() {
C.GalliumWindowUnselect(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))
}
// NativeWindow gets a operating-system dependent handle for this window. Under macOS
// this is NSWindow*.
func (w *Window) NativeWindow() unsafe.Pointer {
return unsafe.Pointer(C.GalliumWindowNativeWindow(w.c))
}
// NativeWindow gets an operating-system dependent handle for the window controller.
// Under macOS this is *NSWindowController.
func (w *Window) NativeController() unsafe.Pointer {
return unsafe.Pointer(C.GalliumWindowNativeController(w.c))
}