2 Commits 4c940ad261 ... b617cb8ef2

Author SHA1 Message Date
  mizusato b617cb8ef2 qt connect 3 years ago
  mizusato 4a08fc72a0 fix 3 years ago

+ 10 - 0
qt/c.go

@@ -0,0 +1,10 @@
+package main
+
+/*
+#include <stdlib.h>
+void CgoInvokeCallback(size_t id) {
+	void invoke_callback(size_t);
+	invoke_callback(id);
+}
+*/
+import "C"

+ 70 - 0
qt/cgohelper/callback.go

@@ -0,0 +1,70 @@
+package cgohelper
+
+import (
+	"sync"
+	"fmt"
+)
+
+
+var __GlobalCallbackRegistry = CreateCallbackRegistry()
+
+func RegisterCallback(cb func()) uint {
+	return __GlobalCallbackRegistry.Register(cb)
+}
+
+func UnregisterCallback(id uint) bool {
+	return __GlobalCallbackRegistry.Unregister(id)
+}
+
+func GetCallback(id uint) func() {
+	return __GlobalCallbackRegistry.Get(id)
+}
+
+type CallbackRegistry struct {
+	mutex      sync.Mutex
+	callbacks  map[uint] func()
+	next       uint
+	available  [] uint
+}
+
+func CreateCallbackRegistry() *CallbackRegistry {
+	return &CallbackRegistry {
+		callbacks: make(map[uint]func()),
+		next:      0,
+		available: make([] uint, 0),
+	}
+}
+
+func (reg *CallbackRegistry) Register(callback func()) uint {
+	reg.mutex.Lock()
+	defer reg.mutex.Unlock()
+	var id = reg.next
+	reg.next = (1 + id)
+	reg.callbacks[id] = callback
+	return id
+}
+
+func (reg *CallbackRegistry) Unregister(id uint) bool {
+	reg.mutex.Lock()
+	defer reg.mutex.Unlock()
+	var _, exists = reg.callbacks[id]
+	if exists {
+		delete(reg.callbacks, id)
+		reg.available = append(reg.available, id)
+		return true
+	} else {
+		return false
+	}
+}
+
+func (reg *CallbackRegistry) Get(id uint) func()  {
+	reg.mutex.Lock()
+	defer reg.mutex.Unlock()
+	var callback, exists = reg.callbacks[id]
+	if exists {
+		return callback
+	} else {
+		panic(fmt.Sprintf("cannot find callback of id %d", id))
+	}
+}
+

+ 24 - 0
qt/cgohelper/string.go

@@ -0,0 +1,24 @@
+package cgohelper
+
+/*
+#include <stdlib.h>
+*/
+import "C"
+import "unsafe"
+
+
+func CreateStringAllocator() (func(string) unsafe.Pointer, func()) {
+	var allocated = make([] *C.char, 0)
+	var allocate = func(str string) unsafe.Pointer {
+		var ptr = C.CString(str)
+		allocated = append(allocated, ptr)
+		return unsafe.Pointer(ptr)
+	}
+	var deallocate = func() {
+		for _, ptr := range allocated {
+			C.free(unsafe.Pointer(ptr))
+		}
+	}
+	return allocate, deallocate
+}
+

+ 41 - 14
qt/example.go

@@ -10,7 +10,11 @@ import "C"
 import (
     "io/ioutil"
     "runtime"
+    "reflect"
     "unsafe"
+    "fmt"
+    "math/rand"
+    "kumachan/qt/cgohelper"
 )
 
 
@@ -18,27 +22,50 @@ func main() {
     runtime.LockOSThread()
     C.QtInit()
     (func() {
+        var new_str, del_all_str = str_alloc()
+        defer del_all_str()
         var ui_bytes, err = ioutil.ReadFile("example.ui")
         if err != nil { panic(err) }
         var ui_str = string(ui_bytes)
-        var ui_c_str *C.char = C.CString(ui_str)
-        defer C.free(unsafe.Pointer(ui_c_str))
-        var w = C.QtLoadWidget(ui_c_str)
+        var w = C.QtLoadWidget(new_str(ui_str))
         if w == nil { panic("failed to load widget") }
         C.QtWidgetShow(w)
-        var label_name *C.char = C.CString("label")
-        defer C.free(unsafe.Pointer(label_name))
-        var label = C.QtWidgetFindChild(w, label_name)
+        var label = C.QtWidgetFindChild(w, new_str("label"))
         if label == nil { panic("unable to find label") }
-        var prop_text *C.char = C.CString("text")
-        defer C.free(unsafe.Pointer(prop_text))
-        var buf [100]rune
-        var text_content = []rune("你好世界")
-        copy(buf[:], text_content)
-        var text = C.QtNewStringUTF32((*C.uint32_t)(unsafe.Pointer(&buf)), (C.size_t)(uint(len(text_content))))
-        defer C.QtDeleteString(text)
-        C.QtObjectSetPropString(label, prop_text, text)
+        var text, del_text = NewStringFromRunes([]rune("你好世界"))
+        defer del_text()
+        C.QtObjectSetPropString(label, new_str("text"), text)
+        var cb = cgohelper.RegisterCallback(func() {
+            var new_str, del_all_str = str_alloc()
+            defer del_all_str()
+            var x, del_x = NewStringFromRunes([]rune(fmt.Sprint(rand.Float64())))
+            defer del_x()
+            C.QtObjectSetPropString(label, new_str("text"), x)
+        })
+        var btn = C.QtWidgetFindChild(w, new_str("button"))
+        if btn == nil { panic("unable to find button") }
+        C.QtConnect(btn, new_str("clicked()"), cgo_callback, C.size_t(cb))
     })()
     C.QtMainLoop()
 }
 
