diff --git a/redo/common_unix.go b/redo/common_unix.go index e495063..e2d9261 100644 --- a/redo/common_unix.go +++ b/redo/common_unix.go @@ -7,6 +7,11 @@ import ( ) // #include "gtk_unix.h" +// /* because cgo doesn't like g_signal_connect() */ +// void gSignalConnect(gpointer obj, gchar *sig, GCallback callback, gpointer data) +// { +// g_signal_connect(obj, sig, callback, data); +// } import "C" func fromgstr(s *C.gchar) string { @@ -20,3 +25,9 @@ func togstr(s string) *C.gchar { func freegstr(s *C.gchar) { C.free(unsafe.Pointer(s)) } + +func g_signal_connect(object C.gpointer, name string, to C.GCallback, data C.gpointer) { + cname := togstr(name) + defer freegstr(cname) + C.gSignalConnect(object, cname, to, data) +} diff --git a/redo/uitask.go b/redo/uitask.go index 76908fb..462a24b 100644 --- a/redo/uitask.go +++ b/redo/uitask.go @@ -45,10 +45,23 @@ type event struct { lock sync.Mutex } +// do should never be nil; TODO should we make setters panic instead? + +func newEvent() *event { + return &event{ + do: func(c Doer) bool { + return false + }, + } +} + func (e *event) set(f func(Doer)) { e.lock.Lock() defer e.lock.Unlock() + if f == nil { + f = func(c Doer) {} + } e.do = func(c Doer) bool { f(c) return false @@ -59,6 +72,11 @@ func (e *event) setbool(f func(Doer) bool) { e.lock.Lock() defer e.lock.Unlock() + if f == nil { + f = func(c Doer) bool { + return false + } + } e.do = f } diff --git a/redo/window_unix.go b/redo/window_unix.go index 6e93fa7..c92fba6 100644 --- a/redo/window_unix.go +++ b/redo/window_unix.go @@ -7,6 +7,7 @@ import ( ) // #include "gtk_unix.h" +// extern gboolean windowClosing(GtkWidget *, GdkEvent *, gpointer); import "C" type window struct { @@ -14,6 +15,8 @@ type window struct { container *C.GtkContainer bin *C.GtkBin window *C.GtkWindow + + closing *event } func newWindow(title string, width int, height int) *Request { @@ -28,8 +31,14 @@ func newWindow(title string, width int, height int) *Request { container: (*C.GtkContainer)(unsafe.Pointer(widget)), bin: (*C.GtkBin)(unsafe.Pointer(widget)), window: (*C.GtkWindow)(unsafe.Pointer(widget)), + closing: newEvent(), } C.gtk_window_set_title(w.window, ctitle) + g_signal_connect( + C.gpointer(unsafe.Pointer(w.window)), + "delete-event", + C.GCallback(C.windowClosing), + C.gpointer(unsafe.Pointer(w))) // TODO size // TODO content c <- w @@ -108,6 +117,22 @@ func (w *window) Close() *Request { } func (w *window) OnClosing(e func(c Doer) bool) *Request { - // TODO - return nil + c := make(chan interface{}) + return &Request{ + op: func() { + w.closing.setbool(e) + c <- struct{}{} + }, + resp: c, + } +} + +//export windowClosing +func windowClosing(wid *C.GtkWidget, e *C.GdkEvent, data C.gpointer) C.gboolean { + w := (*window)(unsafe.Pointer(data)) + close := w.closing.fire() + if close { + return C.GDK_EVENT_PROPAGATE // will do gtk_widget_destroy(), which is what we want (thanks ebassi in irc.gimp.net/#gtk+) + } + return C.GDK_EVENT_STOP // keeps window alive } diff --git a/redo/zz_test.go b/redo/zz_test.go index a4ff669..122005b 100644 --- a/redo/zz_test.go +++ b/redo/zz_test.go @@ -12,10 +12,10 @@ func TestPackage(t *testing.T) { go func() { w := GetNewWindow(Do, "Hello", 320, 240) done := make(chan struct{}) -// Wait(Do, w.OnClosing(func(Doer) bool { -// done <- struct{}{} -// return true -// })) + Wait(Do, w.OnClosing(func(Doer) bool { + done <- struct{}{} + return true + })) Wait(Do, w.Show()) <-done }()