Added the beginning of the Unix (GTK+) implementation.

This commit is contained in:
Pietro Gagliardi 2014-02-16 15:55:51 -05:00
parent 3a99ee6569
commit 21c1ec6f83
4 changed files with 325 additions and 0 deletions

116
gtkcalls_unix.go Normal file
View File

@ -0,0 +1,116 @@
// +build !windows,!darwin,!plan9
// TODO is there a way to simplify the above? :/
// 16 february 2014
package main
import (
"fmt"
"unsafe"
"reflect"
)
// #cgo pkg-config: gtk+-3.0
// #include <stdlib.h>
// #include <gtk/gtk.h>
// /* because cgo is flaky with macros */
// static inline void gSignalConnect(GtkWidget *widget, char *signal, GCallback callback, void *data) { g_signal_connect(widget, signal, callback, data); }
// /* so we can call uistep */
// extern gboolean our_thread_callback(gpointer);
import "C"
type (
gtkWidget C.GtkWidget
)
func fromgbool(b C.gboolean) bool {
return b != C.FALSE
}
func togbool(b bool) C.gboolean {
if b {
return C.TRUE
}
return C.FALSE
}
//export our_thread_callback
func our_thread_callback(C.gpointer) C.gboolean {
uistep()
return C.TRUE
}
func gtk_init() bool {
// TODO allow GTK+ standard command-line argument processing
b := fromgbool(C.gtk_init_check((*C.int)(nil), (***C.char)(nil)))
if !b {
return false
}
// thanks to tristan in irc.gimp.net/#gtk
C.gdk_threads_add_idle(C.GSourceFunc(C.our_thread_callback), C.gpointer(unsafe.Pointer(nil)))
return true
}
func gtk_main() {
C.gtk_main()
}
func gtk_window_new() *gtkWidget {
// 0 == GTK_WINDOW_TOPLEVEL (the only other type, _POPUP, should not be used)
return (*gtkWidget)(unsafe.Pointer(C.gtk_window_new(0)))
}
// because *gtkWidget and *C.GtkWidget are not compatible
func gtkwidget(g *gtkWidget) (*C.GtkWidget) {
return (*C.GtkWidget)(unsafe.Pointer(g))
}
// TODO do we need the argument?
// TODO fine-tune the function type
func g_signal_connect(obj *gtkWidget, sig string, callback interface{}) {
v := reflect.ValueOf(callback)
if v.Kind() != reflect.Func {
panic(fmt.Sprintf("UI library internal error: callback %v given to g_signal_connect not a function", v))
}
ccallback := C.GCallback(unsafe.Pointer(v.Pointer()))
csig := C.CString(sig)
defer C.free(unsafe.Pointer(csig))
C.gSignalConnect(gtkwidget(obj), csig, ccallback, unsafe.Pointer(nil))
}
// TODO ensure this works if called on an individual control
func gtk_widget_show(widget *gtkWidget) {
C.gtk_widget_show_all(gtkwidget(widget))
}
func gtk_widget_hide(widget *gtkWidget) {
C.gtk_widget_hide(gtkwidget(widget))
}
func gtk_window_set_title(window *gtkWidget, title string) {
ctitle := C.CString(title)
defer C.free(unsafe.Pointer(ctitle))
C.gtk_window_set_title((*C.GtkWindow)(unsafe.Pointer(window)),
(*C.gchar)(unsafe.Pointer(ctitle)))
}
func gtk_window_resize(window *gtkWidget, width int, height int) {
C.gtk_window_resize((*C.GtkWindow)(unsafe.Pointer(window)), C.gint(width), C.gint(height))
}
func gtk_fixed_new() *gtkWidget {
return (*gtkWidget)(unsafe.Pointer(C.gtk_fixed_new()))
}
func gtk_container_add(container *gtkWidget, widget *gtkWidget) {
C.gtk_container_add((*C.GtkContainer)(unsafe.Pointer(container)), gtkwidget(widget))
}
func gtk_fixed_put(container *gtkWidget, widget *gtkWidget, x int, y int) {
C.gtk_fixed_put((*C.GtkFixed)(unsafe.Pointer(container)), gtkwidget(widget),
C.gint(x), C.gint(y))
}
func gtk_widget_set_size_request(widget *gtkWidget, width int, height int) {
C.gtk_widget_set_size_request(gtkwidget(widget), C.gint(width), C.gint(height))
}

168
sysdata_unix.go Normal file
View File

