diff --git a/callbacks_unix.go b/callbacks_unix.go index ff3e3b9..717150c 100644 --- a/callbacks_unix.go +++ b/callbacks_unix.go @@ -27,8 +27,9 @@ import "C" func our_window_delete_event_callback(widget *C.GtkWidget, event *C.GdkEvent, what C.gpointer) C.gboolean { // called when the user tries to close the window s := (*sysData)(unsafe.Pointer(what)) - s.signal() - return C.TRUE // do not close the window + b := false // TODO + s.close(&b) + return togbool(!b) // ! because TRUE means don't close } var window_delete_event_callback = C.GCallback(C.our_window_delete_event_callback) @@ -52,7 +53,7 @@ var window_configure_event_callback = C.GCallback(C.our_window_configure_event_c func our_button_clicked_callback(button *C.GtkButton, what C.gpointer) { // called when the user clicks a button s := (*sysData)(unsafe.Pointer(what)) - s.signal() + s.event() } var button_clicked_callback = C.GCallback(C.our_button_clicked_callback) diff --git a/dialog_unix.go b/dialog_unix.go index f22f1c6..9728e2a 100644 --- a/dialog_unix.go +++ b/dialog_unix.go @@ -9,7 +9,6 @@ import ( ) // #include "gtk_unix.h" -// extern void our_dialog_response_callback(GtkDialog *, gint, gpointer); // /* because cgo seems to choke on ... */ // /* parent will be NULL if there is no parent, so this is fine */ // static inline GtkWidget *gtkNewMsgBox(GtkWindow *parent, GtkMessageType type, GtkButtonsType buttons, char *title, char *text) @@ -23,6 +22,11 @@ import ( // } import "C" +// the actual type of these constants is GtkResponseType but gtk_dialog_run() returns a gint +var dialogResponses = map[C.gint]Response{ + C.GTK_RESPONSE_OK: OK, +} + // dialog performs the bookkeeping involved for having a GtkDialog behave the way we want. type dialog struct { parent *Window @@ -30,13 +34,12 @@ type dialog struct { hadgroup C.gboolean prevgroup *C.GtkWindowGroup newgroup *C.GtkWindowGroup - result chan int + result Response } func mkdialog(parent *Window) *dialog { return &dialog{ parent: parent, - result: make(chan int), } } @@ -59,36 +62,6 @@ func (d *dialog) prepare() { } } -func (d *dialog) run(mk func() *C.GtkWidget) { - d.prepare() - box := mk() - if d.parent == dialogWindow { - go func() { - res := make(chan C.gint) - defer close(res) - uitask <- func() { - r := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box))) - d.cleanup(box) - res <- r - } - d.send(<-res) - }() - return - } - // otherwise just connect the delete signal - g_signal_connect_pointer(box, "response", dialog_response_callback, unsafe.Pointer(d)) - C.gtk_widget_show_all(box) -} - -//export our_dialog_response_callback -func our_dialog_response_callback(box *C.GtkDialog, res C.gint, data C.gpointer) { - d := (*dialog)(unsafe.Pointer(data)) - d.cleanup((*C.GtkWidget)(unsafe.Pointer(box))) - go d.send(res) // send on another goroutine, like everything else -} - -var dialog_response_callback = C.GCallback(C.our_dialog_response_callback) - func (d *dialog) cleanup(box *C.GtkWidget) { // have to explicitly close the dialog box, otherwise wacky things will happen C.gtk_widget_destroy(box) @@ -101,43 +74,33 @@ func (d *dialog) cleanup(box *C.GtkWidget) { } } -func (d *dialog) send(res C.gint) { - // this is where processing would go - d.result <- int(res) +func (d *dialog) run(mk func() *C.GtkWidget) { + d.prepare() + box := mk() + r := C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(box))) + d.cleanup(box) + d.result = dialogResponses[r] } -func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (result chan int) { - result = make(chan int) +func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (response Response) { d := mkdialog(parent) - uitask <- func() { - cprimarytext := C.CString(primarytext) - defer C.free(unsafe.Pointer(cprimarytext)) - csecondarytext := (*C.char)(nil) - if secondarytext != "" { - csecondarytext = C.CString(secondarytext) - defer C.free(unsafe.Pointer(csecondarytext)) - } - d.run(func() *C.GtkWidget { - return C.gtkNewMsgBox(d.pwin, msgtype, buttons, cprimarytext, csecondarytext) - }) + cprimarytext := C.CString(primarytext) + defer C.free(unsafe.Pointer(cprimarytext)) + csecondarytext := (*C.char)(nil) + if secondarytext != "" { + csecondarytext = C.CString(secondarytext) + defer C.free(unsafe.Pointer(csecondarytext)) } + d.run(func() *C.GtkWidget { + return C.gtkNewMsgBox(d.pwin, msgtype, buttons, cprimarytext, csecondarytext) + }) return d.result } -func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) { - done = make(chan struct{}) - go func() { - <-_msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_OTHER), C.GtkButtonsType(C.GTK_BUTTONS_OK)) - done <- struct{}{} - }() - return done +func (w *Window) msgBox(primarytext string, secondarytext string) { + _msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_OTHER), C.GtkButtonsType(C.GTK_BUTTONS_OK)) } -func (w *Window) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) { - done = make(chan struct{}) - go func() { - <-_msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK)) - done <- struct{}{} - }() - return done +func (w *Window) msgBoxError(primarytext string, secondarytext string) { + _msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK)) } diff --git a/sysdata_unix.go b/sysdata_unix.go index 7814fb3..b39df50 100644 --- a/sysdata_unix.go +++ b/sysdata_unix.go @@ -118,42 +118,30 @@ var classTypes = [nctypes]*classData{ func (s *sysData) make(window *sysData) error { ct := classTypes[s.ctype] - ret := make(chan *C.GtkWidget) - defer close(ret) - uitask <- func() { - if s.alternate { - ret <- ct.makeAlt() - return - } - ret <- ct.make() + if s.alternate { + s.widget = ct.makeAlt() + } else { + s.widget = ct.make() } - s.widget = <-ret if window == nil { - uitask <- func() { - fixed := gtkNewWindowLayout() - gtk_container_add(s.widget, fixed) - for signame, sigfunc := range ct.signals { - g_signal_connect(s.widget, signame, sigfunc, s) - } - ret <- fixed + fixed := gtkNewWindowLayout() + gtk_container_add(s.widget, fixed) + for signame, sigfunc := range ct.signals { + g_signal_connect(s.widget, signame, sigfunc, s) } - s.container = <-ret + s.container = fixed } else { s.container = window.container - uitask <- func() { - gtkAddWidgetToLayout(s.container, s.widget) - for signame, sigfunc := range ct.signals { - g_signal_connect(s.widget, signame, sigfunc, s) - } - if ct.child != nil { - child := ct.child(s.widget) - for signame, sigfunc := range ct.childsigs { - g_signal_connect(child, signame, sigfunc, s) - } - } - ret <- nil + gtkAddWidgetToLayout(s.container, s.widget) + for signame, sigfunc := range ct.signals { + g_signal_connect(s.widget, signame, sigfunc, s) + } + if ct.child != nil { + child := ct.child(s.widget) + for signame, sigfunc := range ct.childsigs { + g_signal_connect(child, signame, sigfunc, s) + } } - <-ret } return nil } @@ -170,35 +158,17 @@ func (s *sysData) firstShow() error { } func (s *sysData) show() { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - gtk_widget_show(s.widget) - s.resetposition() - ret <- struct{}{} - } - <-ret + gtk_widget_show(s.widget) + s.resetposition() } func (s *sysData) hide() { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - gtk_widget_hide(s.widget) - s.resetposition() - ret <- struct{}{} - } - <-ret + gtk_widget_hide(s.widget) + s.resetposition() } func (s *sysData) setText(text string) { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - classTypes[s.ctype].setText(s.widget, text) - ret <- struct{}{} - } - <-ret + classTypes[s.ctype].setText(s.widget, text) } func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error { @@ -208,103 +178,51 @@ func (s *sysData) setRect(x int, y int, width int, height int, winheight int) er } func (s *sysData) isChecked() bool { - ret := make(chan bool) - defer close(ret) - uitask <- func() { - ret <- gtk_toggle_button_get_active(s.widget) - } - return <-ret + return gtk_toggle_button_get_active(s.widget) } func (s *sysData) text() string { - ret := make(chan string) - defer close(ret) - uitask <- func() { - ret <- classTypes[s.ctype].text(s.widget) - } - return <-ret + return classTypes[s.ctype].text(s.widget) } func (s *sysData) append(what string) { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - classTypes[s.ctype].append(s.widget, what) - ret <- struct{}{} - } - <-ret + classTypes[s.ctype].append(s.widget, what) } func (s *sysData) insertBefore(what string, before int) { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - classTypes[s.ctype].insert(s.widget, before, what) - ret <- struct{}{} - } - <-ret + classTypes[s.ctype].insert(s.widget, before, what) } func (s *sysData) selectedIndex() int { - ret := make(chan int) - defer close(ret) - uitask <- func() { - ret <- classTypes[s.ctype].selected(s.widget) - } - return <-ret + return classTypes[s.ctype].selected(s.widget) } func (s *sysData) selectedIndices() []int { - ret := make(chan []int) - defer close(ret) - uitask <- func() { - ret <- classTypes[s.ctype].selMulti(s.widget) - } - return <-ret + return classTypes[s.ctype].selMulti(s.widget) } func (s *sysData) selectedTexts() []string { - ret := make(chan []string) - defer close(ret) - uitask <- func() { - ret <- classTypes[s.ctype].smtexts(s.widget) - } - return <-ret + return classTypes[s.ctype].smtexts(s.widget) } func (s *sysData) setWindowSize(width int, height int) error { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - // does not take window geometry into account (and cannot, since the window manager won't give that info away) - // thanks to TingPing in irc.gimp.net/#gtk+ - gtk_window_resize(s.widget, width, height) - ret <- struct{}{} - } - <-ret + // does not take window geometry into account (and cannot, since the window manager won't give that info away) + // thanks to TingPing in irc.gimp.net/#gtk+ + gtk_window_resize(s.widget, width, height) return nil } func (s *sysData) delete(index int) { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - classTypes[s.ctype].delete(s.widget, index) - ret <- struct{}{} - } - <-ret + classTypes[s.ctype].delete(s.widget, index) } // With GTK+, we must manually pulse the indeterminate progressbar ourselves. This goroutine does that. func (s *sysData) progressPulse() { + // TODO this could probably be done differently... pulse := func() { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { + touitask(func() { gtk_progress_bar_pulse(s.widget) - ret <- struct{}{} - } - <-ret + }) } var ticker *time.Ticker @@ -345,78 +263,44 @@ func (s *sysData) setProgress(percent int) { } s.pulse <- false <-s.pulse // wait for sysData.progressPulse() to register that - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - gtk_progress_bar_set_fraction(s.widget, percent) - ret <- struct{}{} - } - <-ret + gtk_progress_bar_set_fraction(s.widget, percent) } func (s *sysData) len() int { - ret := make(chan int) - defer close(ret) - uitask <- func() { - ret <- classTypes[s.ctype].len(s.widget) - } - return <-ret + return classTypes[s.ctype].len(s.widget) } func (s *sysData) setAreaSize(width int, height int) { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - c := gtkAreaGetControl(s.widget) - gtk_widget_set_size_request(c, width, height) - s.areawidth = width // for sysData.preferredSize() - s.areaheight = height - C.gtk_widget_queue_draw(c) - ret <- struct{}{} - } - <-ret + c := gtkAreaGetControl(s.widget) + gtk_widget_set_size_request(c, width, height) + s.areawidth = width // for sysData.preferredSize() + s.areaheight = height + C.gtk_widget_queue_draw(c) } +// TODO should this be made safe? (TODO move to area.go) func (s *sysData) repaintAll() { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - c := gtkAreaGetControl(s.widget) - C.gtk_widget_queue_draw(c) - ret <- struct{}{} - } - <-ret + c := gtkAreaGetControl(s.widget) + C.gtk_widget_queue_draw(c) } func (s *sysData) center() { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - if C.gtk_widget_get_visible(s.widget) == C.FALSE { - // hint to the WM to make it centered when it is shown again - // thanks to Jasper in irc.gimp.net/#gtk+ - C.gtk_window_set_position(togtkwindow(s.widget), C.GTK_WIN_POS_CENTER) - } else { - var width, height C.gint + if C.gtk_widget_get_visible(s.widget) == C.FALSE { + // hint to the WM to make it centered when it is shown again + // thanks to Jasper in irc.gimp.net/#gtk+ + C.gtk_window_set_position(togtkwindow(s.widget), C.GTK_WIN_POS_CENTER) + } else { + var width, height C.gint - s.resetposition() - //we should be able to use gravity to simplify this, but it doesn't take effect immediately, and adding show calls does nothing (thanks Jasper in irc.gimp.net/#gtk+) - C.gtk_window_get_size(togtkwindow(s.widget), &width, &height) - C.gtk_window_move(togtkwindow(s.widget), - (C.gdk_screen_width() / 2) - (width / 2), - (C.gdk_screen_height() / 2) - (width / 2)) - } - ret <- struct{}{} + s.resetposition() + //we should be able to use gravity to simplify this, but it doesn't take effect immediately, and adding show calls does nothing (thanks Jasper in irc.gimp.net/#gtk+) + C.gtk_window_get_size(togtkwindow(s.widget), &width, &height) + C.gtk_window_move(togtkwindow(s.widget), + (C.gdk_screen_width() / 2) - (width / 2), + (C.gdk_screen_height() / 2) - (width / 2)) } - <-ret } func (s *sysData) setChecked(checked bool) { - ret := make(chan struct{}) - defer close(ret) - uitask <- func() { - gtk_toggle_button_set_active(s.widget, checked) - ret <- struct{}{} - } - <-ret + gtk_toggle_button_set_active(s.widget, checked) } diff --git a/uitask_unix.go b/uitask_unix.go index 030f096..a8cabd2 100644 --- a/uitask_unix.go +++ b/uitask_unix.go @@ -6,7 +6,6 @@ package ui import ( "fmt" - "runtime" ) // #cgo pkg-config: gtk+-3.0 @@ -15,19 +14,32 @@ import "C" var uitask chan func() -func ui(main func()) error { - runtime.LockOSThread() - - uitask = make(chan func()) +func uiinit() error { err := gtk_init() if err != nil { return fmt.Errorf("gtk_init() failed: %v", err) } + // do this only on success, just to be safe + uitask = make(chan func()) + return nil +} + +func ui() { // thanks to tristan and Daniel_S in irc.gimp.net/#gtk // see our_idle_callback in callbacks_unix.go for details go func() { - for f := range uitask { + for { + var f func() + + select { + case f = <-uitask: + // do nothing + case <-Stop: + f = func() { + C.gtk_main_quit() + } + } done := make(chan struct{}) gdk_threads_add_idle(>kIdleOp{ what: f, @@ -38,11 +50,5 @@ func ui(main func()) error { } }() - go func() { - main() - uitask <- gtk_main_quit - }() - C.gtk_main() - return nil }