Compare commits

...

35 commits

Author SHA1 Message Date
SASANO Takayoshi
f6cf414ca0
cmake: add OpenBSD support (no librt) (#21) 2025-11-20 12:04:34 +00:00
Martin
b3b0f1f40a
source: Generate protocol specific dummy_type names (#20) 2025-08-14 16:16:42 +01:00
fcca0c61f9
version: bump to 0.4.5 2025-07-07 16:18:29 +02:00
8fb426b3e5
server: fix empty interface arrays 2025-07-07 11:44:32 +02:00
aa38edd6e3
CI/Nix: add cache-nix-action
Use nixbuild/nix-quick-install-action which pairs well with
nix-community/cache-nix-action.

Should help with build times by reducing the number of packages needing
to be re-downloaded on each run.

Parameters are taken from https://github.com/nix-community/cache-nix-action
and may be tweaked later.
2025-06-20 01:24:29 +03:00
Friday
817918315e
nix: use gcc15 (#17)
also updated dependencies
2025-06-05 18:49:20 +01:00
NyxTrail
e511882b9c
cmake: Make CMAKE builds arch independent (#16) 2025-05-18 18:04:58 +02:00
Honkazel
206367a08d
core: member + designated init and remove redundant cast (#14)
* core: member initialize and rm redundant cast

Both pResource can be member initialized and cast is redundant since 4a53f1a

* use designated initializers
2025-02-18 10:21:20 +01:00
04146df74a
CI: remove deprecated magic-nix-cache-action 2025-02-08 23:10:28 +02:00
de913476b5 version: bump to 0.4.4 2024-12-29 18:31:14 +01:00
4a53f1acc7 client: drop wl_resource usage 2024-12-29 18:30:56 +01:00
3d6681fd35 version: bump to 0.4.3 2024-12-29 18:03:15 +01:00
c0c13f07a0 client: drop wl_resource typedef
this conflicts with some custom wl extension headers

todo: stop using 'wl_resource' in client code altogether...
2024-12-29 18:02:59 +01:00
4d7367b6ee client: use the proper new_id index in marshalling
fixes #13
2024-12-21 15:05:13 +00:00
Austin Horstman
90e87f7fcf
flake.nix: gcc13 -> gcc14; flake.lock: update (#12)
* flake.nix: gcc13 -> gcc14

* flake.lock: update
2024-12-16 22:26:57 +01:00
500c81a9e1 README: clarify function 2024-09-21 00:27:16 +01:00
b68dab23fc version: bump to 0.4.2 2024-09-20 14:57:53 +01:00
96d33362e6 core: fix overzealous proxy replacement for new_id 2024-09-20 14:13:20 +01:00
eebf1cb749 core: fixup client pointer types 2024-09-19 22:46:48 +01:00
9df631193a version: bump to 0.4.1 2024-09-19 22:40:38 +01:00
4d3d80fcb1 core: fixup client new_id handling 2024-09-19 22:38:51 +01:00
f68f159255 README: add flags 2024-09-16 09:01:29 +01:00
a048a6cb01
flake.lock: update 2024-07-18 20:35:19 +03:00
8907e8a64e
CMake: fix version path 2024-07-18 20:33:41 +03:00
4a1f262f37
CMake: fmt 2024-07-18 20:30:13 +03:00
117b6d93ed
CMake, Nix: add VERSION file 2024-07-18 20:29:59 +03:00
d5fa094ca2 cmake: bump ver to 0.4.0 2024-07-05 23:44:17 +02:00
bd7b1f808e client: destroy proxy when no dtor is available 2024-07-01 14:32:46 +02:00
55c78a56c0 client: do not define destroy listener in client mode
fixes #9
2024-06-27 00:24:36 +02:00
Tom Englund
c1e34fb2b7
core: avoid undefined behaviour in C macro (#8)
to safely use wl_container_of with a class the class has to be no
virtual functions, no inheritance, and uniform access control (e.g all
public)

work around this by putting this into a destroywrapper struct.
2024-06-26 20:35:08 +02:00
914f083741 client: properly release objects on destroy 2024-06-22 16:50:53 +02:00
1413398dca client: use CC for client classes 2024-06-20 18:20:55 +02:00
Vaxry
f2d1d948f1
README: Remove last todos 2024-06-20 00:18:34 +02:00
Vaxry
38140181fe
core: Add client code generation (#6)
Adds a `--client` flag for client code
2024-06-20 00:17:58 +02:00
1419520d5f
Nix: add cross-build package 2024-06-11 18:21:15 +03:00
8 changed files with 389 additions and 168 deletions

View file

@ -7,8 +7,35 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: cachix/install-nix-action@v25 - name: Install Nix
- uses: DeterminateSystems/magic-nix-cache-action@main uses: nixbuild/nix-quick-install-action@v31
with:
nix_conf: |
keep-env-derivations = true
keep-outputs = true
- name: Restore and save Nix store
uses: nix-community/cache-nix-action@v6
with:
# restore and save a cache using this key
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
# if there's no cache hit, restore a cache by this prefix
restore-prefixes-first-match: nix-${{ runner.os }}-
# collect garbage until the Nix store size (in bytes) is at most this number
# before trying to save a new cache
# 1G = 1073741824
gc-max-store-size-linux: 1G
# do purge caches
purge: true
# purge all versions of the cache
purge-prefixes: nix-${{ runner.os }}-
# created more than this number of seconds ago
purge-created: 0
# or, last accessed more than this number of seconds ago
# relative to the start of the `Post Restore and save Nix store` phase
purge-last-accessed: 0
# except any version with the key that is the same as the `primary-key`
purge-primary-key: never
# not needed (yet) # not needed (yet)
# - uses: cachix/cachix-action@v12 # - uses: cachix/cachix-action@v12

View file

@ -1,11 +1,12 @@
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
set(VERSION 0.3.10) file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
string(STRIP ${VER_RAW} VERSION)
project(hyprwayland-scanner project(
hyprwayland-scanner
DESCRIPTION "A hyprland version of wayland-scanner in and for C++" DESCRIPTION "A hyprland version of wayland-scanner in and for C++"
VERSION ${VERSION} VERSION ${VERSION})
)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
@ -22,7 +23,6 @@ else()
message(STATUS "Configuring hyprwayland-scanner in Release with CMake") message(STATUS "Configuring hyprwayland-scanner in Release with CMake")
endif() endif()
# configure # configure
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value
@ -41,25 +41,29 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET pugixml)
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(hyprwayland-scanner ${SRCFILES}) add_executable(hyprwayland-scanner ${SRCFILES})
target_link_libraries(hyprwayland-scanner PRIVATE rt Threads::Threads PkgConfig::deps) find_library(librt rt)
if("${librt}" MATCHES "librt-NOTFOUND")
unset(LIBRT)
else()
set(LIBRT rt)
endif()
target_link_libraries(hyprwayland-scanner PRIVATE ${LIBRT} Threads::Threads
PkgConfig::deps)
configure_package_config_file( configure_package_config_file(
hyprwayland-scanner-config.cmake.in hyprwayland-scanner-config.cmake.in hyprwayland-scanner-config.cmake
hyprwayland-scanner-config.cmake
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/hyprwayland-scanner" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/hyprwayland-scanner"
PATH_VARS CMAKE_INSTALL_BINDIR PATH_VARS CMAKE_INSTALL_BINDIR)
)
write_basic_package_version_file( write_basic_package_version_file(
"hyprwayland-scanner-config-version.cmake" "hyprwayland-scanner-config-version.cmake"
VERSION "${VERSION}" VERSION "${VERSION}"
COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT
) COMPATIBILITY AnyNewerVersion)
# Installation # Installation
install(TARGETS hyprwayland-scanner) install(TARGETS hyprwayland-scanner)
install(FILES ${CMAKE_BINARY_DIR}/hyprwayland-scanner.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) install(FILES ${CMAKE_BINARY_DIR}/hyprwayland-scanner.pc
install(FILES DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
${CMAKE_BINARY_DIR}/hyprwayland-scanner-config.cmake install(FILES ${CMAKE_BINARY_DIR}/hyprwayland-scanner-config.cmake
${CMAKE_BINARY_DIR}/hyprwayland-scanner-config-version.cmake ${CMAKE_BINARY_DIR}/hyprwayland-scanner-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hyprwayland-scanner DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hyprwayland-scanner)
)

View file

@ -1,12 +1,20 @@
# hyprwayland-scanner # hyprwayland-scanner
A Hyprland implementation of wayland-scanner, in and for C++. A Hyprland implementation of wayland-scanner, in and for C++.
hw-s automatically generates properly RAII-ready, modern C++ bindings for Wayland protocols, for
either servers or clients.
## Usage ## Usage
```sh ```sh
hyprwayland-scanner '/path/to/proto' '/path/to/output/directory' hyprwayland-scanner '/path/to/proto' '/path/to/output/directory'
``` ```
### Options
- `--client` -> generate client code
- `--wayland-enums` -> use wayland enum naming (snake instead of camel)
## Dependencies ## Dependencies
Requires a compiler with C++23 support. Requires a compiler with C++23 support.
@ -26,7 +34,3 @@ cmake --build build -j `nproc`
```sh ```sh
sudo cmake --install build sudo cmake --install build
``` ```
## TODO
- [ ] Support for generating client headers

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.4.5

6
flake.lock generated
View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1713537308, "lastModified": 1748929857,
"narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=", "narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f", "rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -18,18 +18,26 @@
localSystem.system = system; localSystem.system = system;
overlays = with self.overlays; [hyprwayland-scanner]; overlays = with self.overlays; [hyprwayland-scanner];
}); });
pkgsCrossFor = eachSystem (system: crossSystem:
import nixpkgs {
localSystem = system;
crossSystem = crossSystem;
overlays = with self.overlays; [hyprwayland-scanner];
});
mkDate = longDate: (lib.concatStringsSep "-" [ mkDate = longDate: (lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate) (builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate) (builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate) (builtins.substring 6 2 longDate)
]); ]);
version = lib.removeSuffix "\n" (builtins.readFile ./VERSION);
in { in {
overlays = { overlays = {
default = self.overlays.hyprwayland-scanner; default = self.overlays.hyprwayland-scanner;
hyprwayland-scanner = final: prev: { hyprwayland-scanner = final: prev: {
hyprwayland-scanner = final.callPackage ./nix/default.nix { hyprwayland-scanner = final.callPackage ./nix/default.nix {
stdenv = final.gcc13Stdenv; stdenv = final.gcc15Stdenv;
version = "0.1.0" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
}; };
}; };
}; };
@ -37,6 +45,7 @@
packages = eachSystem (system: { packages = eachSystem (system: {
default = self.packages.${system}.hyprwayland-scanner; default = self.packages.${system}.hyprwayland-scanner;
inherit (pkgsFor.${system}) hyprwayland-scanner; inherit (pkgsFor.${system}) hyprwayland-scanner;
hyprwayland-scanner-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprwayland-scanner;
}); });
formatter = eachSystem (system: pkgsFor.${system}.alejandra); formatter = eachSystem (system: pkgsFor.${system}.alejandra);

View file

@ -15,6 +15,9 @@ stdenv.mkDerivation {
nativeBuildInputs = [ nativeBuildInputs = [
cmake cmake
pkg-config pkg-config
];
buildInputs = [
pugixml pugixml
]; ];

View file

@ -9,30 +9,29 @@
#include <filesystem> #include <filesystem>
bool waylandEnums = false; bool waylandEnums = false;
bool clientCode = false;
bool noInterfaces = false;
struct SRequestArgument { struct SRequestArgument {
std::string wlType; std::string wlType;
std::string interface; std::string interface;
std::string enumName; std::string enumName;
std::string name; std::string name;
bool newType = false;
bool allowNull = false; bool allowNull = false;
}; };
struct SRequest { struct SWaylandFunction {
std::vector<SRequestArgument> args;
std::string name;
std::string since;
};
struct SEvent {
std::vector<SRequestArgument> args; std::vector<SRequestArgument> args;
std::string name; std::string name;
std::string since; std::string since;
std::string newIdType = ""; // client only
bool destructor = false;
}; };
struct SInterface { struct SInterface {
std::vector<SRequest> requests; std::vector<SWaylandFunction> requests;
std::vector<SEvent> events; std::vector<SWaylandFunction> events;
std::string name; std::string name;
int version = 1; int version = 1;
}; };
@ -48,6 +47,10 @@ struct {
std::vector<SEnum> enums; std::vector<SEnum> enums;
} XMLDATA; } XMLDATA;
const char* resourceName() {
return clientCode ? "wl_proxy" : "wl_resource";
}
std::string sanitize(const std::string& in) { std::string sanitize(const std::string& in) {
if (in == "namespace") if (in == "namespace")
return "namespace_"; return "namespace_";
@ -108,19 +111,24 @@ std::string WPTypeToCType(const SRequestArgument& arg, bool event /* events pass
return "uint32_t"; return "uint32_t";
// enum // enum
if (!arg.enumName.empty()) if (!arg.enumName.empty()) {
for (auto& e : XMLDATA.enums) { for (auto& e : XMLDATA.enums) {
if (e.nameOriginal == arg.enumName) if (e.nameOriginal == arg.enumName)
return e.name; return e.name;
} }
return "uint32_t";
}
if (!event && clientCode && arg.wlType == "new_id")
return "wl_proxy*";
// iface // iface
if (!arg.interface.empty() && event) { if (!arg.interface.empty() && event) {
for (auto& i : XMLDATA.ifaces) { for (auto& i : XMLDATA.ifaces) {
if (i.name == arg.interface) if (i.name == arg.interface)
return camelize("C_" + arg.interface + "*"); return camelize((clientCode ? "CC_" : "C_") + arg.interface + "*");
} }
return "wl_resource*"; return std::string{resourceName()} + "*";
} }
return "uint32_t"; return "uint32_t";
@ -129,10 +137,10 @@ std::string WPTypeToCType(const SRequestArgument& arg, bool event /* events pass
if (!arg.interface.empty() && event && !ignoreTypes) { if (!arg.interface.empty() && event && !ignoreTypes) {
for (auto& i : XMLDATA.ifaces) { for (auto& i : XMLDATA.ifaces) {
if (i.name == arg.interface) if (i.name == arg.interface)
return camelize("C_" + arg.interface + "*"); return camelize((clientCode ? "CC_" : "C_") + arg.interface + "*");
} }
} }
return "wl_resource*"; return std::string{resourceName()} + "*";
} }
if (arg.wlType == "int" || arg.wlType == "fd") if (arg.wlType == "int" || arg.wlType == "fd")
return "int32_t"; return "int32_t";
@ -187,12 +195,17 @@ void parseXML(pugi::xml_document& doc) {
} }
for (auto& rq : iface.children("request")) { for (auto& rq : iface.children("request")) {
SRequest srq; SWaylandFunction srq;
srq.name = rq.attribute("name").as_string(); srq.name = rq.attribute("name").as_string();
srq.since = rq.attribute("since").as_string(); srq.since = rq.attribute("since").as_string();
srq.destructor = rq.attribute("type").as_string() == std::string{"destructor"};
for (auto& arg : rq.children("arg")) { for (auto& arg : rq.children("arg")) {
SRequestArgument sargm; SRequestArgument sargm;
if (arg.attribute("type").as_string() == std::string{"new_id"} && clientCode)
srq.newIdType = arg.attribute("interface").as_string();
sargm.newType = arg.attribute("type").as_string() == std::string{"new_id"} && clientCode;
sargm.name = sanitize(arg.attribute("name").as_string()); sargm.name = sanitize(arg.attribute("name").as_string());
sargm.wlType = arg.attribute("type").as_string(); sargm.wlType = arg.attribute("type").as_string();
sargm.interface = arg.attribute("interface").as_string(); sargm.interface = arg.attribute("interface").as_string();
@ -206,9 +219,10 @@ void parseXML(pugi::xml_document& doc) {
} }
for (auto& ev : iface.children("event")) { for (auto& ev : iface.children("event")) {
SEvent sev; SWaylandFunction sev;
sev.name = ev.attribute("name").as_string(); sev.name = ev.attribute("name").as_string();
sev.since = ev.attribute("since").as_string(); sev.since = ev.attribute("since").as_string();
sev.destructor = ev.attribute("type").as_string() == std::string{"destructor"};
for (auto& arg : ev.children("arg")) { for (auto& arg : ev.children("arg")) {
SRequestArgument sargm; SRequestArgument sargm;
@ -231,19 +245,20 @@ void parseXML(pugi::xml_document& doc) {
void parseHeader() { void parseHeader() {
// add some boilerplate // add some boilerplate
HEADER += R"#(#pragma once HEADER +=
std::format(R"#(#pragma once
#include <functional> #include <functional>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <wayland-server.h> {}
#define F std::function #define F std::function
struct wl_client; {}
struct wl_resource;
)#"; )#",
(clientCode ? "#include <wayland-client.h>" : "#include <wayland-server.h>"), (clientCode ? "struct wl_proxy;" : "struct wl_client;\nstruct wl_resource;"));
// parse all enums // parse all enums
if (!waylandEnums) { if (!waylandEnums) {
@ -258,13 +273,13 @@ struct wl_resource;
// fw declare all classes // fw declare all classes
for (auto& iface : XMLDATA.ifaces) { for (auto& iface : XMLDATA.ifaces) {
const auto IFACE_CLASS_NAME_CAMEL = camelize("C_" + iface.name); const auto IFACE_CLASS_NAME_CAMEL = camelize((clientCode ? "CC_" : "C_") + iface.name);
HEADER += std::format("\nclass {};", IFACE_CLASS_NAME_CAMEL); HEADER += std::format("\nclass {};", IFACE_CLASS_NAME_CAMEL);
for (auto& rq : iface.requests) { for (auto& rq : iface.requests) {
for (auto& arg : rq.args) { for (auto& arg : rq.args) {
if (!arg.interface.empty()) { if (!arg.interface.empty()) {
HEADER += std::format("\nclass {};", camelize("C_" + arg.interface)); HEADER += std::format("\nclass {};", camelize((clientCode ? "CC_" : "C_") + arg.interface));
} }
} }
} }
@ -272,7 +287,7 @@ struct wl_resource;
for (auto& rq : iface.events) { for (auto& rq : iface.events) {
for (auto& arg : rq.args) { for (auto& arg : rq.args) {
if (!arg.interface.empty()) { if (!arg.interface.empty()) {
HEADER += std::format("\nclass {};", camelize("C_" + arg.interface)); HEADER += std::format("\nclass {};", camelize((clientCode ? "CC_" : "C_") + arg.interface));
} }
} }
} }
@ -291,15 +306,32 @@ struct wl_resource;
for (auto& iface : XMLDATA.ifaces) { for (auto& iface : XMLDATA.ifaces) {
const auto IFACE_NAME_CAMEL = camelize(iface.name); const auto IFACE_NAME_CAMEL = camelize(iface.name);
const auto IFACE_CLASS_NAME_CAMEL = camelize("C_" + iface.name); const auto IFACE_CLASS_NAME_CAMEL = camelize((clientCode ? "CC_" : "C_") + iface.name);
if (!clientCode) {
HEADER += std::format(R"#(
struct {}DestroyWrapper {{
wl_listener listener;
{}* parent = nullptr;
}};
)#",
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL);
}
// begin the class // begin the class
HEADER += std::format(R"#( HEADER +=
std::format(R"#(
class {} {{ class {} {{
public: public:
{}(wl_client* client, uint32_t version, uint32_t id); {}({});
~{}(); ~{}();
)#",
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, (clientCode ? "wl_proxy*" : "wl_client* client, uint32_t version, uint32_t id"), IFACE_CLASS_NAME_CAMEL);
if (!clientCode) {
HEADER += std::format(R"#(
// set a listener for when this resource is _being_ destroyed // set a listener for when this resource is _being_ destroyed
void setOnDestroy(F<void({}*)> handler) {{ void setOnDestroy(F<void({}*)> handler) {{
onDestroy = handler; onDestroy = handler;
@ -339,17 +371,46 @@ class {} {{
int version() {{ int version() {{
return wl_resource_get_version(pResource); return wl_resource_get_version(pResource);
}} }}
)#", )#",
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL); IFACE_CLASS_NAME_CAMEL);
} else {
HEADER += R"#(
// set the data for this resource
void setData(void* data) {{
pData = data;
}}
// get the data for this resource
void* data() {{
return pData;
}}
// get the raw wl_resource (wl_proxy) ptr
wl_proxy* resource() {{
return pResource;
}}
// get the raw wl_proxy ptr
wl_proxy* proxy() {{
return pResource;
}}
// get the resource version
int version() {{
return wl_proxy_get_version(pResource);
}}
)#";
}
// add all setters for requests // add all setters for requests
HEADER += "\n // --------------- Requests --------------- //\n\n"; HEADER += "\n // --------------- Requests --------------- //\n\n";
for (auto& rq : iface.requests) { for (auto& rq : (clientCode ? iface.events : iface.requests)) {
std::string args = ", "; std::string args = ", ";
for (auto& arg : rq.args) { for (auto& arg : rq.args) {
if (arg.newType)
continue;
args += WPTypeToCType(arg, false) + ", "; args += WPTypeToCType(arg, false) + ", ";
} }
@ -363,9 +424,11 @@ class {} {{
HEADER += "\n // --------------- Events --------------- //\n\n"; HEADER += "\n // --------------- Events --------------- //\n\n";
for (auto& ev : iface.events) { for (auto& ev : (!clientCode ? iface.events : iface.requests)) {
std::string args = ""; std::string args = "";
for (auto& arg : ev.args) { for (auto& arg : ev.args) {
if (arg.newType)
continue;
args += WPTypeToCType(arg, true) + ", "; args += WPTypeToCType(arg, true) + ", ";
} }
@ -374,13 +437,16 @@ class {} {{
args.pop_back(); args.pop_back();
} }
HEADER += std::format(" void {}({});\n", camelize("send_" + ev.name), args); HEADER += std::format(" {} {}({});\n", ev.newIdType.empty() ? "void" : "wl_proxy*", camelize("send_" + ev.name), args);
} }
// dangerous ones // dangerous ones
for (auto& ev : iface.events) { if (!clientCode) {
for (auto& ev : (!clientCode ? iface.events : iface.requests)) {
std::string args = ""; std::string args = "";
for (auto& arg : ev.args) { for (auto& arg : ev.args) {
if (arg.newType)
continue;
args += WPTypeToCType(arg, true, true) + ", "; args += WPTypeToCType(arg, true, true) + ", ";
} }
@ -391,6 +457,7 @@ class {} {{
HEADER += std::format(" void {}({});\n", camelize("send_" + ev.name + "_raw"), args); HEADER += std::format(" void {}({});\n", camelize("send_" + ev.name + "_raw"), args);
} }
}
// end events // end events
@ -400,10 +467,12 @@ class {} {{
// start requests storage // start requests storage
HEADER += " struct {\n"; HEADER += " struct {\n";
for (auto& rq : iface.requests) { for (auto& rq : (clientCode ? iface.events : iface.requests)) {
std::string args = ", "; std::string args = ", ";
for (auto& arg : rq.args) { for (auto& arg : rq.args) {
if (arg.newType)
continue;
args += WPTypeToCType(arg, false) + ", "; args += WPTypeToCType(arg, false) + ", ";
} }
@ -419,6 +488,7 @@ class {} {{
HEADER += " } requests;\n"; HEADER += " } requests;\n";
// constant resource stuff // constant resource stuff
if (!clientCode) {
HEADER += std::format(R"#( HEADER += std::format(R"#(
void onDestroyCalled(); void onDestroyCalled();
@ -426,10 +496,18 @@ class {} {{
wl_resource* pResource = nullptr; wl_resource* pResource = nullptr;
wl_listener resourceDestroyListener; {}DestroyWrapper resourceDestroyListener;
void* pData = nullptr;)#", void* pData = nullptr;)#",
IFACE_CLASS_NAME_CAMEL); IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL);
} else {
HEADER += R"#(
wl_proxy* pResource = nullptr;
bool destroyed = false;
void* pData = nullptr;)#";
}
HEADER += "\n};\n\n"; HEADER += "\n};\n\n";
} }
@ -438,6 +516,8 @@ class {} {{
} }
void parseSource() { void parseSource() {
std::string DUMMY_TYPE_TABLE_NAME = PROTO_DATA.name + "_dummyTypes";
SOURCE += std::format(R"#(#define private public SOURCE += std::format(R"#(#define private public
#define HYPRWAYLAND_SCANNER_NO_INTERFACES #define HYPRWAYLAND_SCANNER_NO_INTERFACES
#include "{}.hpp" #include "{}.hpp"
@ -449,9 +529,9 @@ void parseSource() {
// reference interfaces // reference interfaces
// dummy // dummy
SOURCE += R"#( SOURCE += std::format(R"#(
static const wl_interface* dummyTypes[] = { nullptr }; static const wl_interface* {}[] = {{ nullptr }};
)#"; )#", DUMMY_TYPE_TABLE_NAME);
SOURCE += R"#( SOURCE += R"#(
// Reference all other interfaces. // Reference all other interfaces.
@ -510,14 +590,16 @@ static const wl_interface* dummyTypes[] = { nullptr };
const auto IFACE_WL_NAME = iface.name + "_interface"; const auto IFACE_WL_NAME = iface.name + "_interface";
const auto IFACE_NAME = iface.name; const auto IFACE_NAME = iface.name;
const auto IFACE_NAME_CAMEL = camelize(iface.name); const auto IFACE_NAME_CAMEL = camelize(iface.name);
const auto IFACE_CLASS_NAME_CAMEL = camelize("C_" + iface.name); const auto IFACE_CLASS_NAME_CAMEL = camelize((clientCode ? "CC_" : "C_") + iface.name);
// create handlers // create handlers
for (auto& rq : iface.requests) { for (auto& rq : (clientCode ? iface.events : iface.requests)) {
const auto REQUEST_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name); const auto REQUEST_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name);
std::string argsC = ", "; std::string argsC = ", ";
for (auto& arg : rq.args) { for (auto& arg : rq.args) {
if (arg.newType)
continue;
argsC += WPTypeToCType(arg, false) + " " + arg.name + ", "; argsC += WPTypeToCType(arg, false) + " " + arg.name + ", ";
} }
@ -534,6 +616,7 @@ static const wl_interface* dummyTypes[] = { nullptr };
argsN.pop_back(); argsN.pop_back();
} }
if (!clientCode) {
SOURCE += std::format(R"#( SOURCE += std::format(R"#(
static void {}(wl_client* client, wl_resource* resource{}) {{ static void {}(wl_client* client, wl_resource* resource{}) {{
const auto PO = ({}*)wl_resource_get_user_data(resource); const auto PO = ({}*)wl_resource_get_user_data(resource);
@ -542,16 +625,29 @@ static void {}(wl_client* client, wl_resource* resource{}) {{
}} }}
)#", )#",
REQUEST_NAME, argsC, IFACE_CLASS_NAME_CAMEL, camelize(rq.name), camelize(rq.name), argsN); REQUEST_NAME, argsC, IFACE_CLASS_NAME_CAMEL, camelize(rq.name), camelize(rq.name), argsN);
} else {
SOURCE += std::format(R"#(
static void {}(void* data, void* resource{}) {{
const auto PO = ({}*)data;
if (PO && PO->requests.{})
PO->requests.{}(PO{});
}}
)#",
REQUEST_NAME, argsC, IFACE_CLASS_NAME_CAMEL, camelize(rq.name), camelize(rq.name), argsN);
}
} }
// destroy handler // destroy handler
if (!clientCode) {
SOURCE += std::format(R"#( SOURCE += std::format(R"#(
static void _{}__DestroyListener(wl_listener* l, void* d) {{ static void _{}__DestroyListener(wl_listener* l, void* d) {{
{}* pResource = wl_container_of(l, pResource, resourceDestroyListener); {}DestroyWrapper *wrap = wl_container_of(l, wrap, listener);
{}* pResource = wrap->parent;
pResource->onDestroyCalled(); pResource->onDestroyCalled();
}} }}
)#", )#",
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL); IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL);
}
// create vtable // create vtable
@ -562,21 +658,27 @@ static const void* {}[] = {{
)#", )#",
IFACE_VTABLE_NAME); IFACE_VTABLE_NAME);
for (auto& rq : iface.requests) { for (auto& rq : (clientCode ? iface.events : iface.requests)) {
const auto REQUEST_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name); const auto REQUEST_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name);
SOURCE += std::format(" (void*){},\n", REQUEST_NAME); SOURCE += std::format(" (void*){},\n", REQUEST_NAME);
} }
if ((clientCode ? iface.events : iface.requests).empty()) {
SOURCE += " nullptr,\n";
}
SOURCE += "};\n"; SOURCE += "};\n";
// create events // create events
int evid = 0; int evid = 0;
for (auto& ev : iface.events) { for (auto& ev : (!clientCode ? iface.events : iface.requests)) {
const auto EVENT_NAME = camelize("send_" + ev.name); const auto EVENT_NAME = camelize("send_" + ev.name);
std::string argsC = ""; std::string argsC = "";
for (auto& arg : ev.args) { for (auto& arg : ev.args) {
if (arg.newType)
continue;
argsC += WPTypeToCType(arg, true) + " " + arg.name + ", "; argsC += WPTypeToCType(arg, true) + " " + arg.name + ", ";
} }
@ -587,15 +689,18 @@ static const void* {}[] = {{
std::string argsN = ", "; std::string argsN = ", ";
for (auto& arg : ev.args) { for (auto& arg : ev.args) {
if (!WPTypeToCType(arg, true).starts_with("C")) if (arg.newType)
argsN += "nullptr, ";
else if (!WPTypeToCType(arg, true).starts_with("C"))
argsN += arg.name + ", "; argsN += arg.name + ", ";
else else
argsN += arg.name + "->pResource, "; argsN += (arg.name + " ? " + arg.name + "->pResource : nullptr, ");
} }
argsN.pop_back(); argsN.pop_back();
argsN.pop_back(); argsN.pop_back();
if (!clientCode) {
SOURCE += std::format(R"#( SOURCE += std::format(R"#(
void {}::{}({}) {{ void {}::{}({}) {{
if (!pResource) if (!pResource)
@ -604,17 +709,36 @@ void {}::{}({}) {{
}} }}
)#", )#",
IFACE_CLASS_NAME_CAMEL, EVENT_NAME, argsC, evid, argsN); IFACE_CLASS_NAME_CAMEL, EVENT_NAME, argsC, evid, argsN);
} else {
std::string retType = ev.newIdType.empty() ? "void" : "wl_proxy";
std::string ptrRetType = ev.newIdType.empty() ? "void" : "wl_proxy*";
std::string flags = ev.destructor ? "1" : "0";
SOURCE += std::format(R"#(
{} {}::{}({}) {{
if (!pResource)
return{};{}
auto proxy = wl_proxy_marshal_flags(pResource, {}, {}, wl_proxy_get_version(pResource), {}{});{}
}}
)#",
ptrRetType, IFACE_CLASS_NAME_CAMEL, EVENT_NAME, argsC, (ev.newIdType.empty() ? "" : " nullptr"),
(ev.destructor ? "\n destroyed = true;" : ""), evid, (ev.newIdType.empty() ? "nullptr" : "&" + ev.newIdType + "_interface"), flags, argsN,
(ev.newIdType.empty() ? "\n proxy;" : "\n\n return proxy;"));
}
evid++; evid++;
} }
// dangerous // dangerous
if (!clientCode) {
evid = 0; evid = 0;
for (auto& ev : iface.events) { for (auto& ev : iface.events) {
const auto EVENT_NAME = camelize("send_" + ev.name + "_raw"); const auto EVENT_NAME = camelize("send_" + ev.name + "_raw");
std::string argsC = ""; std::string argsC = "";
for (auto& arg : ev.args) { for (auto& arg : ev.args) {
if (arg.newType)
continue;
argsC += WPTypeToCType(arg, true, true) + " " + arg.name + ", "; argsC += WPTypeToCType(arg, true, true) + " " + arg.name + ", ";
} }
@ -625,6 +749,8 @@ void {}::{}({}) {{
std::string argsN = ", "; std::string argsN = ", ";
for (auto& arg : ev.args) { for (auto& arg : ev.args) {
if (arg.newType)
continue;
argsN += arg.name + ", "; argsN += arg.name + ", ";
} }
@ -642,6 +768,7 @@ void {}::{}({}) {{
evid++; evid++;
} }
}
// wayland interfaces and stuff // wayland interfaces and stuff
@ -687,6 +814,7 @@ void {}::{}({}) {{
const auto MESSAGE_NAME_EVENTS = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_events"); const auto MESSAGE_NAME_EVENTS = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_events");
// message // message
if (!noInterfaces) {
if (iface.requests.size() > 0) { if (iface.requests.size() > 0) {
SOURCE += std::format(R"#( SOURCE += std::format(R"#(
static const wl_message {}[] = {{ static const wl_message {}[] = {{
@ -696,7 +824,8 @@ static const wl_message {}[] = {{
// create type table // create type table
const auto TYPE_TABLE_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name + "_types"); const auto TYPE_TABLE_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name + "_types");
SOURCE += std::format(" {{ \"{}\", \"{}\", {}}},\n", rq.name, argsToShort(rq.args, rq.since), rq.args.empty() ? "dummyTypes + 0" : TYPE_TABLE_NAME + " + 0"); SOURCE += std::format(" {{ .name = \"{}\", .signature = \"{}\", .types = {}}},\n", rq.name, argsToShort(rq.args, rq.since),
rq.args.empty() ? std::format("{} + 0", DUMMY_TYPE_TABLE_NAME) : TYPE_TABLE_NAME + " + 0");
} }
SOURCE += "};\n"; SOURCE += "};\n";
@ -711,7 +840,8 @@ static const wl_message {}[] = {{
// create type table // create type table
const auto TYPE_TABLE_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + ev.name + "_types"); const auto TYPE_TABLE_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + ev.name + "_types");
SOURCE += std::format(" {{ \"{}\", \"{}\", {}}},\n", ev.name, argsToShort(ev.args, ev.since), ev.args.empty() ? "dummyTypes + 0" : TYPE_TABLE_NAME + " + 0"); SOURCE += std::format(" {{ .name = \"{}\", .signature = \"{}\", .types = {}}},\n", ev.name, argsToShort(ev.args, ev.since),
ev.args.empty() ? std::format("{} + 0", DUMMY_TYPE_TABLE_NAME) : TYPE_TABLE_NAME + " + 0");
} }
SOURCE += "};\n"; SOURCE += "};\n";
@ -720,33 +850,36 @@ static const wl_message {}[] = {{
// iface // iface
SOURCE += std::format(R"#( SOURCE += std::format(R"#(
const wl_interface {} = {{ const wl_interface {} = {{
"{}", {}, .name = "{}", .version = {},
{}, {}, .method_count = {}, .methods = {},
{}, {}, .event_count = {}, .events = {},
}}; }};
)#", )#",
IFACE_WL_NAME, iface.name, iface.version, iface.requests.size(), (iface.requests.size() > 0 ? MESSAGE_NAME_REQUESTS : "nullptr"), iface.events.size(), IFACE_WL_NAME, iface.name, iface.version, iface.requests.size(), (iface.requests.size() > 0 ? MESSAGE_NAME_REQUESTS : "nullptr"),
(iface.events.size() > 0 ? MESSAGE_NAME_EVENTS : "nullptr")); iface.events.size(), (iface.events.size() > 0 ? MESSAGE_NAME_EVENTS : "nullptr"));
}
// protocol body // protocol body
if (!clientCode) {
SOURCE += std::format(R"#( SOURCE += std::format(R"#(
{}::{}(wl_client* client, uint32_t version, uint32_t id) {{ {}::{}(wl_client* client, uint32_t version, uint32_t id) :
pResource = wl_resource_create(client, &{}, version, id); pResource(wl_resource_create(client, &{}, version, id)) {{
if (!pResource) if (!pResource)
return; return;
wl_resource_set_user_data(pResource, this); wl_resource_set_user_data(pResource, this);
wl_list_init(&resourceDestroyListener.link); wl_list_init(&resourceDestroyListener.listener.link);
resourceDestroyListener.notify = _{}__DestroyListener; resourceDestroyListener.listener.notify = _{}__DestroyListener;
wl_resource_add_destroy_listener(pResource, &resourceDestroyListener); resourceDestroyListener.parent = this;
wl_resource_add_destroy_listener(pResource, &resourceDestroyListener.listener);
wl_resource_set_implementation(pResource, {}, this, nullptr); wl_resource_set_implementation(pResource, {}, this, nullptr);
}} }}
{}::~{}() {{ {}::~{}() {{
wl_list_remove(&resourceDestroyListener.link); wl_list_remove(&resourceDestroyListener.listener.link);
wl_list_init(&resourceDestroyListener.link); wl_list_init(&resourceDestroyListener.listener.link);
// if we still own the wayland resource, // if we still own the wayland resource,
// it means we need to destroy it. // it means we need to destroy it.
@ -758,8 +891,8 @@ const wl_interface {} = {{
void {}::onDestroyCalled() {{ void {}::onDestroyCalled() {{
wl_resource_set_user_data(pResource, nullptr); wl_resource_set_user_data(pResource, nullptr);
wl_list_remove(&resourceDestroyListener.link); wl_list_remove(&resourceDestroyListener.listener.link);
wl_list_init(&resourceDestroyListener.link); wl_list_init(&resourceDestroyListener.listener.link);
// set the resource to nullptr, // set the resource to nullptr,
// as it will be freed. If the consumer does not destroy this resource // as it will be freed. If the consumer does not destroy this resource
@ -772,8 +905,38 @@ void {}::onDestroyCalled() {{
)#", )#",
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_NAME + "_interface", IFACE_CLASS_NAME_CAMEL, IFACE_VTABLE_NAME, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_NAME + "_interface", IFACE_CLASS_NAME_CAMEL, IFACE_VTABLE_NAME, IFACE_CLASS_NAME_CAMEL,
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL); IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL);
} else {
std::string DTOR_FUNC = "";
for (auto& rq : iface.requests) { for (auto& rq : iface.requests) {
if (!rq.destructor)
continue;
DTOR_FUNC = camelize("send_" + rq.name) + "()";
break;
}
if (DTOR_FUNC.empty())
DTOR_FUNC = "wl_proxy_destroy(pResource)";
SOURCE += std::format(R"#(
{}::{}(wl_proxy* resource) : pResource(resource) {{
if (!pResource)
return;
wl_proxy_add_listener(pResource, (void (**)(void))&{}, this);
}}
{}::~{}() {{
if (!destroyed)
{};
}}
)#",
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_VTABLE_NAME, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, DTOR_FUNC);
}
for (auto& rq : (clientCode ? iface.events : iface.requests)) {
std::string args = ", "; std::string args = ", ";
for (auto& arg : rq.args) { for (auto& arg : rq.args) {
args += WPTypeToCType(arg, false) + ", "; args += WPTypeToCType(arg, false) + ", ";
@ -808,6 +971,16 @@ int main(int argc, char** argv, char** envp) {
return 0; return 0;
} }
if (curarg == "-c" || curarg == "--client") {
clientCode = true;
continue;
}
if (curarg == "--no-interfaces") {
noInterfaces = true;
continue;
}
if (curarg == "--wayland-enums") { if (curarg == "--wayland-enums") {
waylandEnums = true; waylandEnums = true;
continue; continue;