@ -0,0 +1,168 @@
// +build !windows,!darwin,!plan9
// 16 february 2014
//package ui
package main
import (
"fmt"
)
type sysData struct {
cSysData
widget *gtkWidget
container *gtkWidget // for moving
}
type classData struct {
make func() *gtkWidget
setText func(widget *gtkWidget, text string)
}
var classTypes = [nctypes]*classData{
c_window: &classData{
make: gtk_window_new,
setText: gtk_window_set_title,
},
c_button: &classData{
// make: gtk_button_new,
},
c_checkbox: &classData{
},
c_combobox: &classData{
},
c_lineedit: &classData{
},
c_label: &classData{
},
c_listbox: &classData{
},
}
func (s *sysData) make(initText string, window *sysData) error {
ct := classTypes[s.ctype]
if ct.make == nil { // not yet implemented
println(s.ctype, "not implemented")
return nil
}
ret := make(chan *gtkWidget)
defer close(ret)
uitask <- func() {
ret <- ct.make()
}
s.widget = <-ret
println(s.widget)
if window == nil {
uitask <- func() {
fixed := gtk_fixed_new()
gtk_container_add(s.widget, fixed)
ret <- fixed
}
s.container = <-ret
} else {
s.container = window.container
}
err := s.setText(initText)
if err != nil {
return fmt.Errorf("error setting initial text of new window/control: %v", err)
}
return nil
}
func (s *sysData) show() error {
ret := make(chan struct{})
defer close(ret)
uitask <- func() {
gtk_widget_show(s.widget)
ret <- struct{}{}
}
<-ret
return nil
}
func (s *sysData) hide() error {
ret := make(chan struct{})
defer close(ret)
uitask <- func() {
gtk_widget_hide(s.widget)
ret <- struct{}{}
}
<-ret
return nil
}
func (s *sysData) setText(text string) error {
if classTypes[s.ctype] == nil || classTypes[s.ctype].setText == nil { return nil }
ret := make(chan struct{})
defer close(ret)
uitask <- func() {
classTypes[s.ctype].setText(s.widget, text)
ret <- struct{}{}
}
<-ret
return nil
}
func (s *sysData) setRect(x int, y int, width int, height int) error {
ret := make(chan struct{})
defer close(ret)
uitask <- func() {
gtk_fixed_put(s.container, s.widget, x, y)
gtk_widget_set_size_request(s.widget, width, height)
ret <- struct{}{}
}
<-ret
return nil
}
func (s *sysData) isChecked() bool {
// TODO
return false
}
func (s *sysData) text() string {
// TODO
return ""
}
func (s *sysData) append(what string) error {
// TODO
return nil
}
func (s *sysData) insertBefore(what string, before int) error {
// TODO
return nil
}
func (s *sysData) selectedIndex() int {
// TODO
return -1
}
func (s *sysData) selectedIndices() []int {
// TODO
return nil
}
func (s *sysData) selectedTexts() []string {
// TODO
return nil
}
func (s *sysData) setWindowSize(width int, height int) error {
ret := make(chan struct{})
defer close(ret)
uitask <- func() {
gtk_window_resize(s.widget, width, height)
ret <- struct{}{}
}
<-ret
return nil
}
func (s *sysData) delete(index int) error {
// TODO
return nil
}

View File

@ -19,6 +19,10 @@ so I don't forget:
- Listbox.SelectAll
- have Combobox.InsertBefore, Listbox.InsertBefore, Combobox.Delete, and Listbox.Delete return an error on invalid index before creation
important things:
- the C.gdk_threads_add_idle logic is messy and will break when I implement actual connections...
- there's no GTK+ error handling whatsoever; we need to figure out how it works
super ultra important things:
- the windows build appears to be unstable:
- 64-bit doesn't work, period: it crashes in malloc in wine with heap corruption warnings aplenty during DLL loading; in windows 7 CreateWindowExW complains about an unregistered window class, yet the RegisterClassW appears to have succeeded and examining the stack in WinDbg indicates the correct class name is being sent (see below)

37
uitask_unix.go Normal file
View File

@ -0,0 +1,37 @@
// +build !windows,!darwin,!plan9
// 16 february 2014
//package ui
package main
import (
"fmt"
"runtime"
)
var uitask chan func()
func ui(initDone chan error) {
runtime.LockOSThread()
uitask = make(chan func())
if gtk_init() != true {
initDone <- fmt.Errorf("gtk_init failed (reason unknown; TODO)")
return
}
initDone <- nil
gtk_main()
}
func uistep() {
select {
case f := <-uitask:
f()
default: // do not block
}
}
// temporary
func MsgBox(string, string, ...interface{}) {}
func MsgBoxError(title string, text string, args ...interface{}) {panic(title+"\n"+fmt.Sprintf(text,args...))}