mirror of
https://gitlab.com/eql/lqml.git
synced 2025-12-06 02:30:38 -08:00
make example work on android; revisions
This commit is contained in:
parent
113386fdae
commit
99ea5a081d
49 changed files with 637 additions and 133 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,6 +1,5 @@
|
||||||
TODO
|
TODO
|
||||||
lqml
|
lqml
|
||||||
build
|
|
||||||
_*
|
_*
|
||||||
*.a
|
*.a
|
||||||
*.fas*
|
*.fas*
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ win32 {
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
lib.h \
|
lib.h \
|
||||||
../../src/cpp/marshal.h \
|
../../src/cpp/marshal.h \
|
||||||
../../src/cpp/qt_ech.h
|
../../src/cpp/qt_ecl.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
lib.cpp \
|
lib.cpp \
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ QObject* ini() {
|
||||||
return cpp;
|
return cpp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// functiones defined Q_INVOKABLE
|
// functions defined Q_INVOKABLE
|
||||||
|
|
||||||
QVariant CPP::hello(const QVariant& arg) {
|
QVariant CPP::hello(const QVariant& arg) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,18 @@ the argument and return type simply being defined as `QVariant`, you may also
|
||||||
pass lists, because a `QVariant` can also be of type `QVariantList`, so this
|
pass lists, because a `QVariant` can also be of type `QVariantList`, so this
|
||||||
is a perfect fit for (nested) Lisp lists.
|
is a perfect fit for (nested) Lisp lists.
|
||||||
|
|
||||||
So, we pass a nested Lisp list, and it gets shown on Qt side with the
|
So, we pass a nested Lisp list, and it gets converted and shown on Qt side with
|
||||||
respective types. Then the `QVariantList` is returned to Lisp, where it is
|
the respective types. Then the `QVariantList` is returned to Lisp, where it is
|
||||||
automatically converted back to a nested Lisp list.
|
automatically converted back to a nested Lisp list.
|
||||||
|
|
||||||
Really convenient!
|
Really convenient!
|
||||||
|
|
||||||
|
From the second function -- which calls back to Lisp -- we can see that it
|
||||||
|
suffices to simply pass some intuitive, primitive C++ values to `ecl_fun`,
|
||||||
|
which will be converted automatically (using `QVariant`) to the appropriate
|
||||||
|
Lisp values.
|
||||||
|
|
||||||
|
**Conclusion**: by only allowing `QVariant` arguments for calls between Lisp
|
||||||
|
and C++/Qt, we simplify things to a point where it becomes trivial, especially
|
||||||
|
considering nested lists on both sides.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
(qget *quick-view* |width|)
|
(qget *quick-view* |width|)
|
||||||
|
|
||||||
|
|
||||||
<b>qjs (method-name item/name &rest arguments</b>
|
<b>qjs (method-name item/name &rest arguments)</b>
|
||||||
|
|
||||||
Fast and convenient way to call JS functions defined in QML. You may pass
|
Fast and convenient way to call JS functions defined in QML. You may pass
|
||||||
up to 10 arguments of the following types:
|
up to 10 arguments of the following types:
|
||||||
|
|
@ -123,6 +123,8 @@
|
||||||
mentioned arguments.
|
mentioned arguments.
|
||||||
N.B: Does not work with JS default arguments.
|
N.B: Does not work with JS default arguments.
|
||||||
|
|
||||||
|
(qjs |drawLine| *canvas* x1 y1 x2 y2))
|
||||||
|
|
||||||
|
|
||||||
<b>qlater (function)</b>
|
<b>qlater (function)</b>
|
||||||
|
|
||||||
|
|
@ -137,7 +139,7 @@
|
||||||
The plugin will be reloaded (if supported by the OS) every time you call
|
The plugin will be reloaded (if supported by the OS) every time you call
|
||||||
this function. If the UNLOAD argument is not NIL, the plugin will be
|
this function. If the UNLOAD argument is not NIL, the plugin will be
|
||||||
unloaded (if supported by the OS).
|
unloaded (if supported by the OS).
|
||||||
N.B: This works only for Qt6 functions with the following signature:
|
N.B: This works only for Qt functions with the following signature:
|
||||||
"QVariant foo(QVariant, ...)" ; max 10 QVariant arguments
|
"QVariant foo(QVariant, ...)" ; max 10 QVariant arguments
|
||||||
Since a QVariant can also be of type QVariantList, this is a perfect fit
|
Since a QVariant can also be of type QVariantList, this is a perfect fit
|
||||||
for (nested) Lisp lists.
|
for (nested) Lisp lists.
|
||||||
|
|
@ -147,7 +149,7 @@
|
||||||
(define-qt-wrappers *c++*) ; Lisp wrapper functions
|
(define-qt-wrappers *c++*) ; Lisp wrapper functions
|
||||||
|
|
||||||
|
|
||||||
<b>qlog (arg1 &optional arg2 arg3...)</b>
|
<b>qlog (arg1 &rest args)</b>
|
||||||
|
|
||||||
For log messages on android.
|
For log messages on android.
|
||||||
|
|
||||||
|
|
|
||||||
6
examples/9999/app.asd
Normal file
6
examples/9999/app.asd
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
(defsystem :app
|
||||||
|
:serial t
|
||||||
|
:depends-on ()
|
||||||
|
:components ((:file "lisp/package")
|
||||||
|
(:file "lisp/main")))
|
||||||
|
|
||||||
53
examples/9999/app.pro
Normal file
53
examples/9999/app.pro
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# changes to these files will re-compile the Lisp library when running 'make'
|
||||||
|
LISP_FILES = \
|
||||||
|
lisp/package.lisp \
|
||||||
|
lisp/main.lisp \
|
||||||
|
app.asd \
|
||||||
|
make.lisp
|
||||||
|
|
||||||
|
android {
|
||||||
|
lisp.commands = $$(ECL_ANDROID)/../ecl-android-host/bin/ecl \
|
||||||
|
-norc -shell $$PWD/make.lisp
|
||||||
|
} else:ios {
|
||||||
|
lisp.commands = $$(ECL_IOS)/../ecl-ios-host/bin/ecl \
|
||||||
|
-norc -shell $$PWD/make.lisp
|
||||||
|
} else:unix {
|
||||||
|
lisp.commands = ecl -shell $$PWD/make.lisp
|
||||||
|
}
|
||||||
|
|
||||||
|
lisp.input = LISP_FILES
|
||||||
|
lisp.output = tmp/libapp.a
|
||||||
|
|
||||||
|
QMAKE_EXTRA_COMPILERS += lisp
|
||||||
|
|
||||||
|
QT += quick qml
|
||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += no_keywords release
|
||||||
|
DEFINES += INI_LISP
|
||||||
|
INCLUDEPATH = /usr/local/include
|
||||||
|
LIBS = -L/usr/local/lib -lecl
|
||||||
|
DESTDIR = .
|
||||||
|
TARGET = app
|
||||||
|
OBJECTS_DIR = ./tmp
|
||||||
|
MOC_DIR = ./tmp
|
||||||
|
|
||||||
|
linux: LIBS += -L../../../platforms/linux/lib -llqml -llisp -Ltmp -lapp
|
||||||
|
macx: LIBS += -L../../../platforms/macos/lib -llqml -llisp -Ltmp -lapp
|
||||||
|
|
||||||
|
android {
|
||||||
|
QT += androidextras
|
||||||
|
INCLUDEPATH = $$(ECL_ANDROID)/include
|
||||||
|
LIBS = -L$$(ECL_ANDROID)/lib -lecl
|
||||||
|
LIBS += -L../../../platforms/android/lib -llqml -llisp -Ltmp -lapp
|
||||||
|
|
||||||
|
ANDROID_ABIS = "arm64-v8a"
|
||||||
|
ANDROID_EXTRA_LIBS += $$(ECL_ANDROID)/lib/libecl.so
|
||||||
|
#ANDROID_PACKAGE_SOURCE_DIR = ../platforms/android/sources
|
||||||
|
}
|
||||||
|
|
||||||
|
SOURCES += ../../src/cpp/main.cpp
|
||||||
|
|
||||||
|
RESOURCES = app.qrc
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS += -std=c++17
|
||||||
|
|
||||||
5
examples/9999/app.qrc
Normal file
5
examples/9999/app.qrc
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource>
|
||||||
|
<file>qml/main.qml</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
2
examples/9999/build-android/.gitignore
vendored
Normal file
2
examples/9999/build-android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
examples/9999/build-ios/.gitignore
vendored
Normal file
2
examples/9999/build-ios/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
examples/9999/build/.gitignore
vendored
Normal file
2
examples/9999/build/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
(in-package :qml-user)
|
(in-package :app)
|
||||||
|
|
||||||
(defvar *number* 0)
|
(defvar *number* 0)
|
||||||
|
|
||||||
|
|
|
||||||
6
examples/9999/lisp/package.lisp
Normal file
6
examples/9999/lisp/package.lisp
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
(defpackage :app
|
||||||
|
(:use :cl :qml)
|
||||||
|
(:export
|
||||||
|
#:draw-number
|
||||||
|
#:paint))
|
||||||
|
|
||||||
47
examples/9999/make.lisp
Normal file
47
examples/9999/make.lisp
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
(when (search "/ecl-android/" (first (ext:command-args)))
|
||||||
|
(pushnew :android *features*))
|
||||||
|
|
||||||
|
(require :asdf)
|
||||||
|
|
||||||
|
(push (merge-pathnames "../")
|
||||||
|
asdf:*central-registry*)
|
||||||
|
|
||||||
|
(setf *default-pathname-defaults*
|
||||||
|
(truename (merge-pathnames "../../../"))) ; LQML root
|
||||||
|
|
||||||
|
(defvar *current*
|
||||||
|
(let ((name (namestring *load-truename*)))
|
||||||
|
(subseq name
|
||||||
|
(length (namestring *default-pathname-defaults*))
|
||||||
|
(position #\/ name :from-end t))))
|
||||||
|
|
||||||
|
;; load all LQML symbols
|
||||||
|
(dolist (file (list "package" "x" "ecl-ext" "ini" "qml"))
|
||||||
|
(load (merge-pathnames file "src/lisp/")))
|
||||||
|
|
||||||
|
(defun cc (&rest args)
|
||||||
|
(apply 'concatenate 'string args))
|
||||||
|
|
||||||
|
#-android
|
||||||
|
(progn
|
||||||
|
(asdf:make-build "app"
|
||||||
|
:monolithic t
|
||||||
|
:type :static-library
|
||||||
|
:move-here (cc *current* "/build/tmp/")
|
||||||
|
:init-name "ini_app")
|
||||||
|
(let* ((from (cc *current* "/build/tmp/app--all-systems.a"))
|
||||||
|
(to "libapp.a")
|
||||||
|
(to* (cc *current* "/build/tmp/" to)))
|
||||||
|
(when (probe-file to*)
|
||||||
|
(delete-file to*))
|
||||||
|
(rename-file from to)))
|
||||||
|
|
||||||
|
#+android
|
||||||
|
(progn
|
||||||
|
(defvar *asdf-system* "app")
|
||||||
|
(defvar *ql-libs* (cc *current* "/ql-libs.lisp"))
|
||||||
|
(defvar *library-name* (cc *current* "/build-android/tmp/app"))
|
||||||
|
(defvar *init-name* "ini_app")
|
||||||
|
(defvar *epilogue-code* nil)
|
||||||
|
(load "platforms/shared/make"))
|
||||||
|
|
||||||
3
examples/9999/mkdirs.sh
Executable file
3
examples/9999/mkdirs.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
mkdir build
|
||||||
|
mkdir build-android
|
||||||
|
mkdir build-ios
|
||||||
3
examples/9999/ql-libs.lisp
Normal file
3
examples/9999/ql-libs.lisp
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
;;; define here eventual Quicklisp dependencies
|
||||||
|
;;; e.g. (ql:quickload :alessandria)
|
||||||
|
|
||||||
|
|
@ -1,17 +1,37 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Window 2.15
|
||||||
import Lisp 1.0
|
import Lisp 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 220
|
id: main
|
||||||
height: 320 + input.height
|
width: 200
|
||||||
|
height: 300 + input.height
|
||||||
color: "lavender"
|
color: "lavender"
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: input
|
||||||
|
objectName: "input"
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
text: "0000"
|
||||||
|
inputMask: "9999"
|
||||||
|
inputMethodHints: Qt.ImhDigitsOnly
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
onTextChanged: Lisp.call("app:draw-number", Number(text))
|
||||||
|
}
|
||||||
|
|
||||||
Canvas {
|
Canvas {
|
||||||
id: canvas
|
id: canvas
|
||||||
objectName: "canvas"
|
objectName: "canvas"
|
||||||
width: 220
|
y: input.height
|
||||||
height: 320
|
width: parent.width
|
||||||
|
height: {
|
||||||
|
var h = Qt.inputMethod.keyboardRectangle.y
|
||||||
|
h = (h === 0) ? main.height : h / Screen.devicePixelRatio
|
||||||
|
return (h - input.height)
|
||||||
|
}
|
||||||
|
|
||||||
property var ctx
|
property var ctx
|
||||||
|
|
||||||
|
|
@ -36,23 +56,13 @@ Rectangle {
|
||||||
onPaint: {
|
onPaint: {
|
||||||
ctx = getContext("2d")
|
ctx = getContext("2d")
|
||||||
ctx.reset()
|
ctx.reset()
|
||||||
ctx.translate(110, 160)
|
ctx.translate(canvas.width / 2, canvas.height / 2)
|
||||||
|
var s = height / 340
|
||||||
|
ctx.scale(s, s)
|
||||||
|
|
||||||
Lisp.call("qml-user:paint")
|
Lisp.call("app:paint")
|
||||||
|
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: input
|
|
||||||
objectName: "input"
|
|
||||||
width: parent.width
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
|
||||||
text: "0000"
|
|
||||||
inputMask: "9999"
|
|
||||||
|
|
||||||
onTextChanged: Lisp.call("qml-user:draw-number", Number(text))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
examples/9999/readme-build.md
Normal file
32
examples/9999/readme-build.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
Run desktop
|
||||||
|
-----------
|
||||||
|
|
||||||
|
```
|
||||||
|
$ lqml run.lisp
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Build desktop app
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd build
|
||||||
|
|
||||||
|
$ qmake ../app.pro
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Build android APK
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd build-android
|
||||||
|
|
||||||
|
$ qmake-android ../app.pro
|
||||||
|
$ make apk
|
||||||
|
|
||||||
|
$ adb install -r android-build/*.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
(in-package :qml-user)
|
(in-package :qml-user)
|
||||||
|
|
||||||
(load "lisp/main")
|
(require :asdf)
|
||||||
|
|
||||||
|
(push (merge-pathnames "./")
|
||||||
|
asdf:*central-registry*)
|
||||||
|
|
||||||
|
(asdf:operate 'asdf:load-source-op :app)
|
||||||
|
|
||||||
(qset *quick-view*
|
(qset *quick-view*
|
||||||
|x| 75
|
|x| 75
|
||||||
|y| 75)
|
|y| 75)
|
||||||
|
|
||||||
;;; for Slime after copying 'qml-start-swank.lisp' from LQML sources
|
;;; for Slime after copying 'lqml-start-swank.lisp' from LQML sources
|
||||||
;;; to your Slime directory, which is assumed to be '~/slime/'
|
;;; to your Slime directory, which is assumed to be '~/slime/'
|
||||||
|
|
||||||
(when (find "-slime" (ext:command-args) :test 'string=)
|
(when (find "-slime" (ext:command-args) :test 'string=)
|
||||||
|
|
|
||||||
11
platforms/android/build-ecl/1-make-ecl-host.sh
Executable file
11
platforms/android/build-ecl/1-make-ecl-host.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# build the host ECL, which will then be used
|
||||||
|
# to build the cross-compiled Android version
|
||||||
|
# (assumes a 64bit platform)
|
||||||
|
|
||||||
|
./configure CFLAGS="-g -O2" LDFLAGS="-g -O2" CC=clang \
|
||||||
|
--prefix=`pwd`/ecl-android-host \
|
||||||
|
--disable-c99complex \
|
||||||
|
--enable-manual=no
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
rm -r build
|
||||||
20
platforms/android/build-ecl/2-make-ecl-android.sh
Executable file
20
platforms/android/build-ecl/2-make-ecl-android.sh
Executable file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# use the previously built host ECL to build the android version
|
||||||
|
# requires NDK >= 19
|
||||||
|
# you need to define ANDROID_NDK_TOOLCHAIN
|
||||||
|
|
||||||
|
export AR=$ANDROID_NDK_TOOLCHAIN/bin/aarch64-linux-android-ar
|
||||||
|
export AS=$ANDROID_NDK_TOOLCHAIN/bin/aarch64-linux-android-as
|
||||||
|
export CC=$ANDROID_NDK_TOOLCHAIN/bin/aarch64-linux-android21-clang
|
||||||
|
export LD=$ANDROID_NDK_TOOLCHAIN/bin/aarch64-linux-android-ld
|
||||||
|
export RANLIB=$ANDROID_NDK_TOOLCHAIN/bin/aarch64-linux-android-ranlib
|
||||||
|
export STRIP=$ANDROID_NDK_TOOLCHAIN/bin/aarch64-linux-android-strip
|
||||||
|
|
||||||
|
export ECL_TO_RUN=`pwd`/ecl-android-host/bin/ecl
|
||||||
|
|
||||||
|
./configure --host=aarch64-linux-android \
|
||||||
|
--prefix=`pwd`/ecl-android \
|
||||||
|
--disable-c99complex \
|
||||||
|
--enable-manual=no \
|
||||||
|
--with-cross-config=`pwd`/src/util/android-arm64.cross_config
|
||||||
|
make
|
||||||
|
make install
|
||||||
39
platforms/android/cross-compile.lisp
Normal file
39
platforms/android/cross-compile.lisp
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
;;; please use NDK versions >= 19 (with prebuilt standalone toolchain)
|
||||||
|
|
||||||
|
(in-package :cl-user)
|
||||||
|
|
||||||
|
(pushnew :android *features*)
|
||||||
|
(pushnew :aarch64 *features*)
|
||||||
|
|
||||||
|
(require :cmp)
|
||||||
|
|
||||||
|
(defvar *ndk-toolchain* (ext:getenv "ANDROID_NDK_TOOLCHAIN"))
|
||||||
|
(defvar *ecl-android* (ext:getenv "ECL_ANDROID"))
|
||||||
|
(defvar *architecture* "aarch64-linux-android")
|
||||||
|
|
||||||
|
(defun cc (&rest arguments)
|
||||||
|
(apply 'concatenate 'string arguments))
|
||||||
|
|
||||||
|
(setf c::*ecl-include-directory* (cc *ecl-android* "/include/")
|
||||||
|
c::*ecl-library-directory* (cc *ecl-android* "/lib/"))
|
||||||
|
|
||||||
|
(defun ecl-config (flags)
|
||||||
|
(read-line (ext:run-program (cc *ecl-android* "/bin/ecl-config")
|
||||||
|
(list flags))))
|
||||||
|
|
||||||
|
(setf c::*cc* (let ((path (or (probe-file (cc *ndk-toolchain* "/bin/aarch64-linux-android21-clang"))
|
||||||
|
(error "clang compiler not found"))))
|
||||||
|
(namestring path))
|
||||||
|
c::*ld* (cc *ndk-toolchain* "/bin/aarch64-linux-android-ld")
|
||||||
|
c::*ar* (cc *ndk-toolchain* "/bin/aarch64-linux-android-ar")
|
||||||
|
c::*ranlib* (cc *ndk-toolchain* "/bin/aarch64-linux-android-ranlib")
|
||||||
|
c::*cc-flags* (cc (ecl-config "--cflags")
|
||||||
|
" -DANDROID -DPLATFORM_ANDROID -O2 -fPIC -fno-common -D_THREAD_SAFE -I"
|
||||||
|
*ecl-android* "/build/gmp")
|
||||||
|
c::*ld-flags* (cc "-L" *ecl-android* "/lib -lecl -ldl -lm "
|
||||||
|
"-L" *ndk-toolchain* "/sysroot/usr/lib/aarch64-linux-android/")
|
||||||
|
c::*ld-rpath* nil
|
||||||
|
c::*ld-shared-flags* (cc "-shared " c::*ld-flags*)
|
||||||
|
c::*ld-bundle-flags* c::*ld-shared-flags*)
|
||||||
|
|
||||||
|
(format t "~%*** cross compiling for 'aarch64' ***~%")
|
||||||
75
platforms/shared/make.lisp
Normal file
75
platforms/shared/make.lisp
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
;;; cross-compile ASDF system, using the byte-codes compiler
|
||||||
|
;;; as an intermediate step
|
||||||
|
|
||||||
|
;; optional vars, to be set in 'make.lisp' in your app dir
|
||||||
|
(defvar *epilogue-code* nil)
|
||||||
|
(defvar *ql-libs* nil)
|
||||||
|
|
||||||
|
;;; *** (1) byte-compile ASDF system ***
|
||||||
|
|
||||||
|
(si:install-bytecodes-compiler)
|
||||||
|
|
||||||
|
(when *ql-libs*
|
||||||
|
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
||||||
|
(user-homedir-pathname))))
|
||||||
|
(when (probe-file quicklisp-init)
|
||||||
|
(load quicklisp-init)))
|
||||||
|
(load *ql-libs*))
|
||||||
|
|
||||||
|
;;; load ASDF system and collect file names
|
||||||
|
|
||||||
|
(defvar *source-files* nil)
|
||||||
|
|
||||||
|
(defmethod asdf:perform ((o asdf:load-op) (c asdf:cl-source-file))
|
||||||
|
(let ((source (namestring (asdf:component-pathname c))))
|
||||||
|
(push (subseq source 0 (position #\. source :from-end t))
|
||||||
|
*source-files*))
|
||||||
|
(asdf::perform-lisp-load-fasl o c))
|
||||||
|
|
||||||
|
(asdf:load-system *asdf-system*)
|
||||||
|
|
||||||
|
(setf *source-files* (nreverse *source-files*))
|
||||||
|
|
||||||
|
;;; *** (2) cross-compile ***
|
||||||
|
|
||||||
|
;;; load and prepare cross-compiler
|
||||||
|
|
||||||
|
(si:install-c-compiler)
|
||||||
|
|
||||||
|
(load #+android (merge-pathnames "platforms/android/cross-compile"))
|
||||||
|
|
||||||
|
(setf *load-verbose* nil
|
||||||
|
*compile-verbose* t)
|
||||||
|
|
||||||
|
(setf c::*suppress-compiler-warnings* nil
|
||||||
|
c::*suppress-compiler-notes* nil
|
||||||
|
c::*compile-in-constants* t)
|
||||||
|
|
||||||
|
(load (merge-pathnames "src/lisp/tr.lisp")) ; i18n
|
||||||
|
|
||||||
|
(setf *break-on-signals* 'error)
|
||||||
|
|
||||||
|
;;; compile/link manually (byte-compiled version is already loaded)
|
||||||
|
|
||||||
|
(defvar *object-files* nil)
|
||||||
|
|
||||||
|
(defun cc (&rest args)
|
||||||
|
(apply 'concatenate 'string args))
|
||||||
|
|
||||||
|
(dolist (file *source-files*)
|
||||||
|
(let ((src (cc file ".lisp"))
|
||||||
|
(obj (merge-pathnames (cc "src/.cache/" *architecture* file ".o"))))
|
||||||
|
(when (or (not (probe-file obj))
|
||||||
|
(> (file-write-date src)
|
||||||
|
(file-write-date obj)))
|
||||||
|
(ensure-directories-exist obj)
|
||||||
|
(compile-file src :output-file obj :system-p t))
|
||||||
|
(push obj *object-files*)))
|
||||||
|
|
||||||
|
(setf *object-files* (nreverse *object-files*))
|
||||||
|
|
||||||
|
(c:build-static-library *library-name*
|
||||||
|
:lisp-files *object-files*
|
||||||
|
:init-name *init-name*
|
||||||
|
:epilogue-code *epilogue-code*)
|
||||||
|
|
||||||
|
|
@ -1,21 +1,46 @@
|
||||||
|
|
||||||
Build
|
Build executable
|
||||||
-----
|
----------------
|
||||||
|
|
||||||
Currently still using qmake, will be ported to CMake.
|
Currently still using qmake, will eventually be ported to CMake.
|
||||||
|
|
||||||
* make sure you have both **ECL** and **Qt6** installed
|
Please make sure you have both latest **ECL** (from development branch) and
|
||||||
* make sure to use `qmake` from Qt6
|
either **Qt5.15** or **Qt6** installed (see [readme-qt](readme-qt.md)).
|
||||||
|
|
||||||
**macOS**: please use ECL from development branch.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cd src
|
$ cd src/build
|
||||||
$ mkdir build
|
|
||||||
$ cd build
|
|
||||||
|
|
||||||
$ qmake ../lqml.pro
|
$ qmake ../lqml.pro
|
||||||
$ make -j4
|
$ make -j4
|
||||||
$ sudo make install
|
$ sudo make install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Build library
|
||||||
|
-------------
|
||||||
|
|
||||||
|
To build the static library needed for building your own apps, do:
|
||||||
|
|
||||||
|
* desktop
|
||||||
|
```
|
||||||
|
$ cd src/build
|
||||||
|
|
||||||
|
$ qmake ../lqml-lib.pro
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
* android (note separate build directory)
|
||||||
|
```
|
||||||
|
$ cd src/build-android
|
||||||
|
|
||||||
|
$ qmake-android ../lqml-lib.pro
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
* ios (note separate build directory)
|
||||||
|
```
|
||||||
|
$ cd src/build-ios
|
||||||
|
|
||||||
|
$ qmake-ios ../lqml-lib.pro
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
The library files can be found under `platforms/<os>/lib/`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ Description
|
||||||
|
|
||||||
A lightweight ECL based QML-only binding to Qt5/Qt6.
|
A lightweight ECL based QML-only binding to Qt5/Qt6.
|
||||||
|
|
||||||
|
This small project aims to simplify all the steps needed for building
|
||||||
|
cross-platform apps. The same sources can be used to build executables for both
|
||||||
|
desktop (Linux/macOS) and mobile (android/iOS).
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
@ -33,7 +37,6 @@ port still lacks significant parts of mobile (as of Qt6.2).
|
||||||
TODO
|
TODO
|
||||||
----
|
----
|
||||||
|
|
||||||
* make example work on android
|
|
||||||
* make example work on iOS
|
* make example work on iOS
|
||||||
* add item model example
|
* add item model example
|
||||||
* add sokoban example
|
* add sokoban example
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
Swank/Slime
|
Swank/Slime
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Please put `qml-start-swank.lisp` in your `slime/` directory, and `.swank.lisp`
|
Please put `lqml-start-swank.lisp` in your `slime/` directory, and
|
||||||
in your home directory.
|
`.swank.lisp` in your home directory.
|
||||||
|
|
||||||
Remember to always use `(qquit)` or `(qq)` to gracefully exit the Swank server.
|
Remember to always use `(qquit)` or `(qq)` to gracefully exit the Swank server.
|
||||||
|
|
|
||||||
2
src/build-android/.gitignore
vendored
Normal file
2
src/build-android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
src/build-ios/.gitignore
vendored
Normal file
2
src/build-ios/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
src/build/.gitignore
vendored
Normal file
2
src/build/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
|
@ -214,7 +214,7 @@ cl_object qload_cpp(cl_object l_lib_name, cl_object l_unload) { /// qload-c++
|
||||||
/// The plugin will be reloaded (if supported by the OS) every time you call
|
/// The plugin will be reloaded (if supported by the OS) every time you call
|
||||||
/// this function. If the UNLOAD argument is not NIL, the plugin will be
|
/// this function. If the UNLOAD argument is not NIL, the plugin will be
|
||||||
/// unloaded (if supported by the OS).
|
/// unloaded (if supported by the OS).
|
||||||
/// N.B: This works only for Qt6 functions with the following signature:
|
/// N.B: This works only for Qt functions with the following signature:
|
||||||
/// "QVariant foo(QVariant, ...)" ; max 10 QVariant arguments
|
/// "QVariant foo(QVariant, ...)" ; max 10 QVariant arguments
|
||||||
/// Since a QVariant can also be of type QVariantList, this is a perfect fit
|
/// Since a QVariant can also be of type QVariantList, this is a perfect fit
|
||||||
/// for (nested) Lisp lists.
|
/// for (nested) Lisp lists.
|
||||||
|
|
@ -348,7 +348,7 @@ cl_object qsingle_shot2(cl_object l_msec, cl_object l_fun) {
|
||||||
/// (qsingle-shot 1000 'one-second-later)
|
/// (qsingle-shot 1000 'one-second-later)
|
||||||
ecl_process_env()->nvalues = 1;
|
ecl_process_env()->nvalues = 1;
|
||||||
if (l_fun != ECL_NIL) {
|
if (l_fun != ECL_NIL) {
|
||||||
new SingleShot(toInt(l_msec), l_fun);
|
new SingleShot(toInt(l_msec), l_fun); // see 'deleteLater()' in sources
|
||||||
return l_msec;
|
return l_msec;
|
||||||
}
|
}
|
||||||
error_msg("QSINGLE-SHOT", LIST2(l_msec, l_fun));
|
error_msg("QSINGLE-SHOT", LIST2(l_msec, l_fun));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef ECL_EXT_H
|
#ifndef ECL_EXT_H
|
||||||
#define ECL_EXT_H
|
#define ECL_EXT_H
|
||||||
|
|
||||||
|
#undef SLOT
|
||||||
|
|
||||||
#include <ecl/ecl.h>
|
#include <ecl/ecl.h>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
const char LQML::version[] = "22.1.1"; // Jan 2022
|
const char LQML::version[] = "22.2.1"; // Feb 2022
|
||||||
|
|
||||||
extern "C" void ini_LQML(cl_object);
|
extern "C" void ini_LQML(cl_object);
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ static void logMessageHandler(QtMsgType, const QMessageLogContext& context, cons
|
||||||
report += " function ";
|
report += " function ";
|
||||||
report += QString(context.function);
|
report += QString(context.function);
|
||||||
}
|
}
|
||||||
__android_log_write(ANDROID_LOG_DEBUG, "[EQL5]", report.toLocal8Bit().constData());
|
__android_log_write(ANDROID_LOG_DEBUG, "[LQML]", report.toLocal8Bit().constData());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -44,11 +44,12 @@ LQML::LQML(int argc, char* argv[], QQuickView* view) : QObject() {
|
||||||
qInstallMessageHandler(logMessageHandler); // see above
|
qInstallMessageHandler(logMessageHandler); // see above
|
||||||
#endif
|
#endif
|
||||||
if (!cl_booted_p) {
|
if (!cl_booted_p) {
|
||||||
cl_boot(argc, argv); }
|
cl_boot(argc, argv);
|
||||||
|
}
|
||||||
iniCLFunctions();
|
iniCLFunctions();
|
||||||
ecl_init_module(NULL, ini_LQML);
|
ecl_init_module(NULL, ini_LQML);
|
||||||
eval("(in-package :qml-user)");
|
eval("(in-package :qml-user)");
|
||||||
eval(QString("(setf *quick-view* (qt-object %1))")
|
eval(QString("(setf qml:*quick-view* (qml:qt-object %1))")
|
||||||
.arg(reinterpret_cast<quintptr>(view)));
|
.arg(reinterpret_cast<quintptr>(view)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +88,7 @@ void LQML::eval(const QString& lisp_code, bool slime) {
|
||||||
safe_eval_debug(lisp_code.toLatin1().constData());
|
safe_eval_debug(lisp_code.toLatin1().constData());
|
||||||
} else {
|
} else {
|
||||||
cl_object ret = safe_eval(lisp_code.toLatin1().constData());
|
cl_object ret = safe_eval(lisp_code.toLatin1().constData());
|
||||||
if (ecl_t_of(ret) == t_fixnum && (fix(ret) == EVAL_ERROR_VALUE)) {
|
if ((ecl_t_of(ret) == t_fixnum) && (fix(ret) == EVAL_ERROR_VALUE)) {
|
||||||
qDebug() << "Error evaluating " << lisp_code;
|
qDebug() << "Error evaluating " << lisp_code;
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +97,7 @@ void LQML::eval(const QString& lisp_code, bool slime) {
|
||||||
|
|
||||||
void LQML::ignoreIOStreams() {
|
void LQML::ignoreIOStreams() {
|
||||||
// [Windows] print output would cause a gui exe to crash (without console)
|
// [Windows] print output would cause a gui exe to crash (without console)
|
||||||
eval("(eql::ignore-io-streams)");
|
eval("(qml::ignore-io-streams)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void LQML::exec(lisp_ini ini, const QByteArray& expression, const QByteArray& package) {
|
void LQML::exec(lisp_ini ini, const QByteArray& expression, const QByteArray& package) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef LQML_H
|
#ifndef LQML_H
|
||||||
#define LQML_H
|
#define LQML_H
|
||||||
|
|
||||||
|
#undef SLOT
|
||||||
|
|
||||||
#include <ecl/ecl.h>
|
#include <ecl/ecl.h>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@
|
||||||
#define ADD_MACOS_BUNDLE_IMPORT_PATH
|
#define ADD_MACOS_BUNDLE_IMPORT_PATH
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef INI_LISP
|
||||||
|
extern "C" void ini_app(cl_object);
|
||||||
|
#endif
|
||||||
|
|
||||||
int catch_all_qexec() {
|
int catch_all_qexec() {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
CL_CATCH_ALL_BEGIN(ecl_process_env()) {
|
CL_CATCH_ALL_BEGIN(ecl_process_env()) {
|
||||||
|
|
@ -25,6 +29,7 @@ int catch_all_qexec() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
//app.setOrganizationName("MyProject");
|
//app.setOrganizationName("MyProject");
|
||||||
//app.setOrganizationDomain("my.org");
|
//app.setOrganizationDomain("my.org");
|
||||||
|
|
@ -49,25 +54,26 @@ int main(int argc, char* argv[]) {
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef INI_LISP
|
||||||
|
ecl_init_module(NULL, ini_app);
|
||||||
|
#endif
|
||||||
|
|
||||||
new QQmlFileSelector(view.engine(), &view);
|
new QQmlFileSelector(view.engine(), &view);
|
||||||
QString qml("qml/main.qml");
|
QString qml("qml/main.qml");
|
||||||
QUrl url("qrc:///" + qml); // (1) try resources first (final app)
|
QUrl url;
|
||||||
bool set = false;
|
if (QFile::exists(qml)) { // (1) try local file (development)
|
||||||
if (QFile::exists(url.fileName())) {
|
url = QUrl::fromLocalFile(qml);
|
||||||
set = true;
|
|
||||||
} else {
|
} else {
|
||||||
url = QUrl::fromLocalFile(qml); // (2) use local file (development)
|
url = QUrl("qrc:///" + qml); // (2) use resource file (final app)
|
||||||
if (QFile::exists(QDir::currentPath() + "/" + qml)) {
|
|
||||||
set = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (set) {
|
|
||||||
view.setSource(url);
|
view.setSource(url);
|
||||||
if (view.status() == QQuickView::Error) {
|
if (view.status() != QQuickView::Error) {
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
view.setResizeMode(QQuickView::SizeRootObjectToView);
|
view.setResizeMode(QQuickView::SizeRootObjectToView);
|
||||||
|
#if (defined Q_OS_ANDROID) || (defined Q_OS_IOS)
|
||||||
|
view.show();
|
||||||
|
#else
|
||||||
QTimer::singleShot(0, &view, &QQuickView::show);
|
QTimer::singleShot(0, &view, &QQuickView::show);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// load .eclrc
|
// load .eclrc
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
#include "marshal.h"
|
#include "marshal.h"
|
||||||
#include <ecl/ecl.h>
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef MARSHAL_H
|
#ifndef MARSHAL_H
|
||||||
#define MARSHAL_H
|
#define MARSHAL_H
|
||||||
|
|
||||||
|
#undef SLOT
|
||||||
|
|
||||||
#include <ecl/ecl.h>
|
#include <ecl/ecl.h>
|
||||||
#include <QRectF>
|
#include <QRectF>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include "qt_ecl.h"
|
#include "qt_ecl.h"
|
||||||
#include "marshal.h"
|
#include "marshal.h"
|
||||||
#include "ecl_ext.h"
|
#include "ecl_ext.h"
|
||||||
#include <ecl/ecl.h>
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
|
||||||
25
src/cpp/single_shot.cpp
Normal file
25
src/cpp/single_shot.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "single_shot.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
SingleShot::SingleShot(int msec, void* fun)
|
||||||
|
: function(fun) {
|
||||||
|
id = startTimer(msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleShot::timerEvent(QTimerEvent*) {
|
||||||
|
killTimer(id);
|
||||||
|
const cl_env_ptr l_env = ecl_process_env();
|
||||||
|
CL_CATCH_ALL_BEGIN(l_env) {
|
||||||
|
CL_UNWIND_PROTECT_BEGIN(l_env) {
|
||||||
|
cl_funcall(1, (cl_object)function);
|
||||||
|
}
|
||||||
|
CL_UNWIND_PROTECT_EXIT {}
|
||||||
|
CL_UNWIND_PROTECT_END;
|
||||||
|
}
|
||||||
|
CL_CATCH_ALL_END;
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
|
@ -1,30 +1,24 @@
|
||||||
#ifndef SINGLE_SHOT_H
|
#ifndef SINGLE_SHOT_H
|
||||||
#define SINGLE_SHOT_H
|
#define SINGLE_SHOT_H
|
||||||
|
|
||||||
|
#undef SLOT
|
||||||
|
|
||||||
#include <ecl/ecl.h>
|
#include <ecl/ecl.h>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
struct SingleShot : public QObject {
|
class SingleShot : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
int id;
|
int id;
|
||||||
void* function;
|
void* function;
|
||||||
|
|
||||||
SingleShot(int msec, void* fun) : id(startTimer(msec)), function(fun) {}
|
SingleShot(int, void*);
|
||||||
|
|
||||||
void timerEvent(QTimerEvent*) {
|
protected:
|
||||||
killTimer(id);
|
void timerEvent(QTimerEvent*) override;
|
||||||
const cl_env_ptr l_env = ecl_process_env();
|
|
||||||
CL_CATCH_ALL_BEGIN(l_env) {
|
|
||||||
CL_UNWIND_PROTECT_BEGIN(l_env) {
|
|
||||||
cl_funcall(1, (cl_object)function);
|
|
||||||
}
|
|
||||||
CL_UNWIND_PROTECT_EXIT {}
|
|
||||||
CL_UNWIND_PROTECT_END;
|
|
||||||
}
|
|
||||||
CL_CATCH_ALL_END;
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
|
||||||
32
src/lisp/ecl-ext.lisp
Normal file
32
src/lisp/ecl-ext.lisp
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
;;; needed for cross-compiling
|
||||||
|
|
||||||
|
(in-package :qml)
|
||||||
|
|
||||||
|
(defun %js (a b))
|
||||||
|
(defun pixel-ratio ())
|
||||||
|
(defun %qapropos (a b c))
|
||||||
|
(defun qchildren (a))
|
||||||
|
(defun qescape (a))
|
||||||
|
(defun %qexec (a))
|
||||||
|
(defun qexit ())
|
||||||
|
(defun qfind-child (a b))
|
||||||
|
(defun %qfind-children (a b c))
|
||||||
|
(defun qfrom-utf8 (a))
|
||||||
|
(defun %qinvoke-method (a b c))
|
||||||
|
(defun %qload-c++ (a b))
|
||||||
|
(defun %qlog (a))
|
||||||
|
(defun %qml-get (a b))
|
||||||
|
(defun %qml-set (a b c))
|
||||||
|
(defun qobject-name (a))
|
||||||
|
(defun qprocess-events ())
|
||||||
|
(defun %qquit (a))
|
||||||
|
(defun %qrun-on-ui-thread (a b))
|
||||||
|
(defun %qget (a b))
|
||||||
|
(defun %qset (a b))
|
||||||
|
(defun %qsingle-shot (a b))
|
||||||
|
(defun qtranslate (a b c))
|
||||||
|
(defun qversion ())
|
||||||
|
(defun qt-object-info (a))
|
||||||
|
(defun %reload ())
|
||||||
|
(defun root-item ())
|
||||||
|
(defun %set-shutdown-p (a))
|
||||||
|
|
@ -58,11 +58,9 @@
|
||||||
;; check for LAMBDA, #'LAMBDA
|
;; check for LAMBDA, #'LAMBDA
|
||||||
(if (find (first function) '(lambda function))
|
(if (find (first function) '(lambda function))
|
||||||
;; hold a reference (will be called later from Qt event loop)
|
;; hold a reference (will be called later from Qt event loop)
|
||||||
`(qrun (lambda ()
|
`(qrun* (%qsingle-shot ,milliseconds (setf (symbol-function (intern ,(%reference-name))) ; lambda
|
||||||
(%qsingle-shot ,milliseconds (setf (symbol-function (intern ,(%reference-name))) ; lambda
|
,function)))
|
||||||
,function))))
|
`(qrun* (%qsingle-shot ,milliseconds ,function)))) ; 'foo
|
||||||
`(qrun (lambda ()
|
|
||||||
(%qsingle-shot ,milliseconds ,function))))) ; 'foo
|
|
||||||
|
|
||||||
(defmacro qlater (function)
|
(defmacro qlater (function)
|
||||||
"args: (function)
|
"args: (function)
|
||||||
|
|
@ -217,7 +215,7 @@
|
||||||
;;; for android logging
|
;;; for android logging
|
||||||
|
|
||||||
(defun qlog (arg1 &rest args)
|
(defun qlog (arg1 &rest args)
|
||||||
"args: (arg1 &optional arg2 arg3...)
|
"args: (arg1 &rest args)
|
||||||
For log messages on android.
|
For log messages on android.
|
||||||
(qlog 12)
|
(qlog 12)
|
||||||
(qlog \"width\" 10 \"height\" 20)
|
(qlog \"width\" 10 \"height\" 20)
|
||||||
|
|
@ -235,3 +233,4 @@
|
||||||
(alias qfun qinvoke-method)
|
(alias qfun qinvoke-method)
|
||||||
(alias qrun qrun-on-ui-thread)
|
(alias qrun qrun-on-ui-thread)
|
||||||
(alias qq qquit)
|
(alias qq qquit)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(defpackage :qml
|
(defpackage :qml
|
||||||
(:use :common-lisp)
|
(:use :cl)
|
||||||
(:export
|
(:export
|
||||||
#:*break-on-errors*
|
#:*break-on-errors*
|
||||||
#:*quick-view*
|
#:*quick-view*
|
||||||
|
|
@ -51,6 +51,6 @@
|
||||||
#:!))
|
#:!))
|
||||||
|
|
||||||
(defpackage :qml-user
|
(defpackage :qml-user
|
||||||
(:use :common-lisp :qml))
|
(:use :cl :qml))
|
||||||
|
|
||||||
(pushnew :qml *features*)
|
(pushnew :qml *features*)
|
||||||
|
|
|
||||||
|
|
@ -166,12 +166,13 @@
|
||||||
;;; JS calls
|
;;; JS calls
|
||||||
|
|
||||||
(defmacro qjs (method-name item/name &rest arguments)
|
(defmacro qjs (method-name item/name &rest arguments)
|
||||||
"args: (method-name item/name &rest arguments
|
"args: (method-name item/name &rest arguments)
|
||||||
Fast and convenient way to call JS functions defined in QML. You may pass
|
Fast and convenient way to call JS functions defined in QML. You may pass
|
||||||
up to 10 arguments of the following types:
|
up to 10 arguments of the following types:
|
||||||
T, NIL, INTEGER, FLOAT, STRING, VECTOR of octets, and (nested) lists of
|
T, NIL, INTEGER, FLOAT, STRING, VECTOR of octets, and (nested) lists of
|
||||||
mentioned arguments.
|
mentioned arguments.
|
||||||
N.B: Does not work with JS default arguments."
|
N.B: Does not work with JS default arguments.
|
||||||
|
(qjs |drawLine| *canvas* x1 y1 x2 y2))"
|
||||||
`(qrun* (qfun (quick-item ,item/name)
|
`(qrun* (qfun (quick-item ,item/name)
|
||||||
,(if (symbolp method-name)
|
,(if (symbolp method-name)
|
||||||
(symbol-name method-name)
|
(symbol-name method-name)
|
||||||
|
|
|
||||||
61
src/lqml-lib.pro
Normal file
61
src/lqml-lib.pro
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
QT += quick qml
|
||||||
|
TEMPLATE = lib
|
||||||
|
CONFIG += staticlib no_keywords release
|
||||||
|
LIBS = -L/usr/local/lib -lecl
|
||||||
|
TARGET = lqml
|
||||||
|
OBJECTS_DIR = ./tmp
|
||||||
|
MOC_DIR = ./tmp
|
||||||
|
|
||||||
|
linux {
|
||||||
|
INCLUDEPATH = /usr/local/include
|
||||||
|
DESTDIR = ../../platforms/linux/lib
|
||||||
|
}
|
||||||
|
|
||||||
|
macx {
|
||||||
|
INCLUDEPATH = /usr/local/include
|
||||||
|
DESTDIR = ../../platforms/macos/lib
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
INCLUDEPATH = $$(ECL_ANDROID)/include
|
||||||
|
LIBS = -L$$(ECL_ANDROID)/lib -lecl
|
||||||
|
DESTDIR = ../../platforms/android/lib
|
||||||
|
ANDROID_ABIS = "arm64-v8a"
|
||||||
|
}
|
||||||
|
|
||||||
|
ios {
|
||||||
|
INCLUDEPATH = $$(ECL_IOS)/include
|
||||||
|
LIBS = -L$$(ECL_IOS)/lib -lecl
|
||||||
|
DESTDIR = ../../platforms/ios/lib
|
||||||
|
}
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
cpp/marshal.h \
|
||||||
|
cpp/ecl_ext.h \
|
||||||
|
cpp/lqml.h \
|
||||||
|
cpp/qml.h \
|
||||||
|
cpp/qt_ecl.h \
|
||||||
|
cpp/single_shot.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
cpp/marshal.cpp \
|
||||||
|
cpp/ecl_ext.cpp \
|
||||||
|
cpp/lqml.cpp \
|
||||||
|
cpp/qml.cpp \
|
||||||
|
cpp/qt_ecl.cpp \
|
||||||
|
cpp/single_shot.cpp
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS += -std=c++17
|
||||||
|
|
||||||
|
# compile Lisp code
|
||||||
|
|
||||||
|
android {
|
||||||
|
QMAKE_POST_LINK = $$(ECL_ANDROID)/../ecl-android-host/bin/ecl \
|
||||||
|
-norc -shell $$PWD/make.lisp
|
||||||
|
} else:ios {
|
||||||
|
QMAKE_POST_LINK = $$(ECL_IOS)/../ecl-ios-host/bin/ecl \
|
||||||
|
-norc -shell $$PWD/make.lisp
|
||||||
|
} else:unix {
|
||||||
|
QMAKE_POST_LINK = ecl -shell $$PWD/make.lisp
|
||||||
|
}
|
||||||
|
|
||||||
31
src/lqml.pro
31
src/lqml.pro
|
|
@ -1,40 +1,26 @@
|
||||||
LISP_FILES = \
|
|
||||||
make.lisp \
|
|
||||||
lisp/x.lisp \
|
|
||||||
lisp/package.lisp \
|
|
||||||
lisp/ini.lisp \
|
|
||||||
lisp/qml.lisp \
|
|
||||||
lqml.asd
|
|
||||||
|
|
||||||
lisp.output = liblqml.a
|
|
||||||
lisp.commands = ecl -shell $$PWD/make.lisp
|
|
||||||
lisp.input = LISP_FILES
|
|
||||||
|
|
||||||
QMAKE_EXTRA_COMPILERS += lisp
|
|
||||||
|
|
||||||
QT += quick qml
|
QT += quick qml
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
CONFIG += no_keywords release
|
CONFIG += no_keywords release
|
||||||
INCLUDEPATH += /usr/local/include
|
INCLUDEPATH = /usr/local/include
|
||||||
LIBS += -L/usr/local/lib -lecl -L. -llqml
|
LIBS = -L/usr/local/lib -lecl -llisp
|
||||||
TARGET = lqml
|
TARGET = lqml
|
||||||
DESTDIR = .
|
DESTDIR = .
|
||||||
|
OBJECTS_DIR = ./tmp
|
||||||
|
MOC_DIR = ./tmp
|
||||||
|
|
||||||
linux {
|
linux {
|
||||||
|
LIBS += -L../../platforms/linux/lib
|
||||||
target.path = /usr/bin
|
target.path = /usr/bin
|
||||||
}
|
}
|
||||||
|
|
||||||
osx {
|
macx {
|
||||||
CONFIG -= app_bundle
|
CONFIG -= app_bundle
|
||||||
|
LIBS += -L../../platforms/macos/lib
|
||||||
target.path = /usr/local/bin
|
target.path = /usr/local/bin
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTALLS = target
|
INSTALLS = target
|
||||||
|
|
||||||
win32 {
|
|
||||||
include(windows.pri)
|
|
||||||
}
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
cpp/marshal.h \
|
cpp/marshal.h \
|
||||||
cpp/ecl_ext.h \
|
cpp/ecl_ext.h \
|
||||||
|
|
@ -49,7 +35,10 @@ SOURCES += \
|
||||||
cpp/lqml.cpp \
|
cpp/lqml.cpp \
|
||||||
cpp/qml.cpp \
|
cpp/qml.cpp \
|
||||||
cpp/qt_ecl.cpp \
|
cpp/qt_ecl.cpp \
|
||||||
|
cpp/single_shot.cpp \
|
||||||
cpp/main.cpp
|
cpp/main.cpp
|
||||||
|
|
||||||
QMAKE_CXXFLAGS += -std=c++17
|
QMAKE_CXXFLAGS += -std=c++17
|
||||||
|
|
||||||
|
QMAKE_PRE_LINK = ecl -shell $$PWD/make.lisp
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,41 @@
|
||||||
|
(when (search "/ecl-android/" (first (ext:command-args)))
|
||||||
|
(pushnew :android *features*))
|
||||||
|
|
||||||
(require :asdf)
|
(require :asdf)
|
||||||
|
|
||||||
(push (merge-pathnames "../")
|
(push (merge-pathnames "../")
|
||||||
asdf:*central-registry*)
|
asdf:*central-registry*)
|
||||||
|
|
||||||
(asdf:make-build "lqml"
|
(setf *default-pathname-defaults*
|
||||||
|
(merge-pathnames "../../")) ; LQML root
|
||||||
|
|
||||||
|
#-android
|
||||||
|
(progn
|
||||||
|
(asdf:make-build "lqml"
|
||||||
:monolithic t
|
:monolithic t
|
||||||
:type :static-library
|
:type :static-library
|
||||||
:move-here "./"
|
:move-here (format nil "platforms/~A/lib/"
|
||||||
|
#+linux "linux"
|
||||||
|
#+darwin "macos")
|
||||||
:init-name "ini_LQML")
|
:init-name "ini_LQML")
|
||||||
|
(let* ((from (format nil "platforms/~A/lib/lqml--all-systems.a"
|
||||||
|
#+linux "linux"
|
||||||
|
#+darwin "macos"))
|
||||||
|
(to "liblisp.a")
|
||||||
|
(to* (format nil "platforms/~A/lib/~A"
|
||||||
|
#+linux "linux"
|
||||||
|
#+darwin "macos"
|
||||||
|
to)))
|
||||||
|
(when (probe-file to*)
|
||||||
|
(delete-file to*))
|
||||||
|
(rename-file from to)))
|
||||||
|
|
||||||
(let ((from "lqml--all-systems.a")
|
#+android
|
||||||
(to "liblqml.a"))
|
(progn
|
||||||
(when (probe-file to)
|
(defvar *asdf-system* :lqml)
|
||||||
(delete-file to))
|
(defvar *library-name* "platforms/android/lib/lisp")
|
||||||
(rename-file from to))
|
(defvar *init-name* "ini_LQML")
|
||||||
|
(load "platforms/shared/make"))
|
||||||
|
|
||||||
|
(terpri)
|
||||||
|
|
||||||
|
|
|
||||||
3
src/mkdirs.sh
Executable file
3
src/mkdirs.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
mkdir build
|
||||||
|
mkdir build-android
|
||||||
|
mkdir build-ios
|
||||||
Loading…
Add table
Add a link
Reference in a new issue