aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--NEWS4
-rw-r--r--README.adoc10
-rw-r--r--xA/.gitignore1
-rw-r--r--xA/Makefile2
-rw-r--r--xA/go.mod36
-rw-r--r--xA/go.sum64
-rw-r--r--xC.c5
-rw-r--r--xP/go.mod2
-rw-r--r--xP/xP.go82
-rw-r--r--xT/CMakeLists.txt25
-rw-r--r--xT/xT.cpp2
-rw-r--r--xT/xTq.cpp40
-rw-r--r--xT/xTq.h15
-rw-r--r--xT/xTq.qml105
15 files changed, 317 insertions, 77 deletions
diff --git a/.gitignore b/.gitignore
index ba08178..fcffad1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
/build
# Qt Creator files
+/.qtcreator
/CMakeLists.txt.user*
/xK.config
/xK.files
diff --git a/NEWS b/NEWS
index d9699ca..5affcd7 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,10 @@ Unreleased
* xP: added a network lag indicator to the user interface
+ * xP: started embedding the necessary web resources,
+ and making sure that the files have unique paths after change,
+ so that stale copies are not cached by browsers indefinitely
+
* Bumped relay protocol version
diff --git a/README.adoc b/README.adoc
index 1866b2a..4f1f5da 100644
--- a/README.adoc
+++ b/README.adoc
@@ -137,12 +137,12 @@ The precondition for running 'xC' frontends is enabling its relay interface:
/set general.relay_bind = "127.0.0.1:9000"
-To build the web server, you'll need to install the Go compiler, and run `make`
-from the _xP_ directory. Then start it from the _public_ subdirectory,
-and navigate to the adress you gave it as its first argument--in the following
-example, that would be http://localhost:8080[]:
+To build the web server, install the Go compiler, and run `make`
+from the _xP_ directory. Then start the resulting binary, and navigate to
+the adress you give it as its first argument--in the following example,
+that would be http://localhost:8080[]:
- $ ../xP 127.0.0.1:8080 127.0.0.1:9000
+ $ ./xP 127.0.0.1:8080 127.0.0.1:9000
For remote use, it's recommended to put 'xP' behind a reverse proxy, with TLS,
and some form of HTTP authentication. Pass the external URL of the WebSocket
diff --git a/xA/.gitignore b/xA/.gitignore
index 5e6a147..2fc8b1c 100644
--- a/xA/.gitignore
+++ b/xA/.gitignore
@@ -1,4 +1,5 @@
/xA
+/xA.apk
/proto.go
/FyneApp.toml
/*.png
diff --git a/xA/Makefile b/xA/Makefile
index d0f0449..0770fcf 100644
--- a/xA/Makefile
+++ b/xA/Makefile
@@ -32,5 +32,7 @@ proto.go: $(tools)/lxdrgen.awk $(tools)/lxdrgen-go.awk ../xC.lxdr
xA: xA.go ../xK-version $(generated)
go build -ldflags "-X 'main.projectVersion=$$(cat ../xK-version)'" -o $@ \
-gcflags=all="-N -l"
+xA.apk: $(generated)
+ fyne package -os android
clean:
rm -f $(outputs)
diff --git a/xA/go.mod b/xA/go.mod
index 49c25cc..7b783f8 100644
--- a/xA/go.mod
+++ b/xA/go.mod
@@ -1,25 +1,23 @@
module janouch.name/xK/xA
-go 1.23.0
-
-toolchain go1.24.0
+go 1.24.0
require (
- fyne.io/fyne/v2 v2.6.0
- github.com/ebitengine/oto/v3 v3.3.3
+ fyne.io/fyne/v2 v2.7.0
+ github.com/ebitengine/oto/v3 v3.4.0
)
require (
- fyne.io/systray v1.11.0 // indirect
+ fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/ebitengine/purego v0.8.2 // indirect
- github.com/fredbi/uri v1.1.0 // indirect
+ github.com/ebitengine/purego v0.9.0 // indirect
+ github.com/fredbi/uri v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
- github.com/fyne-io/gl-js v0.1.0 // indirect
- github.com/fyne-io/glfw-js v0.2.0 // indirect
+ github.com/fyne-io/gl-js v0.2.0 // indirect
+ github.com/fyne-io/glfw-js v0.3.0 // indirect
github.com/fyne-io/image v0.1.1 // indirect
- github.com/fyne-io/oksvg v0.1.0 // indirect
+ github.com/fyne-io/oksvg v0.2.0 // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 // indirect
github.com/go-text/render v0.2.0 // indirect
@@ -27,20 +25,20 @@ require (
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
github.com/hack-pad/safejs v0.1.1 // indirect
- github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45 // indirect
+ github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/rymdport/portal v0.4.1 // indirect
+ github.com/rymdport/portal v0.4.2 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
- github.com/stretchr/testify v1.10.0 // indirect
- github.com/yuin/goldmark v1.7.10 // indirect
- golang.org/x/image v0.26.0 // indirect
- golang.org/x/net v0.39.0 // indirect
- golang.org/x/sys v0.32.0 // indirect
- golang.org/x/text v0.24.0 // indirect
+ github.com/stretchr/testify v1.11.1 // indirect
+ github.com/yuin/goldmark v1.7.13 // indirect
+ golang.org/x/image v0.32.0 // indirect
+ golang.org/x/net v0.46.0 // indirect
+ golang.org/x/sys v0.37.0 // indirect
+ golang.org/x/text v0.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/xA/go.sum b/xA/go.sum
index 3a6f7ce..7000c3c 100644
--- a/xA/go.sum
+++ b/xA/go.sum
@@ -1,30 +1,30 @@
-fyne.io/fyne/v2 v2.6.0 h1:Rywo9yKYN4qvNuvkRuLF+zxhJYWbIFM+m4N4KV4p1pQ=
-fyne.io/fyne/v2 v2.6.0/go.mod h1:YZt7SksjvrSNJCwbWFV32WON3mE1Sr7L41D29qMZ/lU=
-fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
-fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
+fyne.io/fyne/v2 v2.7.0 h1:GvZSpE3X0liU/fqstInVvRsaboIVpIWQ4/sfjDGIGGQ=
+fyne.io/fyne/v2 v2.7.0/go.mod h1:xClVlrhxl7D+LT+BWYmcrW4Nf+dJTvkhnPgji7spAwE=
+fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 h1:eA5/u2XRd8OUkoMqEv3IBlFYSruNlXD8bRHDiqm0VNI=
+fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/ebitengine/oto/v3 v3.3.3 h1:m6RV69OqoXYSWCDsHXN9rc07aDuDstGHtait7HXSM7g=
-github.com/ebitengine/oto/v3 v3.3.3/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U=
-github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
-github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
+github.com/ebitengine/oto/v3 v3.4.0 h1:br0PgASsEWaoWn38b2Goe7m1GKFYfNgnsjSd5Gg+/bQ=
+github.com/ebitengine/oto/v3 v3.4.0/go.mod h1:IOleLVD0m+CMak3mRVwsYY8vTctQgOM0iiL6S7Ar7eI=
+github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k=
+github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
-github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
-github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
+github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
+github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/fyne-io/gl-js v0.1.0 h1:8luJzNs0ntEAJo+8x8kfUOXujUlP8gB3QMOxO2mUdpM=
-github.com/fyne-io/gl-js v0.1.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
-github.com/fyne-io/glfw-js v0.2.0 h1:8GUZtN2aCoTPNqgRDxK5+kn9OURINhBEBc7M4O1KrmM=
-github.com/fyne-io/glfw-js v0.2.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
+github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
+github.com/fyne-io/gl-js v0.2.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
+github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk=
+github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
-github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw=
-github.com/fyne-io/oksvg v0.1.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
+github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
+github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
@@ -43,8 +43,8 @@ github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQb
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
-github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45 h1:vFdvrlsVU+p/KFBWTq0lTG4fvWvG88sawGlCzM+RUEU=
-github.com/jeandeaual/go-locale v0.0.0-20250421151639-a9d6ed1b3d45/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
+github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
+github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -59,24 +59,24 @@ github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
-github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
+github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
+github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
-github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
-golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
-golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
-golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
-golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
-golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
-golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
-golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
+github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
+golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ=
+golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc=
+golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
+golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
+golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
+golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/xC.c b/xC.c
index d79b600..c27f5f5 100644
--- a/xC.c
+++ b/xC.c
@@ -653,10 +653,15 @@ input_rl_buffer_destroy (void *input, input_buffer_t input_buffer)
HISTORY_STATE *state = history_get_history_state ();
history_set_history_state (buffer->history);
+
+ // TODO: Actually figure out why these cause crashes later.
+#if RL_READLINE_VERSION <= 0x0802
rl_clear_history ();
// rl_clear_history just removes history entries,
// we have to reclaim memory for their actual container ourselves
free (buffer->history->entries);
+#endif
+
free (buffer->history);
buffer->history = NULL;
diff --git a/xP/go.mod b/xP/go.mod
index dca4d10..b79feff 100644
--- a/xP/go.mod
+++ b/xP/go.mod
@@ -1,6 +1,6 @@
module janouch.name/xK/xP
-go 1.21
+go 1.22
toolchain go1.23.2
diff --git a/xP/xP.go b/xP/xP.go
index 188fd5d..9173afc 100644
--- a/xP/xP.go
+++ b/xP/xP.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
+// Copyright (c) 2022 - 2025, Přemysl Eric Janouch <p@janouch.name>
// SPDX-License-Identifier: 0BSD
package main
@@ -6,12 +6,16 @@ package main
import (
"bufio"
"context"
+ "crypto/sha1"
+ "embed"
"encoding/binary"
+ "encoding/hex"
"encoding/json"
"flag"
"fmt"
"html/template"
"io"
+ "io/fs"
"log"
"net"
"net/http"
@@ -23,7 +27,12 @@ import (
)
var (
- debug = flag.Bool("debug", false, "enable debug output")
+ debug = flag.Bool("debug", false, "enable debug output")
+ webRoot = flag.String("webroot", "", "override bundled web resources")
+
+ //go:embed public/*
+ webResources embed.FS
+ webResourcesHash string
addressBind string
addressConnect string
@@ -167,9 +176,11 @@ func handleWS(w http.ResponseWriter, r *http.Request) {
}
// AppleWebKit can be broken with compression.
- if agent := r.UserAgent(); strings.Contains(agent, " Version/") &&
- (strings.HasPrefix(agent, "Mozilla/5.0 (Macintosh; ") ||
- strings.HasPrefix(agent, "Mozilla/5.0 (iPhone; ")) {
+ // It would be more reliable to check for 'ApplePaySession' in window in JS,
+ // and have us disable compression based on a query parameter.
+ if agent := r.UserAgent(); (strings.Contains(agent, " Version/") &&
+ strings.HasPrefix(agent, "Mozilla/5.0 (Macintosh; ")) ||
+ strings.HasPrefix(agent, "Mozilla/5.0 (iPhone; ") {
opts.CompressionMode = websocket.CompressionDisabled
}
@@ -240,21 +251,20 @@ func handleWS(w http.ResponseWriter, r *http.Request) {
// -----------------------------------------------------------------------------
-var staticHandler = http.FileServer(http.Dir("."))
-
var page = template.Must(template.New("/").Parse(`<!DOCTYPE html>
<html>
<head>
<title>xP</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
+ <base href="{{ .Root }}/">
<link rel="stylesheet" href="xP.css" />
</head>
<body>
<script src="mithril.js">
</script>
<script>
- let proxy = '{{ . }}'
+ let proxy = '{{ .Proxy }}'
</script>
<script type="module" src="xP.js">
</script>
@@ -262,20 +272,49 @@ var page = template.Must(template.New("/").Parse(`<!DOCTYPE html>
</html>`))
func handleDefault(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/" {
- staticHandler.ServeHTTP(w, r)
- return
- }
-
wsURI := addressWS
if wsURI == "" {
wsURI = fmt.Sprintf("ws://%s/ws", r.Host)
}
- if err := page.Execute(w, wsURI); err != nil {
+
+ args := struct {
+ Root string
+ Proxy string
+ }{
+ Root: webResourcesHash,
+ Proxy: wsURI,
+ }
+ if err := page.Execute(w, &args); err != nil {
log.Println("Template execution failed: " + err.Error())
}
}
+func hashFS(root fs.FS) []byte {
+ hasher := sha1.New()
+ callback := func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // Note that this can be fooled.
+ fmt.Fprintln(hasher, path)
+
+ if !d.IsDir() {
+ file, err := root.Open(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+ io.Copy(hasher, file)
+ }
+ return nil
+ }
+ if err := fs.WalkDir(root, ".", callback); err != nil {
+ log.Fatalln(err)
+ }
+ return hasher.Sum(nil)
+}
+
func main() {
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(),
@@ -294,6 +333,21 @@ func main() {
addressWS = flag.Arg(2)
}
+ subResources, err := fs.Sub(webResources, "public")
+ if err != nil {
+ log.Fatalln(err)
+ }
+ if *webRoot != "" {
+ subResources = os.DirFS(*webRoot)
+ }
+
+ // The simplest way of ensuring that web browsers don't use
+ // stale cached copies of our files.
+ webResourcesHash = hex.EncodeToString(hashFS(subResources))
+ http.Handle("/"+webResourcesHash+"/",
+ http.StripPrefix("/"+webResourcesHash+"/",
+ http.FileServerFS(subResources)))
+
http.Handle("/ws", http.HandlerFunc(handleWS))
http.Handle("/", http.HandlerFunc(handleDefault))
diff --git a/xT/CMakeLists.txt b/xT/CMakeLists.txt
index 8f27be3..562c15a 100644
--- a/xT/CMakeLists.txt
+++ b/xT/CMakeLists.txt
@@ -12,8 +12,10 @@ project (xT VERSION "${project_version}"
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
-find_package (Qt6 REQUIRED COMPONENTS Widgets Network Multimedia)
-qt_standard_project_setup ()
+find_package (Qt6 REQUIRED COMPONENTS Widgets Network Multimedia
+ Quick QuickControls2)
+# XXX: The version requirement is probably for Qt Quick only.
+qt_standard_project_setup (REQUIRES 6.5)
add_compile_options ("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options ("$<$<CXX_COMPILER_ID:GNU>:-Wall;-Wextra>")
@@ -77,7 +79,7 @@ else ()
endif ()
# Build the main executable and link it
-find_program (awk_EXECUTABLE awk ${find_program_REQUIRE})
+find_program (awk_EXECUTABLE awk REQUIRED)
add_custom_command (OUTPUT xC-proto.cpp
COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${awk_EXECUTABLE}
-f ${root}/liberty/tools/lxdrgen.awk
@@ -103,11 +105,24 @@ set_target_properties (xT PROPERTIES WIN32_EXECUTABLE ON MACOSX_BUNDLE ON
# https://stackoverflow.com/questions/79079161 and resolved in Qt Creator 16.
set (QT_QML_GENERATE_QMLLS_INI ON)
+# TODO(p): Perhaps do it in one-or-the-other way,
+# as Qt Quick sucks on the desktop, and Qt Widgets is unusable on mobile.
+qt_add_executable (xTq
+ xTq.cpp ${project_config} ${project_sources} "${icon_icns}")
+set_property (SOURCE xTq.qml APPEND PROPERTY QT_QML_SOURCE_TYPENAME Main)
+qt_add_qml_module (xTq URI xTquick VERSION 1.0 QML_FILES xTq.qml)
+add_dependencies (xTq xC-proto)
+qt_add_resources (xTq "rsrc" PREFIX / FILES "${beep}" ${icon_rsrc_list})
+target_link_libraries (xTq PRIVATE
+ Qt6::Quick Qt6::QuickControls2 Qt6::Network Qt6::Multimedia)
+set_target_properties (xTq PROPERTIES WIN32_EXECUTABLE ON MACOSX_BUNDLE ON
+ MACOSX_BUNDLE_GUI_IDENTIFIER name.janouch.xTq)
+
# The files to be installed
include (GNUInstallDirs)
if (ANDROID)
- install (TARGETS xT DESTINATION .)
+ install (TARGETS xTq DESTINATION .)
elseif (APPLE OR WIN32)
install (TARGETS xT
BUNDLE DESTINATION .
@@ -144,7 +159,7 @@ if (WIN32)
foreach (lib ${libs})
string (STRIP "${lib}" lib)
file (COPY "${cygroot}${lib}" DESTINATION "${bindir}")
- endforeach()
+ endforeach ()
endif ()
]=])
endif ()
diff --git a/xT/xT.cpp b/xT/xT.cpp
index d82b87b..b708b95 100644
--- a/xT/xT.cpp
+++ b/xT/xT.cpp
@@ -1,5 +1,5 @@
/*
- * xT.cpp: Qt frontend for xC
+ * xT.cpp: Qt Widgets frontend for xC
*
* Copyright (c) 2024, Přemysl Eric Janouch <p@janouch.name>
*
diff --git a/xT/xTq.cpp b/xT/xTq.cpp
new file mode 100644
index 0000000..a6d48bf
--- /dev/null
+++ b/xT/xTq.cpp
@@ -0,0 +1,40 @@
+/*
+ * xTq.cpp: Qt Quick frontend for xC
+ *
+ * Copyright (c) 2024, Přemysl Eric Janouch <p@janouch.name>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "xC-proto.cpp"
+
+#include <cstdint>
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+#include "xTq.h"
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+int
+main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
+ &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
+ engine.loadFromModule("xTquick", "Main");
+ return app.exec();
+}
diff --git a/xT/xTq.h b/xT/xTq.h
new file mode 100644
index 0000000..70a0374
--- /dev/null
+++ b/xT/xTq.h
@@ -0,0 +1,15 @@
+#ifndef XTQ_H
+#define XTQ_H
+
+#include <QTcpSocket>
+#include <QtQmlIntegration/qqmlintegration.h>
+
+class RelayConnection : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+
+public:
+ QTcpSocket *socket; ///< Buffered relay socket
+};
+
+#endif // XTQ_H
diff --git a/xT/xTq.qml b/xT/xTq.qml
new file mode 100644
index 0000000..50063c9
--- /dev/null
+++ b/xT/xTq.qml
@@ -0,0 +1,105 @@
+import QtQuick
+import QtQuick.Controls.Fusion
+//import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ id: window
+ width: 640
+ height: 480
+ visible: true
+ title: qsTr("xT")
+
+ property RelayConnection connection
+
+ ColumnLayout {
+ id: column
+ anchors.fill: parent
+ anchors.margins: 6
+
+ ScrollView {
+ id: bufferScroll
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ TextArea {
+ id: buffer
+ text: qsTr("Buffer text")
+ }
+ }
+
+ RowLayout {
+ id: row
+ Layout.fillWidth: true
+
+ Label {
+ Layout.fillWidth: true
+ id: prompt
+ text: qsTr("Prompt")
+ }
+
+ Label {
+ Layout.fillWidth: true
+ id: status
+ horizontalAlignment: Text.AlignRight
+ text: qsTr("Status")
+ }
+ }
+
+ TextArea {
+ id: input
+ Layout.fillWidth: true
+ text: qsTr("Input")
+ }
+ }
+
+ Component.onCompleted: {}
+
+ Dialog {
+ id: connect
+ title: "Connect to relay"
+ anchors.centerIn: parent
+ modal: true
+ visible: true
+
+ onRejected: Qt.quit()
+ onAccepted: {
+ // TODO(p): Store the host, store the port, initiate connection.
+ }
+
+ GridLayout {
+ anchors.fill: parent
+ anchors.margins: 6
+ columns: 2
+
+ // It is a bit silly that one has to do everything manually.
+ Keys.onReturnPressed: connect.accept()
+
+ Label { text: "Host:" }
+ TextField {
+ id: connectHost
+ Layout.fillWidth: true
+ // And if this doesn't work reliably, do it after open().
+ focus: true
+ }
+ Label { text: "Port:" }
+ TextField {
+ id: connectPort
+ Layout.fillWidth: true
+ }
+ }
+
+ footer: DialogButtonBox {
+ Button {
+ text: qsTr("Connect")
+ DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
+ Keys.onReturnPressed: connect.accept()
+ highlighted: true
+ }
+ Button {
+ text: qsTr("Close")
+ DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole
+ Keys.onReturnPressed: connect.reject()
+ }
+ }
+ }
+}