Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows support (until external linking will be supported by go build) #149

Open
wants to merge 38 commits into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d594148
Update capi.cpp
neclepsio Mar 4, 2015
35ce016
Update idletimer.cpp
neclepsio Mar 4, 2015
0235b6a
Update README.md
neclepsio Mar 4, 2015
658f1f9
Update README.md
neclepsio Mar 4, 2015
1bc7e1c
Update README.md
neclepsio Mar 4, 2015
4b90968
Update README.md
neclepsio Mar 4, 2015
e299a47
Update capi.cpp
neclepsio Mar 4, 2015
377fcee
Update capi.cpp
neclepsio Mar 4, 2015
271f860
Update idletimer.cpp
neclepsio Mar 4, 2015
0a8c2e7
Detect use of Qt objects after destruction and other safety features
Feb 4, 2016
15fa509
Add engine.AddImportPath and ClearComponentCache
Feb 10, 2016
0fc3d34
Enable static building on Windows
Feb 10, 2016
60887d8
Fixed unsupported variant type: 1024 (QJSValue)
obscuren Jan 23, 2015
07befdf
Changed to non-hardcoded type for QJSValue
obscuren Jan 27, 2015
a5eeb50
Fix panic when passing around nil/null objects
ricochet1k Mar 12, 2016
b29d141
Add more Qt paths list manipulation functions
Mar 16, 2016
424704a
Don't panic in function calls with zero/invalid parameters
Mar 16, 2016
826359a
Porting go-qml to Go 1.6
SjB Apr 5, 2016
a159017
Merge pull request #2 from ricochet1k/safer
neclepsio Apr 5, 2016
724d0ea
Go 1.6 support (partial)
neclepsio Apr 5, 2016
25510fc
Merge branch 'refs/heads/SjB-go1.6-port' into v1
neclepsio Apr 5, 2016
9e7fbdc
Go 1.6 (final)
neclepsio Apr 5, 2016
b752760
Merge pull request #5 from neclepsio/SjB-go1.6-port
neclepsio Apr 5, 2016
3817cca
Merge branch 'v1' of https://github.com/tgerring/qml into tgerring-v1
neclepsio Apr 5, 2016
01021bc
change datatype the foldr from uintptr to C.GoRef
SjB Apr 6, 2016
2628b57
refactor valueFold.
SjB Apr 6, 2016
a4363f7
added reference lookup for signal function
SjB Apr 6, 2016
ea70551
added reference lookup for go TypeSpec type
SjB Apr 6, 2016
0309d2d
fix missing goRef function
SjB Apr 6, 2016
d3b5814
Merge pull request #8 from SjB/go1.6-port
neclepsio Apr 6, 2016
d4c4a72
fix forgotten dereferenced datap pointer.
SjB Jul 1, 2016
1cd7645
Merge pull request #10 from SjB/go1.6-port
neclepsio Jul 2, 2016
1a4a251
Added support for Go 1.12
neclepsio Feb 27, 2019
39c86d2
Add support for Qt 5.11+
neclepsio Feb 27, 2019
6f4b129
Changed import path (1/2)
neclepsio Feb 27, 2019
c923d26
Changed import path (2/2)
neclepsio Feb 27, 2019
dea943b
Update README.md
neclepsio Feb 27, 2019
4d1f36b
Update README.md
neclepsio Feb 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# QML support for the Go language

Notes for neclepsio/qml
-----------------------

This repository aims to keep go-qml/qml working. It includes several fixed and new features from other repositories
forked from go-qml, but some of them are just copy and pasted, so the original author for the commit is sometimes lost (sorry!).
The only fix I wrote are the support for Qt 5.11+ (trivial) and support for Go 1.12.


Documentation
-------------

Expand Down
112 changes: 75 additions & 37 deletions bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ import (
"sync/atomic"
"unsafe"

"gopkg.in/qml.v1/cdata"
"github.com/neclepsio/qml/cdata"
)