+func NewStringFromBuf(buf ([] uint32)) (C.QtString, func()) {
+    var slice = *(*reflect.SliceHeader)(unsafe.Pointer(&buf))
+    var ptr = (*C.uint32_t)(unsafe.Pointer(slice.Data))
+    var size = (C.size_t)(slice.Len)
+    var str = C.QtNewStringUTF32(ptr, size)
+    return str, func() {
+        C.QtDeleteString(str)
+    }
+}
+
+func NewStringFromRunes(runes ([] rune)) (C.QtString, func()) {
+    var slice = *(*reflect.SliceHeader)(unsafe.Pointer(&runes))
+    var ptr = (*C.uint32_t)(unsafe.Pointer(slice.Data))
+    var size = (C.size_t)(slice.Len)
+    var str = C.QtNewStringUTF32(ptr, size)
+    return str, func() {
+        C.QtDeleteString(str)
+    }
+}
+

+ 8 - 1
qt/example.ui

@@ -14,7 +14,7 @@
    <string>MainWindow</string>
   </property>
   <widget class="QWidget" name="centralwidget">
-   <layout class="QHBoxLayout" name="horizontalLayout">
+   <layout class="QVBoxLayout" name="verticalLayout">
     <item>
      <widget class="QLabel" name="label">
       <property name="font">
@@ -28,6 +28,13 @@
       </property>
      </widget>
     </item>
+    <item>
+     <widget class="QPushButton" name="button">
+      <property name="text">
+       <string>ChangeText</string>
+      </property>
+     </widget>
+    </item>
    </layout>
   </widget>
  </widget>

+ 24 - 0
qt/helper.go

@@ -0,0 +1,24 @@
+package main
+
+/*
+#include <stdlib.h>
+typedef void (*CgoCallback)(size_t);
+void CgoInvokeCallback(size_t id);
+*/
+import "C"
+import "kumachan/qt/cgohelper"
+
+func str_alloc() (func(string) *C.char, func()) {
+	var alloc, dealloc = cgohelper.CreateStringAllocator()
+	return func(str string) *C.char {
+		return (*C.char)(alloc(str))
+	}, dealloc
+}
+
+//export invoke_callback
+func invoke_callback(id C.size_t) {
+	var f = cgohelper.GetCallback(uint(id))
+	f()
+}
+
+var cgo_callback = C.CgoCallback(C.CgoInvokeCallback)

+ 35 - 8
qt/qtbinding/qtbinding.cpp

@@ -1,4 +1,5 @@
 #include <QApplication>
+#include <QMetaMethod>
 #include <QUiLoader>
 #include <QBuffer>
 #include <QByteArray>
@@ -10,6 +11,10 @@
 
 QtString QtWrapString(QString str);
 QString QtUnwrapString(QtString str);
