From 00cdf0ff637ba3cc8e72981a8454c9a1f458c9f3 Mon Sep 17 00:00:00 2001 From: rewine Date: Sat, 23 May 2026 17:36:35 +0800 Subject: [PATCH 1/3] build: update xmake-requires.lock dependency versions Update xmake-repo source from gitee mirror to github/gitlab upstream, refresh dependency versions and add new transitive dependencies. --- examples/xmake-requires.lock | 256 ++++++++++++++++++++++++++++++-- xmake-requires.lock | 276 +++++++++++++++++++++-------------- 2 files changed, 407 insertions(+), 125 deletions(-) diff --git a/examples/xmake-requires.lock b/examples/xmake-requires.lock index 30817716d..8d1ecdada 100644 --- a/examples/xmake-requires.lock +++ b/examples/xmake-requires.lock @@ -1,4 +1,230 @@ { + ["linux|x86_64"] = { + ["ca-certificates#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "20250131" + }, + ["cairo#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.18.0" + }, + ["cmake#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "4.2.3" + }, + ["expat#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "2.7.4" + }, + ["fontconfig#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "2.17.1" + }, + ["freetype#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "2.14.1" + }, + ["gperf#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "3.2.1" + }, + ["libffi#0b3b0fcf"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "3.4.8" + }, + ["libpng#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "v1.6.55" + }, + ["libpthread-stubs#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "0.5" + }, + ["libx11#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.8.13" + }, + ["libxau#16fb575b"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.0.12" + }, + ["libxcb#16fb575b"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.17.0" + }, + ["libxdmcp#16fb575b"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.1.5" + }, + ["libxext#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.3.7" + }, + ["libxrender#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "0.9.12" + }, + ["meson#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.10.2" + }, + ["ninja#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "v1.13.1" + }, + ["openssl >=1.1.1-a#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.1.1-w" + }, + ["pixman#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "0.46.2" + }, + ["pkg-config#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "0.29.2" + }, + ["python 3.x#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "3.13.11" + }, + ["util-macros#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.20.0" + }, + ["xcb-proto#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.17.0" + }, + ["xorgproto#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "2023.2" + }, + ["xtrans#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "1.6.0" + }, + ["zlib#0b3b0fcf"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "v1.3.2" + }, + ["zlib#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://gitlab.com/tboox/xmake-repo.git" + }, + version = "v1.3.2" + } + }, __meta__ = { version = "1.0" }, @@ -53,19 +279,19 @@ }, ["lcui#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libcss#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libi18n#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, @@ -95,7 +321,7 @@ }, ["libplatform#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, @@ -109,55 +335,55 @@ }, ["libthread#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libtimer#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libui#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libui-cursor#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libui-router#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libui-server#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libui-widgets#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libui-xml#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, ["libworker#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, @@ -195,7 +421,7 @@ }, ["pandagl#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, @@ -225,7 +451,7 @@ }, ["yutil#31fecfc4"] = { repo = { - url = "../build" + url = "../build/packages" }, version = "latest" }, diff --git a/xmake-requires.lock b/xmake-requires.lock index f7674df84..6cde30a4a 100644 --- a/xmake-requires.lock +++ b/xmake-requires.lock @@ -1,241 +1,297 @@ { - __meta__ = { - version = "1.0" - }, ["linux|x86_64"] = { - ["ca-certificates#31fecfc4"] = { + ["bison#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "20240207" + version = "3.8.2" }, - ["cmake#31fecfc4"] = { + ["ca-certificates#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "3.28.3" + version = "20250131" }, - ["expat#31fecfc4"] = { + ["cairo#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", url = "https://gitee.com/tboox/xmake-repo.git" }, - version = "2.6.1" + version = "1.18.0" }, - ["fontconfig#31fecfc4"] = { + ["cmake#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "2.15.0" + version = "4.2.3" }, - ["freetype#31fecfc4"] = { + ["expat#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "26.1.20" + version = "2.7.4" }, - ["freetype#8cb27d06"] = { + ["fontconfig#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "26.1.20" + version = "2.17.1" }, - ["gperf#31fecfc4"] = { + ["freetype#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", url = "https://gitee.com/tboox/xmake-repo.git" }, - version = "3.1" + version = "2.14.1" }, - ["libffi#f26e00a1"] = { + ["gperf#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "3.4.6" + version = "3.2.1" }, - ["libjpeg#31fecfc4"] = { + ["libffi#0b3b0fcf"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" + }, + version = "3.4.8" + }, + ["libffi#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" + }, + version = "3.4.8" + }, + ["libjpeg#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, version = "v9f" }, - ["libomp#31fecfc4"] = { + ["libomp#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "12.0.1" + version = "19.1.0" }, - ["libpng#31fecfc4"] = { + ["libpng#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.6.43" + version = "v1.6.55" }, - ["libpthread-stubs#31fecfc4"] = { + ["libpthread-stubs#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "0.4" + version = "0.5" }, - ["libx11#31fecfc4"] = { + ["libx11#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.8.7" + version = "1.8.13" }, - ["libxau#31fecfc4"] = { + ["libxau#16fb575b"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.0.9" + version = "1.0.12" }, - ["libxcb#31fecfc4"] = { + ["libxcb#16fb575b"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.15" + version = "1.17.0" }, - ["libxdmcp#31fecfc4"] = { + ["libxdmcp#16fb575b"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.1.3" + version = "1.1.5" }, - ["libxml2#31fecfc4"] = { + ["libxext#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", url = "https://gitee.com/tboox/xmake-repo.git" }, - version = "2.9.14" + version = "1.3.7" }, - ["libyaml#31fecfc4"] = { + ["libxml2#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" + }, + version = "v2.14.3" + }, + ["libxrender#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", url = "https://gitee.com/tboox/xmake-repo.git" }, + version = "0.9.12" + }, + ["libyaml#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" + }, version = "0.2.5" }, - ["meson#31fecfc4"] = { + ["m4#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.3.2" + version = "1.4.19" }, - ["ninja#31fecfc4"] = { + ["meson#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.11.1" + version = "1.10.2" }, - ["openssl >=1.1.1-a#31fecfc4"] = { + ["ninja#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" + }, + version = "v1.13.1" + }, + ["openssl >=1.1.1-a#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, version = "1.1.1-w" }, - ["pkg-config#31fecfc4"] = { + ["pixman#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", url = "https://gitee.com/tboox/xmake-repo.git" }, - version = "1.8.1" + version = "0.46.2" }, - ["python 3.x#31fecfc4"] = { + ["pkg-config#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "3.12.3" + version = "0.29.2" }, - ["util-macros#31fecfc4"] = { + ["python 3.x#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" + }, + version = "3.13.11" + }, + ["util-macros#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, version = "1.20.0" }, - ["xcb-proto#31fecfc4"] = { + ["wayland#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.16.0" + version = "1.24.0" }, - ["xorgproto#31fecfc4"] = { + ["xcb-proto#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.0" + version = "1.17.0" }, - ["xtrans#31fecfc4"] = { + ["xorgproto#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "1.4.0" + version = "2023.2" }, - ["zlib#31fecfc4"] = { + ["xtrans#f56260b5"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "v1.3.1" + version = "1.6.0" }, - ["zlib#f26e00a1"] = { + ["zlib#0b3b0fcf"] = { repo = { branch = "master", - commit = "d5382a3bab22616b1c5b0292a6ce460d116df916", - url = "https://gitee.com/tboox/xmake-repo.git" + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" }, - version = "v1.3.1" + version = "v1.3.2" + }, + ["zlib#f56260b5"] = { + repo = { + branch = "master", + commit = "75c3fcffb897a8aeb37863c495b9f58266216b26", + url = "https://github.com/xmake-io/xmake-repo.git" + }, + version = "v1.3.2" } }, + __meta__ = { + version = "1.0" + }, ["windows|x64"] = { ["cmake#31fecfc4"] = { repo = { From e75c0f365d6b9357322ebfffdadf6eb32f4b4511 Mon Sep 17 00:00:00 2001 From: rewine Date: Sat, 23 May 2026 17:36:50 +0800 Subject: [PATCH 2/3] build(examples): switch from add_packages to add_deps for lcui Include examples in main build via includes("examples/*/xmake.lua") and use add_deps("lcui") to reference the project target directly, instead of the previous add_repositories + add_requires flow. --- examples/fabric/xmake.lua | 4 +++- examples/hello/xmake.lua | 5 +++-- examples/todolist/xmake.lua | 3 ++- examples/xmake.lua | 2 -- xmake.lua | 1 + 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/examples/fabric/xmake.lua b/examples/fabric/xmake.lua index 65de36793..a8b969f9b 100644 --- a/examples/fabric/xmake.lua +++ b/examples/fabric/xmake.lua @@ -2,8 +2,10 @@ add_requires("cairo", { optional = true }) target("fabric") set_kind("binary") + set_default(false) add_files("src/*.c") - add_packages("cairo", "lcui") + add_deps("lcui") + add_packages("cairo") if has_package("cairo") then add_defines("HAS_CAIRO") end diff --git a/examples/hello/xmake.lua b/examples/hello/xmake.lua index f5bb3ab21..0d447a201 100644 --- a/examples/hello/xmake.lua +++ b/examples/hello/xmake.lua @@ -1,6 +1,7 @@ target("hello") - add_installfiles("app/*") set_kind("binary") + set_default(false) set_rundir("app") - add_packages("lcui") add_files("src/*.c") + add_deps("lcui") + add_syslinks("m") diff --git a/examples/todolist/xmake.lua b/examples/todolist/xmake.lua index 1cd8585ae..8da13ac07 100644 --- a/examples/todolist/xmake.lua +++ b/examples/todolist/xmake.lua @@ -2,5 +2,6 @@ target("todolist") add_installfiles("app/*") set_kind("binary") set_rundir("app") - add_packages("lcui") + set_default(false) + add_deps("lcui") add_files("src/*.c") diff --git a/examples/xmake.lua b/examples/xmake.lua index 26fff7ff5..62eabb666 100644 --- a/examples/xmake.lua +++ b/examples/xmake.lua @@ -1,6 +1,4 @@ set_policy("package.requires_lock", true) -add_repositories("lcui-repo ../build") -add_requires("lcui") add_rules("mode.debug", "mode.release") if is_plat("windows") then add_rules("win.sdk.application") diff --git a/xmake.lua b/xmake.lua index 768c83438..6f3e20347 100644 --- a/xmake.lua +++ b/xmake.lua @@ -24,6 +24,7 @@ add_includedirs( ) includes("lib/*/xmake.lua") includes("tests/xmake.lua") +includes("examples/*/xmake.lua") option("ci-env", {showmenu = true, default = false}) From 2cc4ea963800ec4f90438212ca6f07044a3cf218 Mon Sep 17 00:00:00 2001 From: rewine Date: Sat, 23 May 2026 17:37:01 +0800 Subject: [PATCH 3/3] feat(ptk): add preliminary Wayland backend implementation --- lib/i18n/include/i18n.h | 2 +- lib/ptk/include/ptk/types.h | 4 +- lib/ptk/rules/xmake.lua | 27 ++ lib/ptk/src/linux/app.c | 22 +- lib/ptk/src/linux/ime.c | 24 +- lib/ptk/src/linux/wayland_internal.h | 100 +++++++ lib/ptk/src/linux/waylandapp.h | 9 + lib/ptk/src/linux/waylandapp_core.c | 311 +++++++++++++++++++++ lib/ptk/src/linux/waylandapp_input.c | 370 ++++++++++++++++++++++++ lib/ptk/src/linux/waylandapp_output.c | 46 +++ lib/ptk/src/linux/waylandapp_window.c | 388 ++++++++++++++++++++++++++ lib/ptk/xmake.lua | 18 +- lib/router/include/router/version.h | 2 +- lib/thread/include/thread.h | 2 +- lib/ui-server/src/server.c | 1 + 15 files changed, 1308 insertions(+), 18 deletions(-) create mode 100644 lib/ptk/rules/xmake.lua create mode 100644 lib/ptk/src/linux/wayland_internal.h create mode 100644 lib/ptk/src/linux/waylandapp.h create mode 100644 lib/ptk/src/linux/waylandapp_core.c create mode 100644 lib/ptk/src/linux/waylandapp_input.c create mode 100644 lib/ptk/src/linux/waylandapp_output.c create mode 100644 lib/ptk/src/linux/waylandapp_window.c diff --git a/lib/i18n/include/i18n.h b/lib/i18n/include/i18n.h index 823942d96..1cc18e6f2 100644 --- a/lib/i18n/include/i18n.h +++ b/lib/i18n/include/i18n.h @@ -1,4 +1,4 @@ -/* +/* * lib/i18n/include/i18n.h * * Copyright (c) 2023-2025, Liu Chao All rights reserved. diff --git a/lib/ptk/include/ptk/types.h b/lib/ptk/include/ptk/types.h index bd5e19b80..8adf85979 100644 --- a/lib/ptk/include/ptk/types.h +++ b/lib/ptk/include/ptk/types.h @@ -1,4 +1,4 @@ -/* +/* * lib/ptk/include/ptk/types.h * * Copyright (c) 2023-2025, Liu Chao All rights reserved. @@ -22,6 +22,7 @@ typedef enum { PTK_APP_ID_UNKNOWN, PTK_APP_ID_LINUX, PTK_APP_ID_LINUX_X11, + PTK_APP_ID_LINUX_WAYLAND, PTK_APP_ID_WIN_DESKTOP, PTK_APP_ID_UWP } ptk_app_id_t; @@ -285,6 +286,7 @@ typedef struct ptk_window_driver { void (*set_size)(ptk_window_t *, int, int); void (*set_position)(ptk_window_t *, int, int); void *(*get_handle)(ptk_window_t *); + unsigned (*get_dpi)(ptk_window_t *); int (*get_width)(ptk_window_t *); int (*get_height)(ptk_window_t *); void (*set_min_width)(ptk_window_t *, int); diff --git a/lib/ptk/rules/xmake.lua b/lib/ptk/rules/xmake.lua new file mode 100644 index 000000000..2959b9a7b --- /dev/null +++ b/lib/ptk/rules/xmake.lua @@ -0,0 +1,27 @@ +rule("wayland.protocol") + on_load(function(target) + if target:sourcebatches()["wayland.protocol"] then + local generatedir = path.join(target:autogendir(), "rules", "wayland") + target:add("includedirs", generatedir) + end + end) + + before_buildcmd_file(function(target, batchcmds, sourcefile, opt) + local generatedir = path.join(target:autogendir(), "rules", "wayland") + local basename = path.basename(sourcefile) + local headerfile = path.join(generatedir, basename .. "-client-protocol.h") + local sourcefile_c = path.join(generatedir, basename .. "-protocol.c") + local objectfile = target:objectfile(sourcefile_c) + local scanner = os.getenv("WAYLAND_SCANNER") or "/usr/bin/wayland-scanner" + local code_mode = target:kind() == "shared" and "public-code" or "private-code" + + table.insert(target:objectfiles(), objectfile) + batchcmds:show_progress(opt.progress, "${color.build.object}wayland-scanner %s", sourcefile) + batchcmds:mkdir(generatedir) + batchcmds:vrunv(scanner, {"client-header", path(sourcefile), path(headerfile)}) + batchcmds:vrunv(scanner, {code_mode, path(sourcefile), path(sourcefile_c)}) + batchcmds:compile(sourcefile_c, objectfile) + batchcmds:add_depfiles(sourcefile) + batchcmds:set_depmtime(os.mtime(objectfile)) + batchcmds:set_depcache(target:dependfile(objectfile)) + end) diff --git a/lib/ptk/src/linux/app.c b/lib/ptk/src/linux/app.c index 7c14b8b3b..a01674c45 100644 --- a/lib/ptk/src/linux/app.c +++ b/lib/ptk/src/linux/app.c @@ -16,6 +16,7 @@ #include "keyboard.h" #include "fbapp.h" #include "x11app.h" +#include "waylandapp.h" #include "x11clipboard.h" #ifdef PTK_LINUX @@ -101,8 +102,10 @@ void *ptk_window_get_handle(ptk_window_t *wnd) unsigned ptk_window_get_dpi(ptk_window_t *wnd) { - // TODO: get dpi from x11 window or framebuffer? - return 96; + if (linux_app.window.get_dpi) { + return linux_app.window.get_dpi(wnd); + } + return 96; } int ptk_window_get_width(ptk_window_t *wnd) @@ -183,8 +186,19 @@ ptk_app_id_t ptk_get_app_id(void) int ptk_app_init(const wchar_t *name) { - linux_app.id = PTK_APP_ID_LINUX; + linux_app.id = PTK_APP_ID_LINUX; + ptk_waylandapp_driver_init(&linux_app.app); + ptk_waylandwindow_driver_init(&linux_app.window); + if (linux_app.app.init(name) == 0) { + logger_debug("[app] use engine: waylandapp\n"); + linux_app.id = PTK_APP_ID_LINUX_WAYLAND; + linux_app.active = true; + return 0; + } + memset(&linux_app.app, 0, sizeof(linux_app.app)); + memset(&linux_app.window, 0, sizeof(linux_app.window)); #ifdef PTK_HAS_LIBX11 + ptk_x11app_driver_init(&linux_app.app); ptk_x11window_driver_init(&linux_app.window); if (linux_app.app.init(name) == 0) { @@ -214,7 +228,7 @@ int ptk_app_destroy(void) return -1; } linux_app.active = false; - if (linux_app.id != PTK_APP_ID_LINUX_X11) { + if (linux_app.id == PTK_APP_ID_LINUX) { ptk_linux_mouse_destroy(); ptk_linux_keyboard_destroy(); } diff --git a/lib/ptk/src/linux/ime.c b/lib/ptk/src/linux/ime.c index 177015756..7f91d948e 100644 --- a/lib/ptk/src/linux/ime.c +++ b/lib/ptk/src/linux/ime.c @@ -1,4 +1,4 @@ -/* +/* * lib/ptk/src/linux/ime.c: -- The input method engine support for linux. * * Copyright (c) 2018-2025, Liu chao All rights reserved. @@ -17,6 +17,14 @@ #include #include +/* Shared by both X11 and Wayland: convert KEYPRESS key.code (Unicode codepoint) + * to a wchar_t and commit it through the IME layer. */ +static void on_key_press(ptk_event_t *e, void *arg) +{ + wchar_t text[2] = { (wchar_t)e->key.code, 0 }; + ptk_ime_commit(text, 2); +} + #ifdef PTK_HAS_LIBX11 static bool ptk_x11ime_process_key(int key, bool pressed) @@ -30,12 +38,6 @@ static void ptk_x11ime_to_text(int ch) ptk_ime_commit(text, 2); } -static void on_key_press(ptk_event_t *e, void *arg) -{ - wchar_t text[2] = { e->key.code, 0 }; - ptk_ime_commit(text, 2); -} - static bool ptk_x11ime_open(void) { ptk_on_event(PTK_EVENT_KEYPRESS, on_key_press, NULL); @@ -62,6 +64,14 @@ int ptk_ime_add_linux(void) return ptk_ime_add("LCUI X11 Input Method", &handler); } #endif + if (ptk_get_app_id() == PTK_APP_ID_LINUX_WAYLAND) { + /* Wayland backend: PTK_EVENT_KEYPRESS carries the Unicode + * codepoint in key.code (written by xkb_state_key_get_utf32). + * Register directly — no full IME handler needed for basic + * ASCII/Unicode input without a compositor text-input protocol. */ + ptk_on_event(PTK_EVENT_KEYPRESS, on_key_press, NULL); + return 0; + } return -1; } diff --git a/lib/ptk/src/linux/wayland_internal.h b/lib/ptk/src/linux/wayland_internal.h new file mode 100644 index 000000000..7855ef4d3 --- /dev/null +++ b/lib/ptk/src/linux/wayland_internal.h @@ -0,0 +1,100 @@ +#ifndef PTK_LINUX_WAYLAND_INTERNAL_H +#define PTK_LINUX_WAYLAND_INTERNAL_H + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ptk.h" +#include "keyboard.h" +#include "waylandapp.h" +#include "xdg-shell-client-protocol.h" +#include "xdg-decoration-unstable-v1-client-protocol.h" + +#define WAYLAND_DEFAULT_SCREEN_WIDTH 1280 +#define WAYLAND_DEFAULT_SCREEN_HEIGHT 720 + +typedef struct ptk_window { + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct zxdg_toplevel_decoration_v1 *decoration; + struct wl_buffer *buffer; + void *buffer_data; + size_t buffer_size; + int width; + int height; + bool configured; + /** Full-window canvas wrapping buffer_data (external memory, not + * owned by pandagl). Used as the quote source in begin_paint. */ + pd_canvas_t canvas; + pd_context_t *paint_ctx; + list_node_t node; +} ptk_window_t; + +typedef struct ptk_wayland_app { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shm *shm; + struct zxdg_decoration_manager_v1 *decoration_manager; + struct xdg_wm_base *wm_base; + struct wl_output *output; + struct wl_seat *seat; + struct wl_pointer *pointer; + struct wl_keyboard *keyboard; + ptk_window_t *pointer_focus; + ptk_window_t *keyboard_focus; + double pointer_x; + double pointer_y; + list_t windows; + int exit_code; + int screen_width; + int screen_height; + int output_scale; + + struct xkb_context *xkb_context; + struct xkb_keymap *xkb_map; + struct xkb_state *xkb_state; + uint32_t shift_mask; + uint32_t ctrl_mask; + uint32_t alt_mask; + uint32_t meta_mask; + uint32_t modifiers; + + /* Key repeat parameters from wl_keyboard.repeat_info (rate=0 means disabled) */ + int repeat_rate; /* characters/second */ + int repeat_delay; /* milliseconds before first repeat */ + + struct wl_cursor_theme *cursor_theme; + struct wl_cursor *default_cursor; + struct wl_surface *cursor_surface; + + bool running; +} ptk_wayland_app_t; + +extern ptk_wayland_app_t wl_app; + +extern const struct wl_output_listener output_listener; +extern const struct wl_seat_listener seat_listener; +extern const struct wl_registry_listener registry_listener; +extern const struct xdg_wm_base_listener wm_base_listener; +extern const struct xdg_surface_listener xdg_surface_listener; +extern const struct xdg_toplevel_listener xdg_toplevel_listener; + +ptk_window_t *ptk_waylandapp_get_window(void *handle); +void ptk_waylandwindow_destroy_buffer(ptk_window_t *wnd); +void ptk_waylandwindow_post_size_event(ptk_window_t *wnd); +ptk_window_t *ptk_waylandwindow_create(const wchar_t *title, int x, int y, int width, int height, ptk_window_t *parent); + +#endif diff --git a/lib/ptk/src/linux/waylandapp.h b/lib/ptk/src/linux/waylandapp.h new file mode 100644 index 000000000..48760aea1 --- /dev/null +++ b/lib/ptk/src/linux/waylandapp.h @@ -0,0 +1,9 @@ +#ifndef LIB_PTK_SRC_LINUX_WAYLANDAPP_H +#define LIB_PTK_SRC_LINUX_WAYLANDAPP_H + +#include "ptk/types.h" + +void ptk_waylandapp_driver_init(ptk_app_driver_t *driver); +void ptk_waylandwindow_driver_init(ptk_window_driver_t *driver); + +#endif diff --git a/lib/ptk/src/linux/waylandapp_core.c b/lib/ptk/src/linux/waylandapp_core.c new file mode 100644 index 000000000..154f3118f --- /dev/null +++ b/lib/ptk/src/linux/waylandapp_core.c @@ -0,0 +1,311 @@ +#include "wayland_internal.h" + +ptk_wayland_app_t wl_app; + +ptk_window_t * ptk_waylandapp_get_window(void *handle) +{ + list_node_t *node; + + for (list_each(node, &wl_app.windows)) { + ptk_window_t *wnd = node->data; + + if (wnd && wnd->surface == handle) { + return wnd; + } + } + return NULL; +} + +static void ptk_waylandapp_on_registry_global(void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + if (strcmp(interface, wl_compositor_interface.name) == 0) { + wl_app.compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 4); + return; + } + if (strcmp(interface, wl_shm_interface.name) == 0) { + wl_app.shm = + wl_registry_bind(registry, name, &wl_shm_interface, 1); + return; + } + if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + uint32_t bind_version = version < 1 ? version : 1; + wl_app.wm_base = wl_registry_bind(registry, name, + &xdg_wm_base_interface, + bind_version); + return; + } + if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { + wl_app.decoration_manager = wl_registry_bind(registry, name, + &zxdg_decoration_manager_v1_interface, + 1); + return; + } + if (strcmp(interface, wl_output_interface.name) == 0 && !wl_app.output) { + wl_app.output = wl_registry_bind(registry, name, + &wl_output_interface, 2); + wl_output_add_listener(wl_app.output, &output_listener, NULL); + return; + } + if (strcmp(interface, wl_seat_interface.name) == 0 && !wl_app.seat) { + wl_app.seat = wl_registry_bind(registry, name, + &wl_seat_interface, 5); + wl_seat_add_listener(wl_app.seat, &seat_listener, NULL); + return; + } +} + +static void ptk_waylandapp_on_registry_global_remove(void *data, + struct wl_registry *registry, + uint32_t name) +{ +} + +const struct wl_registry_listener registry_listener = { + ptk_waylandapp_on_registry_global, + ptk_waylandapp_on_registry_global_remove +}; + +static int ptk_waylandapp_poll_events(int timeout_ms, bool dispatch_all) +{ + struct pollfd pfd; + int result; + + result = wl_display_dispatch_pending(wl_app.display); + if (result < 0) { + return -1; + } + if (dispatch_all && result > 0) { + do { + result = wl_display_dispatch_pending(wl_app.display); + } while (result > 0); + if (result < 0) { + return -1; + } + } + if (wl_display_flush(wl_app.display) < 0 && errno != EAGAIN) { + return -1; + } + + pfd.fd = wl_display_get_fd(wl_app.display); + pfd.events = POLLIN; + pfd.revents = 0; + result = poll(&pfd, 1, timeout_ms); + if (result <= 0) { + return result; + } + if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + return -1; + } + if (!(pfd.revents & POLLIN)) { + return 0; + } + result = wl_display_dispatch(wl_app.display); + if (result < 0) { + return -1; + } + if (!dispatch_all) { + return result; + } + while ((result = wl_display_dispatch_pending(wl_app.display)) > 0) { + } + return result < 0 ? -1 : 1; +} + +static int ptk_waylandapp_init(const wchar_t *name) +{ + memset(&wl_app, 0, sizeof(wl_app)); + wl_app.output_scale = 1; + wl_app.display = wl_display_connect(NULL); + if (!wl_app.display) { + logger_error("[wayland] wl_display_connect() failed\n"); + return -1; + } + wl_app.screen_width = WAYLAND_DEFAULT_SCREEN_WIDTH; + wl_app.screen_height = WAYLAND_DEFAULT_SCREEN_HEIGHT; + wl_app.registry = wl_display_get_registry(wl_app.display); + if (!wl_app.registry) { + logger_error("[wayland] wl_display_get_registry() failed\n"); + wl_display_disconnect(wl_app.display); + wl_app.display = NULL; + return -1; + } + list_create(&wl_app.windows); + wl_registry_add_listener(wl_app.registry, ®istry_listener, NULL); + if (wl_display_roundtrip(wl_app.display) < 0) { + logger_error("[wayland] wl_display_roundtrip() failed during registry init\n"); + return -1; + } + if (!wl_app.compositor || !wl_app.shm || !wl_app.wm_base) { + logger_error("[wayland] missing globals: compositor=%p shm=%p wm_base=%p\n", + wl_app.compositor, wl_app.shm, wl_app.wm_base); + return -1; + } + xdg_wm_base_add_listener(wl_app.wm_base, &wm_base_listener, NULL); + wl_app.running = true; + wl_app.exit_code = 0; + return 0; +} + +static int ptk_waylandapp_destroy(void) +{ + list_node_t *node, *next; + ptk_window_t *wnd; + + for (node = wl_app.windows.head.next; node; node = next) { + next = node->next; + wnd = node->data; + if (wnd) { + if (wnd->decoration) { + zxdg_toplevel_decoration_v1_destroy(wnd->decoration); + } + if (wnd->xdg_toplevel) { + xdg_toplevel_destroy(wnd->xdg_toplevel); + } + if (wnd->xdg_surface) { + xdg_surface_destroy(wnd->xdg_surface); + } + if (wnd->surface) { + wl_surface_destroy(wnd->surface); + } + ptk_waylandwindow_destroy_buffer(wnd); + free(wnd->paint_ctx); + free(wnd); + } + } + list_create(&wl_app.windows); + if (wl_app.pointer) { + wl_pointer_destroy(wl_app.pointer); + } + if (wl_app.cursor_surface) { + wl_surface_destroy(wl_app.cursor_surface); + wl_app.cursor_surface = NULL; + } + if (wl_app.cursor_theme) { + wl_cursor_theme_destroy(wl_app.cursor_theme); + wl_app.cursor_theme = NULL; + wl_app.default_cursor = NULL; + } + if (wl_app.keyboard) { + wl_keyboard_destroy(wl_app.keyboard); + } + if (wl_app.xkb_state) { + xkb_state_unref(wl_app.xkb_state); + wl_app.xkb_state = NULL; + } + if (wl_app.xkb_map) { + xkb_keymap_unref(wl_app.xkb_map); + wl_app.xkb_map = NULL; + } + if (wl_app.xkb_context) { + xkb_context_unref(wl_app.xkb_context); + wl_app.xkb_context = NULL; + } + if (wl_app.seat) { + wl_seat_destroy(wl_app.seat); + } + if (wl_app.output) { + wl_output_destroy(wl_app.output); + } + if (wl_app.decoration_manager) { + zxdg_decoration_manager_v1_destroy(wl_app.decoration_manager); + } + if (wl_app.wm_base) { + xdg_wm_base_destroy(wl_app.wm_base); + } + if (wl_app.shm) { + wl_shm_destroy(wl_app.shm); + } + if (wl_app.compositor) { + wl_compositor_destroy(wl_app.compositor); + } + if (wl_app.registry) { + wl_registry_destroy(wl_app.registry); + } + if (wl_app.display) { + wl_display_disconnect(wl_app.display); + } + memset(&wl_app, 0, sizeof(wl_app)); + return 0; +} + +static int ptk_waylandapp_process_events(ptk_process_events_option_t option) +{ + int result = 0; + + wl_app.exit_code = 0; + if (option == PTK_PROCESS_EVENTS_ONE_IF_PRESENT || + option == PTK_PROCESS_EVENTS_ALL_IF_PRESENT) { + ptk_tick(); + ptk_process_events(); + result = ptk_waylandapp_poll_events( + 0, option == PTK_PROCESS_EVENTS_ALL_IF_PRESENT); + ptk_process_events(); + return result < 0 ? -1 : wl_app.exit_code; + } + while (wl_app.running) { + ptk_tick(); + ptk_process_events(); + result = ptk_waylandapp_poll_events(1, true); + if (result < 0) { + break; + } + ptk_process_events(); + } + return wl_app.exit_code; +} + +static void ptk_waylandapp_present(void) +{ + wl_display_flush(wl_app.display); +} + +static void ptk_waylandapp_exit(int exit_code) +{ + wl_app.running = false; + wl_app.exit_code = exit_code; +} + +static int ptk_waylandapp_on_event(int type, + ptk_native_event_handler_t handler, + void *data) +{ + return -1; +} + +static int ptk_waylandapp_off_event(int type, + ptk_native_event_handler_t handler) +{ + return -1; +} + +static int ptk_waylandapp_get_screen_width(void) +{ + return wl_app.screen_width / wl_app.output_scale; +} + +static int ptk_waylandapp_get_screen_height(void) +{ + return wl_app.screen_height / wl_app.output_scale; +} + +void ptk_waylandapp_driver_init(ptk_app_driver_t *driver) +{ + memset(driver, 0, sizeof(*driver)); + driver->init = ptk_waylandapp_init; + driver->destroy = ptk_waylandapp_destroy; + driver->process_events = ptk_waylandapp_process_events; + driver->on_event = ptk_waylandapp_on_event; + driver->off_event = ptk_waylandapp_off_event; + driver->get_screen_width = ptk_waylandapp_get_screen_width; + driver->get_screen_height = ptk_waylandapp_get_screen_height; + driver->create_window = ptk_waylandwindow_create; + driver->get_window = ptk_waylandapp_get_window; + driver->present = ptk_waylandapp_present; + driver->exit = ptk_waylandapp_exit; +} + diff --git a/lib/ptk/src/linux/waylandapp_input.c b/lib/ptk/src/linux/waylandapp_input.c new file mode 100644 index 000000000..e74a8402f --- /dev/null +++ b/lib/ptk/src/linux/waylandapp_input.c @@ -0,0 +1,370 @@ +#include "wayland_internal.h" + +static void ptk_waylandapp_on_pointer_enter( + void *data, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + wl_app.pointer_focus = ptk_waylandapp_get_window(surface); + /* surface_x/y are in logical pixels (compositor coordinate space); + * store physical coords internally for cursor hotspot math */ + wl_app.pointer_x = wl_fixed_to_double(surface_x); + wl_app.pointer_y = wl_fixed_to_double(surface_y); + + if (wl_app.default_cursor && wl_app.cursor_surface) { + struct wl_cursor_image *image = wl_app.default_cursor->images[0]; + wl_pointer_set_cursor(wl_pointer, serial, wl_app.cursor_surface, + image->hotspot_x / wl_app.output_scale, + image->hotspot_y / wl_app.output_scale); + wl_surface_set_buffer_scale(wl_app.cursor_surface, + wl_app.output_scale); + wl_surface_attach(wl_app.cursor_surface, + wl_cursor_image_get_buffer(image), 0, 0); + wl_surface_damage_buffer(wl_app.cursor_surface, 0, 0, image->width, + image->height); + wl_surface_commit(wl_app.cursor_surface); + } + + /* Wayland enter does not emit motion, but LCUI needs initial coordinate + * state applied immediately */ + if (wl_app.pointer_focus) { + ptk_event_t e = { 0 }; + e.type = PTK_EVENT_MOUSEMOVE; + e.window = wl_app.pointer_focus; + e.mouse.x = (int)wl_app.pointer_x; + e.mouse.y = (int)wl_app.pointer_y; + ptk_post_event(&e); + } +} + +static void ptk_waylandapp_on_pointer_leave(void *data, + struct wl_pointer *wl_pointer, + uint32_t serial, + struct wl_surface *surface) +{ + wl_app.pointer_focus = NULL; +} + +static void ptk_waylandapp_on_pointer_motion(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + ptk_event_t e = { 0 }; + + /* surface_x/y are in logical pixels (compositor coordinate space) */ + wl_app.pointer_x = wl_fixed_to_double(surface_x); + wl_app.pointer_y = wl_fixed_to_double(surface_y); + + if (wl_app.pointer_focus) { + e.type = PTK_EVENT_MOUSEMOVE; + e.window = wl_app.pointer_focus; + e.mouse.x = (int)wl_app.pointer_x; + e.mouse.y = (int)wl_app.pointer_y; + ptk_post_event(&e); + } +} + +static void ptk_waylandapp_on_pointer_button(void *data, + struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, + uint32_t button, uint32_t state) +{ + ptk_event_t e = { 0 }; + + if (wl_app.pointer_focus) { + e.type = state == WL_POINTER_BUTTON_STATE_PRESSED + ? PTK_EVENT_MOUSEDOWN + : PTK_EVENT_MOUSEUP; + e.window = wl_app.pointer_focus; + e.mouse.x = (int)wl_app.pointer_x; + e.mouse.y = (int)wl_app.pointer_y; + if (button == (0x110)) { /* BTN_LEFT */ + e.mouse.button = MOUSE_BUTTON_LEFT; + } else if (button == (0x111)) { /* BTN_RIGHT */ + e.mouse.button = MOUSE_BUTTON_RIGHT; + } else if (button == (0x112)) { /* BTN_MIDDLE */ + e.mouse.button = MOUSE_BUTTON_MIDDLE; + } + ptk_post_event(&e); + } +} + +static void ptk_waylandapp_on_pointer_axis(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, + wl_fixed_t value) +{ + ptk_event_t e = { 0 }; + + if (wl_app.pointer_focus) { + e.type = PTK_EVENT_WHEEL; + e.window = wl_app.pointer_focus; + e.wheel.delta_mode = APP_WHEEL_DELTA_PIXEL; + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + e.wheel.delta_y = (int)wl_fixed_to_double(value); + } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + e.wheel.delta_x = (int)wl_fixed_to_double(value); + } + ptk_post_event(&e); + } +} + +static void ptk_waylandapp_on_pointer_frame(void *data, + struct wl_pointer *wl_pointer) +{ +} + +static void ptk_waylandapp_on_pointer_axis_source(void *data, + struct wl_pointer *wl_pointer, + uint32_t axis_source) +{ +} + +static void ptk_waylandapp_on_pointer_axis_stop(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) +{ +} + +static void ptk_waylandapp_on_pointer_axis_discrete( + void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) +{ +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = ptk_waylandapp_on_pointer_enter, + .leave = ptk_waylandapp_on_pointer_leave, + .motion = ptk_waylandapp_on_pointer_motion, + .button = ptk_waylandapp_on_pointer_button, + .axis = ptk_waylandapp_on_pointer_axis, + .frame = ptk_waylandapp_on_pointer_frame, + .axis_source = ptk_waylandapp_on_pointer_axis_source, + .axis_stop = ptk_waylandapp_on_pointer_axis_stop, + .axis_discrete = ptk_waylandapp_on_pointer_axis_discrete, +}; + +/* Map an XKB keysym to LCUI's keycode (Web/Windows-style virtual key codes). */ +static int ptk_waylandapp_keysym_to_keycode(xkb_keysym_t sym) +{ + /* Latin letters: XKB_KEY_a..z → KEY_A..Z ('A'..'Z') */ + if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) { + return KEY_A + (sym - XKB_KEY_a); + } + /* Already uppercase */ + if (sym >= XKB_KEY_A && sym <= XKB_KEY_Z) { + return (int)sym; /* XKB_KEY_A = 0x41 = 'A' = KEY_A */ + } + /* Digits: XKB_KEY_0..9 = 0x30..0x39 = '0'..'9' = KEY_0..KEY_9 */ + if (sym >= XKB_KEY_0 && sym <= XKB_KEY_9) { + return (int)sym; + } + switch (sym) { + case XKB_KEY_BackSpace: return KEY_BACKSPACE; + case XKB_KEY_Tab: return KEY_TAB; + case XKB_KEY_Return: return KEY_ENTER; + case XKB_KEY_Escape: return KEY_ESCAPE; + case XKB_KEY_space: return KEY_SPACE; + case XKB_KEY_Delete: return KEY_DELETE; + case XKB_KEY_Insert: return KEY_INSERT; + case XKB_KEY_Home: return KEY_HOME; + case XKB_KEY_End: return KEY_END; + case XKB_KEY_Page_Up: return KEY_PAGEUP; + case XKB_KEY_Page_Down: return KEY_PAGEDOWN; + case XKB_KEY_Left: return KEY_LEFT; + case XKB_KEY_Right: return KEY_RIGHT; + case XKB_KEY_Up: return KEY_UP; + case XKB_KEY_Down: return KEY_DOWN; + case XKB_KEY_Shift_L: + case XKB_KEY_Shift_R: return KEY_SHIFT; + case XKB_KEY_Control_L: + case XKB_KEY_Control_R: return KEY_CONTROL; + case XKB_KEY_Alt_L: + case XKB_KEY_Alt_R: return KEY_ALT; + case XKB_KEY_Caps_Lock: return KEY_CAPITAL; + case XKB_KEY_semicolon: return KEY_SEMICOLON; + case XKB_KEY_equal: return KEY_EQUAL; + case XKB_KEY_comma: return KEY_COMMA; + case XKB_KEY_minus: return KEY_MINUS; + case XKB_KEY_period: return KEY_PERIOD; + case XKB_KEY_slash: return KEY_SLASH; + case XKB_KEY_grave: return KEY_GRAVE; + case XKB_KEY_bracketleft: return KEY_BRACKETLEFT; + case XKB_KEY_backslash: return KEY_BACKSLASH; + case XKB_KEY_bracketright: return KEY_BRACKETRIGHT; + case XKB_KEY_apostrophe: return KEY_APOSTROPHE; + default: return 0; + } +} + +static void ptk_waylandapp_on_keyboard_keymap(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t format, int32_t fd, + uint32_t size) +{ + void *map_str; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map_str == MAP_FAILED) { + close(fd); + return; + } + if (!wl_app.xkb_context) { + wl_app.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + } + if (wl_app.xkb_state) { + xkb_state_unref(wl_app.xkb_state); + wl_app.xkb_state = NULL; + } + if (wl_app.xkb_map) { + xkb_keymap_unref(wl_app.xkb_map); + } + wl_app.xkb_map = xkb_keymap_new_from_string( + wl_app.xkb_context, map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_str, size); + close(fd); + if (!wl_app.xkb_map) { + return; + } + wl_app.xkb_state = xkb_state_new(wl_app.xkb_map); + /* Cache modifier indices for fast lookup in modifiers handler */ + wl_app.shift_mask = 1u << xkb_keymap_mod_get_index(wl_app.xkb_map, "Shift"); + wl_app.ctrl_mask = 1u << xkb_keymap_mod_get_index(wl_app.xkb_map, "Control"); + wl_app.alt_mask = 1u << xkb_keymap_mod_get_index(wl_app.xkb_map, "Mod1"); + wl_app.meta_mask = 1u << xkb_keymap_mod_get_index(wl_app.xkb_map, "Super"); +} + +static void ptk_waylandapp_on_keyboard_enter(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t serial, + struct wl_surface *surface, + struct wl_array *keys) +{ + wl_app.keyboard_focus = ptk_waylandapp_get_window(surface); +} + +static void ptk_waylandapp_on_keyboard_leave(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t serial, + struct wl_surface *surface) +{ + wl_app.keyboard_focus = NULL; +} + +static void ptk_waylandapp_on_keyboard_key(void *data, + struct wl_keyboard *wl_keyboard, + uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) +{ + /* evdev scancode → XKB keycode: XKB adds a fixed offset of 8 */ + xkb_keycode_t xkb_key = key + 8; + xkb_keysym_t sym = XKB_KEY_NoSymbol; + uint32_t codepoint; + ptk_event_t e = { 0 }; + bool pressed; + + /* Some compositors send WL_KEYBOARD_KEY_STATE_REPEATED for auto-repeat. + * Map it to pressed, same as SDL does (SDL_waylandevents.c line 2273). */ + if (state == WL_KEYBOARD_KEY_STATE_PRESSED || + (uint32_t)state == 2 /* WL_KEYBOARD_KEY_STATE_REPEATED */) { + pressed = true; + } else { + pressed = false; + } + + if (wl_app.xkb_state) { + sym = xkb_state_key_get_one_sym(wl_app.xkb_state, xkb_key); + } + if (!wl_app.keyboard_focus || sym == XKB_KEY_NoSymbol) { + return; + } + e.type = pressed ? PTK_EVENT_KEYDOWN : PTK_EVENT_KEYUP; + e.window = wl_app.keyboard_focus; + e.key.code = ptk_waylandapp_keysym_to_keycode(sym); + e.key.shift_key = !!(wl_app.modifiers & wl_app.shift_mask); + e.key.ctrl_key = !!(wl_app.modifiers & wl_app.ctrl_mask); + e.key.alt_key = !!(wl_app.modifiers & wl_app.alt_mask); + e.key.meta_key = !!(wl_app.modifiers & wl_app.meta_mask); + ptk_post_event(&e); + + /* After KEYDOWN/REPEATED, post PTK_EVENT_KEYPRESS for printable chars + * (non-Ctrl combinations). key.code = Unicode codepoint, matching the + * X11 backend and what the LCUI text widget / IME layer expect. */ + if (pressed && !e.key.ctrl_key && wl_app.xkb_state) { + codepoint = xkb_state_key_get_utf32(wl_app.xkb_state, xkb_key); + if (codepoint >= 0x20 && codepoint != 0x7f) { + e.type = PTK_EVENT_KEYPRESS; + e.key.code = (int)codepoint; + ptk_post_event(&e); + } + } +} + +static void ptk_waylandapp_on_keyboard_modifiers( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) +{ + if (!wl_app.xkb_state) { + return; + } + xkb_state_update_mask(wl_app.xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, group); + wl_app.modifiers = xkb_state_serialize_mods(wl_app.xkb_state, + XKB_STATE_MODS_EFFECTIVE); +} + +static void ptk_waylandapp_on_keyboard_repeat_info( + void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) +{ + /* rate: characters per second (0 = no repeat); delay: ms before repeat starts. + * Store for future key-repeat implementation (cf. SDL keyboard_handle_repeat_info). */ + wl_app.repeat_rate = rate; + wl_app.repeat_delay = delay; +} + +static const struct wl_keyboard_listener keyboard_listener = { + .keymap = ptk_waylandapp_on_keyboard_keymap, + .enter = ptk_waylandapp_on_keyboard_enter, + .leave = ptk_waylandapp_on_keyboard_leave, + .key = ptk_waylandapp_on_keyboard_key, + .modifiers = ptk_waylandapp_on_keyboard_modifiers, + .repeat_info = ptk_waylandapp_on_keyboard_repeat_info, +}; + +static void ptk_waylandapp_on_seat_capabilities(void *data, + struct wl_seat *seat, + uint32_t caps) +{ + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl_app.pointer) { + wl_app.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(wl_app.pointer, &pointer_listener, NULL); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl_app.pointer) { + wl_pointer_destroy(wl_app.pointer); + wl_app.pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl_app.keyboard) { + wl_app.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(wl_app.keyboard, &keyboard_listener, + NULL); + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl_app.keyboard) { + wl_keyboard_destroy(wl_app.keyboard); + wl_app.keyboard = NULL; + } +} + +static void ptk_waylandapp_on_seat_name(void *data, struct wl_seat *seat, + const char *name) +{ +} + +const struct wl_seat_listener seat_listener = { + .capabilities = ptk_waylandapp_on_seat_capabilities, + .name = ptk_waylandapp_on_seat_name, +}; diff --git a/lib/ptk/src/linux/waylandapp_output.c b/lib/ptk/src/linux/waylandapp_output.c new file mode 100644 index 000000000..881cd862d --- /dev/null +++ b/lib/ptk/src/linux/waylandapp_output.c @@ -0,0 +1,46 @@ +#include "wayland_internal.h" + +static void ptk_waylandapp_on_output_geometry(void *data, + struct wl_output *wl_output, + int32_t x, int32_t y, + int32_t physical_width, + int32_t physical_height, + int32_t subpixel, + const char *make, + const char *model, + int32_t transform) +{ +} + +static void ptk_waylandapp_on_output_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, int32_t width, + int32_t height, int32_t refresh) +{ + if (flags & WL_OUTPUT_MODE_CURRENT) { + wl_app.screen_width = width; + wl_app.screen_height = height; + } +} + +static void ptk_waylandapp_on_output_done(void *data, + struct wl_output *wl_output) +{ +} + +static void ptk_waylandapp_on_output_scale(void *data, + struct wl_output *wl_output, + int32_t factor) +{ + if (factor > 0) { + wl_app.output_scale = factor; + } +} + +const struct wl_output_listener output_listener = { + .geometry = ptk_waylandapp_on_output_geometry, + .mode = ptk_waylandapp_on_output_mode, + .done = ptk_waylandapp_on_output_done, + .scale = ptk_waylandapp_on_output_scale, +}; + diff --git a/lib/ptk/src/linux/waylandapp_window.c b/lib/ptk/src/linux/waylandapp_window.c new file mode 100644 index 000000000..f18c9b857 --- /dev/null +++ b/lib/ptk/src/linux/waylandapp_window.c @@ -0,0 +1,388 @@ +#include "wayland_internal.h" + +void ptk_waylandwindow_destroy_buffer(ptk_window_t *wnd) +{ + if (wnd->buffer) { + wl_buffer_destroy(wnd->buffer); + wnd->buffer = NULL; + } + if (wnd->buffer_data) { + munmap(wnd->buffer_data, wnd->buffer_size); + wnd->buffer_data = NULL; + } + wnd->buffer_size = 0; +} + +void ptk_waylandwindow_post_size_event(ptk_window_t *wnd) +{ + ptk_event_t e = { 0 }; + + e.type = PTK_EVENT_SIZE; + e.window = wnd; + e.size.width = wnd->width; + e.size.height = wnd->height; + ptk_post_event(&e); +} + +static void ptk_waylandapp_on_wm_base_ping(void *data, + struct xdg_wm_base *wm_base, + uint32_t serial) +{ + xdg_wm_base_pong(wm_base, serial); +} + +const struct xdg_wm_base_listener wm_base_listener = { + ptk_waylandapp_on_wm_base_ping +}; + +static void ptk_waylandwindow_on_xdg_surface_configure( + void *data, struct xdg_surface *surface, uint32_t serial) +{ + ptk_window_t *wnd = data; + + xdg_surface_ack_configure(surface, serial); + wnd->configured = true; +} + +const struct xdg_surface_listener xdg_surface_listener = { + ptk_waylandwindow_on_xdg_surface_configure +}; + +static void ptk_waylandwindow_on_toplevel_configure( + void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, + int32_t height, struct wl_array *states) +{ + ptk_window_t *wnd = data; + + if (width <= 0 || height <= 0) { + return; + } + if (wnd->width == width && wnd->height == height) { + return; + } + wnd->width = width; + wnd->height = height; + ptk_waylandwindow_destroy_buffer(wnd); + ptk_waylandwindow_post_size_event(wnd); +} + +static void ptk_waylandwindow_on_toplevel_close(void *data, + struct xdg_toplevel *xdg_toplevel) +{ + ptk_window_t *wnd = data; + ptk_event_t e = { 0 }; + + e.type = PTK_EVENT_CLOSE; + e.window = wnd; + ptk_post_event(&e); +} + +static void ptk_waylandwindow_on_toplevel_configure_bounds( + void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ +} + +static void ptk_waylandwindow_on_toplevel_wm_capabilities( + void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities) +{ +} + +const struct xdg_toplevel_listener xdg_toplevel_listener = { + ptk_waylandwindow_on_toplevel_configure, + ptk_waylandwindow_on_toplevel_close, + ptk_waylandwindow_on_toplevel_configure_bounds, + ptk_waylandwindow_on_toplevel_wm_capabilities +}; + +ptk_window_t * ptk_waylandwindow_create(const wchar_t *title, int x, int y, + int width, int height, + ptk_window_t *parent) +{ + ptk_window_t *wnd; + + if (!wl_app.compositor || !wl_app.wm_base || !wl_app.shm) { + return NULL; + } + wnd = calloc(1, sizeof(*wnd)); + if (!wnd) { + return NULL; + } + wnd->width = (width > 0 ? width : PTK_WINDOW_DEFAULT_WIDTH) * wl_app.output_scale; + wnd->height = (height > 0 ? height : PTK_WINDOW_DEFAULT_HEIGHT) * wl_app.output_scale; + wnd->surface = wl_compositor_create_surface(wl_app.compositor); + if (!wnd->surface) { + free(wnd); + return NULL; + } + wnd->xdg_surface = xdg_wm_base_get_xdg_surface(wl_app.wm_base, wnd->surface); + wnd->xdg_toplevel = xdg_surface_get_toplevel(wnd->xdg_surface); + if (!wnd->xdg_surface || !wnd->xdg_toplevel) { + if (wnd->xdg_toplevel) { + xdg_toplevel_destroy(wnd->xdg_toplevel); + } + if (wnd->xdg_surface) { + xdg_surface_destroy(wnd->xdg_surface); + } + wl_surface_destroy(wnd->surface); + free(wnd); + return NULL; + } + wnd->node.data = wnd; + xdg_surface_add_listener(wnd->xdg_surface, &xdg_surface_listener, wnd); + xdg_toplevel_add_listener(wnd->xdg_toplevel, &xdg_toplevel_listener, wnd); + + if (wl_app.decoration_manager) { + wnd->decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + wl_app.decoration_manager, wnd->xdg_toplevel); + if (wnd->decoration) { + zxdg_toplevel_decoration_v1_set_mode( + wnd->decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + } + if (title) { + size_t len = encode_utf8(NULL, title, 0) + 1; + char *utf8_title = malloc(sizeof(char) * len); + + if (utf8_title) { + encode_utf8(utf8_title, title, len); + xdg_toplevel_set_title(wnd->xdg_toplevel, utf8_title); + free(utf8_title); + } + } + wl_surface_commit(wnd->surface); + wl_display_roundtrip(wl_app.display); + list_append_node(&wl_app.windows, &wnd->node); + return wnd; +} + +static void ptk_waylandwindow_close(ptk_window_t *wnd) +{ + ptk_event_t e = { 0 }; + + e.type = PTK_EVENT_CLOSE; + e.window = wnd; + ptk_post_event(&e); +} + +static void ptk_waylandwindow_show(ptk_window_t *wnd) +{ + wl_surface_commit(wnd->surface); +} + +static void ptk_waylandwindow_activate(ptk_window_t *wnd) +{ + wl_surface_commit(wnd->surface); +} + +static void ptk_waylandwindow_set_title(ptk_window_t *wnd, const wchar_t *title) +{ + size_t len; + char *utf8_title; + + if (!title) { + return; + } + len = encode_utf8(NULL, title, 0) + 1; + utf8_title = malloc(sizeof(char) * len); + if (!utf8_title) { + return; + } + encode_utf8(utf8_title, title, len); + xdg_toplevel_set_title(wnd->xdg_toplevel, utf8_title); + free(utf8_title); +} + +static void ptk_waylandwindow_destroy(ptk_window_t *wnd) +{ + if (!wnd) { + return; + } + list_unlink(&wl_app.windows, &wnd->node); + if (wnd->decoration) { + zxdg_toplevel_decoration_v1_destroy(wnd->decoration); + } + if (wnd->xdg_toplevel) { + xdg_toplevel_destroy(wnd->xdg_toplevel); + } + if (wnd->xdg_surface) { + xdg_surface_destroy(wnd->xdg_surface); + } + if (wnd->surface) { + wl_surface_destroy(wnd->surface); + } + ptk_waylandwindow_destroy_buffer(wnd); + free(wnd->paint_ctx); + free(wnd); +} + +static void ptk_waylandwindow_set_size(ptk_window_t *wnd, int width, int height) +{ + if (width <= 0 || height <= 0) { + return; + } + int physical_width = width * wl_app.output_scale; + int physical_height = height * wl_app.output_scale; + + if (wnd->width == physical_width && wnd->height == physical_height) { + return; + } + wnd->width = physical_width; + wnd->height = physical_height; + ptk_waylandwindow_destroy_buffer(wnd); +} + +static void ptk_waylandwindow_set_position(ptk_window_t *wnd, int x, int y) +{ +} + +static void *ptk_waylandwindow_get_handle(ptk_window_t *wnd) +{ + return wnd->surface; +} + +static int ptk_waylandwindow_get_width(ptk_window_t *wnd) +{ + return wnd->width / wl_app.output_scale; +} + +static int ptk_waylandwindow_get_height(ptk_window_t *wnd) +{ + return wnd->height / wl_app.output_scale; +} + +static void ptk_waylandwindow_set_min_width(ptk_window_t *wnd, int min_width) +{ + xdg_toplevel_set_min_size(wnd->xdg_toplevel, min_width * wl_app.output_scale, 0); +} + +static void ptk_waylandwindow_set_min_height(ptk_window_t *wnd, int min_height) +{ + xdg_toplevel_set_min_size(wnd->xdg_toplevel, 0, min_height * wl_app.output_scale); +} + +static void ptk_waylandwindow_set_max_width(ptk_window_t *wnd, int max_width) +{ + xdg_toplevel_set_max_size(wnd->xdg_toplevel, max_width * wl_app.output_scale, 0); +} + +static void ptk_waylandwindow_set_max_height(ptk_window_t *wnd, int max_height) +{ + xdg_toplevel_set_max_size(wnd->xdg_toplevel, 0, max_height * wl_app.output_scale); +} + +static unsigned ptk_waylandwindow_get_dpi(ptk_window_t *wnd) +{ + return wl_app.output_scale * 96; +} + +static ptk_window_paint_t *ptk_waylandwindow_begin_paint(ptk_window_t *wnd, + pd_rect_t *rect) +{ + size_t size; + int fd; + struct wl_shm_pool *pool; + + if (!wnd->configured || !rect) { + return NULL; + } + if (!wnd->buffer) { + size = (size_t)wnd->width * (size_t)wnd->height * 4; + fd = memfd_create("lcui-wayland-buffer", MFD_CLOEXEC); + if (fd < 0) { + return NULL; + } + if (ftruncate(fd, (off_t)size) < 0) { + close(fd); + return NULL; + } + wnd->buffer_data = + mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (wnd->buffer_data == MAP_FAILED) { + wnd->buffer_data = NULL; + close(fd); + return NULL; + } + pool = wl_shm_create_pool(wl_app.shm, fd, (int)size); + wnd->buffer = wl_shm_pool_create_buffer( + pool, 0, wnd->width, wnd->height, wnd->width * 4, + WL_SHM_FORMAT_XRGB8888); + wl_shm_pool_destroy(pool); + close(fd); + wnd->buffer_size = size; + /* Initialize the backing canvas once per buffer lifetime. + * mem_size=0 signals pandagl that this is external memory. */ + pd_canvas_init(&wnd->canvas); + wnd->canvas.width = wnd->width; + wnd->canvas.height = wnd->height; + wnd->canvas.color_type = PD_COLOR_TYPE_ARGB; + wnd->canvas.bytes = wnd->buffer_data; + wnd->canvas.bytes_per_pixel = 4; + wnd->canvas.bytes_per_row = wnd->width * 4; + } + if (!wnd->paint_ctx) { + wnd->paint_ctx = calloc(1, sizeof(*wnd->paint_ctx)); + if (!wnd->paint_ctx) { + return NULL; + } + } + /* Set up the paint context following pd_context_create() semantics: + * paint->canvas must be a pd_canvas_quote() of the full backing canvas + * clipped to the dirty rect. Pandagl renderers draw at (0,0) within + * this quoted canvas, which corresponds to (rect->x, rect->y) in the + * actual shm buffer. Setting canvas = full buffer (old code) caused + * all renders to write to buffer[0,0] regardless of the dirty rect. */ + wnd->paint_ctx->rect = *rect; + wnd->paint_ctx->with_alpha = false; + pd_rect_correct(&wnd->paint_ctx->rect, wnd->width, wnd->height); + pd_canvas_init(&wnd->paint_ctx->canvas); + pd_canvas_quote(&wnd->paint_ctx->canvas, &wnd->canvas, + &wnd->paint_ctx->rect); + /* Clear the dirty region to white before the widget renders into it */ + pd_canvas_fill(&wnd->paint_ctx->canvas, pd_rgb(255, 255, 255)); + return wnd->paint_ctx; +} + +static void ptk_waylandwindow_end_paint(ptk_window_t *wnd, + ptk_window_paint_t *paint) +{ + if (!wnd || !paint || !wnd->buffer) { + return; + } + wl_surface_set_buffer_scale(wnd->surface, wl_app.output_scale); + wl_surface_attach(wnd->surface, wnd->buffer, 0, 0); + wl_surface_damage_buffer(wnd->surface, paint->rect.x, paint->rect.y, + paint->rect.width, paint->rect.height); + wl_surface_commit(wnd->surface); + wl_display_flush(wl_app.display); +} + +static void ptk_waylandwindow_present(ptk_window_t *wnd) +{ + wl_surface_commit(wnd->surface); + wl_display_flush(wl_app.display); +} + +void ptk_waylandwindow_driver_init(ptk_window_driver_t *driver) +{ + memset(driver, 0, sizeof(*driver)); + driver->show = ptk_waylandwindow_show; + driver->activate = ptk_waylandwindow_activate; + driver->close = ptk_waylandwindow_close; + driver->destroy = ptk_waylandwindow_destroy; + driver->set_title = ptk_waylandwindow_set_title; + driver->set_size = ptk_waylandwindow_set_size; + driver->set_position = ptk_waylandwindow_set_position; + driver->get_handle = ptk_waylandwindow_get_handle; + driver->get_width = ptk_waylandwindow_get_width; + driver->get_height = ptk_waylandwindow_get_height; + driver->get_dpi = ptk_waylandwindow_get_dpi; + driver->set_min_width = ptk_waylandwindow_set_min_width; + driver->set_min_height = ptk_waylandwindow_set_min_height; + driver->set_max_width = ptk_waylandwindow_set_max_width; + driver->set_max_height = ptk_waylandwindow_set_max_height; + driver->begin_paint = ptk_waylandwindow_begin_paint; + driver->end_paint = ptk_waylandwindow_end_paint; + driver->present = ptk_waylandwindow_present; +} + diff --git a/lib/ptk/xmake.lua b/lib/ptk/xmake.lua index e2c8a2967..31d93098c 100644 --- a/lib/ptk/xmake.lua +++ b/lib/ptk/xmake.lua @@ -1,5 +1,11 @@ +includes("rules/xmake.lua") + +local wayland_protocol_root = os.getenv("WAYLAND_PROTOCOLS_DIR") or "/usr/share/wayland-protocols" +local xdg_shell_xml = path.join(wayland_protocol_root, "stable", "xdg-shell", "xdg-shell.xml") +local xdg_decoration_xml = path.join(wayland_protocol_root, "unstable", "xdg-decoration", "xdg-decoration-unstable-v1.xml") + if is_plat("linux") then - add_requires("libx11", {optional = true}) + add_requires("libx11", "wayland", {optional = true}) end option("enable-touch") @@ -26,9 +32,15 @@ target("libptk") add_links("Shell32") else add_files("src/linux/*.c") - add_packages("libx11") + add_rules("wayland.protocol") + add_files(xdg_shell_xml, {rule = "wayland.protocol"}) + add_files(xdg_decoration_xml, {rule = "wayland.protocol"}) + add_packages("libx11", "wayland", "wayland-cursor") if has_package("libx11") then set_configvar("PTK_HAS_LIBX11", 1) end - add_syslinks("pthread", "dl") + if has_package("wayland") then + set_configvar("PTK_HAS_WAYLAND", 1) + end + add_syslinks("pthread", "dl", "xkbcommon", "wayland-cursor") end diff --git a/lib/router/include/router/version.h b/lib/router/include/router/version.h index bb1db901c..495f2a706 100644 --- a/lib/router/include/router/version.h +++ b/lib/router/include/router/version.h @@ -1,4 +1,4 @@ -/* +/* * lib/router/include/router/version.h * * Copyright (c) 2023-2025, Liu Chao All rights reserved. diff --git a/lib/thread/include/thread.h b/lib/thread/include/thread.h index 6bc2856e1..8daf8b25f 100644 --- a/lib/thread/include/thread.h +++ b/lib/thread/include/thread.h @@ -1,4 +1,4 @@ -/* +/* * lib/thread/include/thread.h: -- basic thread management * * Copyright (c) 2018-2025, Liu chao All rights reserved. diff --git a/lib/ui-server/src/server.c b/lib/ui-server/src/server.c index 5550e92ae..7bd883ac0 100644 --- a/lib/ui-server/src/server.c +++ b/lib/ui-server/src/server.c @@ -568,6 +568,7 @@ size_t ui_server_render(void) for (list_each(node, &ui_server.connections)) { conn = node->data; if (!conn->window_visible) { + DEBUG_MSG("[ui-server] skip render: window_visible is false\n"); continue; } ui_metrics.dpi = 1.f * ptk_window_get_dpi(conn->window);