Removed uitask and made the Window creation functions only callable from the main thread. This si the first part in the real major change, which bans all concurrent use of the API and provides a Post() function for communication. I don't like this, but it's the only way. Untested.
This commit is contained in:
parent
8a81650b3d
commit
9eb9aa82c0
|
@ -4,7 +4,6 @@ package ui
|
|||
|
||||
/*
|
||||
This creates a class goAppDelegate that will be used as the delegate for /everything/. Specifically, it:
|
||||
- runs uitask requests (uitask:)
|
||||
- handles window close events (windowShouldClose:)
|
||||
- handles window resize events (windowDidResize:)
|
||||
- handles button click events (buttonClicked:)
|
||||
|
|
|
@ -56,15 +56,6 @@ extern NSRect dummyRect;
|
|||
|
||||
@implementation appDelegate
|
||||
|
||||
// these are the uitask actions
|
||||
|
||||
- (void)createWindow:(NSValue *)fp
|
||||
{
|
||||
uitask_createWindow([fp pointerValue]);
|
||||
}
|
||||
|
||||
// these are the other delegate functions
|
||||
|
||||
- (BOOL)windowShouldClose:(id)win
|
||||
{
|
||||
return appDelegate_windowShouldClose(win);
|
||||
|
@ -115,9 +106,6 @@ id windowGetContentView(id window)
|
|||
return [((NSWindow *) window) contentView];
|
||||
}
|
||||
|
||||
// these are for douitask() but are here because @selector() is not a constant expression
|
||||
SEL createWindow;
|
||||
|
||||
BOOL initCocoa(id appDelegate)
|
||||
{
|
||||
// on 10.6 the -[NSApplication setDelegate:] method complains if we don't have one
|
||||
|
@ -131,26 +119,10 @@ BOOL initCocoa(id appDelegate)
|
|||
return NO;
|
||||
[NSApp activateIgnoringOtherApps:YES]; // TODO actually do C.NO here? Russ Cox does YES in his devdraw; the docs say the Finder does NO
|
||||
[NSApp setDelegate:appDelegate];
|
||||
// uitask selectors
|
||||
createWindow = @selector(createWindow:);
|
||||
[pool release];
|
||||
return YES;
|
||||
}
|
||||
|
||||
void douitask(id appDelegate, SEL sel, void *p)
|
||||
{
|
||||
NSAutoreleasePool *pool;
|
||||
NSValue *fp;
|
||||
|
||||
// we need to make an NSAutoreleasePool, otherwise we get leak warnings on stderr
|
||||
pool = [NSAutoreleasePool new];
|
||||
fp = [NSValue valueWithPointer:p];
|
||||
[appDelegate performSelectorOnMainThread:sel
|
||||
withObject:fp
|
||||
waitUntilDone:YES]; // wait so we can properly drain the autorelease pool; on other platforms we wind up waiting anyway (since the main thread can only handle one thing at a time) so
|
||||
[pool release];
|
||||
}
|
||||
|
||||
void breakMainLoop(void)
|
||||
{
|
||||
NSEvent *e;
|
||||
|
|
|
@ -63,8 +63,6 @@ extern uintptr_t keyCode(id);
|
|||
extern id makeAppDelegate(void);
|
||||
extern id windowGetContentView(id);
|
||||
extern BOOL initCocoa(id);
|
||||
extern SEL createWindow;
|
||||
extern void douitask(id, SEL, void *);
|
||||
extern void breakMainLoop(void);
|
||||
extern void cocoaMainLoop(void);
|
||||
|
||||
|
|
16
uitask.go
16
uitask.go
|
@ -27,23 +27,7 @@ func Go() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Ready is pulsed when Go() is ready to begin accepting requests to the safe methods.
|
||||
// Go() will wait for something to receive on Ready, then Ready will be closed.
|
||||
var Ready = make(chan struct{})
|
||||
|
||||
// Stop should be pulsed when you are ready for Go() to return.
|
||||
// Pulsing Stop will cause Go() to return immediately; the programmer is responsible for cleaning up (for instance, hiding open Windows) beforehand.
|
||||
// Do not pulse Stop more than once.
|
||||
var Stop = make(chan struct{})
|
||||
|
||||
// uitask is an object of a type implemented by each uitask_***.go that does everything that needs to be communicated to the main thread.
|
||||
type _uitask struct{}
|
||||
var uitask = _uitask{}
|
||||
|
||||
// and the required methods are:
|
||||
var xuitask interface {
|
||||
// creates a window
|
||||
// TODO whether this waits for the window creation to finish is implementation defined?
|
||||
createWindow(*Window, Control, bool)
|
||||
} = uitask
|
||||
// compilation will fail if uitask doesn't have all these methods
|
||||
|
|
|
@ -13,29 +13,6 @@ import (
|
|||
// #include "objc_darwin.h"
|
||||
import "C"
|
||||
|
||||
// the performSelectorOnMainThread: in our uitask functions is told to wait until the action is done before it returns
|
||||
// so we're fine keeping this on the Go side since the GC won't collect it from under us
|
||||
type uitaskParams struct {
|
||||
window *Window // createWindow
|
||||
control Control // createWindow
|
||||
show bool // createWindow
|
||||
}
|
||||
|
||||
//export uitask_createWindow
|
||||
func uitask_createWindow(data unsafe.Pointer) {
|
||||
uc := (*uitaskParams)(data)
|
||||
uc.window.create(uc.control, uc.show)
|
||||
}
|
||||
|
||||
func (_uitask) createWindow(w *Window, c Control, s bool) {
|
||||
uc := &uitaskParams{
|
||||
window: w,
|
||||
control: c,
|
||||
show: s,
|
||||
}
|
||||
C.douitask(appDelegate, C.createWindow, unsafe.Pointer(uc))
|
||||
}
|
||||
|
||||
func uiinit() error {
|
||||
err := initCocoa()
|
||||
if err != nil {
|
||||
|
@ -63,9 +40,3 @@ func initCocoa() (err error) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//export appDelegate_uitask
|
||||
func appDelegate_uitask(p unsafe.Pointer) {
|
||||
f := (*func())(unsafe.Pointer(p))
|
||||
(*f)()
|
||||
}
|
||||
|
|
|
@ -11,23 +11,6 @@ import (
|
|||
|
||||
// #cgo pkg-config: gtk+-3.0
|
||||
// #include "gtk_unix.h"
|
||||
// /* unfortunately, there's no way to differentiate between the main thread and other threads; in fact, doing what we do on other platforms is discouraged by the GTK+ devs!
|
||||
// but I can't avoid this any other way... so we have structures defined on the C side to skirt the garbage collector */
|
||||
// struct uitaskParams {
|
||||
// void *window; /* createWindow */
|
||||
// void *control; /* createWindow */
|
||||
// gboolean show; /* createWindow */
|
||||
// };
|
||||
// static struct uitaskParams *mkParams(void)
|
||||
// {
|
||||
// /* g_malloc0() will abort on not enough memory */
|
||||
// return (struct uitaskParams *) g_malloc0(sizeof (struct uitaskParams));
|
||||
// }
|
||||
// static void freeParams(struct uitaskParams *p)
|
||||
// {
|
||||
// g_free(p);
|
||||
// }
|
||||
// extern gboolean our_createWindow_callback(gpointer);
|
||||
// /* this is called when we're done */
|
||||
// static inline gboolean our_quit_callback(gpointer data)
|
||||
// {
|
||||
|
@ -41,29 +24,6 @@ import (
|
|||
// }
|
||||
import "C"
|
||||
|
||||
//export our_createWindow_callback
|
||||
func our_createWindow_callback(what C.gpointer) C.gboolean {
|
||||
uc := (*C.struct_uitaskParams)(unsafe.Pointer(what))
|
||||
w := (*Window)(unsafe.Pointer(uc.window))
|
||||
c := *(*Control)(unsafe.Pointer(uc.control))
|
||||
s := fromgbool(uc.show)
|
||||
w.create(c, s)
|
||||
C.freeParams(uc)
|
||||
return C.FALSE // remove this idle function; we're finished
|
||||
}
|
||||
|
||||
func (_uitask) createWindow(w *Window, c Control, s bool) {
|
||||
uc := C.mkParams()
|
||||
uc.window = unsafe.Pointer(w)
|
||||
uc.control = unsafe.Pointer(&c)
|
||||
uc.show = togbool(s)
|
||||
gdk_threads_add_idle(C.our_createWindow_callback, unsafe.Pointer(uc))
|
||||
}
|
||||
|
||||
func gdk_threads_add_idle(f unsafe.Pointer, what unsafe.Pointer) {
|
||||
C.gdk_threads_add_idle(C.GCallback(f), C.gpointer(what))
|
||||
}
|
||||
|
||||
func uiinit() error {
|
||||
err := gtk_init()
|
||||
if err != nil {
|
||||
|
|
|
@ -29,32 +29,8 @@ const (
|
|||
msgQuit = _WM_APP + iota + 1 // + 1 just to be safe
|
||||
msgSetAreaSize
|
||||
msgRepaintAll
|
||||
msgCreateWindow
|
||||
)
|
||||
|
||||
type uitaskParams struct {
|
||||
window *Window // createWindow
|
||||
control Control // createWindow
|
||||
show bool // createWindow
|
||||
}
|
||||
|
||||
// SendMessage() won't return unti lthe deed is done, even if the deed is on another thread
|
||||
// SendMessage() does a thread switch if necessary
|
||||
// this also means we don't have to worry about the uitaskParams object being garbage collected
|
||||
|
||||
func (_uitask) createWindow(w *Window, c Control, s bool) {
|
||||
uc := &uitaskParams{
|
||||
window: w,
|
||||
control: c,
|
||||
show: s,
|
||||
}
|
||||
_sendMessage.Call(
|
||||
uintptr(msghwnd),
|
||||
msgCreateWindow,
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(uc)))
|
||||
}
|
||||
|
||||
func uiinit() error {
|
||||
err := doWindowsInit()
|
||||
if err != nil {
|
||||
|
@ -182,10 +158,6 @@ func messageHandlerWndProc(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPAR
|
|||
// does not return a value according to MSDN
|
||||
_postQuitMessage.Call(0)
|
||||
return 0
|
||||
case msgCreateWindow:
|
||||
uc := (*uitaskParams)(unsafe.Pointer(lParam))
|
||||
uc.window.create(uc.control, uc.show)
|
||||
return 0
|
||||
}
|
||||
return defWindowProc(hwnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
|
10
window.go
10
window.go
|
@ -71,15 +71,12 @@ func (w *Window) SetSpaced(spaced bool) {
|
|||
|
||||
// Open creates the Window with Create and then shows the Window with Show. As with Create, you cannot call Open more than once per window.
|
||||
func (w *Window) Open(control Control) {
|
||||
uitask.createWindow(w, control, true)
|
||||
w.Create(control)
|
||||
w.Show()
|
||||
}
|
||||
|
||||
// Create creates the Window, setting its control to the given control. It does not show the window. This can only be called once per window, and finalizes all initialization of the control.
|
||||
func (w *Window) Create(control Control) {
|
||||
uitask.createWindow(w, control, false)
|
||||
}
|
||||
|
||||
func (w *Window) create(control Control, show bool) {
|
||||
if w.created {
|
||||
panic("window already open")
|
||||
}
|
||||
|
@ -107,9 +104,6 @@ func (w *Window) create(control Control, show bool) {
|
|||
}
|
||||
w.sysData.setText(w.initTitle)
|
||||
w.created = true
|
||||
if show {
|
||||
w.Show()
|
||||
}
|
||||
}
|
||||
|
||||
// Show shows the window.
|
||||
|
|
Loading…
Reference in New Issue