+QMetaObject::Connection QtDynamicConnect (
+        QObject *emitter , const QString &signalName,
+        QObject *receiver, const QString &slotName
+);
 
 struct __QtConnHandle {
     QMetaObject::Connection
@@ -45,8 +50,8 @@ int QtMainLoop() {
     return app->exec();
 }
 
-void QtCommitTask(void (*task)(void*), void* arg) {
-    bridge->QueueCallback(task, arg);
+void QtCommitTask(callback_t cb, size_t payload) {
+    bridge->QueueCallback(cb, payload);
 }
 
 void QtExit(int code) {
@@ -92,15 +97,15 @@ QtString QtObjectGetPropString(void* obj_ptr, const char* prop) {
 }
 
 QtConnHandle QtConnect (
-        void* widget_ptr,
+        void* obj_ptr,
         const char* signal,
-        void (*callback)(void*,void*),
-        void* payload
+        callback_t cb,
+        size_t payload
 ) {
-    QWidget* widget = (QWidget*) widget_ptr;
-    CallbackObject* cb_obj = new CallbackObject(callback, widget_ptr, payload);
+    QObject* target_obj = (QObject*) obj_ptr;
+    CallbackObject* cb_obj = new CallbackObject(cb, payload);
     __QtConnHandle* handle = new __QtConnHandle;
-    handle->conn = QObject::connect(widget, signal, cb_obj, "Callback");
+    handle->conn = QtDynamicConnect(target_obj, signal, cb_obj, "slot()");
     handle->cb_obj = cb_obj;
     return { (void*) handle };
 }
@@ -153,3 +158,25 @@ QString QtUnwrapString(QtString str) {
 void QtDeleteString(QtString str) {
     delete (QString*)(str.ptr);
 }
+
+QMetaObject::Connection QtDynamicConnect (
+        QObject *emitter , const QString &signalName,
+        QObject *receiver, const QString &slotName
+) {
+    /* ref: https://stackoverflow.com/questions/26208851/qt-connecting-signals-and-slots-from-text */
+    int index = emitter->metaObject()
+                ->indexOfSignal(QMetaObject::normalizedSignature(qPrintable(signalName)));
+    if (index == -1) {
+        qWarning("Wrong signal name: %s", qPrintable(signalName));
+        return QMetaObject::Connection();
+    }
+    QMetaMethod signal = emitter->metaObject()->method(index);
+    index = receiver->metaObject()
+            ->indexOfSlot(QMetaObject::normalizedSignature(qPrintable(slotName)));
+    if (index == -1) {
+        qWarning("Wrong slot name: %s", qPrintable(slotName));
+        return QMetaObject::Connection();
+    }
+    QMetaMethod slot = receiver->metaObject()->method(index);
+    return QObject::connect(emitter, signal, receiver, slot);
+}

+ 2 - 2
qt/qtbinding/qtbinding.h

@@ -20,7 +20,7 @@ extern "C" {
 #endif
     void QtInit();
     int QtMainLoop();
-    void QtCommitTask(void (*task)(void*), void* arg);
+    void QtCommitTask(void (*cb)(size_t), size_t payload);
     void QtExit(int code);
     void QtQuit();
     void* QtLoadWidget(const char* definition);
@@ -29,7 +29,7 @@ extern "C" {
     void QtWidgetHide(void* widget_ptr);
     QtBool QtObjectSetPropString(void* obj_ptr, const char* prop, QtString val);
     QtString QtObjectGetPropString(void* obj_ptr, const char* prop);
-    QtConnHandle QtConnect(void* widget_ptr, const char* signal, void (*callback)(void*,void*), void* payload);
+    QtConnHandle QtConnect(void* obj_ptr, const char* signal, void (*cb)(size_t), size_t payload);
     QtBool QtIsConnectionValid(QtConnHandle handle);
     QtBool QtDisconnect(QtConnHandle handle);
     void QtDeleteConnection(QtConnHandle handle);

+ 12 - 12
qt/qtbinding/qtbinding.hpp

@@ -2,8 +2,10 @@
 #define HELLO_H
 #include <QObject>
 #include <QWidget>
+#include <cstdlib>
 
-typedef void (*Callback1)(void*);
+
+typedef void (*callback_t)(size_t);
 
 class Bridge: public QObject {
 Q_OBJECT
@@ -17,28 +19,26 @@ public:
     };
     virtual ~Bridge() {};
 signals:
-    void QueueCallback(Callback1 callback, void* payload);
+    void QueueCallback(callback_t cb, size_t payload);
 private slots:
-    void __InvokeCallback(Callback1 callback, void* payload) {
-        callback(payload);
+    void __InvokeCallback(callback_t cb, size_t payload) {
+        cb(payload);
     };
 };
 
 class CallbackObject: public QObject {
 Q_OBJECT
 public:
-    void (*callback)(void*,void*);
-    void* target;
-    void* payload;
-    CallbackObject(void (*callback)(void*,void*), void* target, void* payload): QObject(nullptr) {
-        this->callback = callback;
-        this->target = target;
+    callback_t cb;
+    size_t payload;
+    CallbackObject(callback_t cb, size_t payload): QObject(nullptr) {
+        this->cb = cb;
         this->payload = payload;
     };
     virtual ~CallbackObject() {};
 public slots:
-    void Callback() {
-        this->callback(this->target, this->payload);
+    void slot() {
+        this->cb(this->payload);
     };
 };
 

+ 0 - 0
qt/qtbinding/qtbinding.pro.user


Some files were not shown because too many files changed in this diff