Migrated the GTK+ backend to the new API.

This commit is contained in:
Pietro Gagliardi 2014-06-28 21:40:44 -04:00
parent affc65a5a4
commit 385993792f
4 changed files with 108 additions and 254 deletions

View File

@ -27,8 +27,9 @@ import "C"
func our_window_delete_event_callback(widget *C.GtkWidget, event *C.GdkEvent, what C.gpointer) C.gboolean { 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 // called when the user tries to close the window
s := (*sysData)(unsafe.Pointer(what)) s := (*sysData)(unsafe.Pointer(what))
s.signal() b := false // TODO
return C.TRUE // do not close the window 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) 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) { func our_button_clicked_callback(button *C.GtkButton, what C.gpointer) {
// called when the user clicks a button // called when the user clicks a button
s := (*sysData)(unsafe.Pointer(what)) s := (*sysData)(unsafe.Pointer(what))
s.signal() s.event()
} }
var button_clicked_callback = C.GCallback(C.our_button_clicked_callback) var button_clicked_callback = C.GCallback(C.our_button_clicked_callback)

View File

@ -9,7 +9,6 @@ import (
) )
// #include "gtk_unix.h" // #include "gtk_unix.h"
// extern void our_dialog_response_callback(GtkDialog *, gint, gpointer);
// /* because cgo seems to choke on ... */ // /* because cgo seems to choke on ... */
// /* parent will be NULL if there is no parent, so this is fine */ // /* 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) // static inline GtkWidget *gtkNewMsgBox(GtkWindow *parent, GtkMessageType type, GtkButtonsType buttons, char *title, char *text)
@ -23,6 +22,11 @@ import (
// } // }
import "C" 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. // dialog performs the bookkeeping involved for having a GtkDialog behave the way we want.
type dialog struct { type dialog struct {
parent *Window parent *Window
@ -30,13 +34,12 @@ type dialog struct {
hadgroup C.gboolean hadgroup C.gboolean
prevgroup *C.GtkWindowGroup prevgroup *C.GtkWindowGroup
newgroup *C.GtkWindowGroup newgroup *C.GtkWindowGroup
result chan int result Response
} }
func mkdialog(parent *Window) *dialog { func mkdialog(parent *Window) *dialog {
return &dialog{ return &dialog{
parent: parent, 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) { func (d *dialog) cleanup(box *C.GtkWidget) {
// have to explicitly close the dialog box, otherwise wacky things will happen // have to explicitly close the dialog box, otherwise wacky things will happen
C.gtk_widget_destroy(box) C.gtk_widget_destroy(box)
@ -101,43 +74,33 @@ func (d *dialog) cleanup(box *C.GtkWidget) {
} }
} }
func (d *dialog) send(res C.gint) { func (d *dialog) run(mk func() *C.GtkWidget) {
// this is where processing would go d.prepare()
d.result <- int(res) 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) { func _msgBox(parent *Window, primarytext string, secondarytext string, msgtype C.GtkMessageType, buttons C.GtkButtonsType) (response Response) {
result = make(chan int)
d := mkdialog(parent) d := mkdialog(parent)
uitask <- func() { cprimarytext := C.CString(primarytext)
cprimarytext := C.CString(primarytext) defer C.free(unsafe.Pointer(cprimarytext))
defer C.free(unsafe.Pointer(cprimarytext)) csecondarytext := (*C.char)(nil)
csecondarytext := (*C.char)(nil) if secondarytext != "" {
if secondarytext != "" { csecondarytext = C.CString(secondarytext)
csecondarytext = C.CString(secondarytext) defer C.free(unsafe.Pointer(csecondarytext))
defer C.free(unsafe.Pointer(csecondarytext))
}
d.run(func() *C.GtkWidget {
return C.gtkNewMsgBox(d.pwin, msgtype, buttons, cprimarytext, csecondarytext)
})
} }
d.run(func() *C.GtkWidget {
return C.gtkNewMsgBox(d.pwin, msgtype, buttons, cprimarytext, csecondarytext)
})
return d.result return d.result
} }
func (w *Window) msgBox(primarytext string, secondarytext string) (done chan struct{}) { func (w *Window) msgBox(primarytext string, secondarytext string) {
done = make(chan struct{}) _msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_OTHER), C.GtkButtonsType(C.GTK_BUTTONS_OK))
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) msgBoxError(primarytext string, secondarytext string) (done chan struct{}) { func (w *Window) msgBoxError(primarytext string, secondarytext string) {
done = make(chan struct{}) _msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK))
go func() {
<-_msgBox(w, primarytext, secondarytext, C.GtkMessageType(C.GTK_MESSAGE_ERROR), C.GtkButtonsType(C.GTK_BUTTONS_OK))
done <- struct{}{}
}()
return done
} }

View File

@ -118,42 +118,30 @@ var classTypes = [nctypes]*classData{
func (s *sysData) make(window *sysData) error { func (s *sysData) make(window *sysData) error {
ct := classTypes[s.ctype] ct := classTypes[s.ctype]
ret := make(chan *C.GtkWidget) if s.alternate {
defer close(ret) s.widget = ct.makeAlt()
uitask <- func() { } else {
if s.alternate { s.widget = ct.make()
ret <- ct.makeAlt()
return
}
ret <- ct.make()
} }
s.widget = <-ret
if window == nil { if window == nil {
uitask <- func() { fixed := gtkNewWindowLayout()
fixed := gtkNewWindowLayout() gtk_container_add(s.widget, fixed)
gtk_container_add(s.widget, fixed) for signame, sigfunc := range ct.signals {
for signame, sigfunc := range ct.signals { g_signal_connect(s.widget, signame, sigfunc, s)
g_signal_connect(s.widget, signame, sigfunc, s)
}
ret <- fixed
} }
s.container = <-ret s.container = fixed
} else { } else {
s.container = window.container s.container = window.container
uitask <- func() { gtkAddWidgetToLayout(s.container, s.widget)
gtkAddWidgetToLayout(s.container, s.widget) for signame, sigfunc := range ct.signals {
for signame, sigfunc := range ct.signals { g_signal_connect(s.widget, signame, sigfunc, s)
g_signal_connect(s.widget, signame, sigfunc, s) }
} if ct.child != nil {
if ct.child != nil { child := ct.child(s.widget)
child := ct.child(s.widget) for signame, sigfunc := range ct.childsigs {
for signame, sigfunc := range ct.childsigs { g_signal_connect(child, signame, sigfunc, s)
g_signal_connect(child, signame, sigfunc, s) }
}
}
ret <- nil
} }
<-ret
} }
return nil return nil
} }
@ -170,35 +158,17 @@ func (s *sysData) firstShow() error {
} }
func (s *sysData) show() { func (s *sysData) show() {
ret := make(chan struct{}) gtk_widget_show(s.widget)
defer close(ret) s.resetposition()
uitask <- func() {
gtk_widget_show(s.widget)
s.resetposition()
ret <- struct{}{}
}
<-ret
} }
func (s *sysData) hide() { func (s *sysData) hide() {
ret := make(chan struct{}) gtk_widget_hide(s.widget)
defer close(ret) s.resetposition()
uitask <- func() {
gtk_widget_hide(s.widget)
s.resetposition()
ret <- struct{}{}
}
<-ret
} }
func (s *sysData) setText(text string) { func (s *sysData) setText(text string) {
ret := make(chan struct{}) classTypes[s.ctype].setText(s.widget, text)
defer close(ret)
uitask <- func() {
classTypes[s.ctype].setText(s.widget, text)
ret <- struct{}{}
}
<-ret
} }
func (s *sysData) setRect(x int, y int, width int, height int, winheight int) error { 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 { func (s *sysData) isChecked() bool {
ret := make(chan bool) return gtk_toggle_button_get_active(s.widget)
defer close(ret)
uitask <- func() {
ret <- gtk_toggle_button_get_active(s.widget)
}
return <-ret
} }
func (s *sysData) text() string { func (s *sysData) text() string {
ret := make(chan string) return classTypes[s.ctype].text(s.widget)
defer close(ret)
uitask <- func() {
ret <- classTypes[s.ctype].text(s.widget)
}
return <-ret
} }
func (s *sysData) append(what string) { func (s *sysData) append(what string) {
ret := make(chan struct{}) classTypes[s.ctype].append(s.widget, what)
defer close(ret)
uitask <- func() {
classTypes[s.ctype].append(s.widget, what)
ret <- struct{}{}
}
<-ret
} }
func (s *sysData) insertBefore(what string, before int) { func (s *sysData) insertBefore(what string, before int) {
ret := make(chan struct{}) classTypes[s.ctype].insert(s.widget, before, what)
defer close(ret)
uitask <- func() {
classTypes[s.ctype].insert(s.widget, before, what)
ret <- struct{}{}
}
<-ret
} }
func (s *sysData) selectedIndex() int { func (s *sysData) selectedIndex() int {
ret := make(chan int) return classTypes[s.ctype].selected(s.widget)
defer close(ret)
uitask <- func() {
ret <- classTypes[s.ctype].selected(s.widget)
}
return <-ret
} }
func (s *sysData) selectedIndices() []int { func (s *sysData) selectedIndices() []int {
ret := make(chan []int) return classTypes[s.ctype].selMulti(s.widget)
defer close(ret)
uitask <- func() {
ret <- classTypes[s.ctype].selMulti(s.widget)
}
return <-ret
} }
func (s *sysData) selectedTexts() []string { func (s *sysData) selectedTexts() []string {
ret := make(chan []string) return classTypes[s.ctype].smtexts(s.widget)
defer close(ret)
uitask <- func() {
ret <- classTypes[s.ctype].smtexts(s.widget)
}
return <-ret
} }
func (s *sysData) setWindowSize(width int, height int) error { func (s *sysData) setWindowSize(width int, height int) error {
ret := make(chan struct{}) // does not take window geometry into account (and cannot, since the window manager won't give that info away)
defer close(ret) // thanks to TingPing in irc.gimp.net/#gtk+
uitask <- func() { gtk_window_resize(s.widget, width, height)
// 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
return nil return nil
} }
func (s *sysData) delete(index int) { func (s *sysData) delete(index int) {
ret := make(chan struct{}) classTypes[s.ctype].delete(s.widget, index)
defer close(ret)
uitask <- func() {
classTypes[s.ctype].delete(s.widget, index)
ret <- struct{}{}
}
<-ret
} }
// With GTK+, we must manually pulse the indeterminate progressbar ourselves. This goroutine does that. // With GTK+, we must manually pulse the indeterminate progressbar ourselves. This goroutine does that.
func (s *sysData) progressPulse() { func (s *sysData) progressPulse() {
// TODO this could probably be done differently...
pulse := func() { pulse := func() {
ret := make(chan struct{}) touitask(func() {
defer close(ret)
uitask <- func() {
gtk_progress_bar_pulse(s.widget) gtk_progress_bar_pulse(s.widget)
ret <- struct{}{} })
}
<-ret
} }
var ticker *time.Ticker var ticker *time.Ticker
@ -345,78 +263,44 @@ func (s *sysData) setProgress(percent int) {
} }
s.pulse <- false s.pulse <- false
<-s.pulse // wait for sysData.progressPulse() to register that <-s.pulse // wait for sysData.progressPulse() to register that
ret := make(chan struct{}) gtk_progress_bar_set_fraction(s.widget, percent)
defer close(ret)
uitask <- func() {
gtk_progress_bar_set_fraction(s.widget, percent)
ret <- struct{}{}
}
<-ret
} }
func (s *sysData) len() int { func (s *sysData) len() int {
ret := make(chan int) return classTypes[s.ctype].len(s.widget)
defer close(ret)
uitask <- func() {
ret <- classTypes[s.ctype].len(s.widget)
}
return <-ret
} }
func (s *sysData) setAreaSize(width int, height int) { func (s *sysData) setAreaSize(width int, height int) {
ret := make(chan struct{}) c := gtkAreaGetControl(s.widget)
defer close(ret) gtk_widget_set_size_request(c, width, height)
uitask <- func() { s.areawidth = width // for sysData.preferredSize()
c := gtkAreaGetControl(s.widget) s.areaheight = height
gtk_widget_set_size_request(c, width, height) C.gtk_widget_queue_draw(c)
s.areawidth = width // for sysData.preferredSize()
s.areaheight = height
C.gtk_widget_queue_draw(c)
ret <- struct{}{}
}
<-ret
} }
// TODO should this be made safe? (TODO move to area.go)
func (s *sysData) repaintAll() { func (s *sysData) repaintAll() {
ret := make(chan struct{}) c := gtkAreaGetControl(s.widget)
defer close(ret) C.gtk_widget_queue_draw(c)
uitask <- func() {
c := gtkAreaGetControl(s.widget)
C.gtk_widget_queue_draw(c)
ret <- struct{}{}
}
<-ret
} }
func (s *sysData) center() { func (s *sysData) center() {
ret := make(chan struct{}) if C.gtk_widget_get_visible(s.widget) == C.FALSE {
defer close(ret) // hint to the WM to make it centered when it is shown again
uitask <- func() { // thanks to Jasper in irc.gimp.net/#gtk+
if C.gtk_widget_get_visible(s.widget) == C.FALSE { C.gtk_window_set_position(togtkwindow(s.widget), C.GTK_WIN_POS_CENTER)
// hint to the WM to make it centered when it is shown again } else {
// thanks to Jasper in irc.gimp.net/#gtk+ var width, height C.gint
C.gtk_window_set_position(togtkwindow(s.widget), C.GTK_WIN_POS_CENTER)
} else {
var width, height C.gint
s.resetposition() 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+) //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_get_size(togtkwindow(s.widget), &width, &height)
C.gtk_window_move(togtkwindow(s.widget), C.gtk_window_move(togtkwindow(s.widget),
(C.gdk_screen_width() / 2) - (width / 2), (C.gdk_screen_width() / 2) - (width / 2),
(C.gdk_screen_height() / 2) - (width / 2)) (C.gdk_screen_height() / 2) - (width / 2))
}
ret <- struct{}{}
} }
<-ret
} }
func (s *sysData) setChecked(checked bool) { func (s *sysData) setChecked(checked bool) {
ret := make(chan struct{}) gtk_toggle_button_set_active(s.widget, checked)
defer close(ret)
uitask <- func() {
gtk_toggle_button_set_active(s.widget, checked)
ret <- struct{}{}
}
<-ret
} }

View File

@ -6,7 +6,6 @@ package ui
import ( import (
"fmt" "fmt"
"runtime"
) )
// #cgo pkg-config: gtk+-3.0 // #cgo pkg-config: gtk+-3.0
@ -15,19 +14,32 @@ import "C"
var uitask chan func() var uitask chan func()
func ui(main func()) error { func uiinit() error {
runtime.LockOSThread()
uitask = make(chan func())
err := gtk_init() err := gtk_init()
if err != nil { if err != nil {
return fmt.Errorf("gtk_init() failed: %v", err) 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 // thanks to tristan and Daniel_S in irc.gimp.net/#gtk
// see our_idle_callback in callbacks_unix.go for details // see our_idle_callback in callbacks_unix.go for details
go func() { 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{}) done := make(chan struct{})
gdk_threads_add_idle(&gtkIdleOp{ gdk_threads_add_idle(&gtkIdleOp{
what: f, what: f,
@ -38,11 +50,5 @@ func ui(main func()) error {
} }
}() }()
go func() {
main()
uitask <- gtk_main_quit
}()
C.gtk_main() C.gtk_main()
return nil
} }