var (
guiFunc = make(chan func())
guiDone = make(chan struct{})
guiLock = 0
guiMainRef uintptr
guiPaintRef uintptr
guiIdleRun int32
guiFunc = make(chan func())
guiDone = make(chan struct{})
guiLock = 0
guiMainRef int64
guiPaintRef int64
guiIdleRun int32

initialized int32
)
Expand Down Expand Up @@ -70,7 +70,7 @@ func Run(f func() error) error {
// underlying QML logic.
func RunMain(f func()) {
ref := cdata.Ref()
if ref == guiMainRef || ref == atomic.LoadUintptr(&guiPaintRef) {
if ref == guiMainRef || ref == atomic.LoadInt64(&guiPaintRef) {
// Already within the GUI or render threads. Attempting to wait would deadlock.
f()
return
Expand Down Expand Up @@ -203,6 +203,35 @@ type valueFold struct {
owner valueOwner
}

// cgoFolds holds all fold values that get reference to on the cgo space.
// Since Go Pointer are not allowed to be held by cgo. We need a lookup
// table to interface the two space.
var cgoFolds = make(map[C.GoRef]*valueFold)

// GoRef index the valueFold value and return a lookup
// key (C.GoRef), so that we can retrieve the fold using
// the C.GoRef. need to cross between go and cgo
func (f *valueFold) goRef() C.GoRef {
ref := C.GoRef(uintptr(unsafe.Pointer(f)))
cgoFolds[ref] = f
return ref
}

// destroyRef remove the valueFold reference from the lookup table.
func (f *valueFold) destroyRef() {
ref := C.GoRef(uintptr(unsafe.Pointer(f)))
delete(cgoFolds, ref)
}

// getFoldFromGoRef return the valueFold value located at ref
func getFoldFromGoRef(ref C.GoRef) *valueFold {
fold := cgoFolds[ref]
if fold == nil {
panic("cannot find fold go reference")
}
return fold
}

type valueOwner uint8

const (
Expand All @@ -228,7 +257,7 @@ func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue u
panic("cannot hand pointer of pointer to QML logic; use a simple pointer instead")
}

painting := cdata.Ref() == atomic.LoadUintptr(&guiPaintRef)
painting := cdata.Ref() == atomic.LoadInt64(&guiPaintRef)

// Cannot reuse a jsOwner because the QML runtime may choose to destroy
// the value _after_ we hand it a new reference to the same value.
Expand All @@ -251,7 +280,7 @@ func wrapGoValue(engine *Engine, gvalue interface{}, owner valueOwner) (cvalue u
gvalue: gvalue,
owner: owner,
}
fold.cvalue = C.newGoValue(unsafe.Pointer(fold), typeInfo(gvalue), parent)
fold.cvalue = C.newGoValue(fold.goRef(), typeInfo(gvalue), parent)
if prev != nil {
// Put new fold first so the single cppOwner, if any, is always the first entry.
fold.next = prev
Expand Down Expand Up @@ -289,29 +318,37 @@ func addrOf(gvalue interface{}) uintptr {
var typeNew = make(map[*valueFold]bool)

//export hookGoValueTypeNew
func hookGoValueTypeNew(cvalue unsafe.Pointer, specp unsafe.Pointer) (foldp unsafe.Pointer) {
func hookGoValueTypeNew(cvalue unsafe.Pointer, specr C.GoTypeSpec_) (foldr C.GoRef) {
// Initialization is postponed until the engine is available, so that
// we can hand Init the qml.Object that represents the object.
init := reflect.ValueOf((*TypeSpec)(specp).Init)
spec := types[specr]
if spec == nil {
panic("cannot find the specified TypeSpec")
}

init := reflect.ValueOf(spec.Init)
fold := &valueFold{
init: init,
gvalue: reflect.New(init.Type().In(0).Elem()).Interface(),
cvalue: cvalue,
owner: jsOwner,
}

typeNew[fold] = true
//fmt.Printf("[DEBUG] value alive (type-created): cvalue=%x gvalue=%x/%#v\n", fold.cvalue, addrOf(fold.gvalue), fold.gvalue)
stats.valuesAlive(+1)
return unsafe.Pointer(fold)
return fold.goRef()
}

//export hookGoValueDestroyed
func hookGoValueDestroyed(enginep unsafe.Pointer, foldp unsafe.Pointer) {
fold := (*valueFold)(foldp)
func hookGoValueDestroyed(enginep unsafe.Pointer, foldr C.GoRef) {
fold := getFoldFromGoRef(foldr)

engine := fold.engine
if engine == nil {
before := len(typeNew)
delete(typeNew, fold)
fold.destroyRef()
if len(typeNew) == before {
panic("destroying value without an associated engine; who created the value?")
}
Expand Down Expand Up @@ -342,6 +379,7 @@ func hookGoValueDestroyed(enginep unsafe.Pointer, foldp unsafe.Pointer) {
delete(engines, engine.addr)
}
}
fold.destroyRef()
}
//fmt.Printf("[DEBUG] value destroyed: cvalue=%x gvalue=%x/%#v\n", fold.cvalue, addrOf(fold.gvalue), fold.gvalue)
stats.valuesAlive(-1)
Expand All @@ -360,8 +398,8 @@ func deref(value reflect.Value) reflect.Value {
}

//export hookGoValueReadField
func hookGoValueReadField(enginep, foldp unsafe.Pointer, reflectIndex, getIndex, setIndex C.int, resultdv *C.DataValue) {
fold := ensureEngine(enginep, foldp)
func hookGoValueReadField(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex, getIndex, setIndex C.int, resultdv *C.DataValue) {
fold := ensureEngine(enginep, foldr)

var field reflect.Value
if getIndex >= 0 {
Expand All @@ -376,7 +414,7 @@ func hookGoValueReadField(enginep, foldp unsafe.Pointer, reflectIndex, getIndex,
// TODO Handle getters that return []qml.Object.
// TODO Handle other GoValue slices (!= []qml.Object).
resultdv.dataType = C.DTListProperty
*(*unsafe.Pointer)(unsafe.Pointer(&resultdv.data)) = C.newListProperty(foldp, C.intptr_t(reflectIndex), C.intptr_t(setIndex))
*(*unsafe.Pointer)(unsafe.Pointer(&resultdv.data)) = C.newListProperty(C.GoRef(foldr), C.intptr_t(reflectIndex), C.intptr_t(setIndex))
return
}

Expand Down Expand Up @@ -406,8 +444,8 @@ func hookGoValueReadField(enginep, foldp unsafe.Pointer, reflectIndex, getIndex,
}

//export hookGoValueWriteField
func hookGoValueWriteField(enginep, foldp unsafe.Pointer, reflectIndex, setIndex C.int, assigndv *C.DataValue) {
fold := ensureEngine(enginep, foldp)
func hookGoValueWriteField(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex, setIndex C.int, assigndv *C.DataValue) {
fold := ensureEngine(enginep, foldr)
v := reflect.ValueOf(fold.gvalue)
ve := v
for ve.Type().Kind() == reflect.Ptr {
Expand Down Expand Up @@ -483,8 +521,8 @@ var (
)

//export hookGoValueCallMethod
func hookGoValueCallMethod(enginep, foldp unsafe.Pointer, reflectIndex C.int, args *C.DataValue) {
fold := ensureEngine(enginep, foldp)
func hookGoValueCallMethod(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex C.int, args *C.DataValue) {
fold := ensureEngine(enginep, foldr)
v := reflect.ValueOf(fold.gvalue)

// TODO Must assert that v is necessarily a pointer here, but we shouldn't have to manipulate
Expand Down Expand Up @@ -548,16 +586,16 @@ func printPaintPanic() {
}

//export hookGoValuePaint
func hookGoValuePaint(enginep, foldp unsafe.Pointer, reflectIndex C.intptr_t) {
func hookGoValuePaint(enginep unsafe.Pointer, foldr C.GoRef, reflectIndex C.intptr_t) {
// Besides a convenience this is a workaround for http://golang.org/issue/8588
defer printPaintPanic()
defer atomic.StoreUintptr(&guiPaintRef, 0)
defer atomic.StoreInt64(&guiPaintRef, 0)

// The main GUI thread is mutex-locked while paint methods are called,
// so no two paintings should be happening at the same time.
atomic.StoreUintptr(&guiPaintRef, cdata.Ref())
atomic.StoreInt64(&guiPaintRef, cdata.Ref())

fold := ensureEngine(enginep, foldp)
fold := ensureEngine(enginep, foldr)
if fold.init.IsValid() {
return
}
Expand All @@ -568,8 +606,8 @@ func hookGoValuePaint(enginep, foldp unsafe.Pointer, reflectIndex C.intptr_t) {
method.Call([]reflect.Value{reflect.ValueOf(painter)})
}

func ensureEngine(enginep, foldp unsafe.Pointer) *valueFold {
fold := (*valueFold)(foldp)
func ensureEngine(enginep unsafe.Pointer, foldr C.GoRef) *valueFold {
fold := getFoldFromGoRef(foldr)
if fold.engine != nil {
if fold.init.IsValid() {
initGoType(fold)
Expand Down Expand Up @@ -605,7 +643,7 @@ func ensureEngine(enginep, foldp unsafe.Pointer) *valueFold {
}

func initGoType(fold *valueFold) {
if cdata.Ref() == atomic.LoadUintptr(&guiPaintRef) {
if cdata.Ref() == atomic.LoadInt64(&guiPaintRef) {
go RunMain(func() { _initGoType(fold, true) })
} else {
_initGoType(fold, false)
Expand Down Expand Up @@ -637,22 +675,22 @@ func listSlice(fold *valueFold, reflectIndex C.intptr_t) *[]Object {
}

//export hookListPropertyAt
func hookListPropertyAt(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t, index C.int) (objp unsafe.Pointer) {
fold := (*valueFold)(foldp)
func hookListPropertyAt(foldr C.GoRef, reflectIndex, setIndex C.intptr_t, index C.int) (objp unsafe.Pointer) {
fold := getFoldFromGoRef(foldr)
slice := listSlice(fold, reflectIndex)
return (*slice)[int(index)].Common().addr
}

//export hookListPropertyCount
func hookListPropertyCount(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t) C.int {
fold := (*valueFold)(foldp)
func hookListPropertyCount(foldr C.GoRef, reflectIndex, setIndex C.intptr_t) C.int {
fold := getFoldFromGoRef(foldr)
slice := listSlice(fold, reflectIndex)
return C.int(len(*slice))
}

//export hookListPropertyAppend
func hookListPropertyAppend(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t, objp unsafe.Pointer) {
fold := (*valueFold)(foldp)
func hookListPropertyAppend(foldr C.GoRef, reflectIndex, setIndex C.intptr_t, objp unsafe.Pointer) {
fold := getFoldFromGoRef(foldr)
slice := listSlice(fold, reflectIndex)
var objdv C.DataValue
objdv.dataType = C.DTObject
Expand All @@ -666,8 +704,8 @@ func hookListPropertyAppend(foldp unsafe.Pointer, reflectIndex, setIndex C.intpt
}

//export hookListPropertyClear
func hookListPropertyClear(foldp unsafe.Pointer, reflectIndex, setIndex C.intptr_t) {
fold := (*valueFold)(foldp)
func hookListPropertyClear(foldr C.GoRef, reflectIndex, setIndex C.intptr_t) {
fold := getFoldFromGoRef(foldr)
slice := listSlice(fold, reflectIndex)
newslice := (*slice)[0:0]
if setIndex >= 0 {
Expand Down
2 changes: 1 addition & 1 deletion cdata/cdata.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Package cdata supports the implementation of the qml package.
package cdata

func Ref() uintptr
//func Ref() uintptr

func Addrs() (uintptr, uintptr)
2 changes: 2 additions & 0 deletions cdata/cdata14_386.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

#include "textflag.h"

/*
TEXT ·Ref(SB),NOSPLIT,$4-4
CALL runtime·acquirem(SB)
MOVL 0(SP), AX
MOVL AX, ret+0(FP)
CALL runtime·releasem(SB)
RET
*/

TEXT ·Addrs(SB),NOSPLIT,$0-8
MOVL $runtime·main(SB), AX
Expand Down
2 changes: 2 additions & 0 deletions cdata/cdata14_amd64.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

#include "textflag.h"

/*
TEXT ·Ref(SB),NOSPLIT,$8-8
CALL runtime·acquirem(SB)
MOVQ 0(SP), AX
MOVQ AX, ret+0(FP)
CALL runtime·releasem(SB)
RET
*/

TEXT ·Addrs(SB),NOSPLIT,$0-16
MOVQ $runtime·main(SB), AX
Expand Down
2 changes: 2 additions & 0 deletions cdata/cdata14_arm.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

#include "textflag.h"

/*
TEXT ·Ref(SB),NOSPLIT,$4-4
BL runtime·acquirem(SB)
MOVW 4(R13), R0
MOVW R0, ret+0(FP)
MOVW R0, 4(R13)
BL runtime·releasem(SB)
RET
*/

TEXT ·Addrs(SB),NOSPLIT,$0-8
MOVW $runtime·main(SB), R0
Expand Down
7 changes: 7 additions & 0 deletions cdata/cdata_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cdata

import "syscall"

func Ref() int64 {
return int64(syscall.Gettid())
}
4 changes: 2 additions & 2 deletions cdata/cdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

type refPair struct {
ref1, ref2 uintptr
ref1, ref2 int64
}

func TestRef(t *testing.T) {
Expand All @@ -28,7 +28,7 @@ func TestRef(t *testing.T) {
}()
}
wg.Wait()
refs := make(map[uintptr]bool)
refs := make(map[uint32]bool)
for i := 0; i < N; i++ {
pair := <-ch
if pair.ref1 != pair.ref2 {
Expand Down
9 changes: 9 additions & 0 deletions cdata/cdata_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cdata

import (
"golang.org/x/sys/windows"
)

func Ref() int64 {
return int64(windows.GetCurrentThreadId())
}
6 changes: 3 additions & 3 deletions cmd/genqrc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import (
"path/filepath"
"text/template"

"gopkg.in/qml.v1"
"github.com/neclepsio/qml"
)

const doc = `
Expand Down Expand Up @@ -159,14 +159,14 @@ func buildTemplate(name, content string) *template.Template {

var tmpl = buildTemplate("qrc.go", `package {{.PackageName}}

// This file is automatically generated by gopkg.in/qml.v1/cmd/genqrc
// This file is automatically generated by github.com/neclepsio/qml/cmd/genqrc

import (
"io/ioutil"
"os"
"path/filepath"

"gopkg.in/qml.v1"
"github.com/neclepsio/qml"
)

func init() {
Expand Down
Loading