Initial commit

This commit is contained in:
LoveSy 2022-11-26 22:45:45 +08:00
commit 50471176ba
No known key found for this signature in database
56 changed files with 68021 additions and 0 deletions

12
.gitattributes vendored Normal file
View File

@ -0,0 +1,12 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto eol=lf
# Declare files that will always have CRLF line endings on checkout.
*.cmd text eol=crlf
*.bat text eol=crlf
# Denote all files that are truly binary and should not be modified.
*.so binary
*.dex binary
*.jar binary
*.png binary

109
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,109 @@
name: Build
on:
push:
branches: ["master"]
paths-ignore:
- 'README.md'
pull_request:
jobs:
build:
name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macOS-latest ]
steps:
- name: Check out
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
~/.gradle/buildOutputCleanup/cache.properties
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds-${{ runner.os }}
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ runner.os }}-${{ github.sha }}
restore-keys: ${{ runner.os }}
- name: Build with Gradle
run: |
ccache -o cache_dir=${{ github.workspace }}/.ccache
ccache -o hash_dir=false
ccache -o compiler_check='%compiler% -dumpmachine; %compiler% -dumpversion'
ccache -p
echo 'android.native.buildOutput=verbose' >> gradle.properties
./gradlew :lsplt:publishToMavenLocal :lsplt:prefabDebugPackage
env:
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.maven_pgp_signingKey }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.maven_pgp_signingPassword }}
- name: Upload library
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.os }}-library
path: ~/.m2
agp-test:
name: Test using AGP
runs-on: macos-latest
steps:
- name: checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Cache Gradle Build
uses: actions/cache@v3
with:
path: |
~/.gradle/caches/build-cache-*
~/.gradle/buildOutputCleanup/cache.properties
key: gradle-builds-core-${{ github.sha }}
restore-keys: |
gradle-builds-${{ runner.os }}
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: ${{ runner.os }}-${{ github.sha }}
restore-keys: ${{ runner.os }}
save: false
- name: Test with Gradle
run: |
ccache -o cache_dir=${{ github.workspace }}/.ccache
ccache -o hash_dir=false
ccache -o compiler_check='%compiler% -dumpmachine; %compiler% -dumpversion'
echo -e "84831b9409646a918e30573bab4c9c91346d8abd" > $ANDROID_SDK_ROOT/licenses/android-sdk-preview-license
echo 'android.testoptions.manageddevices.emulator.gpu=swiftshader_indirect' >> gradle.properties
echo 'android.native.buildOutput=verbose' >> gradle.properties
echo 'android.sdk.channel=3' >> gradle.properties
./gradlew :test:testOnAllMVDs
- name: Prepare upload
if: always()
run: rm -vf test/build/outputs/androidTest-results/managedDevice/*/testlog/adb.additional_test_output*
- name: Upload outputs
if: always()
uses: actions/upload-artifact@v3
with:
name: test-outputs
path: test/build/outputs

28
.github/workflows/maven.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Maven
on:
workflow_dispatch:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'
cache: 'gradle'
- name: Build with Gradle
run: ./gradlew :lsplt:publish
env:
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.maven_pgp_signingKey }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.maven_pgp_signingPassword }}
ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.maven_ossrhUsername }}
ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.maven_ossrhPassword }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

46
.github/workflows/pages.yml vendored Normal file
View File

@ -0,0 +1,46 @@
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Install doxygen
run: sudo apt install -y doxygen
- name: Generate doxygen
run: doxygen docs/doxygen.cfg
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
path: 'docs/docs'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@main

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "docs/doxygen-awesome-css"]
path = docs/doxygen-awesome-css
url = https://github.com/jothepro/doxygen-awesome-css.git

165
LICENSE Normal file
View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2022 LSPosed <https://lsposed.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

1
README.md Normal file
View File

@ -0,0 +1 @@
###

6
build.gradle.kts Normal file
View File

@ -0,0 +1,6 @@
val androidTargetSdkVersion by extra(33)
val androidMinSdkVersion by extra(21)
val androidBuildToolsVersion by extra("33.0.0")
val androidCompileSdkVersion by extra(33)
val androidNdkVersion by extra("25.1.8937393")
val androidCmakeVersion by extra("3.22.1+")

1
docs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
docs

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
name: publish
on:
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: install Doxygen 1.9.2
run: |
sudo apt-get update
sudo apt-get install -y graphviz libclang-cpp1-9 libclang1-9
wget https://www.doxygen.nl/files/doxygen-1.9.2.linux.bin.tar.gz
tar -xvzf doxygen-1.9.2.linux.bin.tar.gz
ln -s doxygen-1.9.2/bin/doxygen doxygen
- name: set version
run: echo "PROJECT_NUMBER = `git describe --tags`" >> Doxyfile
- name: Generate Documentation
run: ./doxygen Doxyfile
- name: Publish generated content to GitHub Pages
uses: tsunematsu21/actions-publish-gh-pages@v1.0.1
with:
dir: docs/html
branch: gh-pages
token: ${{ secrets.ACCESS_TOKEN }}

3
docs/doxygen-awesome-css/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
docs/html
.DS_Store
.idea

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,90 @@
# Doxygen Awesome
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/releases/latest)
[![GitHub](https://img.shields.io/github/license/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/blob/main/LICENSE)
![GitHub Repo stars](https://img.shields.io/github/stars/jothepro/doxygen-awesome-css)
<div style="filter: drop-shadow(0px 3px 10px rgba(0,0,0,0.22)); max-width: 500px">
![Screenshot of Doxygen Awesome CSS](img/screenshot.png)
</div>
**Doxygen Awesome** is a custom **CSS theme for Doxygen HTML-documentation** with lots of customization parameters.
## Motivation
I really like how the Doxygen HTML-documentation is structured! But IMHO it looks a bit outdated.
This theme is an attemt to update the visuals of Doxygen without changing it's overall layout too much.
## Features
- 🌈 Clean, modern design
- 🚀 Heavily customizable by adjusting CSS-variables
- 🧩 No changes to the HTML structure of Doxygen required
- 📱 Improved mobile usability
- 🌘 Dark mode support!
- 🥇 Works best with **doxygen 1.9.1** - **1.9.3**
## Examples
- Sidebar-Only theme: [Documentation of this repository](https://jothepro.github.io/doxygen-awesome-css/)
- Base theme: [libsl3](https://a4z.github.io/libsl3/)
## Installation
Copy the file `doxygen-awesome.css` from this repository into your project or add this repository as submodule and check out the latest release:
```bash
git submodule add https://github.com/jothepro/doxygen-awesome-css.git
cd doxygen-awesome-css
git checkout v2.0.3
```
Choose one of the theme variants and configure Doxygen accordingly:
<span id="variants_image">
![Available theme variants](img/theme-variants.drawio.svg)
</span>
1. **Base theme**:
```
# Doxyfile
GENERATE_TREEVIEW = YES # optional. Also works without treeview
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css
```
2. **Sidebar-only theme**:
```
# Doxyfile
GENERATE_TREEVIEW = YES # required!
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css \
doxygen-awesome-css/doxygen-awesome-sidebar-only.css
```
Further installation instructions:
- [How to install extensions](docs/extensions.md)
- [How to customize the theme (colors, spacing, border-radius, ...)](docs/customization.md)
- [Tips and Tricks for further configuration](docs/tricks.md)
## Browser support
Tested with
- Chrome 98, Chrome 98 for Android, Chrome 87 for iOS
- Safari 15, Safari for iOS 15
- Firefox 97, Firefox Daylight 97 for Android, Firefox Daylight 96 for iOS
## Credits
- This theme is inspired by the [vuepress](https://vuepress.vuejs.org/) static site generator default theme.
- Thank you for all the feedback on github!
<span class="next_section_button">
Read Next: [Extensions](docs/extensions.md)
</span>

View File

@ -0,0 +1,157 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
// SVG icons from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
static title = "Toggle Light/Dark Mode"
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
static _staticConstructor = function() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
// Update the color scheme when the browsers preference changes
// without user interaction on the website.
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
})
// Update the color scheme when the tab is made visible again.
// It is possible that the appearance was changed in another tab
// while this tab was in the background.
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
}
});
}()
static init() {
$(function() {
$(document).ready(function() {
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
toggleButton.updateIcon()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
toggleButton.updateIcon()
})
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
toggleButton.updateIcon()
}
});
$(document).ready(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
$(window).resize(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
})
})
}
constructor() {
super();
this.onclick=this.toggleDarkMode
}
/**
* @returns `true` for dark-mode, `false` for light-mode system preference
*/
static get systemPreference() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
/**
* @returns `true` for dark-mode, `false` for light-mode user preference
*/
static get userPreference() {
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
}
static set userPreference(userPreference) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
if(!userPreference) {
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
}
} else {
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
}
}
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
}
static enableDarkMode(enable) {
if(enable) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
document.documentElement.classList.add("dark-mode")
document.documentElement.classList.remove("light-mode")
} else {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
document.documentElement.classList.remove("dark-mode")
document.documentElement.classList.add("light-mode")
}
}
static onSystemPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
static onUserPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
toggleDarkMode() {
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
this.updateIcon()
}
updateIcon() {
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
} else {
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
}
}
}
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);

View File

@ -0,0 +1,85 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
constructor() {
super();
this.onclick=this.copyContent
}
static title = "Copy to clipboard"
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`
static successDuration = 980
static init() {
$(function() {
$(document).ready(function() {
if(navigator.clipboard) {
const fragments = document.getElementsByClassName("fragment")
for(const fragment of fragments) {
const fragmentWrapper = document.createElement("div")
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
fragmentWrapper.appendChild(fragment)
fragmentWrapper.appendChild(fragmentCopyButton)
}
}
})
})
}
copyContent() {
const content = this.previousSibling.cloneNode(true)
// filter out line number from file listings
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
node.remove()
})
let textContent = content.textContent
// remove trailing newlines that appear in file listings
let numberOfTrailingNewlines = 0
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
numberOfTrailingNewlines++;
}
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
navigator.clipboard.writeText(textContent);
this.classList.add("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
window.setTimeout(() => {
this.classList.remove("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
}, DoxygenAwesomeFragmentCopyButton.successDuration);
}
}
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)

View File

@ -0,0 +1,51 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeParagraphLink {
// Icon from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static icon = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/></svg>`
static title = "Permanent Link"
static init() {
$(function() {
$(document).ready(function() {
document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => {
let anchorlink = document.createElement("a")
anchorlink.setAttribute("href", `#${node.getAttribute("id")}`)
anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title)
anchorlink.classList.add("anchorlink")
node.classList.add("anchor")
anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon
node.parentElement.appendChild(anchorlink)
})
})
})
}
}

View File

@ -0,0 +1,40 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@media screen and (min-width: 768px) {
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
}
}

View File

@ -0,0 +1,113 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
html {
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
* Make sure it is wide enough to contain the page title (logo + title + version)
*/
--side-nav-fixed-width: 335px;
--menu-display: none;
--top-height: 120px;
}
#projectname {
white-space: nowrap;
}
@media screen and (min-width: 768px) {
html {
--searchbar-background: var(--page-background-color);
}
#side-nav {
min-width: var(--side-nav-fixed-width);
max-width: var(--side-nav-fixed-width);
top: var(--top-height);
overflow: visible;
}
#nav-tree, #side-nav {
height: calc(100vh - var(--top-height)) !important;
}
#nav-tree {
padding: 0;
}
#top {
display: block;
border-bottom: none;
height: var(--top-height);
margin-bottom: calc(0px - var(--top-height));
max-width: var(--side-nav-fixed-width);
overflow: hidden;
background: var(--side-nav-background);
}
#main-nav {
float: left;
padding-right: 0;
}
.ui-resizable-handle {
cursor: default;
width: 1px !important;
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
}
#nav-path {
position: fixed;
right: 0;
left: var(--side-nav-fixed-width);
bottom: 0;
width: auto;
}
#doc-content {
height: calc(100vh - 31px) !important;
padding-bottom: calc(3 * var(--spacing-large));
padding-top: calc(var(--top-height) - 80px);
box-sizing: border-box;
margin-left: var(--side-nav-fixed-width) !important;
}
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
}
#MSearchResultsWindow {
left: var(--spacing-medium) !important;
right: auto;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
html.alternative {
/* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #7aabd6;
--primary-lighter-color: #cae1f1;
--primary-lightest-color: #e9f1f8;
/* page base colors */
--page-background-color: white;
--page-foreground-color: #2c3e50;
--page-secondary-foreground-color: #67727e;
--border-radius-large: 22px;
--border-radius-small: 9px;
--border-radius-medium: 14px;
--spacing-small: 8px;
--spacing-medium: 14px;
--spacing-large: 19px;
--top-height: 125px;
--side-nav-background: #324067;
--side-nav-foreground: #F1FDFF;
--header-foreground: var(--side-nav-foreground);
--searchbar-background: var(--side-nav-foreground);
--searchbar-border-radius: var(--border-radius-medium);
--header-background: var(--side-nav-background);
--header-foreground: var(--side-nav-foreground);
--toc-background: rgb(243, 240, 252);
--toc-foreground: var(--page-foreground-color);
}
html.alternative.dark-mode {
color-scheme: dark;
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #4779ac;
--primary-lighter-color: #191e21;
--primary-lightest-color: #191a1c;
--page-background-color: #1C1D1F;
--page-foreground-color: #d2dbde;
--page-secondary-foreground-color: #859399;
--separator-color: #3a3246;
--side-nav-background: #171D32;
--side-nav-foreground: #F1FDFF;
--toc-background: #20142C;
--searchbar-background: var(--page-background-color);
}

View File

@ -0,0 +1,78 @@
.github-corner svg {
fill: var(--primary-light-color);
color: var(--page-background-color);
width: 72px;
height: 72px;
}
@media screen and (max-width: 767px) {
.github-corner svg {
width: 50px;
height: 50px;
}
#projectnumber {
margin-right: 22px;
}
}
.alter-theme-button {
display: inline-block;
cursor: pointer;
background: var(--primary-color);
color: var(--page-background-color) !important;
border-radius: var(--border-radius-medium);
padding: var(--spacing-small) var(--spacing-medium);
text-decoration: none;
}
.next_section_button {
display: block;
padding: var(--spacing-large) 0 var(--spacing-small) 0;
color: var(--page-background-color);
user-select: none;
}
.next_section_button::after {
/* clearfix */
content: "";
clear: both;
display: table;
}
.next_section_button a {
overflow: hidden;
float: right;
border: 1px solid var(--separator-color);
padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large);
border-radius: var(--border-radius-medium);
color: var(--page-secondary-foreground-color) !important;
text-decoration: none;
background-color: var(--page-background-color);
transition: color .08s ease-in-out, background-color .1s ease-in-out;
}
.next_section_button a:hover {
color: var(--page-foreground-color) !important;
background-color: var(--odd-color);
}
.next_section_button a::after {
content: '〉';
color: var(--page-secondary-foreground-color) !important;
padding-left: var(--spacing-large);
display: inline-block;
transition: color .08s ease-in-out, transform .09s ease-in-out;
}
.next_section_button a:hover::after {
color: var(--page-foreground-color) !important;
transform: translateX(3px);
}
.alter-theme-button:hover {
background: var(--primary-dark-color);
}
html.dark-mode #variants_image img {
filter: brightness(87%) hue-rotate(180deg) invert();
}

View File

@ -0,0 +1,86 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!-- BEGIN opengraph metadata -->
<meta property="og:title" content="Doxygen Awesome" />
<meta property="og:image" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta property="og:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<meta property="og:url" content="https://jothepro.github.io/doxygen-awesome-css/" />
<!-- END opengraph metadata -->
<!-- BEGIN twitter metadata -->
<meta name="twitter:image:src" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta name="twitter:title" content="Doxygen Awesome" />
<meta name="twitter:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<!-- END twitter metadata -->
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<link rel="icon" type="image/svg+xml" href="logo.drawio.svg"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript" src="$relpath^toggle-alternative-theme.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
DoxygenAwesomeDarkModeToggle.init()
DoxygenAwesomeParagraphLink.init()
</script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<!-- https://tholman.com/github-corners/ -->
<a href="https://github.com/jothepro/doxygen-awesome-css" class="github-corner" title="View source on GitHub" target="_blank">
<svg viewBox="0 0 250 250" width="40" height="40" style="position: absolute; top: 0; border: 0; right: 0; z-index: 99;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

View File

@ -0,0 +1,12 @@
let original_theme_active = true;
function toggle_alternative_theme() {
if(original_theme_active) {
document.documentElement.classList.add("alternative")
original_theme_active = false;
} else {
document.documentElement.classList.remove("alternative")
original_theme_active = true;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

View File

@ -0,0 +1,240 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1001px" height="301px" viewBox="-0.5 -0.5 1001 301" content="&lt;mxfile&gt;&lt;diagram id=&quot;6E4AiNPWWr3a8GvC3Ypl&quot; name=&quot;Page-1&quot;&gt;7Vlbk5owGP01ztiHMiSBAI/rbfvS9sHO9DkrWWAaiMW4an99EwgCBna1orsPhRmFLxeSc06+HHSEpun+MSfr+CsPKRtBO9yP0GwEoR/48lMFDmXAcWAZiPIkLEOgDiyTP1QHbR3dJiHdtCoKzplI1u3gimcZXYlWjOQ537WrPXPWfuqaRNQILFeEmdGfSShiPS3XruNfaBLF1ZOBrUtSUlXWgU1MQr5rhNB8hKY556K8SvdTyhR2FS5lu0VP6XFgOc3EWQ3cssULYdtqcpa8n5ANlV8/YppSPVJxqKYv6F52PolFymQAyEvCkiiT1yv5WJrLwAvNRSIBe9AFaRKGqvlkFyeCLtdkpfraSXXIWM63WUhD3dczz4SmXI5O3+uHF+UJY1POeF4MBi0e1KnrNeJ2cci4CYlGSY2R7hshDdEj5SkV+UFWqUo9TZfWK/T1/a5mHyANZdxgvqKZaMFFx65rTuSFpqWHIt+gCCqKlnINPJH88/eMHS6gaiNy/otWQGU8o/fmr4enPl4H4A+7vuW5b1PoedatSAwMEg2uIonj+vzZHtMbeap6sF9FwQXmlJ3A7pgwHmDGZ0y41o3do6xe3UqB4Lk6uwUS9GBmYnMXLCrgG2BM5SpQ4x0Wk3lxXotJVRr4lguD+vBaKwhgC3uGoGQrC+MOHBGyEBoASmBAqRPhSMkfM5XunuQNjtTV+EciGC2KJCb2N/KSREQkPPt0HfKnycpT511UCiC2PHgroUID3YX0IlSBO57klISrfJuuN1eidyOU9k2EHIC8AAEbOS5qZ35ZjFuHIWKEkQU918fQxn6A3Y5NoZBz88ADoI9MbVOSr+JX0AbXoH2yFUOszsFYkOmhwYLfzh4OaJNgZhJgA6tq7PiSjsBkAZxS6Q/AgtOxc5V5ReFV7LsVZPj3Vjn1SQ1eI1RmIJ2Ayg7ks8s+yjKD1zPtWjP7nDg4Rp/Fv/s3u+3fYI9/HloMAHTT2FySyGpqwccda/IWaoDm1m2wVhq3UxzlwtUwuhe52PN93V28C357/gPl/oiRzUZ3IiEQJMnUu8isW4RHYj6IxzOzxt0t3gWQNC1ee3t0HMs/39YB37XAALbO9B1F3ixt3bi2bUcnV26Mw7q42Xw+Wywu9CcD6rCAHniBU31ej2uHozi65fGHcsMX4PiqUgv3h26nVPMHs/d2yJcjZ1jgwICxU6C3ML2eqdAP4nkvT6fIA+03j7bLOV3e0BTvO3le8yfG/5b32q0VW8WrI6gEAS8Vw50sr7yt/28oyhp/2qD5Xw==&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="170" y="280" width="135" height="20" rx="3" ry="3" fill="#fafafa" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 290px; margin-left: 171px;">
<div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 15px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
1. Base Theme
</div>
</div>
</div>
</foreignObject>
<text x="238" y="295" fill="#000000" font-family="Helvetica" font-size="15px" text-anchor="middle" font-weight="bold">
1. Base Theme
</text>
</switch>
</g>
<rect x="658.75" y="280" width="177.5" height="20" rx="3" ry="3" fill="#fafafa" stroke="none" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 176px; height: 1px; padding-top: 290px; margin-left: 660px;">
<div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 15px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
2. Sidebar-Only Theme
</div>
</div>
</div>
</foreignObject>
<text x="748" y="295" fill="#000000" font-family="Helvetica" font-size="15px" text-anchor="middle" font-weight="bold">
2. Sidebar-Only Theme
</text>
</switch>
</g>
<rect x="510" y="0" width="490" height="260" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<rect x="708.53" y="16.67" width="219.66" height="233.33" fill="rgb(255, 255, 255)" stroke="#e3e3e3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 133px; margin-left: 710px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Content
</div>
</div>
</div>
</foreignObject>
<text x="818" y="137" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Content
</text>
</switch>
</g>
<rect x="510" y="0" width="126.72" height="260" fill="#f7f7f7" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 130px; margin-left: 511px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Sidebar
<br/>
(Title + Navigation)
</div>
</div>
</div>
</foreignObject>
<text x="573" y="134" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Sidebar...
</text>
</switch>
</g>
<rect x="636.72" y="226.67" width="363.28" height="33.33" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 361px; height: 1px; padding-top: 243px; margin-left: 638px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Footer (Breadcrumps)
</div>
</div>
</div>
</foreignObject>
<text x="818" y="247" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Footer (Breadcrumps)
</text>
</switch>
</g>
<rect x="522.67" y="41.67" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 50px; margin-left: 524px;">
<div data-drawio-colors="color: #262626; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(38, 38, 38); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Search
</div>
</div>
</div>
</foreignObject>
<text x="573" y="54" fill="#262626" font-family="Helvetica" font-size="12px" text-anchor="middle">
Search
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 20px; margin-left: 525px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
<div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
<font color="#262626">
Title
</font>
</div>
</div>
</div>
</foreignObject>
<text x="525" y="26" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="20px">
Tit...
</text>
</switch>
</g>
<rect x="0" y="0" width="490" height="260" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<rect x="198.53" y="44.87" width="219.66" height="185.13" fill="rgb(255, 255, 255)" stroke="#e3e3e3" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 137px; margin-left: 200px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Content
</div>
</div>
</div>
</foreignObject>
<text x="308" y="141" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Content
</text>
</switch>
</g>
<rect x="0" y="0" width="490" height="44.87" fill="#deedff" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 22px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Titlebar (Navigation + Search)
</div>
</div>
</div>
</foreignObject>
<text x="245" y="26" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Titlebar (Navigation + Search)
</text>
</switch>
</g>
<rect x="0" y="44.87" width="126.73" height="185.13" fill="#f7f7f7" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 125px; height: 1px; padding-top: 137px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Sidebar (Navigation)
</div>
</div>
</div>
</foreignObject>
<text x="63" y="141" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Sidebar (Navigation)
</text>
</switch>
</g>
<rect x="0" y="226.67" width="490" height="33.33" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 243px; margin-left: 1px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Footer (Breadcrumps)
</div>
</div>
</div>
</foreignObject>
<text x="245" y="247" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Footer (Breadcrumps)
</text>
</switch>
</g>
<rect x="371.72" y="14.87" width="101.38" height="16.67" rx="2.5" ry="2.5" fill="rgb(255, 255, 255)" stroke="#6e6e6e" pointer-events="none"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 99px; height: 1px; padding-top: 23px; margin-left: 373px;">
<div data-drawio-colors="color: #262626; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(38, 38, 38); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
Search
</div>
</div>
</div>
</foreignObject>
<text x="422" y="27" fill="#262626" font-family="Helvetica" font-size="12px" text-anchor="middle">
Search
</text>
</switch>
</g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 32px; height: 1px; padding-top: 23px; margin-left: 19px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
<div style="display: inline-block; font-size: 20px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">
<font color="#262626">
Title
</font>
</div>
</div>
</div>
</foreignObject>
<text x="19" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="20px">
Tit...
</text>
</switch>
</g>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Viewer does not support full SVG 1.1
</text>
</a>
</switch>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,110 @@
#pragma once
#include <string>
namespace MyLibrary {
enum Color { red, green, blue };
/**
* @brief Example class to demonstrate the features of the custom CSS.
*
* @author jothepro
*
*/
class Example {
public:
/**
* @brief brief summary
*
* doxygen test documentation
*
* @param test this is the only parameter of this test function. It does nothing!
*
* # Supported elements
*
* These elements have been tested with the custom CSS.
*
* ## Tables
*
* The table content is scrollable if the table gets too wide.
*
* | first_column | second_column | third_column | fourth_column | fifth_column | sixth_column | seventh_column | eighth_column | ninth_column |
* |--------------|---------------|--------------|---------------|--------------|--------------|----------------|---------------|--------------|
* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
*
*
* ## Lists
*
* - element 1
* - element 2
*
* 1. element 1
* ```
* code in lists
* ```
* 2. element 2
*
* ## Quotes
*
* > Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
* > ut labore et dolore magna aliqua. Vitae proin sagittis nisl rhoncus mattis rhoncus urna neque viverra.
* > Velit sed ullamcorper morbi tincidunt ornare.
* >
* > Lorem ipsum dolor sit amet consectetur adipiscing elit duis.
* *- jothepro*
*
* ## Code block
*
* ```cpp
* auto x = "code within md fences (```)";
* ```
*
* @code{.cpp}
* // code within @code block
* while(true) {
* auto example = std::make_shared<Example>(5);
* example->test("test");
* }
*
* @endcode
*
* // code within indented code block
* auto test = std::shared_ptr<Example(5);
*
*
* Inline `code` elements in a text. *Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.* This also works within multiline text and does not break the `layout`.
*
*
* ## Special hints
*
* @warning this is a warning only for demonstration purposes
*
* @note this is a note to show that notes work. They can also include `code`:
* @code{.c}
* void this_looks_awesome();
* @endcode
*
* @bug example bug
*
* @deprecated None of this will be deprecated, because it's beautiful!
*
* @invariant This is an invariant
*
* @pre This is a precondition
*
* @todo This theme is never finished!
*
* @remark This is awesome!
*
*/
std::string test(const std::string& test);
virtual int virtualfunc() = 0;
static bool staticfunc();
};
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <string>
#include "example.hpp"
#include <iostream>
namespace MyLibrary {
/**
* @brief some subclass
*/
class SubclassExample : public Example {
public:
/**
* @bug second bug
* @return
*/
int virtualfunc() override;
/**
* @brief Extra long function with lots of parameters
* @param param1 first parameter
* @param param2 second parameter
* @param parameter3 third parameter
*/
template <typename T>
std::shared_ptr<std::string> long_function_with_many_parameters(std::shared_ptr<T>& param1, std::shared_ptr<std::string>& param2, bool parameter3) {
if(true) {
std::cout << "this even has some code." << std::endl;
}
}
};
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="61px" height="74px" viewBox="-0.5 -0.5 61 74" content="&lt;mxfile host=&quot;drawio-plugin&quot; modified=&quot;2021-03-16T23:58:23.462Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36&quot; version=&quot;13.7.9&quot; etag=&quot;JoeaGLJ54FcERO7YrWLQ&quot; type=&quot;embed&quot;&gt;&lt;diagram id=&quot;JMB9aH8b_oZ7EWDuqJgx&quot; name=&quot;Page-1&quot;&gt;7VdNc5swEP01HDsjkGPDsSVJe+lMZnzoWYENaAwsI8ux6a+vCCtA4KSu62kmSS+M9LT7tB9P0uDxuDx8VaLOv2MKhRew9ODxay8Igigy3xZoCOC8AzIl0w7yB2AtfwKBjNCdTGHrGGrEQsvaBROsKki0gwmlcO+aPWDh7lqLDGbAOhHFHP0hU513aHjFBvwbyCy3O/uMVkphjQnY5iLF/QjiNx6PFaLuRuUhhqKtna1L53f7zGofmIJKn+RAcTyKYkfJUWC6sdlmCnc1mYHScDhWY3Fvzdk8Br/PzCgCsAStGmNCRJy2JDH4pIV8VMG+edS4rCcZcjMDSu+ZVP3fpwpV+rnVh5ndF5hsPP4l16VhvPbN8AErTWI0re7mMRaonpw5Y8tlHBvcsNzKwnpttVDaslZYgcXIhj3NFW56LS1bbrM44l6m4Wq5MLhxzEDfgZKmAKDWtUhklRFNgqVM7LYb0Enu8I9j9dkVC80KtgS6Lb3fGnYVgXSm/1Ez2fFu7oeTYA/CuIUWU1AILR9d/mN9pR3uUJqde7F88leOWhYLl2GLO5UAOY2FP+GxMm3c6CwNlXlKY9oompFZ3Rps59EOkuw8BoH2BTtNs8EfaZbUdYZkXQGuXhDgR9DYRBycXURj00D+UmMT2ktJLnr9B8HG0IzFcPkHYfUe3oPZqfOjMEiDs1+KEw5n9P/+/1f3f/gq1394lt7erqQ+0HVvpsPPRWc+/KHxm18=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 13 57 L 13.01 57.01 L 15.87 50.14 L 18.37 43.14 L 20.91 36.15 L 23.67 29.25 L 26.4 22.33 Q 30 13 33.71 22.28 L 33.55 22.22 L 35.48 26.91 L 37.49 31.64 L 39.48 36.36 L 41.2 40.97 L 43.05 45.63" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 47.51 56.77 L 47.65 56.93 L 45.43 54.91 L 43.41 53.11 L 41.43 51.35 L 39.63 49.8 L 37.48 47.86 L 37.39 47.64 L 39.79 47.17 L 41.9 45.98 L 44.24 45.37 L 46.48 44.52 L 48.62 43.4 L 48.54 43.39 L 48.58 46.09 L 48.04 48.74 L 48.04 51.43 L 47.8 54.1 L 47.51 56.77 Z Z" fill-opacity="0.1" fill="#010508" stroke="#010508" stroke-opacity="0.1" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 10 43 L 9.94 42.88 L 12.16 41.98 L 14.31 40.96 L 16.51 40.01 L 18.62 38.89 L 20.88 38.1 Q 30 34 40 34 L 40 33.75 L 42 33.83 L 44 33.8 L 46 33.79 L 48 34.05 L 50 34" fill="none" stroke="#010508" stroke-opacity="0.1" stroke-width="7" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 10 54 L 9.97 53.99 L 12.69 47.07 L 15.43 40.16 L 18.07 33.21 L 20.65 26.24 L 23.4 19.33 Q 27 10 30.71 19.28 L 30.66 19.26 L 32.46 23.91 L 34.55 28.66 L 36.26 33.27 L 38.35 38.03 L 40.05 42.63" fill="none" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 44.51 53.77 L 44.56 53.83 L 42.48 51.97 L 40.5 50.21 L 38.48 48.41 L 36.41 46.56 L 34.48 44.86 L 34.55 45.02 L 36.72 44 L 39 43.24 L 41.21 42.28 L 43.48 41.51 L 45.62 40.4 L 45.78 40.42 L 45.51 43.09 L 45.01 45.74 L 44.87 48.42 L 44.94 51.12 L 44.51 53.77 Z Z" fill="#1982d2" stroke="#1982d2" stroke-width="6" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 7 40 L 7.02 40.05 L 9.28 39.25 L 11.33 38 L 13.48 36.96 L 15.73 36.14 L 17.88 35.1 Q 27 31 37 31 L 37 30.79 L 39 31.11 L 41 30.85 L 43 30.78 L 45 30.89 L 47 31" fill="none" stroke="#1982d2" stroke-width="8" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="stroke"/></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

2672
docs/doxygen.cfg Normal file

File diff suppressed because it is too large Load Diff

1987
docs/jni.h Normal file

File diff suppressed because it is too large Load Diff

4
gradle.properties Normal file
View File

@ -0,0 +1,4 @@
android.nonTransitiveRClass=true
android.useAndroidX=true
android.experimental.testOptions.managedDevices.allowOldApiLevelDevices=true
android.library.defaults.buildfeatures.androidresources=false

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored Normal file
View File

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

5
lsplt/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/build
/libs
/obj
/release

247
lsplt/build.gradle.kts Normal file
View File

@ -0,0 +1,247 @@
import java.nio.file.Paths
plugins {
id("com.android.library")
id("maven-publish")
id("signing")
}
val androidTargetSdkVersion: Int by rootProject.extra
val androidMinSdkVersion: Int by rootProject.extra
val androidBuildToolsVersion: String by rootProject.extra
val androidCompileSdkVersion: Int by rootProject.extra
val androidNdkVersion: String by rootProject.extra
val androidCmakeVersion: String by rootProject.extra
fun findInPath(executable: String): String? {
val pathEnv = System.getenv("PATH")
return pathEnv.split(File.pathSeparator).map { folder ->
Paths.get("${folder}${File.separator}${executable}${if (org.gradle.internal.os.OperatingSystem.current().isWindows) ".exe" else ""}")
.toFile()
}.firstOrNull { path ->
path.exists()
}?.absolutePath
}
android {
compileSdk = androidCompileSdkVersion
ndkVersion = androidNdkVersion
buildToolsVersion = androidBuildToolsVersion
buildFeatures {
buildConfig = false
prefabPublishing = true
androidResources = false
prefab = true
}
packagingOptions {
jniLibs {
excludes += "**.so"
}
}
prefab {
register("lsplt") {
headers = "src/main/jni/include"
}
}
defaultConfig {
minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
}
buildTypes {
all {
externalNativeBuild {
cmake {
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
val flags = arrayOf(
"-Wall",
"-Werror",
"-Qunused-arguments",
"-Wno-gnu-string-literal-operator-template",
"-fno-rtti",
"-fvisibility=hidden",
"-fvisibility-inlines-hidden",
"-fno-exceptions",
"-fno-stack-protector",
"-fomit-frame-pointer",
"-Wno-builtin-macro-redefined",
"-ffunction-sections",
"-fdata-sections",
"-Wno-unused-value",
"-D__FILE__=__FILE_NAME__",
"-Wl,--exclude-libs,ALL",
)
cppFlags("-std=c++20", *flags)
cFlags("-std=c18", *flags)
val configFlags = arrayOf(
"-Oz",
"-DNDEBUG"
).joinToString(" ")
arguments(
"-DCMAKE_CXX_FLAGS_RELEASE=$configFlags",
"-DCMAKE_C_FLAGS_RELEASE=$configFlags",
"-DDEBUG_SYMBOLS_PATH=${project.buildDir.absolutePath}/symbols/$name",
)
findInPath("ccache")?.let {
println("Using ccache $it")
arguments += "-DANDROID_CCACHE=$it"
}
}
}
}
release {
externalNativeBuild {
val flags = arrayOf(
"-Wl,--gc-sections",
"-flto",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
)
cmake {
cppFlags += flags
cFlags += flags
arguments += "-DANDROID_STL=c++_shared"
arguments += "-DCMAKE_BUILD_TYPE=Release"
}
}
}
debug {
externalNativeBuild {
cmake {
arguments += "-DANDROID_STL=c++_shared"
}
}
}
create("standalone") {
initWith(getByName("release"))
externalNativeBuild {
val flags = arrayOf(
"-Wl,--gc-sections",
"-flto",
"-fno-unwind-tables",
"-fno-asynchronous-unwind-tables",
)
cmake {
cppFlags += flags
cFlags += flags
arguments += "-DANDROID_STL=none"
arguments += "-DCMAKE_BUILD_TYPE=Release"
arguments += "-DLSPLT_STANDALONE=ON"
}
}
}
}
lint {
abortOnError = true
checkReleaseBuilds = false
}
externalNativeBuild {
cmake {
path = file("src/main/jni/CMakeLists.txt")
version = androidCmakeVersion
}
}
namespace = "org.lsposed.lsplt"
publishing {
singleVariant("release") {
withSourcesJar()
withJavadocJar()
}
singleVariant("standalone") {
withSourcesJar()
withJavadocJar()
}
}
}
val symbolsReleaseTask = tasks.register<Jar>("generateReleaseSymbolsJar") {
from("${project.buildDir.absolutePath}/symbols/release")
exclude("**/dex_builder")
archiveClassifier.set("symbols")
}
val symbolsStandaloneTask = tasks.register<Jar>("generateStandaloneSymbolsJar") {
from("${project.buildDir.absolutePath}/symbols/standalone")
exclude("**/dex_builder")
archiveClassifier.set("symbols")
}
publishing {
publications {
fun MavenPublication.setup() {
group = "org.lsposed.lsplt"
version = "5.2"
pom {
name.set("LSPlt")
description.set("A plt hook framework for Android")
url.set("https://github.com/LSPosed/LSPlt")
licenses {
license {
name.set("GNU Lesser General Public License v3.0")
url.set("https://github.com/LSPosed/LSPlt/blob/master/LICENSE")
}
}
developers {
developer {
name.set("Lsposed")
url.set("https://lsposed.org")
}
}
scm {
connection.set("scm:git:https://github.com/LSPosed/LSPlt.git")
url.set("https://github.com/LSPosed/LSPlt")
}
}
}
register<MavenPublication>("lsplt") {
artifactId = "lsplt"
afterEvaluate {
from(components.getByName("release"))
artifact(symbolsReleaseTask)
}
setup()
}
register<MavenPublication>("lspltStandalone") {
artifactId = "lsplt-standalone"
afterEvaluate {
from(components.getByName("standalone"))
artifact(symbolsStandaloneTask)
}
setup()
}
}
repositories {
maven {
name = "ossrh"
url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials(PasswordCredentials::class)
}
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/LSPosed/LSPlt")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
"standaloneCompileOnly"("dev.rikka.ndk.thirdparty:cxx:1.2.0")
}
}
signing {
val signingKey = findProperty("signingKey") as String?
val signingPassword = findProperty("signingPassword") as String?
if (signingKey != null && signingPassword != null) {
useInMemoryPgpKeys(signingKey, signingPassword)
}
sign(publishing.publications)
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -0,0 +1,19 @@
---
BasedOnStyle: Google
IndentWidth: 4
UseCRLF: false
UseTab: false
---
Language: Cpp
DerivePointerAlignment: true
PointerAlignment: Right
ColumnLimit: 100
AlignEscapedNewlines: Right
Cpp11BracedListStyle: true
Standard: Latest
# IndentAccessModifiers: false
IndentCaseLabels: false
BreakStringLiterals: false
IndentExternBlock: false
AccessModifierOffset: -4
# EmptyLineBeforeAccessModifier: true

View File

@ -0,0 +1,65 @@
---
Checks: >
-*,
bugprone-*,
google-*,
misc-*,
modernize-*,
performance-*,
portability-*,
readability-*,
clang-analyzer-*,
llvm-include-order,
-modernize-use-trailing-return-type,
-readability-implicit-bool-conversion,
-performance-no-int-to-ptr,
CheckOptions:
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.ClassMemberCase
value: lower_case
- key: readability-identifier-naming.EnumCase
value: CamelCase
- key: readability-identifier-naming.EnumConstantCase
value: CamelCase
- key: readability-identifier-naming.EnumConstantPrefix
value: k
- key: readability-identifier-naming.FunctionCase
value: CamelCase
- key: readability-identifier-naming.GlobalConstantCase
value: CamelCase
- key: readability-identifier-naming.GlobalConstantPrefix
value: k
- key: readability-identifier-naming.StaticConstantCase
value: CamelCase
- key: readability-identifier-naming.StaticConstantPrefix
value: k
- key: readability-identifier-naming.StaticVariableCase
value: CamelCase
- key: readability-identifier-naming.StaticVariablePrefix
value: k
- key: readability-identifier-naming.MacroDefinitionCase
value: UPPER_CASE
- key: readability-identifier-naming.MemberCase
value: lower_case
- key: readability-identifier-naming.PrivateMemberSuffix
value: _
- key: readability-identifier-naming.ProtectedMemberSuffix
value: _
- key: readability-identifier-naming.NamespaceCase
value: lower_case
- key: readability-identifier-naming.ParameterCase
value: lower_case
- key: readability-identifier-naming.TypeAliasCase
value: CamelCase
- key: readability-identifier-naming.TypedefCase
value: CamelCase
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.IgnoreMainLikeFunctions
value: 1
- key: readability-braces-around-statements.ShortStatementLines
value: 1
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: '1'

View File

@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.4.1)
project(lsplt)
find_program(CCACHE ccache)
if (CCACHE)
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
endif ()
if (LSPLT_STANDALONE)
find_package(cxx REQUIRED CONFIG)
link_libraries(cxx::cxx)
endif()
add_definitions(-std=c++20)
set(SOURCES lsplt.cc elf_util.cc)
option(LSPLT_BUILD_SHARED "If ON, lsplt will also build shared library" ON)
if (LSPLT_BUILD_SHARED)
message(STATUS "Building lsplt as a shared library")
add_library(${PROJECT_NAME} SHARED ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(${PROJECT_NAME} PRIVATE -flto)
target_link_options(${PROJECT_NAME} PRIVATE -flto)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}
COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $<TARGET_FILE:${PROJECT_NAME}>
${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>)
target_link_libraries(${PROJECT_NAME} PUBLIC log)
endif()
add_library(${PROJECT_NAME}_static STATIC ${SOURCES})
target_include_directories(${PROJECT_NAME}_static PUBLIC include)
target_include_directories(${PROJECT_NAME}_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
if (NOT DEFINED DEBUG_SYMBOLS_PATH)
set(DEBUG_SYMBOLS_PATH ${CMAKE_BINARY_DIR}/symbols)
endif()
target_link_libraries(${PROJECT_NAME}_static PUBLIC log)

View File

@ -0,0 +1,299 @@
#include "elf_util.hpp"
#include <cstring>
#include <type_traits>
#include <vector>
#if defined(__arm__)
#define ELF_R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT //.rel.plt
#define ELF_R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT //.rel.dyn
#define ELF_R_GENERIC_ABS R_ARM_ABS32 //.rel.dyn
#elif defined(__aarch64__)
#define ELF_R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT
#define ELF_R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT
#define ELF_R_GENERIC_ABS R_AARCH64_ABS64
#elif defined(__i386__)
#define ELF_R_GENERIC_JUMP_SLOT R_386_JMP_SLOT
#define ELF_R_GENERIC_GLOB_DAT R_386_GLOB_DAT
#define ELF_R_GENERIC_ABS R_386_32
#elif defined(__x86_64__)
#define ELF_R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT
#define ELF_R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT
#define ELF_R_GENERIC_ABS R_X86_64_64
#endif
#if defined(__LP64__)
#define ELF_R_SYM(info) ELF64_R_SYM(info)
#define ELF_R_TYPE(info) ELF64_R_TYPE(info)
#else
#define ELF_R_SYM(info) ELF32_R_SYM(info)
#define ELF_R_TYPE(info) ELF32_R_TYPE(info)
#endif
namespace {
template <typename T>
inline constexpr auto OffsetOf(ElfW(Ehdr) * head, ElfW(Off) off) {
return reinterpret_cast<std::conditional_t<std::is_pointer_v<T>, T, T *>>(
reinterpret_cast<uintptr_t>(head) + off);
}
template <typename T>
inline constexpr auto SetByOffset(T &ptr, ElfW(Addr) base, ElfW(Addr) bias, ElfW(Addr) off) {
if (auto val = bias + off; val > base) {
ptr = reinterpret_cast<T>(val);
return true;
}
ptr = 0;
return false;
}
} // namespace
Elf::Elf(uintptr_t base_addr) : base_addr_(base_addr) {
header_ = reinterpret_cast<decltype(header_)>(base_addr);
// check magic
if (0 != memcmp(header_->e_ident, ELFMAG, SELFMAG)) return;
// check class (64/32)
#if defined(__LP64__)
if (ELFCLASS64 != header_->e_ident[EI_CLASS]) return;
#else
if (ELFCLASS32 != header_->e_ident[EI_CLASS]) return;
#endif
// check endian (little/big)
if (ELFDATA2LSB != header_->e_ident[EI_DATA]) return;
// check version
if (EV_CURRENT != header_->e_ident[EI_VERSION]) return;
// check type
if (ET_EXEC != header_->e_type && ET_DYN != header_->e_type) return;
// check machine
#if defined(__arm__)
if (EM_ARM != header_->e_machine) return;
#elif defined(__aarch64__)
if (EM_AARCH64 != header_->e_machine) return;
#elif defined(__i386__)
if (EM_386 != header_->e_machine) return;
#elif defined(__x86_64__)
if (EM_X86_64 != header_->e_machine) return;
#else
return;
#endif
// check version
if (EV_CURRENT != header_->e_version) return;
program_header_ = OffsetOf<decltype(program_header_)>(header_, header_->e_phoff);
auto ph_off = reinterpret_cast<uintptr_t>(program_header_);
for (int i = 0; i < header_->e_phnum; i++, ph_off += header_->e_phentsize) {
auto *program_header = reinterpret_cast<ElfW(Phdr) *>(ph_off);
if (program_header->p_type == PT_LOAD && program_header->p_offset == 0) {
if (base_addr_ >= program_header->p_vaddr) {
bias_addr_ = base_addr_ - program_header->p_vaddr;
}
} else if (program_header->p_type == PT_DYNAMIC) {
dynamic_ = reinterpret_cast<decltype(dynamic_)>(program_header->p_vaddr);
dynamic_size_ = program_header->p_memsz;
}
}
if (!dynamic_ || !bias_addr_) return;
dynamic_ =
reinterpret_cast<decltype(dynamic_)>(bias_addr_ + reinterpret_cast<uintptr_t>(dynamic_));
for (auto *dynamic = dynamic_, *dynamic_end = dynamic_ + (dynamic_size_ / sizeof(dynamic[0]));
dynamic < dynamic_end; ++dynamic) {
switch (dynamic->d_tag) {
case DT_NULL:
// the end of the dynamic-section
dynamic = dynamic_end;
break;
case DT_STRTAB: {
if (!SetByOffset(dyn_str_, base_addr_, bias_addr_, dynamic->d_un.d_ptr)) return;
break;
}
case DT_SYMTAB: {
if (!SetByOffset(dyn_sym_, base_addr_, bias_addr_, dynamic->d_un.d_ptr)) return;
break;
}
case DT_PLTREL:
// use rel or rela?
is_use_rela_ = dynamic->d_un.d_val == DT_RELA;
break;
case DT_JMPREL: {
if (!SetByOffset(rel_plt_, base_addr_, bias_addr_, dynamic->d_un.d_ptr)) return;
break;
}
case DT_PLTRELSZ:
rel_plt_size_ = dynamic->d_un.d_val;
break;
case DT_REL:
case DT_RELA: {
if (!SetByOffset(rel_dyn_, base_addr_, bias_addr_, dynamic->d_un.d_ptr)) return;
break;
}
case DT_RELSZ:
case DT_RELASZ:
rel_dyn_size_ = dynamic->d_un.d_val;
break;
case DT_ANDROID_REL:
case DT_ANDROID_RELA: {
if (!SetByOffset(rel_android_, base_addr_, bias_addr_, dynamic->d_un.d_ptr)) return;
break;
}
case DT_ANDROID_RELSZ:
case DT_ANDROID_RELASZ:
rel_android_size_ = dynamic->d_un.d_val;
break;
case DT_HASH: {
// ignore DT_HASH when ELF contains DT_GNU_HASH hash table
if (bloom_) continue;
auto *raw = reinterpret_cast<ElfW(Word) *>(bias_addr_, dynamic->d_un.d_ptr);
bucket_count_ = raw[0];
chain_count_ = raw[1];
bucket_ = raw + 2;
chain_ = bucket_ + bucket_count_;
break;
}
case DT_GNU_HASH: {
auto *raw = reinterpret_cast<ElfW(Word) *>(bias_addr_, dynamic->d_un.d_ptr);
bucket_count_ = raw[0];
sym_offset_ = raw[1];
bloom_size_ = raw[2];
bloom_shift_ = raw[3];
bloom_ = reinterpret_cast<decltype(bloom_)>(raw + 4);
bucket_ = reinterpret_cast<decltype(bucket_)>(bloom_ + bloom_size_);
chain_ = bucket_ + bucket_count_ - sym_offset_;
// is_use_gnu_hash_ = true;
break;
}
default:
break;
}
}
// check android rel/rela
if (0 != rel_android_) {
const auto *rel = reinterpret_cast<const char *>(rel_android_);
if (rel_android_size_ < 4 || rel[0] != 'A' || rel[1] != 'P' || rel[2] != 'S' ||
rel[3] != '2') {
return;
}
rel_android_ += 4;
rel_android_size_ -= 4;
}
valid_ = true;
}
uint32_t Elf::GnuLookup(std::string_view name) const {
static constexpr auto kBloomMaskBits = sizeof(ElfW(Addr)) * 8;
static constexpr uint32_t kInitialHash = 5381;
static constexpr uint32_t kHashShift = 5;
if (!bucket_ || !bloom_) return 0;
uint32_t hash = kInitialHash;
for (unsigned char chr : name) {
hash += (hash << kHashShift) + chr;
}
auto bloom_word = bloom_[(hash / kBloomMaskBits) % bloom_size_];
uintptr_t mask = 0 | uintptr_t{1} << (hash % kBloomMaskBits) |
uintptr_t{1} << ((hash >> bloom_shift_) % kBloomMaskBits);
if ((mask & bloom_word) == mask) {
auto idx = bucket_[hash % bucket_count_];
if (idx >= sym_offset_) {
const char *strings = dyn_str_;
do {
auto *sym = dyn_sym_ + idx;
if (((chain_[idx] ^ hash) >> 1) == 0 && name == strings + sym->st_name) {
return idx;
}
} while ((chain_[idx++] & 1) == 0);
}
}
return 0;
}
uint32_t Elf::ElfLookup(std::string_view name) const {
static constexpr uint32_t kHashMask = 0xf0000000;
static constexpr uint32_t kHashShift = 24;
uint32_t hash = 0;
uint32_t tmp;
if (!bucket_ || bloom_) return 0;
for (unsigned char chr : name) {
hash = (hash << 4) + chr;
tmp = hash & kHashMask;
hash ^= tmp;
hash ^= tmp >> kHashShift;
}
const char *strings = dyn_str_;
for (auto idx = bucket_[hash % bucket_count_]; idx != 0; idx = chain_[idx]) {
auto *sym = dyn_sym_ + idx;
if (name == strings + sym->st_name) {
return idx;
}
}
return 0;
}
uint32_t Elf::LinearLookup(std::string_view name) const {
if (!dyn_sym_ || !sym_offset_) return 0;
for (uint32_t idx = 0; idx < sym_offset_; idx++) {
auto *sym = dyn_sym_ + idx;
if (name == dyn_str_ + sym->st_name) {
return idx;
}
}
return 0;
}
std::vector<uintptr_t> Elf::FindPltAddr(std::string_view name) const {
std::vector<uintptr_t> res;
uint32_t idx = GnuLookup(name);
if (!idx) idx = ElfLookup(name);
if (!idx) idx = LinearLookup(name);
if (!idx) return res;
auto looper = [&]<typename T>(auto begin, auto size, bool is_plt) -> void {
const auto *rel_end = reinterpret_cast<const T *>(begin + size);
for (const auto *rel = reinterpret_cast<const T *>(begin); rel < rel_end; ++rel) {
auto r_info = rel->r_info;
auto r_offset = rel->r_offset;
auto r_sym = ELF_R_SYM(r_info);
auto r_type = ELF_R_TYPE(r_info);
if (r_sym != idx) continue;
if (is_plt && r_type != ELF_R_GENERIC_JUMP_SLOT) continue;
if (!is_plt && r_type != ELF_R_GENERIC_ABS && r_type != ELF_R_GENERIC_GLOB_DAT) {
continue;
}
auto addr = bias_addr_ + r_offset;
if (addr > base_addr_) res.emplace_back(addr);
if (is_plt) break;
}
};
for (const auto &[rel, rel_size, is_plt] :
{std::make_tuple(rel_plt_, rel_plt_size_, true),
std::make_tuple(rel_dyn_, rel_dyn_size_, false),
std::make_tuple(rel_android_, rel_android_size_, false)}) {
if (!rel) continue;
if (is_use_rela_) {
looper.template operator()<ElfW(Rela)>(rel, rel_size, is_plt);
} else {
looper.template operator()<ElfW(Rel)>(rel, rel_size, is_plt);
}
}
return res;
}

View File

@ -0,0 +1,50 @@
#pragma once
#include <link.h>
#include <stdint.h>
#include <string_view>
class Elf {
ElfW(Addr) base_addr_ = 0;
ElfW(Addr) bias_addr_ = 0;
ElfW(Ehdr) *header_ = nullptr;
ElfW(Phdr) *program_header_ = nullptr;
ElfW(Dyn) *dynamic_ = nullptr; //.dynamic
ElfW(Word) dynamic_size_ = 0;
const char *dyn_str_ = nullptr; //.dynstr (string-table)
ElfW(Sym) *dyn_sym_ = nullptr; //.dynsym (symbol-index to string-table's offset)
ElfW(Addr) rel_plt_ = 0; //.rel.plt or .rela.plt
ElfW(Word) rel_plt_size_ = 0;
ElfW(Addr) rel_dyn_ = 0; //.rel.dyn or .rela.dyn
ElfW(Word) rel_dyn_size_ = 0;
ElfW(Addr) rel_android_ = 0; // android compressed rel or rela
ElfW(Word) rel_android_size_ = 0;
// for ELF hash
uint32_t *bucket_ = nullptr;
uint32_t bucket_count_ = 0;
uint32_t *chain_ = nullptr;
uint32_t chain_count_ = 0; // invalid for GNU hash
// append for GNU hash
uint32_t sym_offset_ = 0;
ElfW(Addr) *bloom_ = nullptr;
uint32_t bloom_size_ = 0;
uint32_t bloom_shift_ = 0;
bool is_use_rela_ = false;
bool valid_ = false;
uint32_t GnuLookup(std::string_view name) const;
uint32_t ElfLookup(std::string_view name) const;
uint32_t LinearLookup(std::string_view name) const;
public:
std::vector<uintptr_t> FindPltAddr(std::string_view name) const;
Elf(uintptr_t base_addr);
bool Valid() const { return valid_; };
};

View File

@ -0,0 +1,29 @@
#pragma once
#include <sys/types.h>
#include <string_view>
namespace lsplt {
inline namespace v1 {
[[deprecated(
"This hooks multiple functions at once, which makes the backup not accurate. Use register_hook with the first argument as ino_t instead."),
maybe_unused]] bool
RegisterHook(std::string_view regex, std::string_view symbol, void *callback, void **backup);
[[maybe_unused]] bool RegisterHook(ino_t ino, std::string_view symbol, void *callback,
void **backup);
[[deprecated("This is used with regex version of RegisterHook, which is deprecated."),
maybe_unused]] void
IgnoreHook(std::string_view regex, std::string_view symbol);
[[deprecated("This is used with regex version of RegisterHook, which is deprecated."),
maybe_unused]] void
IgnoreHook(ino_t ino, std::string_view symbol);
[[maybe_unused]] bool CommitHook();
[[maybe_unused]] bool InvalidateBackup();
} // namespace v1
} // namespace lsplt

View File

@ -0,0 +1,36 @@
#pragma once
#include <android/log.h>
#ifndef LOG_TAG
#define LOG_TAG "LSPlt"
#endif
#ifdef LOG_DISABLED
#define LOGD(...) 0
#define LOGV(...) 0
#define LOGI(...) 0
#define LOGW(...) 0
#define LOGE(...) 0
#else
#ifndef NDEBUG
#define LOGD(fmt, ...) \
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, \
"%s:%d#%s" \
": " fmt, \
__FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)
#define LOGV(fmt, ...) \
__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, \
"%s:%d#%s" \
": " fmt, \
__FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)
#else
#define LOGD(...) 0
#define LOGV(...) 0
#endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#endif

323
lsplt/src/main/jni/lsplt.cc Normal file
View File

@ -0,0 +1,323 @@
#include "include/lsplt.hpp"
#include <regex.h>
#include <sys/mman.h>
#include <array>
#include <cinttypes>
#include <list>
#include <map>
#include <mutex>
#include <vector>
#include "elf_util.hpp"
namespace {
struct BaseInfo {
regex_t regex;
ino_t inode;
std::string symbol;
~BaseInfo() { regfree(&regex); }
};
struct RegisterInfo : BaseInfo {
void *callback;
void **backup;
~RegisterInfo() { regfree(&regex); }
};
struct IgnoreInfo : BaseInfo {};
struct MapInfo {
std::string path;
ino_t inode;
uintptr_t start;
uintptr_t end;
std::map<uintptr_t, uintptr_t> hooks;
uintptr_t backup;
std::unique_ptr<Elf> elf;
[[nodiscard]] bool Match(const BaseInfo &info) const {
return (info.inode != 0 && info.inode == inode) ||
(info.inode == 0 && regexec(&info.regex, path.c_str(), 0, nullptr, 0) == 0);
}
};
class MapInfos : public std::map<uintptr_t, MapInfo, std::greater<>> {
public:
static MapInfos ScanMapInfo() {
constexpr static auto kPermLength = 5;
constexpr static auto kMapEntry = 5;
MapInfos info;
auto maps =
std::unique_ptr<FILE, decltype(&fclose)>{fopen("/proc/self/maps", "r"), &fclose};
if (maps) {
char *line = nullptr;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, maps.get())) != -1) {
uintptr_t start = 0;
uintptr_t end = 0;
uintptr_t off = 0;
ino_t inode = 0;
std::array<char, kPermLength> perm{'\0'};
int path_off;
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %lu %n%*s",
&start, &end, perm.data(), &off, &inode, &path_off) != kMapEntry) {
continue;
}
// we basically only care about r--p entry
// and for offset == 0 it's an ELF header
// and for offset != 0 it's what we hook
// if (perm[0] != 'r') continue;
if (perm[3] != 'p') continue;
if (perm[2] == 'x') continue;
// if (off != 0) continue;
while (path_off < read && isspace(line[path_off])) path_off++;
if (path_off >= read) continue;
std::string path{line + path_off};
if (path.empty()) continue;
if (path[0] == '[') continue;
info.emplace(start, MapInfo{std::move(path), inode, start, end, {}, 0, nullptr});
}
free(line);
}
return info;
}
// fiter out ignored
void Filter(const std::list<RegisterInfo> &register_info,
const std::list<IgnoreInfo> &ignore_info) {
for (auto iter = begin(); iter != end();) {
const auto &info = iter->second;
bool matched = false;
for (const auto &reg : register_info) {
if (!info.Match(reg)) {
continue;
}
bool ignored = false;
for (const auto &ign : ignore_info) {
if (!info.Match(ign)) {
continue;
}
if (ign.symbol == reg.symbol) {
ignored = true;
break;
}
}
if (!ignored) {
matched = true;
break;
}
}
if (matched) {
++iter;
} else {
iter = erase(iter);
}
}
}
void Merge(MapInfos &old) {
// merge with old map info
for (auto &info : old) {
if (info.second.backup) {
erase(info.second.backup);
}
if (auto iter = find(info.first); iter != end()) {
iter->second = std::move(info.second);
} else {
emplace(info.first, std::move(info.second));
}
}
}
bool DoHook(uintptr_t addr, uintptr_t callback, uintptr_t *backup) {
auto iter = lower_bound(addr);
if (iter == end()) return false;
// iter.first < addr
auto &info = iter->second;
if (info.end <= addr) return false;
if (!iter->second.backup) {
auto len = info.end - info.start;
// let os find a suitable address
auto *backup_addr = mmap(nullptr, len, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (backup_addr == MAP_FAILED) return false;
if (auto *new_addr = mremap(reinterpret_cast<void *>(info.start), len, len,
MREMAP_FIXED | MREMAP_MAYMOVE, backup_addr);
new_addr == MAP_FAILED || new_addr != backup_addr) {
return false;
}
if (auto *new_addr =
mmap(reinterpret_cast<void *>(info.start), len, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0);
new_addr == MAP_FAILED) {
return false;
}
memcpy(reinterpret_cast<void *>(info.start), backup_addr, len);
info.backup = reinterpret_cast<uintptr_t>(backup_addr);
}
auto *the_addr = reinterpret_cast<uintptr_t *>(addr);
*backup = *the_addr;
*the_addr = callback;
if (auto hook_iter = info.hooks.find(addr); hook_iter != info.hooks.end()) {
if (hook_iter->second == callback) info.hooks.erase(hook_iter);
} else {
info.hooks.emplace(addr, *backup);
}
if (info.hooks.empty()) {
auto len = info.end - info.start;
if (auto *new_addr =
mremap(reinterpret_cast<void *>(info.backup), len, len,
MREMAP_FIXED | MREMAP_MAYMOVE, reinterpret_cast<void *>(info.start));
new_addr == MAP_FAILED || reinterpret_cast<uintptr_t>(new_addr) != info.start) {
return false;
}
info.backup = 0;
}
return true;
}
bool DoHook(std::list<RegisterInfo> &register_info,
const std::list<IgnoreInfo> &ignore_info) {
bool res = true;
for (auto &[_, info] : *this) {
for (auto iter = register_info.begin(); iter != register_info.end();) {
const auto &reg = *iter;
if (!info.Match(reg)) {
continue;
}
bool ignored = false;
for (const auto &ign : ignore_info) {
if (!info.Match(ign)) {
continue;
}
if (ign.symbol == reg.symbol) {
ignored = true;
break;
}
}
if (ignored) {
++iter;
continue;
}
if (info.start == 0 && !info.elf) {
info.elf = std::make_unique<Elf>(info.start);
}
if (info.elf && info.elf->Valid()) {
for (auto addr: info.elf->FindPltAddr(reg.symbol)) {
res = DoHook(addr, reinterpret_cast<uintptr_t>(reg.callback),
reinterpret_cast<uintptr_t *>(reg.backup)) && res;
}
}
if (reg.inode != 0) {
iter = register_info.erase(iter);
} else {
++iter;
}
}
}
return res;
}
bool InvalidateBackup() {
bool res = true;
for (auto &[_, info] : *this) {
if (info.backup) {
for (auto &[addr, backup] : info.hooks) {
// store new address to backup since we don't need backup
backup = *reinterpret_cast<uintptr_t *>(addr);
}
auto len = info.end - info.start;
if (auto *new_addr =
mremap(reinterpret_cast<void *>(info.backup), len, len,
MREMAP_FIXED | MREMAP_MAYMOVE, reinterpret_cast<void *>(info.start));
new_addr == MAP_FAILED || reinterpret_cast<uintptr_t>(new_addr) != info.start) {
res = false;
info.hooks.clear();
continue;
}
for (auto &[addr, backup] : info.hooks) {
*reinterpret_cast<uintptr_t *>(addr) = backup;
}
info.hooks.clear();
info.backup = 0;
}
}
return res;
}
};
std::mutex register_mutex;
std::list<RegisterInfo> register_info;
std::mutex ignore_mutex;
std::list<IgnoreInfo> ignore_info;
MapInfos map_info;
} // namespace
namespace lsplt {
[[maybe_unused]] bool RegisterHook(std::string_view regex_str, std::string_view symbol,
void *callback, void **backup) {
if (regex_str.empty() || symbol.empty() || !callback) return false;
regex_t regex;
if (regcomp(&regex, regex_str.data(), REG_NOSUB) != 0) return false;
std::unique_lock lock(register_mutex);
register_info.emplace_back(RegisterInfo{{regex, 0, std::string{symbol}}, callback, backup});
return true;
}
[[maybe_unused]] bool RegisterHook(ino_t ino, std::string_view symbol, void *callback,
void **backup) {
if (symbol.empty() || !callback) return false;
std::unique_lock lock(register_mutex);
register_info.emplace_back(RegisterInfo{{{}, ino, std::string{symbol}}, callback, backup});
return true;
}
[[maybe_unused]] void IgnoreHook(std::string_view regex, std::string_view symbol) {
if (regex.empty() || symbol.empty()) return;
regex_t reg;
if (regcomp(&reg, regex.data(), REG_NOSUB) != 0) return;
std::unique_lock lock(ignore_mutex);
ignore_info.emplace_back(IgnoreInfo{{reg, 0, std::string{symbol}}});
}
[[maybe_unused]] void IgnoreHook(ino_t ino, std::string_view symbol) {
if (symbol.empty()) return;
std::unique_lock lock(ignore_mutex);
ignore_info.emplace_back(IgnoreInfo{{regex_t{}, ino, std::string{symbol}}});
}
[[maybe_unused]] bool CommitHook() {
std::unique_lock lock(register_mutex);
if (register_info.empty()) return true;
std::unique_lock lock2(ignore_mutex);
auto new_map_info = MapInfos::ScanMapInfo();
if (new_map_info.empty()) return false;
new_map_info.Filter(register_info, ignore_info);
new_map_info.Merge(map_info);
// update to new map info
map_info = std::move(new_map_info);
return map_info.DoHook(register_info, ignore_info);
}
[[gnu::destructor]] [[maybe_unused]] bool InvalidateBackup() {
std::unique_lock lock(register_mutex);
std::unique_lock lock2(ignore_mutex);
return map_info.InvalidateBackup();
}
} // namespace lsplt

24
settings.gradle.kts Normal file
View File

@ -0,0 +1,24 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
plugins {
id("com.android.application") version "7.3.1"
id("com.android.library") version "7.3.1"
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "LSPlt"
include(
":lsplt",
":test",
)

1
test/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

117
test/build.gradle.kts Normal file
View File

@ -0,0 +1,117 @@
import com.android.build.api.dsl.ManagedVirtualDevice
import com.android.build.gradle.internal.tasks.ManagedDeviceInstrumentationTestTask
plugins {
id("com.android.application")
}
val androidTargetSdkVersion: Int by rootProject.extra
val androidMinSdkVersion: Int by rootProject.extra
val androidBuildToolsVersion: String by rootProject.extra
val androidCompileSdkVersion: Int by rootProject.extra
val androidNdkVersion: String by rootProject.extra
val androidCmakeVersion: String by rootProject.extra
android {
namespace = "org.lsposed.lsplt.test"
compileSdk = androidCompileSdkVersion
ndkVersion = androidNdkVersion
buildToolsVersion = androidBuildToolsVersion
buildFeatures {
buildConfig = false
prefab = true
}
defaultConfig {
applicationId = "org.lsposed.lsplt"
minSdk = androidMinSdkVersion
targetSdk = androidTargetSdkVersion
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
arguments += "-DANDROID_STL=c++_shared"
}
}
}
externalNativeBuild {
cmake {
path = file("src/main/jni/CMakeLists.txt")
version = androidCmakeVersion
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
testOptions {
managedDevices {
devices {
fun createDevice(api: Int, is64: Boolean, target: String = "default") = create<ManagedVirtualDevice>("""avd-$api-${if(is64) "x86_64" else "x86"}-$target""") {
device = "Pixel 2"
apiLevel = api
systemImageSource = target
require64Bit = is64
}
createDevice(21, false)
createDevice(21, true)
createDevice(22, false)
createDevice(22, true)
createDevice(23, false)
createDevice(23, true)
createDevice(24, false)
createDevice(24, true)
createDevice(25, false)
createDevice(25, true)
createDevice(26, false)
createDevice(26, true)
createDevice(27, false)
createDevice(27, true)
createDevice(28, false)
createDevice(28, true)
createDevice(29, false)
createDevice(29, true)
createDevice(30, false, "aosp_atd")
createDevice(30, true)
// createDevice(31, false, "android-tv")
createDevice(31, true, "aosp_atd")
createDevice(32, true, "google_apis")
createDevice(33, true, "google_apis")
}
}
}
}
dependencies {
implementation(project(":lsplt"))
androidTestImplementation("androidx.test.ext:junit:1.1.4")
androidTestImplementation("androidx.test:runner:1.5.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
}
afterEvaluate {
task("testOnAllMVDs") {
dependsOn("assembleAndroidTest")
doLast {
tasks.withType(ManagedDeviceInstrumentationTestTask::class.java) {
println("::group::$this")
exec {
executable = "${rootProject.buildFile.parent}/gradlew"
args = listOf(":${project.name}:$name")
}
exec {
executable = "${rootProject.buildFile.parent}/gradlew"
args = listOf(":${project.name}:cleanManagedDevices")
}
println("::endgroup::")
}
}
}
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.18.1)
project("lsplt_test")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(test SHARED test.cpp)
find_package(lsplt REQUIRED CONFIG)
target_link_libraries(test log lsplt::lsplt)

View File

@ -0,0 +1,51 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
#ifndef _LOGGING_H
#define _LOGGING_H
#include <android/log.h>
#ifndef LOG_TAG
#define LOG_TAG "LSPlt-test"
#endif
#ifdef LOG_DISABLED
#define LOGD(...) 0
#define LOGV(...) 0
#define LOGI(...) 0
#define LOGW(...) 0
#define LOGE(...) 0
#else
#ifndef NDEBUG
#define LOGD(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "%s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__)
#define LOGV(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, "%s:%d#%s" ": " fmt, __FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(,) __VA_ARGS__)
#else
#define LOGD(...) 0
#define LOGV(...) 0
#endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#endif
#endif // _LOGGING_H

View File

@ -0,0 +1,11 @@
#include "logging.h"
#include "lsplt.hpp"
#include <jni.h>
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}