New upstream version 0.3.10
This commit is contained in:
commit
91ab610821
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: true
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
ColumnLimit: 180
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: false
|
||||
IncludeBlocks: Preserve
|
||||
IndentCaseLabels: true
|
||||
IndentWidth: 4
|
||||
PointerAlignment: Left
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
|
||||
BraceWrapping:
|
||||
AfterEnum: false
|
||||
|
||||
AlignConsecutiveDeclarations: AcrossEmptyLines
|
||||
|
||||
NamespaceIndentation: All
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
name: Build
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
jobs:
|
||||
nix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: cachix/install-nix-action@v25
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
# not needed (yet)
|
||||
# - uses: cachix/cachix-action@v12
|
||||
# with:
|
||||
# name: hyprland
|
||||
# authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
|
||||
- name: Build
|
||||
run: nix build -L
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
build/
|
||||
result*
|
||||
/.vscode/
|
||||
/.idea/
|
||||
.envrc
|
||||
.cache
|
||||
|
||||
*.o
|
||||
*-protocol.c
|
||||
*-protocol.h
|
||||
.ccls-cache
|
||||
*.so
|
||||
|
||||
hyprctl/hyprctl
|
||||
|
||||
gmon.out
|
||||
*.out
|
||||
*.tar.gz
|
||||
|
||||
PKGBUILD
|
||||
|
||||
src/version.h
|
||||
|
||||
.direnv
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
set(VERSION 0.3.10)
|
||||
|
||||
project(hyprwayland-scanner
|
||||
DESCRIPTION "A hyprland version of wayland-scanner in and for C++"
|
||||
VERSION ${VERSION}
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
set(PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Configuring hyprwayland-scanner in Debug with CMake")
|
||||
add_compile_definitions(HYPRLAND_DEBUG)
|
||||
else()
|
||||
add_compile_options(-O3)
|
||||
message(STATUS "Configuring hyprwayland-scanner in Release with CMake")
|
||||
endif()
|
||||
|
||||
|
||||
# configure
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value
|
||||
-Wno-missing-field-initializers -Wno-narrowing)
|
||||
|
||||
add_compile_definitions(SCANNER_VERSION="${VERSION}")
|
||||
|
||||
configure_file(hyprwayland-scanner.pc.in hyprwayland-scanner.pc @ONLY)
|
||||
|
||||
# dependencies
|
||||
message(STATUS "Checking deps...")
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET pugixml)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
add_executable(hyprwayland-scanner ${SRCFILES})
|
||||
target_link_libraries(hyprwayland-scanner PRIVATE rt Threads::Threads PkgConfig::deps)
|
||||
|
||||
configure_package_config_file(
|
||||
hyprwayland-scanner-config.cmake.in
|
||||
hyprwayland-scanner-config.cmake
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/hyprwayland-scanner"
|
||||
PATH_VARS CMAKE_INSTALL_BINDIR
|
||||
)
|
||||
write_basic_package_version_file(
|
||||
"hyprwayland-scanner-config-version.cmake"
|
||||
VERSION "${VERSION}"
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
# Installation
|
||||
install(TARGETS hyprwayland-scanner)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/hyprwayland-scanner.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install(FILES
|
||||
${CMAKE_BINARY_DIR}/hyprwayland-scanner-config.cmake
|
||||
${CMAKE_BINARY_DIR}/hyprwayland-scanner-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hyprwayland-scanner
|
||||
)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, Hypr Development
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# hyprwayland-scanner
|
||||
A Hyprland implementation of wayland-scanner, in and for C++.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
hyprwayland-scanner '/path/to/proto' '/path/to/output/directory'
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Requires a compiler with C++23 support.
|
||||
|
||||
Dep list:
|
||||
- pugixml
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -B build
|
||||
cmake --build build -j `nproc`
|
||||
```
|
||||
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
sudo cmake --install build
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Support for generating client headers
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1713537308,
|
||||
"narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1689347949,
|
||||
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-linux",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
description = "A Hyprland version of wayland-scanner in and for C++";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
systems.url = "github:nix-systems/default-linux";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
systems,
|
||||
}: let
|
||||
inherit (nixpkgs) lib;
|
||||
eachSystem = lib.genAttrs (import systems);
|
||||
pkgsFor = eachSystem (system:
|
||||
import nixpkgs {
|
||||
localSystem.system = system;
|
||||
overlays = with self.overlays; [hyprwayland-scanner];
|
||||
});
|
||||
mkDate = longDate: (lib.concatStringsSep "-" [
|
||||
(builtins.substring 0 4 longDate)
|
||||
(builtins.substring 4 2 longDate)
|
||||
(builtins.substring 6 2 longDate)
|
||||
]);
|
||||
in {
|
||||
overlays = {
|
||||
default = self.overlays.hyprwayland-scanner;
|
||||
hyprwayland-scanner = final: prev: {
|
||||
hyprwayland-scanner = final.callPackage ./nix/default.nix {
|
||||
stdenv = final.gcc13Stdenv;
|
||||
version = "0.1.0" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
packages = eachSystem (system: {
|
||||
default = self.packages.${system}.hyprwayland-scanner;
|
||||
inherit (pkgsFor.${system}) hyprwayland-scanner;
|
||||
});
|
||||
|
||||
formatter = eachSystem (system: pkgsFor.${system}.alejandra);
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(BINDIR "@PACKAGE_CMAKE_INSTALL_BINDIR@")
|
||||
|
||||
function(hyprwayland_protocol targets protoName protoPath outputPath)
|
||||
add_custom_command(
|
||||
OUTPUT "${outputPath}/${protoName}.cpp"
|
||||
COMMAND "${BINDIR}/hyprwayland-scanner" "${protoPath}/${protoName}.xml" "${outputPath}"
|
||||
)
|
||||
foreach(target ${targets})
|
||||
target_sources(${target} PRIVATE "${outputPath}/${protoName}.cpp")
|
||||
target_sources(${target} PRIVATE "${outputPath}/${protoName}.hpp")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
check_required_components(hyprwayland-scanner)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
hyprwayland_scanner=@PREFIX@/bin/hyprwayland-scanner
|
||||
|
||||
Name: HyprWayland Scanner
|
||||
URL: https://github.com/hyprwm/hyprwayland-scanner
|
||||
Description: A Hyprland version of wayland-scanner in and for C++
|
||||
Version: @VERSION@
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
cmake,
|
||||
pkg-config,
|
||||
pugixml,
|
||||
version ? "git",
|
||||
doCheck ? false,
|
||||
}:
|
||||
stdenv.mkDerivation {
|
||||
pname = "hyprwayland-scanner";
|
||||
inherit version doCheck;
|
||||
src = ../.;
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
pugixml
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/hyprwm/hyprwayland-scanner";
|
||||
description = "A Hyprland version of wayland-scanner in and for C++";
|
||||
license = licenses.bsd3;
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,896 @@
|
|||
#include <pugixml.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <format>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
#include <filesystem>
|
||||
|
||||
bool waylandEnums = false;
|
||||
|
||||
struct SRequestArgument {
|
||||
std::string wlType;
|
||||
std::string interface;
|
||||
std::string enumName;
|
||||
std::string name;
|
||||
bool allowNull = false;
|
||||
};
|
||||
|
||||
struct SRequest {
|
||||
std::vector<SRequestArgument> args;
|
||||
std::string name;
|
||||
std::string since;
|
||||
};
|
||||
|
||||
struct SEvent {
|
||||
std::vector<SRequestArgument> args;
|
||||
std::string name;
|
||||
std::string since;
|
||||
};
|
||||
|
||||
struct SInterface {
|
||||
std::vector<SRequest> requests;
|
||||
std::vector<SEvent> events;
|
||||
std::string name;
|
||||
int version = 1;
|
||||
};
|
||||
|
||||
struct SEnum {
|
||||
std::string name;
|
||||
std::string nameOriginal;
|
||||
std::vector<std::pair<std::string, int>> values;
|
||||
};
|
||||
|
||||
struct {
|
||||
std::vector<SInterface> ifaces;
|
||||
std::vector<SEnum> enums;
|
||||
} XMLDATA;
|
||||
|
||||
std::string sanitize(const std::string& in) {
|
||||
if (in == "namespace")
|
||||
return "namespace_";
|
||||
if (in == "class")
|
||||
return "class_";
|
||||
if (in == "delete")
|
||||
return "delete_";
|
||||
if (in == "new")
|
||||
return "new_";
|
||||
return in;
|
||||
}
|
||||
|
||||
std::string argsToShort(std::vector<SRequestArgument>& args, const std::string& since) {
|
||||
std::string shortt = since;
|
||||
for (auto& a : args) {
|
||||
if (a.wlType == "int")
|
||||
shortt += "i";
|
||||
else if (a.wlType == "new_id") {
|
||||
if (a.interface.empty())
|
||||
shortt += "su";
|
||||
shortt += "n";
|
||||
} else if (a.wlType == "uint")
|
||||
shortt += "u";
|
||||
else if (a.wlType == "fixed")
|
||||
shortt += "f";
|
||||
else if (a.wlType == "string")
|
||||
shortt += std::string(a.allowNull ? "?s" : "s");
|
||||
else if (a.wlType == "object")
|
||||
shortt += std::string(a.allowNull ? "?" : "") + "o";
|
||||
else if (a.wlType == "array")
|
||||
shortt += "a";
|
||||
else if (a.wlType == "fd")
|
||||
shortt += "h";
|
||||
else
|
||||
throw std::runtime_error("Unknown arg in argsToShort");
|
||||
}
|
||||
return shortt;
|
||||
}
|
||||
|
||||
std::string camelize(std::string snake) {
|
||||
std::string result = "";
|
||||
for (size_t i = 0; i < snake.length(); ++i) {
|
||||
if (snake[i] == '_' && i != 0 && i + 1 < snake.length() && snake[i + 1] != '_') {
|
||||
result += ::toupper(snake[i + 1]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
result += snake[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string WPTypeToCType(const SRequestArgument& arg, bool event /* events pass iface ptrs, requests ids */, bool ignoreTypes = false /* for dangerous */) {
|
||||
if (arg.wlType == "uint" || arg.wlType == "new_id") {
|
||||
if (arg.enumName.empty() && arg.interface.empty())
|
||||
return "uint32_t";
|
||||
|
||||
// enum
|
||||
if (!arg.enumName.empty())
|
||||
for (auto& e : XMLDATA.enums) {
|
||||
if (e.nameOriginal == arg.enumName)
|
||||
return e.name;
|
||||
}
|
||||
|
||||
// iface
|
||||
if (!arg.interface.empty() && event) {
|
||||
for (auto& i : XMLDATA.ifaces) {
|
||||
if (i.name == arg.interface)
|
||||
return camelize("C_" + arg.interface + "*");
|
||||
}
|
||||
return "wl_resource*";
|
||||
}
|
||||
|
||||
return "uint32_t";
|
||||
}
|
||||
if (arg.wlType == "object") {
|
||||
if (!arg.interface.empty() && event && !ignoreTypes) {
|
||||
for (auto& i : XMLDATA.ifaces) {
|
||||
if (i.name == arg.interface)
|
||||
return camelize("C_" + arg.interface + "*");
|
||||
}
|
||||
}
|
||||
return "wl_resource*";
|
||||
}
|
||||
if (arg.wlType == "int" || arg.wlType == "fd")
|
||||
return "int32_t";
|
||||
if (arg.wlType == "fixed")
|
||||
return "wl_fixed_t";
|
||||
if (arg.wlType == "array")
|
||||
return "wl_array*";
|
||||
if (arg.wlType == "string")
|
||||
return "const char*";
|
||||
throw std::runtime_error("unknown wp type");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string HEADER;
|
||||
std::string SOURCE;
|
||||
|
||||
struct {
|
||||
std::string name;
|
||||
std::string nameOriginal;
|
||||
std::string fileName;
|
||||
} PROTO_DATA;
|
||||
|
||||
void parseXML(pugi::xml_document& doc) {
|
||||
|
||||
for (auto& ge : doc.child("protocol").children("enum")) {
|
||||
SEnum enum_;
|
||||
enum_.nameOriginal = ge.attribute("name").as_string();
|
||||
enum_.name = waylandEnums ? "enum " + PROTO_DATA.name + "_" + enum_.nameOriginal : camelize(PROTO_DATA.name + "_" + enum_.nameOriginal);
|
||||
for (auto& entry : ge.children("entry")) {
|
||||
auto VALUENAME = enum_.nameOriginal + "_" + entry.attribute("name").as_string();
|
||||
std::transform(VALUENAME.begin(), VALUENAME.end(), VALUENAME.begin(), ::toupper);
|
||||
enum_.values.emplace_back(std::make_pair<>(VALUENAME, entry.attribute("value").as_int()));
|
||||
}
|
||||
XMLDATA.enums.push_back(enum_);
|
||||
}
|
||||
|
||||
for (auto& iface : doc.child("protocol").children("interface")) {
|
||||
SInterface ifc;
|
||||
ifc.name = iface.attribute("name").as_string();
|
||||
ifc.version = iface.attribute("version").as_int();
|
||||
|
||||
for (auto& en : iface.children("enum")) {
|
||||
SEnum enum_;
|
||||
enum_.nameOriginal = en.attribute("name").as_string();
|
||||
enum_.name = waylandEnums ? "enum " + ifc.name + "_" + enum_.nameOriginal : camelize(ifc.name + "_" + enum_.nameOriginal);
|
||||
for (auto& entry : en.children("entry")) {
|
||||
auto VALUENAME = ifc.name + "_" + enum_.nameOriginal + "_" + entry.attribute("name").as_string();
|
||||
std::transform(VALUENAME.begin(), VALUENAME.end(), VALUENAME.begin(), ::toupper);
|
||||
enum_.values.emplace_back(std::make_pair<>(VALUENAME, entry.attribute("value").as_int()));
|
||||
}
|
||||
XMLDATA.enums.push_back(enum_);
|
||||
}
|
||||
|
||||
for (auto& rq : iface.children("request")) {
|
||||
SRequest srq;
|
||||
srq.name = rq.attribute("name").as_string();
|
||||
srq.since = rq.attribute("since").as_string();
|
||||
|
||||
for (auto& arg : rq.children("arg")) {
|
||||
SRequestArgument sargm;
|
||||
sargm.name = sanitize(arg.attribute("name").as_string());
|
||||
sargm.wlType = arg.attribute("type").as_string();
|
||||
sargm.interface = arg.attribute("interface").as_string();
|
||||
sargm.enumName = arg.attribute("enum").as_string();
|
||||
sargm.allowNull = arg.attribute("allow-null").as_string() == std::string{"true"};
|
||||
|
||||
srq.args.push_back(sargm);
|
||||
}
|
||||
|
||||
ifc.requests.push_back(srq);
|
||||
}
|
||||
|
||||
for (auto& ev : iface.children("event")) {
|
||||
SEvent sev;
|
||||
sev.name = ev.attribute("name").as_string();
|
||||
sev.since = ev.attribute("since").as_string();
|
||||
|
||||
for (auto& arg : ev.children("arg")) {
|
||||
SRequestArgument sargm;
|
||||
sargm.name = sanitize(arg.attribute("name").as_string());
|
||||
sargm.interface = arg.attribute("interface").as_string();
|
||||
sargm.wlType = arg.attribute("type").as_string();
|
||||
sargm.enumName = arg.attribute("enum").as_string();
|
||||
sargm.allowNull = arg.attribute("allow-null").as_string() == std::string{"true"};
|
||||
|
||||
sev.args.push_back(sargm);
|
||||
}
|
||||
|
||||
ifc.events.push_back(sev);
|
||||
}
|
||||
|
||||
XMLDATA.ifaces.push_back(ifc);
|
||||
}
|
||||
}
|
||||
|
||||
void parseHeader() {
|
||||
|
||||
// add some boilerplate
|
||||
HEADER += R"#(#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <wayland-server.h>
|
||||
|
||||
#define F std::function
|
||||
|
||||
struct wl_client;
|
||||
struct wl_resource;
|
||||
|
||||
)#";
|
||||
|
||||
// parse all enums
|
||||
if (!waylandEnums) {
|
||||
for (auto& en : XMLDATA.enums) {
|
||||
HEADER += std::format("enum {} : uint32_t {{\n", en.name);
|
||||
for (auto& [k, v] : en.values) {
|
||||
HEADER += std::format(" {} = {},\n", k, v);
|
||||
}
|
||||
HEADER += "};\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// fw declare all classes
|
||||
for (auto& iface : XMLDATA.ifaces) {
|
||||
const auto IFACE_CLASS_NAME_CAMEL = camelize("C_" + iface.name);
|
||||
HEADER += std::format("\nclass {};", IFACE_CLASS_NAME_CAMEL);
|
||||
|
||||
for (auto& rq : iface.requests) {
|
||||
for (auto& arg : rq.args) {
|
||||
if (!arg.interface.empty()) {
|
||||
HEADER += std::format("\nclass {};", camelize("C_" + arg.interface));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& rq : iface.events) {
|
||||
for (auto& arg : rq.args) {
|
||||
if (!arg.interface.empty()) {
|
||||
HEADER += std::format("\nclass {};", camelize("C_" + arg.interface));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HEADER += "\n\n#ifndef HYPRWAYLAND_SCANNER_NO_INTERFACES\n";
|
||||
|
||||
for (auto& iface : XMLDATA.ifaces) {
|
||||
const auto IFACE_WL_NAME = iface.name + "_interface";
|
||||
const auto IFACE_WL_NAME_CAMEL = camelize(iface.name + "_interface");
|
||||
|
||||
HEADER += std::format("extern const wl_interface {};\n", IFACE_WL_NAME, IFACE_WL_NAME_CAMEL, IFACE_WL_NAME);
|
||||
}
|
||||
|
||||
HEADER += "\n#endif\n";
|
||||
|
||||
for (auto& iface : XMLDATA.ifaces) {
|
||||
const auto IFACE_NAME_CAMEL = camelize(iface.name);
|
||||
const auto IFACE_CLASS_NAME_CAMEL = camelize("C_" + iface.name);
|
||||
|
||||
// begin the class
|
||||
HEADER += std::format(R"#(
|
||||
class {} {{
|
||||
public:
|
||||
{}(wl_client* client, uint32_t version, uint32_t id);
|
||||
~{}();
|
||||
|
||||
// set a listener for when this resource is _being_ destroyed
|
||||
void setOnDestroy(F<void({}*)> handler) {{
|
||||
onDestroy = handler;
|
||||
}}
|
||||
|
||||
// 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 ptr
|
||||
wl_resource* resource() {{
|
||||
return pResource;
|
||||
}}
|
||||
|
||||
// get the client
|
||||
wl_client* client() {{
|
||||
return wl_resource_get_client(pResource);
|
||||
}}
|
||||
|
||||
// send an error
|
||||
void error(uint32_t error, const std::string& message) {{
|
||||
wl_resource_post_error(pResource, error, "%s", message.c_str());
|
||||
}}
|
||||
|
||||
// send out of memory
|
||||
void noMemory() {{
|
||||
wl_resource_post_no_memory(pResource);
|
||||
}}
|
||||
|
||||
// get the resource version
|
||||
int version() {{
|
||||
return wl_resource_get_version(pResource);
|
||||
}}
|
||||
|
||||
)#",
|
||||
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL);
|
||||
|
||||
// add all setters for requests
|
||||
HEADER += "\n // --------------- Requests --------------- //\n\n";
|
||||
|
||||
for (auto& rq : iface.requests) {
|
||||
|
||||
std::string args = ", ";
|
||||
for (auto& arg : rq.args) {
|
||||
args += WPTypeToCType(arg, false) + ", ";
|
||||
}
|
||||
|
||||
args.pop_back();
|
||||
args.pop_back();
|
||||
|
||||
HEADER += std::format(" void {}(F<void({}*{})> handler);\n", camelize("set_" + rq.name), IFACE_CLASS_NAME_CAMEL, args);
|
||||
}
|
||||
|
||||
// start events
|
||||
|
||||
HEADER += "\n // --------------- Events --------------- //\n\n";
|
||||
|
||||
for (auto& ev : iface.events) {
|
||||
std::string args = "";
|
||||
for (auto& arg : ev.args) {
|
||||
args += WPTypeToCType(arg, true) + ", ";
|
||||
}
|
||||
|
||||
if (!args.empty()) {
|
||||
args.pop_back();
|
||||
args.pop_back();
|
||||
}
|
||||
|
||||
HEADER += std::format(" void {}({});\n", camelize("send_" + ev.name), args);
|
||||
}
|
||||
|
||||
// dangerous ones
|
||||
for (auto& ev : iface.events) {
|
||||
std::string args = "";
|
||||
for (auto& arg : ev.args) {
|
||||
args += WPTypeToCType(arg, true, true) + ", ";
|
||||
}
|
||||
|
||||
if (!args.empty()) {
|
||||
args.pop_back();
|
||||
args.pop_back();
|
||||
}
|
||||
|
||||
HEADER += std::format(" void {}({});\n", camelize("send_" + ev.name + "_raw"), args);
|
||||
}
|
||||
|
||||
// end events
|
||||
|
||||
// start private section
|
||||
HEADER += "\n private:\n";
|
||||
|
||||
// start requests storage
|
||||
HEADER += " struct {\n";
|
||||
|
||||
for (auto& rq : iface.requests) {
|
||||
|
||||
std::string args = ", ";
|
||||
for (auto& arg : rq.args) {
|
||||
args += WPTypeToCType(arg, false) + ", ";
|
||||
}
|
||||
|
||||
if (!args.empty()) {
|
||||
args.pop_back();
|
||||
args.pop_back();
|
||||
}
|
||||
|
||||
HEADER += std::format(" F<void({}*{})> {};\n", IFACE_CLASS_NAME_CAMEL, args, camelize(rq.name));
|
||||
}
|
||||
|
||||
// end requests storage
|
||||
HEADER += " } requests;\n";
|
||||
|
||||
// constant resource stuff
|
||||
HEADER += std::format(R"#(
|
||||
void onDestroyCalled();
|
||||
|
||||
F<void({}*)> onDestroy;
|
||||
|
||||
wl_resource* pResource = nullptr;
|
||||
|
||||
wl_listener resourceDestroyListener;
|
||||
|
||||
void* pData = nullptr;)#",
|
||||
IFACE_CLASS_NAME_CAMEL);
|
||||
|
||||
HEADER += "\n};\n\n";
|
||||
}
|
||||
|
||||
HEADER += "\n\n#undef F\n";
|
||||
}
|
||||
|
||||
void parseSource() {
|
||||
SOURCE += std::format(R"#(#define private public
|
||||
#define HYPRWAYLAND_SCANNER_NO_INTERFACES
|
||||
#include "{}.hpp"
|
||||
#undef private
|
||||
#define F std::function
|
||||
)#",
|
||||
PROTO_DATA.fileName);
|
||||
|
||||
// reference interfaces
|
||||
|
||||
// dummy
|
||||
SOURCE += R"#(
|
||||
static const wl_interface* dummyTypes[] = { nullptr };
|
||||
)#";
|
||||
|
||||
SOURCE += R"#(
|
||||
// Reference all other interfaces.
|
||||
// The reason why this is in snake is to
|
||||
// be able to cooperate with existing
|
||||
// wayland_scanner interfaces (they are interop)
|
||||
)#";
|
||||
|
||||
std::vector<std::string> declaredIfaces;
|
||||
|
||||
for (auto& iface : XMLDATA.ifaces) {
|
||||
const auto IFACE_WL_NAME = iface.name + "_interface";
|
||||
const auto IFACE_WL_NAME_CAMEL = camelize(iface.name + "_interface");
|
||||
declaredIfaces.push_back(IFACE_WL_NAME);
|
||||
|
||||
SOURCE += std::format("extern const wl_interface {};\n", IFACE_WL_NAME, IFACE_WL_NAME_CAMEL, IFACE_WL_NAME);
|
||||
}
|
||||
|
||||
for (auto& iface : XMLDATA.ifaces) {
|
||||
// do all referenced too
|
||||
for (auto& rq : iface.requests) {
|
||||
for (auto& arg : rq.args) {
|
||||
if (arg.interface.empty())
|
||||
continue;
|
||||
|
||||
const auto IFACE_WL_NAME2 = arg.interface + "_interface";
|
||||
const auto IFACE_WL_NAME_CAMEL2 = camelize(arg.interface + "_interface");
|
||||
|
||||
if (std::find(declaredIfaces.begin(), declaredIfaces.end(), IFACE_WL_NAME2) == declaredIfaces.end()) {
|
||||
SOURCE += std::format("extern const wl_interface {};\n", IFACE_WL_NAME2, IFACE_WL_NAME_CAMEL2, IFACE_WL_NAME2);
|
||||
declaredIfaces.push_back(IFACE_WL_NAME2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& ev : iface.events) {
|
||||
for (auto& arg : ev.args) {
|
||||
if (arg.interface.empty())
|
||||
continue;
|
||||
|
||||
const auto IFACE_WL_NAME2 = arg.interface + "_interface";
|
||||
const auto IFACE_WL_NAME_CAMEL2 = camelize(arg.interface + "_interface");
|
||||
|
||||
if (std::find(declaredIfaces.begin(), declaredIfaces.end(), IFACE_WL_NAME2) == declaredIfaces.end()) {
|
||||
SOURCE += std::format("extern const wl_interface {};\n", IFACE_WL_NAME2, IFACE_WL_NAME_CAMEL2, IFACE_WL_NAME2);
|
||||
declaredIfaces.push_back(IFACE_WL_NAME2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// declare ifaces
|
||||
|
||||
for (auto& iface : XMLDATA.ifaces) {
|
||||
|
||||
const auto IFACE_WL_NAME = iface.name + "_interface";
|
||||
const auto IFACE_NAME = iface.name;
|
||||
const auto IFACE_NAME_CAMEL = camelize(iface.name);
|
||||
const auto IFACE_CLASS_NAME_CAMEL = camelize("C_" + iface.name);
|
||||
|
||||
// create handlers
|
||||
for (auto& rq : iface.requests) {
|
||||
const auto REQUEST_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name);
|
||||
|
||||
std::string argsC = ", ";
|
||||
for (auto& arg : rq.args) {
|
||||
argsC += WPTypeToCType(arg, false) + " " + arg.name + ", ";
|
||||
}
|
||||
|
||||
argsC.pop_back();
|
||||
argsC.pop_back();
|
||||
|
||||
std::string argsN = ", ";
|
||||
for (auto& arg : rq.args) {
|
||||
argsN += arg.name + ", ";
|
||||
}
|
||||
|
||||
if (!argsN.empty()) {
|
||||
argsN.pop_back();
|
||||
argsN.pop_back();
|
||||
}
|
||||
|
||||
SOURCE += std::format(R"#(
|
||||
static void {}(wl_client* client, wl_resource* resource{}) {{
|
||||
const auto PO = ({}*)wl_resource_get_user_data(resource);
|
||||
if (PO && PO->requests.{})
|
||||
PO->requests.{}(PO{});
|
||||
}}
|
||||
)#",
|
||||
REQUEST_NAME, argsC, IFACE_CLASS_NAME_CAMEL, camelize(rq.name), camelize(rq.name), argsN);
|
||||
}
|
||||
|
||||
// destroy handler
|
||||
SOURCE += std::format(R"#(
|
||||
static void _{}__DestroyListener(wl_listener* l, void* d) {{
|
||||
{}* pResource = wl_container_of(l, pResource, resourceDestroyListener);
|
||||
pResource->onDestroyCalled();
|
||||
}}
|
||||
)#",
|
||||
IFACE_CLASS_NAME_CAMEL, IFACE_CLASS_NAME_CAMEL);
|
||||
|
||||
// create vtable
|
||||
|
||||
const auto IFACE_VTABLE_NAME = "_" + IFACE_CLASS_NAME_CAMEL + "VTable";
|
||||
|
||||
SOURCE += std::format(R"#(
|
||||
static const void* {}[] = {{
|
||||
)#",
|
||||
IFACE_VTABLE_NAME);
|
||||
|
||||
for (auto& rq : iface.requests) {
|
||||
const auto REQUEST_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name);
|
||||
SOURCE += std::format(" (void*){},\n", REQUEST_NAME);
|
||||
}
|
||||
|
||||
SOURCE += "};\n";
|
||||
|
||||
// create events
|
||||
|
||||
int evid = 0;
|
||||
for (auto& ev : iface.events) {
|
||||
const auto EVENT_NAME = camelize("send_" + ev.name);
|
||||
|
||||
std::string argsC = "";
|
||||
for (auto& arg : ev.args) {
|
||||
argsC += WPTypeToCType(arg, true) + " " + arg.name + ", ";
|
||||
}
|
||||
|
||||
if (!argsC.empty()) {
|
||||
argsC.pop_back();
|
||||
argsC.pop_back();
|
||||
}
|
||||
|
||||
std::string argsN = ", ";
|
||||
for (auto& arg : ev.args) {
|
||||
if (!WPTypeToCType(arg, true).starts_with("C"))
|
||||
argsN += arg.name + ", ";
|
||||
else
|
||||
argsN += arg.name + "->pResource, ";
|
||||
}
|
||||
|
||||
argsN.pop_back();
|
||||
argsN.pop_back();
|
||||
|
||||
SOURCE += std::format(R"#(
|
||||
void {}::{}({}) {{
|
||||
if (!pResource)
|
||||
return;
|
||||
wl_resource_post_event(pResource, {}{});
|
||||
}}
|
||||
)#",
|
||||
IFACE_CLASS_NAME_CAMEL, EVENT_NAME, argsC, evid, argsN);
|
||||
|
||||
evid++;
|
||||
}
|
||||
|
||||
// dangerous
|
||||
evid = 0;
|
||||
for (auto& ev : iface.events) {
|
||||
const auto EVENT_NAME = camelize("send_" + ev.name + "_raw");
|
||||
|
||||
std::string argsC = "";
|
||||
for (auto& arg : ev.args) {
|
||||
argsC += WPTypeToCType(arg, true, true) + " " + arg.name + ", ";
|
||||
}
|
||||
|
||||
if (!argsC.empty()) {
|
||||
argsC.pop_back();
|
||||
argsC.pop_back();
|
||||
}
|
||||
|
||||
std::string argsN = ", ";
|
||||
for (auto& arg : ev.args) {
|
||||
argsN += arg.name + ", ";
|
||||
}
|
||||
|
||||
argsN.pop_back();
|
||||
argsN.pop_back();
|
||||
|
||||
SOURCE += std::format(R"#(
|
||||
void {}::{}({}) {{
|
||||
if (!pResource)
|
||||
return;
|
||||
wl_resource_post_event(pResource, {}{});
|
||||
}}
|
||||
)#",
|
||||
IFACE_CLASS_NAME_CAMEL, EVENT_NAME, argsC, evid, argsN);
|
||||
|
||||
evid++;
|
||||
}
|
||||
|
||||
// wayland interfaces and stuff
|
||||
|
||||
// type tables
|
||||
for (auto& rq : iface.requests) {
|
||||
if (rq.args.empty())
|
||||
continue;
|
||||
|
||||
const auto TYPE_TABLE_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + rq.name + "_types");
|
||||
SOURCE += std::format("static const wl_interface* {}[] = {{\n", TYPE_TABLE_NAME);
|
||||
|
||||
for (auto& arg : rq.args) {
|
||||
if (arg.interface.empty()) {
|
||||
SOURCE += " nullptr,\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
SOURCE += std::format(" &{}_interface,\n", arg.interface);
|
||||
}
|
||||
|
||||
SOURCE += "};\n";
|
||||
}
|
||||
for (auto& ev : iface.events) {
|
||||
if (ev.args.empty())
|
||||
continue;
|
||||
|
||||
const auto TYPE_TABLE_NAME = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_" + ev.name + "_types");
|
||||
SOURCE += std::format("static const wl_interface* {}[] = {{\n", TYPE_TABLE_NAME);
|
||||
|
||||
for (auto& arg : ev.args) {
|
||||
if (arg.interface.empty()) {
|
||||
SOURCE += " nullptr,\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
SOURCE += std::format(" &{}_interface,\n", arg.interface);
|
||||
}
|
||||
|
||||
SOURCE += "};\n";
|
||||
}
|
||||
|
||||
const auto MESSAGE_NAME_REQUESTS = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_requests");
|
||||
const auto MESSAGE_NAME_EVENTS = camelize(std::string{"_"} + "C_" + IFACE_NAME + "_events");
|
||||
|
||||
// message
|
||||
if (iface.requests.size() > 0) {
|
||||
SOURCE += std::format(R"#(
|
||||
static const wl_message {}[] = {{
|
||||
)#",
|
||||
MESSAGE_NAME_REQUESTS);
|
||||
for (auto& rq : iface.requests) {
|
||||
// create type table
|
||||
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 += "};\n";
|
||||
}
|
||||
|
||||
if (iface.events.size() > 0) {
|
||||
SOURCE += std::format(R"#(
|
||||
static const wl_message {}[] = {{
|
||||
)#",
|
||||
MESSAGE_NAME_EVENTS);
|
||||
for (auto& ev : iface.events) {
|
||||
// create type table
|
||||
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 += "};\n";
|
||||
}
|
||||
|
||||
// iface
|
||||
SOURCE += std::format(R"#(
|
||||
const wl_interface {} = {{
|
||||
"{}", {},
|
||||
{}, {},
|
||||
{}, {},
|
||||
}};
|
||||
)#",
|
||||
IFACE_WL_NAME, iface.name, iface.version, iface.requests.size(), (iface.requests.size() > 0 ? MESSAGE_NAME_REQUESTS : "nullptr"), iface.events.size(),
|
||||
(iface.events.size() > 0 ? MESSAGE_NAME_EVENTS : "nullptr"));
|
||||
|
||||
// protocol body
|
||||
SOURCE += std::format(R"#(
|
||||
{}::{}(wl_client* client, uint32_t version, uint32_t id) {{
|
||||
pResource = wl_resource_create(client, &{}, version, id);
|
||||
|
||||
if (!pResource)
|
||||
return;
|
||||
|
||||
wl_resource_set_user_data(pResource, this);
|
||||
wl_list_init(&resourceDestroyListener.link);
|
||||
resourceDestroyListener.notify = _{}__DestroyListener;
|
||||
wl_resource_add_destroy_listener(pResource, &resourceDestroyListener);
|
||||
|
||||
wl_resource_set_implementation(pResource, {}, this, nullptr);
|
||||
}}
|
||||
|
||||
{}::~{}() {{
|
||||
wl_list_remove(&resourceDestroyListener.link);
|
||||
wl_list_init(&resourceDestroyListener.link);
|
||||
|
||||
// if we still own the wayland resource,
|
||||
// it means we need to destroy it.
|
||||
if (pResource && wl_resource_get_user_data(pResource) == this) {{
|
||||
wl_resource_set_user_data(pResource, nullptr);
|
||||
wl_resource_destroy(pResource);
|
||||
}}
|
||||
}}
|
||||
|
||||
void {}::onDestroyCalled() {{
|
||||
wl_resource_set_user_data(pResource, nullptr);
|
||||
wl_list_remove(&resourceDestroyListener.link);
|
||||
wl_list_init(&resourceDestroyListener.link);
|
||||
|
||||
// set the resource to nullptr,
|
||||
// as it will be freed. If the consumer does not destroy this resource
|
||||
// in onDestroy here, we'd be doing a UAF in the ~dtor
|
||||
pResource = nullptr;
|
||||
|
||||
if (onDestroy)
|
||||
onDestroy(this);
|
||||
}}
|
||||
)#",
|
||||
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);
|
||||
|
||||
for (auto& rq : iface.requests) {
|
||||
std::string args = ", ";
|
||||
for (auto& arg : rq.args) {
|
||||
args += WPTypeToCType(arg, false) + ", ";
|
||||
}
|
||||
|
||||
args.pop_back();
|
||||
args.pop_back();
|
||||
|
||||
SOURCE += std::format(R"#(
|
||||
void {}::{}(F<void({}*{})> handler) {{
|
||||
requests.{} = handler;
|
||||
}}
|
||||
)#",
|
||||
IFACE_CLASS_NAME_CAMEL, camelize("set_" + rq.name), IFACE_CLASS_NAME_CAMEL, args, camelize(rq.name));
|
||||
}
|
||||
}
|
||||
|
||||
SOURCE += "\n#undef F\n";
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
std::string outpath = "";
|
||||
std::string protopath = "";
|
||||
|
||||
int pathsTaken = 0;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string curarg = argv[i];
|
||||
|
||||
if (curarg == "-v" || curarg == "--version") {
|
||||
std::cout << SCANNER_VERSION << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (curarg == "--wayland-enums") {
|
||||
waylandEnums = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pathsTaken == 0) {
|
||||
protopath = curarg;
|
||||
pathsTaken++;
|
||||
continue;
|
||||
} else if (pathsTaken == 1) {
|
||||
outpath = curarg;
|
||||
pathsTaken++;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "Too many args or unknown arg " << curarg << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (outpath.empty() || protopath.empty()) {
|
||||
std::cerr << "Not enough args\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// build!
|
||||
|
||||
pugi::xml_document doc;
|
||||
if (!doc.load_file(protopath.c_str())) {
|
||||
std::cerr << "Couldn't load proto\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
PROTO_DATA.nameOriginal = doc.child("protocol").attribute("name").as_string();
|
||||
PROTO_DATA.name = camelize(PROTO_DATA.nameOriginal);
|
||||
PROTO_DATA.fileName = protopath.substr(protopath.find_last_of('/') + 1, protopath.length() - (protopath.find_last_of('/') + 1) - 4);
|
||||
|
||||
const auto COPYRIGHT =
|
||||
std::format("// Generated with hyprwayland-scanner {}. Made with vaxry's keyboard and ❤️.\n// {}\n\n/*\n This protocol's authors' copyright notice is:\n\n{}\n*/\n\n",
|
||||
SCANNER_VERSION, PROTO_DATA.nameOriginal, std::string{doc.child("protocol").child("copyright").child_value()});
|
||||
|
||||
parseXML(doc);
|
||||
parseHeader();
|
||||
parseSource();
|
||||
|
||||
const auto HPATH = outpath + "/" + PROTO_DATA.fileName + ".hpp";
|
||||
const auto CPATH = outpath + "/" + PROTO_DATA.fileName + ".cpp";
|
||||
bool needsToWriteHeader = true, needsToWriteSource = true;
|
||||
|
||||
if (std::filesystem::exists(HPATH)) {
|
||||
// check if we need to overwrite
|
||||
|
||||
std::ifstream headerIn(HPATH);
|
||||
std::string content((std::istreambuf_iterator<char>(headerIn)), (std::istreambuf_iterator<char>()));
|
||||
|
||||
if (content == COPYRIGHT + HEADER)
|
||||
needsToWriteHeader = false;
|
||||
|
||||
headerIn.close();
|
||||
}
|
||||
|
||||
if (std::filesystem::exists(CPATH)) {
|
||||
// check if we need to overwrite
|
||||
|
||||
std::ifstream sourceIn(CPATH);
|
||||
std::string content((std::istreambuf_iterator<char>(sourceIn)), (std::istreambuf_iterator<char>()));
|
||||
|
||||
if (content == COPYRIGHT + SOURCE)
|
||||
needsToWriteSource = false;
|
||||
|
||||
sourceIn.close();
|
||||
}
|
||||
|
||||
if (needsToWriteHeader) {
|
||||
std::ofstream header(HPATH, std::ios::trunc);
|
||||
header << COPYRIGHT << HEADER;
|
||||
header.close();
|
||||
}
|
||||
|
||||
if (needsToWriteSource) {
|
||||
std::ofstream source(CPATH, std::ios::trunc);
|
||||
source << COPYRIGHT << SOURCE;
|
||||
source.close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue