mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 04:04:21 +02:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bb7a1ee86 | ||
|
|
363497fdd5 | ||
|
|
ff52f50c0c | ||
|
|
e288946b81 | ||
|
|
41f08ac9a2 | ||
|
|
98a5ec45bb | ||
|
|
e4421d966a | ||
|
|
efffd72c52 | ||
|
|
d5e8193ce5 | ||
|
|
6765fbcb20 | ||
|
|
75b2478a55 | ||
|
|
5bf1268c82 | ||
|
|
038dcd48cd | ||
|
|
9e9e5950d0 | ||
|
|
268afccf05 | ||
|
|
e1c0ac983c | ||
|
|
077eab2b79 | ||
|
|
35ef3af54b | ||
|
|
f10282294d | ||
|
|
6217b5f2fa | ||
|
|
b9e31178f3 | ||
|
|
50d5e14539 | ||
|
|
fd15f1de4c | ||
|
|
064cf95f6d | ||
|
|
8d75ede0ab | ||
|
|
0dbe18d824 | ||
|
|
a1bc6522c3 | ||
|
|
724b020e93 |
No files matched your search
@@ -6,3 +6,6 @@ compat-db/.env.lakebed.server
|
|||||||
compat-db/.lakebed/
|
compat-db/.lakebed/
|
||||||
tests/smoke/results/
|
tests/smoke/results/
|
||||||
mac/bin/
|
mac/bin/
|
||||||
|
|
||||||
|
# Downloaded at build time (frame/kdeconnect/fetch.py, app/build/fetch-deps.js)
|
||||||
|
frame/kdeconnect/packages/
|
||||||
@@ -72,6 +72,9 @@ Drag files onto the window to send them. Drop a game's .zip, folder or .exe to a
|
|||||||
**📸 Screenshots**<br>
|
**📸 Screenshots**<br>
|
||||||
Browse the shots you take in the headset and save them to your Pictures folder.
|
Browse the shots you take in the headset and save them to your Pictures folder.
|
||||||
|
|
||||||
|
**⌨️ Keyboard and trackpad**<br>
|
||||||
|
Type and point in the Frame's apps from your computer or phone, through KDE Connect, which Frame Control brings along and sets up on the Frame. Nothing else to install, anywhere.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -251,5 +254,7 @@ into a draft release, which reaches users once published. See
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE). Steam, Steam Frame and SteamVR are trademarks of Valve
|
[MIT](LICENSE). The apps also ship other people's software under its own
|
||||||
|
licence, notably KDE Connect (GPL) for the keyboard and trackpad; see
|
||||||
|
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). Steam, Steam Frame and SteamVR are trademarks of Valve
|
||||||
Corporation. This project isn't affiliated with or endorsed by Valve.
|
Corporation. This project isn't affiliated with or endorsed by Valve.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Third-party software in Frame Control
|
||||||
|
|
||||||
|
Frame Control's own code is under the [MIT licence](LICENSE). The apps also
|
||||||
|
ship other people's software, unchanged, each under its own licence:
|
||||||
|
|
||||||
|
| What | Where | Licence | Details |
|
||||||
|
|---|---|---|---|
|
||||||
|
| KDE Connect 24.02.2 and five libraries, as Valve builds them for the Frame | Desktop apps and the iPhone app; copied to the Frame for the keyboard and trackpad | GPL and LGPL (per package) | [frame/kdeconnect/NOTICE.md](frame/kdeconnect/NOTICE.md), licence texts in [frame/kdeconnect/LICENSES](frame/kdeconnect/LICENSES), complete source in the [kdeconnect-frame-24.02.2-1 release](https://github.com/saphid/frame-control/releases/tag/kdeconnect-frame-24.02.2-1) |
|
||||||
|
| Python 3.12 ([python-build-standalone](https://github.com/astral-sh/python-build-standalone)) | Desktop apps | PSF License and others | Included with it, in the app's `python` folder |
|
||||||
|
| adb (Android SDK Platform-Tools) | Desktop apps (not Linux arm64) | Apache-2.0 and others | `NOTICE.txt` in the app's `tools` folder |
|
||||||
|
| Mozilla's CA certificate list, as published by curl | Desktop apps | MPL-2.0 | https://curl.se/docs/caextract.html |
|
||||||
|
| Electron | Desktop apps | MIT (Chromium: BSD-3-Clause and others) | `LICENSE` and `LICENSES.chromium.html` in the app |
|
||||||
|
|
||||||
|
Frame Control starts KDE Connect and talks to it over its network protocol;
|
||||||
|
it doesn't link to it or include its code.
|
||||||
+21
-1
@@ -1,7 +1,9 @@
|
|||||||
// Downloads what the app bundles so users install nothing else: a standalone
|
// Downloads what the app bundles so users install nothing else: a standalone
|
||||||
// Python (python-build-standalone), adb (Android platform-tools) and a CA
|
// Python (python-build-standalone), adb (Android platform-tools) and a CA
|
||||||
// bundle. Each goes in build/deps/<os>-<arch>/{python,tools}, which package.json
|
// bundle. Each goes in build/deps/<os>-<arch>/{python,tools}, which package.json
|
||||||
// copies into the app's resources. Everything is pinned by version and SHA-256.
|
// copies into the app's resources. Also KDE Connect for the Frame (the same arm64
|
||||||
|
// packages for every build), into ../frame/kdeconnect/packages as listed in
|
||||||
|
// ../frame/kdeconnect/packages.json. Everything is pinned by version and SHA-256.
|
||||||
// node build/fetch-deps.js mac arm64 | win x64 | linux x64 arm64
|
// node build/fetch-deps.js mac arm64 | win x64 | linux x64 arm64
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
@@ -127,8 +129,26 @@ async function fetch(os, arch) {
|
|||||||
console.log(`${key}: ${version} -> ${out}`);
|
console.log(`${key}: ${version} -> ${out}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// As frame/kdeconnect/fetch.py does for the iPhone app's bundle.
|
||||||
|
async function fetchKdeConnect() {
|
||||||
|
const dir = path.join(__dirname, "..", "..", "frame", "kdeconnect");
|
||||||
|
const manifest = JSON.parse(fs.readFileSync(path.join(dir, "packages.json"), "utf8"));
|
||||||
|
const out = path.join(dir, "packages");
|
||||||
|
fs.mkdirSync(out, { recursive: true });
|
||||||
|
const sha = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
|
||||||
|
for (const p of manifest.packages) {
|
||||||
|
const file = path.join(out, p.file);
|
||||||
|
if (fs.existsSync(file) && sha(file) === p.sha256) continue;
|
||||||
|
await download(manifest.release + p.file, p.sha256, file);
|
||||||
|
}
|
||||||
|
const wanted = new Set(manifest.packages.map((p) => p.file));
|
||||||
|
for (const f of fs.readdirSync(out)) if (!wanted.has(f)) fs.rmSync(path.join(out, f), { recursive: true, force: true });
|
||||||
|
console.log(`KDE Connect for the Frame: ${manifest.packages.length} packages -> ${out}`);
|
||||||
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const [os, ...archs] = process.argv.slice(2);
|
const [os, ...archs] = process.argv.slice(2);
|
||||||
if (!os || !archs.length) throw new Error("usage: node build/fetch-deps.js <mac|win|linux> <arch>...");
|
if (!os || !archs.length) throw new Error("usage: node build/fetch-deps.js <mac|win|linux> <arch>...");
|
||||||
for (const arch of archs) await fetch(os, arch);
|
for (const arch of archs) await fetch(os, arch);
|
||||||
|
await fetchKdeConnect();
|
||||||
})().catch((e) => { console.error(e.message); process.exit(1); });
|
})().catch((e) => { console.error(e.message); process.exit(1); });
|
||||||
@@ -249,6 +249,7 @@ function fromUi(e) {
|
|||||||
|
|
||||||
ipcMain.handle("clipboard:read", (e) => fromUi(e) ? clipboard.readText() : "");
|
ipcMain.handle("clipboard:read", (e) => fromUi(e) ? clipboard.readText() : "");
|
||||||
ipcMain.handle("connection:setup", (e) => { if (fromUi(e)) setUpConnection(); });
|
ipcMain.handle("connection:setup", (e) => { if (fromUi(e)) setUpConnection(); });
|
||||||
|
ipcMain.on("keys:capture", (e, on) => { if (fromUi(e)) win.webContents.setIgnoreMenuShortcuts(on === true); });
|
||||||
ipcMain.handle("update:get", (e) => fromUi(e) ? publicUpdate() : null);
|
ipcMain.handle("update:get", (e) => fromUi(e) ? publicUpdate() : null);
|
||||||
ipcMain.handle("update:check", (e) => fromUi(e) ? checkForUpdate({ manual: true }).then(publicUpdate) : null);
|
ipcMain.handle("update:check", (e) => fromUi(e) ? checkForUpdate({ manual: true }).then(publicUpdate) : null);
|
||||||
ipcMain.handle("update:install", (e) => { if (fromUi(e)) installUpdate(); });
|
ipcMain.handle("update:install", (e) => { if (fromUi(e)) installUpdate(); });
|
||||||
|
|||||||
@@ -66,6 +66,14 @@
|
|||||||
"*.py"
|
"*.py"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"from": "../frame/openxr-compat",
|
||||||
|
"to": "frame/openxr-compat",
|
||||||
|
"filter": [
|
||||||
|
"XrApiLayer_FRAME_compat.json",
|
||||||
|
"prebuilt/**/*.so"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"from": "../frame/devkit-utils",
|
"from": "../frame/devkit-utils",
|
||||||
"to": "frame/devkit-utils",
|
"to": "frame/devkit-utils",
|
||||||
@@ -74,6 +82,16 @@
|
|||||||
"!**/__pycache__/**"
|
"!**/__pycache__/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"from": "../frame/kdeconnect",
|
||||||
|
"to": "frame/kdeconnect",
|
||||||
|
"filter": [
|
||||||
|
"packages.json",
|
||||||
|
"NOTICE.md",
|
||||||
|
"LICENSES/**/*",
|
||||||
|
"packages/*.pkg.tar.zst"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"from": "../apk-catalog",
|
"from": "../apk-catalog",
|
||||||
"to": "apk-catalog",
|
"to": "apk-catalog",
|
||||||
@@ -96,6 +114,14 @@
|
|||||||
"filter": [
|
"filter": [
|
||||||
"**/*"
|
"**/*"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "../THIRD_PARTY_NOTICES.md",
|
||||||
|
"to": "THIRD_PARTY_NOTICES.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "../LICENSE",
|
||||||
|
"to": "LICENSE"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mac": {
|
"mac": {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ const { contextBridge, ipcRenderer, webUtils } = require("electron");
|
|||||||
contextBridge.exposeInMainWorld("frameApp", {
|
contextBridge.exposeInMainWorld("frameApp", {
|
||||||
readClipboard: () => ipcRenderer.invoke("clipboard:read"),
|
readClipboard: () => ipcRenderer.invoke("clipboard:read"),
|
||||||
setUpConnection: () => ipcRenderer.invoke("connection:setup"),
|
setUpConnection: () => ipcRenderer.invoke("connection:setup"),
|
||||||
|
// While the keyboard-and-trackpad panel holds the keyboard, ⌘W, ⌘R and the rest go to the Frame.
|
||||||
|
captureKeys: (on) => ipcRenderer.send("keys:capture", !!on),
|
||||||
pathForFile: (file) => { try { return webUtils.getPathForFile(file) || ""; } catch { return ""; } },
|
pathForFile: (file) => { try { return webUtils.getPathForFile(file) || ""; } catch { return ""; } },
|
||||||
// Updates (app/updater.js): the page shows a banner and an Update button.
|
// Updates (app/updater.js): the page shows a banner and an Update button.
|
||||||
update: {
|
update: {
|
||||||
|
|||||||
@@ -92,8 +92,11 @@ app bundles `ui/`, `scripts/`, `frame/android/`, Valve's `frame/devkit-utils/` a
|
|||||||
([python-build-standalone](https://github.com/astral-sh/python-build-standalone))
|
([python-build-standalone](https://github.com/astral-sh/python-build-standalone))
|
||||||
and `adb` from Google's platform-tools, so there's nothing else to install. It
|
and `adb` from Google's platform-tools, so there's nothing else to install. It
|
||||||
also bundles curl's copy of Mozilla's CA list, because Python on Windows only
|
also bundles curl's copy of Mozilla's CA list, because Python on Windows only
|
||||||
trusts root certificates already in the Windows store.
|
trusts root certificates already in the Windows store. And it bundles KDE
|
||||||
`app/build/fetch-deps.js` downloads both, pinned by SHA-256.
|
Connect for the Frame (Valve's arm64 build and five libraries, 3.6 MB,
|
||||||
|
[`frame/kdeconnect`](../frame/kdeconnect/NOTICE.md)), which it copies to the
|
||||||
|
Frame for the keyboard and trackpad.
|
||||||
|
`app/build/fetch-deps.js` downloads all of it, pinned by SHA-256.
|
||||||
|
|
||||||
The server is Python stdlib only and listens on 127.0.0.1. It rejects requests
|
The server is Python stdlib only and listens on 127.0.0.1. It rejects requests
|
||||||
with a non-local `Host` header, and any `/api/` request without a custom
|
with a non-local `Host` header, and any `/api/` request without a custom
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ Lepton (Android 11, podman container "lepton-dev") ← its own panel, app 305600
|
|||||||
| SteamVR settings live in `~/.config/openvr/config/steamvr.vrsettings`, not under `~/.local/share/Steam/config/`. `dashboard.lastAccessedExternalOverlayKey` names the last panel you used. | Settings tweaks |
|
| SteamVR settings live in `~/.config/openvr/config/steamvr.vrsettings`, not under `~/.local/share/Steam/config/`. `dashboard.lastAccessedExternalOverlayKey` names the last panel you used. | Settings tweaks |
|
||||||
| The Steam client's journal (`journalctl --user`) carries SteamVR system UI lines such as `[Overlays] Created: …` and `vroverlay_uid<appid>`. It's the quickest way to see panels come and go. | Debugging |
|
| The Steam client's journal (`journalctl --user`) carries SteamVR system UI lines such as `[Overlays] Created: …` and `vroverlay_uid<appid>`. It's the quickest way to see panels come and go. | Debugging |
|
||||||
| Present: `rsync`, `flatpak`, `python3`, `git`, `qdbus6`, `xrdp`, `xprop`, `xwininfo`, `xterm`, `konsole`, `dolphin`, `gamescopectl`. Missing: `wl-copy`, `xclip`, `xsel`, `kdeconnect-cli`, `tailscale` (installable in `~`, see below), `krfb`, `wayvnc`. | Script design |
|
| Present: `rsync`, `flatpak`, `python3`, `git`, `qdbus6`, `xrdp`, `xprop`, `xwininfo`, `xterm`, `konsole`, `dolphin`, `gamescopectl`. Missing: `wl-copy`, `xclip`, `xsel`, `kdeconnect-cli`, `tailscale` (installable in `~`, see below), `krfb`, `wayvnc`. | Script design |
|
||||||
|
| **SteamOS updates arrive on their own.** The Frame went from 0.3.0 (build 20260922.6101926) to **0.4.1, build 20260925.6191901**, between 2026-09-27 and 2026-09-28 with no action from us; `~` (keys, user Flatpaks, `~/.local/share`) survived. **Verified 2026-09-28.** | Keep changes in `~` |
|
||||||
|
| **Valve's package repository has more than the image.** `pacman -Si` / `pacman -Sp` work as `steamos` without root and list Valve's own builds, such as `kdeconnect` 24.02.2 and `python-evdev` 1.7.0 in `extra`. Unpacking those packages into `~` runs them without touching the read-only root. The repository URLs say not to share them, so never write them down; Valve also publishes each build's source package there (`sources/packages/`), which is how Frame Control got the complete source for the KDE Connect it ships. **Verified 2026-09-28**, SteamOS 0.4.1. | [streaming.md](streaming.md#input-type-and-point-in-the-frame-from-the-mac-or-iphone) |
|
||||||
|
| **gamescope runs two Xwayland displays.** `:0` holds Steam's VR bar and menus (`valve.steam.gamepadui.*`) and ignores XTest pointer motion; `:1` holds apps such as Chromium and takes it. There's also a libei socket, `/run/user/1000/gamescope-0-ei`. **Verified 2026-09-28**, SteamOS 0.4.1. | Keyboard and trackpad |
|
||||||
| Flathub is a **system** remote. `--user` installs over SSH work and show up in the desktop menu. | `install-apps.sh` |
|
| Flathub is a **system** remote. `--user` installs over SSH work and show up in the desktop menu. | `install-apps.sh` |
|
||||||
| `/` is 10 GB and read-only. `/home` is 929 GB. | Where to put things |
|
| `/` is 10 GB and read-only. `/home` is 929 GB. | Where to put things |
|
||||||
| Clipboard: Klipper over the nested D-Bus bus (`qdbus6 org.kde.klipper …`). | `paste-to-frame.sh` |
|
| Clipboard: Klipper over the nested D-Bus bus (`qdbus6 org.kde.klipper …`). | `paste-to-frame.sh` |
|
||||||
|
|||||||
+2
-1
@@ -12,7 +12,8 @@ An iPhone can't run Python or `ssh`, but the Frame can. So the app:
|
|||||||
1. connects to the Frame over SSH itself (the [Citadel](https://github.com/orlandos-nl/Citadel)
|
1. connects to the Frame over SSH itself (the [Citadel](https://github.com/orlandos-nl/Citadel)
|
||||||
Swift SSH library), with its own ed25519 key from the Keychain;
|
Swift SSH library), with its own ed25519 key from the Keychain;
|
||||||
2. copies Frame Control's server and helpers (`ios/scripts/make_frame_bundle.py`,
|
2. copies Frame Control's server and helpers (`ios/scripts/make_frame_bundle.py`,
|
||||||
under 1 MB) to `~/.cache/frame-control/<version>` on the Frame, once per version;
|
4.6 MB, 3.6 MB of it the KDE Connect the keyboard and trackpad use) to
|
||||||
|
`~/.cache/frame-control/<version>` on the Frame, once per version;
|
||||||
3. starts `ui/server.py` there with `FRAME_LOCAL=1`. It listens only on the
|
3. starts `ui/server.py` there with `FRAME_LOCAL=1`. It listens only on the
|
||||||
Frame's own 127.0.0.1, and it stops when the phone disconnects (`--exit-on-eof`);
|
Frame's own 127.0.0.1, and it stops when the phone disconnects (`--exit-on-eof`);
|
||||||
4. tunnels to it through the SSH session and shows the same page as the desktop
|
4. tunnels to it through the SSH session and shows the same page as the desktop
|
||||||
|
|||||||
+81
-9
@@ -113,18 +113,90 @@ capture it.
|
|||||||
|
|
||||||
## Input: type and point in the Frame from the Mac or iPhone
|
## Input: type and point in the Frame from the Mac or iPhone
|
||||||
|
|
||||||
**Verified 2026-09-27** on the headset: `steamos` is in the `input` group and
|
**Built: Home → Keyboard and trackpad**, in every version of Frame Control
|
||||||
`/dev/uinput` is `crw-rw-r-- root input`, so **our own code can create a
|
(Mac, Windows, Linux, iPhone and iPad), with nothing to install on the device
|
||||||
virtual keyboard and mouse without sudo**. The Frame has no `python-evdev`,
|
you're holding. On a phone the panel is a trackpad (drag to move, tap to click,
|
||||||
`ydotool`, `wtype` or KDE Connect; `kwin_wayland` and `plasmashell` run only
|
two fingers to scroll, two-finger tap to right-click) plus a text field that
|
||||||
while the desktop panel is open in the headset.
|
types on the Frame. On a computer, clicking the pad passes your mouse and
|
||||||
|
keyboard through to the Frame until you press Esc (⌘ is sent as Ctrl on a Mac).
|
||||||
|
|
||||||
| Option | Mac | iPhone | Notes |
|
It goes through **KDE Connect**, the first-party route (KDE makes the Frame's
|
||||||
|
desktop): Frame Control's server runs [`ui/frame_input_agent.py`](../ui/frame_input_agent.py)
|
||||||
|
on the Frame, which talks KDE Connect's own LAN protocol to the Frame's
|
||||||
|
`kdeconnectd` as if it were a phone. KDE Connect does the typing and clicking.
|
||||||
|
|
||||||
|
**Verified 2026-09-28** (SteamOS 0.4.1, build 20260925.6191901):
|
||||||
|
|
||||||
|
- KDE Connect isn't installed on the Frame, so **Frame Control ships it**:
|
||||||
|
Valve's own build for the Frame (`kdeconnect` 24.02.2-1 from its `extra`
|
||||||
|
repository) plus the five libraries it links that the Frame lacks
|
||||||
|
(`kcontacts`, `kpeople`, `modemmanager-qt`, `pulseaudio-qt`, `libfakekey`),
|
||||||
|
pinned by SHA-256 in [`frame/kdeconnect/packages.json`](../frame/kdeconnect/packages.json).
|
||||||
|
The builds download them from the
|
||||||
|
[kdeconnect-frame-24.02.2-1 release](https://github.com/saphid/frame-control/releases/tag/kdeconnect-frame-24.02.2-1)
|
||||||
|
(`app/build/fetch-deps.js`, `frame/kdeconnect/fetch.py`). The address of
|
||||||
|
Valve's repository for the Frame isn't to be shared, and Valve's public
|
||||||
|
aarch64 preview repository has KDE Connect 25.08, built against newer KDE
|
||||||
|
libraries than the Frame has.
|
||||||
|
- On first use, the computer copies them to the Frame over the SSH connection
|
||||||
|
it already has. The iPhone app's bundle, already copied to the Frame, has
|
||||||
|
them too. The agent checks each SHA-256 and unpacks them into
|
||||||
|
`~/.local/share/frame-control/kdeconnect` (3.6 MB copied, 18 MB unpacked,
|
||||||
|
about 2 s). There's no internet download on the Frame, no root, and nothing
|
||||||
|
on the read-only system, so SteamOS updates leave it alone. A stamp there
|
||||||
|
(`root/.frame-control-packages`) records which build it is; a newer Frame
|
||||||
|
Control replaces it.
|
||||||
|
- `pacman -Sp kdeconnect …` also pulls in ModemManager, libqmi, libmbim,
|
||||||
|
libqrtr-glib and ppp (packaging dependencies). `kdeconnectd` and its plugins
|
||||||
|
don't link any of them (checked with `ldd` against the six packages alone),
|
||||||
|
so Frame Control leaves them out.
|
||||||
|
- Licences: the packages are GPL and LGPL; Frame Control stays MIT because it
|
||||||
|
only starts `kdeconnectd` and speaks its protocol. The notice, licence texts
|
||||||
|
and complete source are in [`frame/kdeconnect`](../frame/kdeconnect/NOTICE.md),
|
||||||
|
[`THIRD_PARTY_NOTICES.md`](../THIRD_PARTY_NOTICES.md) and the app's
|
||||||
|
**About and licences** (Tools).
|
||||||
|
- It pairs by itself: the agent asks to pair and accepts on KDE Connect's side
|
||||||
|
over D-Bus (`qdbus6 … acceptPairing`). It keeps its identity in
|
||||||
|
`…/kdeconnect/bridge`, so later connections are already paired. (A pair
|
||||||
|
request to a device that's already paired makes KDE Connect unpair it, so
|
||||||
|
the agent only asks when it isn't paired.)
|
||||||
|
- Protocol version 7: whoever opens the TCP connection sends its identity line
|
||||||
|
in plain text, then acts as the **TLS server** (KDE Connect's
|
||||||
|
`lanlinkprovider.cpp`). Remote input is `kdeconnect.mousepad.request` with
|
||||||
|
`dx`/`dy`, `singleclick`, `rightclick`, `singlehold`/`singlerelease`,
|
||||||
|
`scroll`, `key` (any text) or `specialKey` (1 Backspace … 14 Escape,
|
||||||
|
21–32 F1–F12) and modifier flags.
|
||||||
|
- **KDE Connect runs only while something uses the keyboard and trackpad.**
|
||||||
|
Each device gets its own KDE Connect identity (KDE Connect keeps one
|
||||||
|
connection per device, so a shared one would make a phone and a computer
|
||||||
|
knock each other off). The last one to disconnect stops KDE Connect, so it
|
||||||
|
isn't left running, or discoverable on your network, afterwards.
|
||||||
|
- KDE Connect 24.02 **hangs or crashes when asked to unpair a device that's
|
||||||
|
offline** (seen twice: once spinning at 100% CPU with D-Bus unresponsive,
|
||||||
|
once exiting). Frame Control never unpairs. If its copy stops answering, the
|
||||||
|
agent restarts it once (tested by freezing it with `kill -STOP`).
|
||||||
|
- Moves from the iPhone app (Simulator) and the Mac's server moved the Frame's
|
||||||
|
X pointer by exactly the amount sent, including with the bundled packages
|
||||||
|
copied over SSH (2026-09-28).
|
||||||
|
- gamescope runs **two Xwayland displays**. `:0` holds Steam's VR bar and menus
|
||||||
|
and ignores injected pointer motion; `:1` holds apps such as Chromium and
|
||||||
|
takes it. KDE Connect runs on `:1`, so it reaches apps, not Steam's own menus.
|
||||||
|
There's also a `gamescope-0-ei` (libei) socket.
|
||||||
|
- **Not yet tested:** typing and clicking as seen in the headset, and whether
|
||||||
|
it reaches the KDE desktop panel (Plasma is its own session).
|
||||||
|
- **Known limit:** keys and clicks typed while the link is reconnecting wait
|
||||||
|
and are sent once it's back, but anything sent in the moment the Wi-Fi
|
||||||
|
drops, before SSH notices, can be lost. Confirming every event would add a
|
||||||
|
round trip to each pointer move.
|
||||||
|
|
||||||
|
Our own `uinput` keyboard and mouse would also work (`steamos` is in the
|
||||||
|
`input` group and `/dev/uinput` is group-writable, verified 2026-09-27), and
|
||||||
|
remains the fallback if the bundled KDE Connect ever stops working on a new SteamOS.
|
||||||
|
|
||||||
|
| Other option | Mac | iPhone | Why not |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| **A uinput keyboard and mouse in Frame Control's server** | ✓ | ✓ | **Recommended.** The server opens `/dev/uinput` with `ctypes` (standard library only) and the page sends key and pointer events through the tunnel it already has. On the phone: a trackpad area (drag to move, tap to click, two fingers to scroll) and the iOS keyboard for typing. On the Mac: a "control the Frame" mode that captures the keyboard and pointer (Esc to release). Uinput devices look like real hardware to the kernel, so libinput, KWin and gamescope should take them; [frame-voice](https://github.com/DeeJanuz/frame-voice) already types into a Frame through a uinput keyboard. **Untested**: which surfaces in VR (desktop panel, SteamVR dashboard, games, Android apps in Lepton) accept the pointer. About a day or two of work |
|
| **Bluetooth keyboard and mouse** | – | – | Needs real hardware, paired in SteamOS settings. The iPhone can't pretend to be a Bluetooth keyboard: iOS won't advertise the HID service ([Apple forums](https://developer.apple.com/forums/thread/733916)) |
|
||||||
| **Bluetooth keyboard and mouse** | – | – | Real hardware paired in SteamOS settings. The iPhone can't pretend to be a Bluetooth keyboard: iOS won't advertise the HID service ([Apple forums](https://developer.apple.com/forums/thread/733916)) |
|
|
||||||
| **Deskflow** (formerly Input Leap / Barrier) | ✓ | – | Moves the Mac's own mouse and keyboard onto the Frame's screen edge. Flathub has an aarch64 build ([Flathub](https://flathub.org/apps/org.deskflow.deskflow)); on Wayland it needs the InputCapture/libei portal, and only works while Plasma is running. No iPhone client |
|
| **Deskflow** (formerly Input Leap / Barrier) | ✓ | – | Moves the Mac's own mouse and keyboard onto the Frame's screen edge. Flathub has an aarch64 build ([Flathub](https://flathub.org/apps/org.deskflow.deskflow)); on Wayland it needs the InputCapture/libei portal, and only works while Plasma is running. No iPhone client |
|
||||||
| **KDE Connect** | ~ | ✓ | Its iOS app has a remote touchpad and keyboard, but the Frame would need KDE Connect installed (not on Flathub; `pacman` on a read-only root). More moving parts than the uinput route |
|
|
||||||
| **Remmina / Steam Link / RDP** | ✓ | – | Input only reaches the streamed session, not the headset's own apps |
|
| **Remmina / Steam Link / RDP** | ✓ | – | Input only reaches the streamed session, not the headset's own apps |
|
||||||
|
|
||||||
Other ways to get text in:
|
Other ways to get text in:
|
||||||
|
|||||||
+148
@@ -0,0 +1,148 @@
|
|||||||
|
# VR APKs and Quest games in Lepton
|
||||||
|
|
||||||
|
What it takes to run an immersive (OpenXR) Android app, including Meta Quest
|
||||||
|
builds, on the Frame. Checked on SteamOS BUILD_ID 20260925.6191901, Lepton
|
||||||
|
v2.8.14 (rootfs v2.8.11), SteamVR 2.18.1, on 2026-09-28, unless marked
|
||||||
|
**inferred**.
|
||||||
|
|
||||||
|
## How a VR APK reaches SteamVR (verified)
|
||||||
|
|
||||||
|
- Lepton ships a standard Khronos system runtime manifest,
|
||||||
|
`/vendor/etc/openxr/1/active_runtime.json`, pointing at SteamVR's Android
|
||||||
|
client, `/data/steamvr/runtime/bin/androidarm64/vrclient.so`. That is the
|
||||||
|
host's `/opt/steamvr/bin/androidarm64/`, bind-mounted in.
|
||||||
|
- An APK's own Khronos-style `libopenxr_loader.so` tries the runtime brokers
|
||||||
|
(`org.khronos.openxr.runtime_broker`, `…system_runtime_broker`), finds
|
||||||
|
neither, then falls back to that manifest. Nothing in the APK has to change
|
||||||
|
for discovery.
|
||||||
|
- Install the APK without the flatscreen marker
|
||||||
|
(`python3 ui/frame_android.py install app.apk --vr`). The marker only
|
||||||
|
controls Lepton's 2D Android surface; the app itself has to start an
|
||||||
|
OpenXR session.
|
||||||
|
- Lepton also loads Valve's `XR_APILAYER_VALVE_fdm_injection` layer from
|
||||||
|
`/vendor/etc/openxr/1/api_layers/implicit.d/`. Only layers in Valve's own
|
||||||
|
directories are picked up (`liblepton/vulkan_layers.sh`), so a third-party
|
||||||
|
layer has to ship inside the APK.
|
||||||
|
|
||||||
|
**Open Brush 2.32.29, the Quest APK from its GitHub release (Unity OpenXR,
|
||||||
|
Vulkan), works unmodified.** Its manifest already has `LAUNCHER` next to
|
||||||
|
`com.oculus.intent.category.VR`. Unity asked for OpenXR 1.1, got
|
||||||
|
`XR_ERROR_API_VERSION_UNSUPPORTED`, retried with 1.0 and succeeded. SteamVR
|
||||||
|
took it as the scene app, created Touch, simple-controller and Frame-controller
|
||||||
|
bindings, and the session reached `XR_SESSION_STATE_FOCUSED`. A headset capture
|
||||||
|
(`ui/frame_vrshot.py`, after waking the compositor and closing the dashboard)
|
||||||
|
showed a dark sky over a mountain horizon; nobody wore the headset to confirm
|
||||||
|
it was Open Brush's scene or to try drawing.
|
||||||
|
|
||||||
|
**Khronos `hello_xr` (Vulkan, 1.1.63 release APK) works unmodified:**
|
||||||
|
`Instance RuntimeName=SteamVR/OpenXR RuntimeVersion=2.18.1`, 1728×1728
|
||||||
|
swapchains per eye, session `IDLE → READY → SYNCHRONIZED` (the headset was
|
||||||
|
not being worn, so it did not reach `FOCUSED`).
|
||||||
|
|
||||||
|
## What SteamVR's Android runtime supports (verified, from `vrclient.so`)
|
||||||
|
|
||||||
|
- **OpenXR 1.0 only.** An app requesting `XR_API_VERSION_1_0` works; one
|
||||||
|
requesting 1.1 (`XR_CURRENT_API_VERSION` in a 1.1 SDK) gets
|
||||||
|
`XR_ERROR_API_VERSION_UNSUPPORTED` from the runtime.
|
||||||
|
- Extensions include `XR_KHR_opengl_es_enable`, `XR_KHR_vulkan_enable{,2}`,
|
||||||
|
`XR_KHR_composition_layer_depth`, `XR_KHR_locate_spaces`,
|
||||||
|
`XR_EXT_local_floor`, `XR_EXT_uuid`, `XR_EXT_palm_pose`,
|
||||||
|
`XR_EXT_hand_tracking`, `XR_EXT_eye_gaze_interaction`, and these Meta ones:
|
||||||
|
`XR_FB_display_refresh_rate`, `XR_FB_foveation{,_configuration,_vulkan}`,
|
||||||
|
`XR_FB_space_warp`, `XR_FB_swapchain_update_state`,
|
||||||
|
`XR_META_foveation_eye_tracked`, `XR_META_recommended_layer_resolution`,
|
||||||
|
`XR_META_vulkan_swapchain_create_info`, `XR_META_performance_metrics`.
|
||||||
|
- Not present: `XR_FB_passthrough`, `XR_FB_hand_tracking_*`,
|
||||||
|
`XR_FB_spatial_entity*`, `XR_FB_color_space`,
|
||||||
|
`XR_KHR_android_thread_settings`, `XR_OCULUS_*`.
|
||||||
|
- Interaction profiles include `oculus/touch_controller`, `khr/simple_controller`,
|
||||||
|
`valve/frame_controller` and the usual PC controllers. Valve documents Touch
|
||||||
|
bindings as a working fallback on the Frame controllers.
|
||||||
|
|
||||||
|
## What stops a Quest APK (verified with Wolvic 1.9, `oculusvr` build)
|
||||||
|
|
||||||
|
1. **Lepton won't start it.** Lepton's `apk-info-extractor` only accepts an
|
||||||
|
activity whose intent filter has `android.intent.action.MAIN` and
|
||||||
|
`android.intent.category.LAUNCHER`. Quest apps use
|
||||||
|
`com.oculus.intent.category.VR` instead, so Lepton logs `APP_ACTIVITY is
|
||||||
|
empty` and exits. There is no override. **Fix:** add the `LAUNCHER`
|
||||||
|
category to that intent filter and re-sign. After that, Wolvic started.
|
||||||
|
2. **OpenXR 1.1.** Wolvic's Quest build then requested OpenXR 1.1 and aborted
|
||||||
|
on `XR_ERROR_API_VERSION_UNSUPPORTED`. Unity's OpenXR plugin retries with
|
||||||
|
1.0 (Open Brush, above), so this mostly bites native and non-Unity apps. **Fix (inferred):** an API layer
|
||||||
|
inside the APK that asks the runtime for 1.0 and maps the 1.1 core
|
||||||
|
functions to the extensions the runtime does have (`XR_KHR_locate_spaces`,
|
||||||
|
`XR_EXT_local_floor`, `XR_EXT_uuid`, `XR_EXT_palm_pose`).
|
||||||
|
3. **Lepton's missing clipboard service** still applies to VR apps. The Godot
|
||||||
|
XR Tools demo's Quest build (itch.io) dies in `Godot.<init>` casting the
|
||||||
|
null clipboard service to `ClipboardManager`, before any OpenXR call. See
|
||||||
|
the clipboard table in [apks.md](apks.md).
|
||||||
|
4. **Not yet reached:** required Meta-only extensions (each app differs),
|
||||||
|
swapchain formats (the Lynx Wolvic build needed `GL_SRGB8_ALPHA8`), and
|
||||||
|
Meta platform services.
|
||||||
|
|
||||||
|
The loader was never the problem: Wolvic's Quest `libopenxr_loader.so` is a
|
||||||
|
Khronos-style loader and found SteamVR through `/vendor`.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- **Meta entitlement.** Apps that call the Oculus Platform SDK
|
||||||
|
(`libovrplatformloader.so`) to check the Quest store licence need Meta's
|
||||||
|
services. Frame Control won't work around that.
|
||||||
|
- **VrApi-era apps** (`libvrapi.so`, before OpenXR) need an API translator,
|
||||||
|
not a patch.
|
||||||
|
|
||||||
|
## Frame Control does this for you
|
||||||
|
|
||||||
|
APK uploads and `python3 ui/frame_android.py install app.apk` detect VR
|
||||||
|
manifest categories, Samsung's `vr_only` flag and the arm64 OpenXR loader.
|
||||||
|
VR apps default to immersive mode without the flatscreen marker. The upload
|
||||||
|
selector or CLI `--flat` / `--vr` overrides that choice. Compatibility notes
|
||||||
|
identify legacy VrApi, Meta platform SDK and OpenXR libraries.
|
||||||
|
|
||||||
|
Lepton only starts an `<activity>` whose MAIN intent filter has LAUNCHER; it
|
||||||
|
ignores `<activity-alias>`, which is where Godot 4 exports put LAUNCHER. When no
|
||||||
|
real activity qualifies, Frame Control adds LAUNCHER to the VR activity's MAIN
|
||||||
|
filter, or to the activity the launcher alias targets, then repacks and v2-signs
|
||||||
|
the APK locally before copying it; `meta.json` records
|
||||||
|
`"patched": ["launcher"]`. Unchanged ZIP members retain their compressed
|
||||||
|
bytes; stored libraries are aligned to 16 KiB. The RSA signing identity lives
|
||||||
|
in Frame Control's per-user app-data directory as `apk-signing-key.json`
|
||||||
|
(mode 0600). Keep this key to preserve the signer on subsequent patched
|
||||||
|
updates. A re-signed APK cannot update an installation signed by its original
|
||||||
|
publisher; Android also treats it as a different signer for signature checks.
|
||||||
|
|
||||||
|
VR apps with an arm64 OpenXR loader also get the OpenXR compatibility layer
|
||||||
|
([frame/openxr-compat](../frame/openxr-compat/README.md)): an implicit API
|
||||||
|
layer in the APK's `assets/openxr/1/api_layers/implicit.d/`, which the app's
|
||||||
|
own loader picks up next to Valve's layer. It asks SteamVR for OpenXR 1.0 when
|
||||||
|
the app wants 1.1 and enables the extensions that became 1.1 core; maps
|
||||||
|
`xrLocateSpaces` to `xrLocateSpacesKHR` and `grip_surface` to `palm_ext`; drops
|
||||||
|
1.1 controller profiles SteamVR doesn't know; stubs
|
||||||
|
`XR_KHR_android_thread_settings` and `XR_OCULUS_android_session_state_enable`;
|
||||||
|
and keeps the current refresh rate when SteamVR refuses a requested one.
|
||||||
|
`meta.json` records `"patched": ["openxr-compat"]`. Skip it with
|
||||||
|
`install … --no-xr-compat`. Its decisions go to logcat under `FrameXrCompat`.
|
||||||
|
|
||||||
|
Verified on the headset (2026-09-28):
|
||||||
|
|
||||||
|
- **Wolvic 1.9, Quest build**, installed as downloaded: Frame Control added
|
||||||
|
`LAUNCHER` and the layer. The layer turned OpenXR 1.1.48 into 1.0.63, the
|
||||||
|
instance and session were created, and a 144 Hz refresh request that SteamVR
|
||||||
|
refused was kept at the current rate. The session reached `SYNCHRONIZED`;
|
||||||
|
then Wolvic's Gecko engine crashed (null SIGSEGV on its Gecko thread, the
|
||||||
|
same crash its Lynx build has), which is Wolvic's, not OpenXR's.
|
||||||
|
- **Open Brush, Quest build**, with the layer: 1.1.54 → 1.0.63, the thread
|
||||||
|
settings stub in use, `bytedance/pico4_controller` bindings dropped, and the
|
||||||
|
session reached `FOCUSED`, the same as without the layer.
|
||||||
|
|
||||||
|
Inspect or prepare an APK without contacting the headset:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 ui/frame_android.py info app.apk
|
||||||
|
python3 ui/frame_android.py patch app.apk patched.apk
|
||||||
|
python3 ui/frame_android.py patch app.apk patched.apk --add assets/openxr/1/api_layers/implicit.d/X.json=X.json --add lib/arm64-v8a/libX.so=libX.so
|
||||||
|
```
|
||||||
|
|
||||||
|
The patch fixes Lepton's launch-category requirement. It does not supply an
|
||||||
|
OpenXR 1.1 translation layer, Meta services or a VrApi implementation.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
Copyright (c) <year> <owner>. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the library GPL. It is numbered 2 because
|
||||||
|
it goes with version 2 of the ordinary GPL.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public Licenses are intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Library General Public License, applies to some specially
|
||||||
|
designated Free Software Foundation software, and to any other libraries whose
|
||||||
|
authors decide to use it. You can use it for your libraries, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our
|
||||||
|
General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish), that you receive source code or can get it if you want it, that you
|
||||||
|
can change the software or use pieces of it in new free programs; and that
|
||||||
|
you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid anyone to
|
||||||
|
deny you these rights or to ask you to surrender the rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the library, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis or for
|
||||||
|
a fee, you must give the recipients all the rights that we gave you. You must
|
||||||
|
make sure that they, too, receive or can get the source code. If you link
|
||||||
|
a program with the library, you must provide complete object files to the
|
||||||
|
recipients so that they can relink them with the library, after making changes
|
||||||
|
to the library and recompiling it. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Our method of protecting your rights has two steps: (1) copyright the library,
|
||||||
|
and (2) offer you this license which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the library.
|
||||||
|
|
||||||
|
Also, for each distributor's protection, we want to make certain that everyone
|
||||||
|
understands that there is no warranty for this free library. If the library
|
||||||
|
is modified by someone else and passed on, we want its recipients to know
|
||||||
|
that what they have is not the original version, so that any problems introduced
|
||||||
|
by others will not reflect on the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software patents. We
|
||||||
|
wish to avoid the danger that companies distributing free software will individually
|
||||||
|
obtain patent licenses, thus in effect transforming the program into proprietary
|
||||||
|
software. To prevent this, we have made it clear that any patent must be licensed
|
||||||
|
for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||||
|
General Public License, which was designed for utility programs. This license,
|
||||||
|
the GNU Library General Public License, applies to certain designated libraries.
|
||||||
|
This license is quite different from the ordinary one; be sure to read it
|
||||||
|
in full, and don't assume that anything in it is the same as in the ordinary
|
||||||
|
license.
|
||||||
|
|
||||||
|
The reason we have a separate public license for some libraries is that they
|
||||||
|
blur the distinction we usually make between modifying or adding to a program
|
||||||
|
and simply using it. Linking a program with a library, without changing the
|
||||||
|
library, is in some sense simply using the library, and is analogous to running
|
||||||
|
a utility program or application program. However, in a textual and legal
|
||||||
|
sense, the linked executable is a combined work, a derivative of the original
|
||||||
|
library, and the ordinary General Public License treats it as such.
|
||||||
|
|
||||||
|
Because of this blurred distinction, using the ordinary General Public License
|
||||||
|
for libraries did not effectively promote software sharing, because most developers
|
||||||
|
did not use the libraries. We concluded that weaker conditions might promote
|
||||||
|
sharing better.
|
||||||
|
|
||||||
|
However, unrestricted linking of non-free programs would deprive the users
|
||||||
|
of those programs of all benefit from the free status of the libraries themselves.
|
||||||
|
This Library General Public License is intended to permit developers of non-free
|
||||||
|
programs to use free libraries, while preserving your freedom as a user of
|
||||||
|
such programs to change the free libraries that are incorporated in them.
|
||||||
|
(We have not seen how to achieve this as regards changes in header files,
|
||||||
|
but we have achieved it as regards changes in the actual functions of the
|
||||||
|
Library.) The hope is that this will lead to faster development of free libraries.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow. Pay close attention to the difference between a "work based on the
|
||||||
|
library" and a "work that uses the library". The former contains code derived
|
||||||
|
from the library, while the latter only works together with the library.
|
||||||
|
|
||||||
|
Note that it is possible for a library to be covered by the ordinary General
|
||||||
|
Public License rather than by this special one.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library which contains a
|
||||||
|
notice placed by the copyright holder or other authorized party saying it
|
||||||
|
may be distributed under the terms of this Library General Public License
|
||||||
|
(also called "this License"). Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data prepared
|
||||||
|
so as to be conveniently linked with application programs (which use some
|
||||||
|
of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work which has
|
||||||
|
been distributed under these terms. A "work based on the Library" means either
|
||||||
|
the Library or any derivative work under copyright law: that is to say, a
|
||||||
|
work containing the Library or a portion of it, either verbatim or with modifications
|
||||||
|
and/or translated straightforwardly into another language. (Hereinafter, translation
|
||||||
|
is included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for making modifications
|
||||||
|
to it. For a library, complete source code means all the source code for all
|
||||||
|
modules it contains, plus any associated interface definition files, plus
|
||||||
|
the scripts used to control compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running a program
|
||||||
|
using the Library is not restricted, and output from such a program is covered
|
||||||
|
only if its contents constitute a work based on the Library (independent of
|
||||||
|
the use of the Library in a tool for writing it). Whether that is true depends
|
||||||
|
on what the Library does and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's complete source
|
||||||
|
code as you receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice and disclaimer
|
||||||
|
of warranty; keep intact all the notices that refer to this License and to
|
||||||
|
the absence of any warranty; and distribute a copy of this License along with
|
||||||
|
the Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion of it,
|
||||||
|
thus forming a work based on the Library, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no charge to all
|
||||||
|
third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a table of
|
||||||
|
data to be supplied by an application program that uses the facility, other
|
||||||
|
than as an argument passed when the facility is invoked, then you must make
|
||||||
|
a good faith effort to ensure that, in the event an application does not supply
|
||||||
|
such function or table, the facility still operates, and performs whatever
|
||||||
|
part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has a purpose
|
||||||
|
that is entirely well-defined independent of the application. Therefore, Subsection
|
||||||
|
2d requires that any application-supplied function or table used by this function
|
||||||
|
must be optional: if the application does not supply it, the square root function
|
||||||
|
must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Library, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Library, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library with
|
||||||
|
the Library (or with a work based on the Library) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public License
|
||||||
|
instead of this License to a given copy of the Library. To do this, you must
|
||||||
|
alter all the notices that refer to this License, so that they refer to the
|
||||||
|
ordinary GNU General Public License, version 2, instead of to this License.
|
||||||
|
(If a newer version than version 2 of the ordinary GNU General Public License
|
||||||
|
has appeared, then you can specify that version instead if you wish.) Do not
|
||||||
|
make any other change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for that copy,
|
||||||
|
so the ordinary GNU General Public License applies to all subsequent copies
|
||||||
|
and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of the Library
|
||||||
|
into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or derivative of
|
||||||
|
it, under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you accompany it with the complete corresponding
|
||||||
|
machine-readable source code, which must be distributed under the terms of
|
||||||
|
Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy from a designated
|
||||||
|
place, then offering equivalent access to copy the source code from the same
|
||||||
|
place satisfies the requirement to distribute the source code, even though
|
||||||
|
third parties are not compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the Library, but
|
||||||
|
is designed to work with the Library by being compiled or linked with it,
|
||||||
|
is called a "work that uses the Library". Such a work, in isolation, is not
|
||||||
|
a derivative work of the Library, and therefore falls outside the scope of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library creates an
|
||||||
|
executable that is a derivative of the Library (because it contains portions
|
||||||
|
of the Library), rather than a "work that uses the library". The executable
|
||||||
|
is therefore covered by this License. Section 6 states terms for distribution
|
||||||
|
of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file that
|
||||||
|
is part of the Library, the object code for the work may be a derivative work
|
||||||
|
of the Library even though the source code is not. Whether this is true is
|
||||||
|
especially significant if the work can be linked without the Library, or if
|
||||||
|
the work is itself a library. The threshold for this to be true is not precisely
|
||||||
|
defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data structure layouts
|
||||||
|
and accessors, and small macros and small inline functions (ten lines or less
|
||||||
|
in length), then the use of the object file is unrestricted, regardless of
|
||||||
|
whether it is legally a derivative work. (Executables containing this object
|
||||||
|
code plus portions of the Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||||
|
the object code for the work under the terms of Section 6. Any executables
|
||||||
|
containing that work also fall under Section 6, whether or not they are linked
|
||||||
|
directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also compile or link a "work
|
||||||
|
that uses the Library" with the Library to produce a work containing portions
|
||||||
|
of the Library, and distribute that work under terms of your choice, provided
|
||||||
|
that the terms permit modification of the work for the customer's own use
|
||||||
|
and reverse engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
You must supply a copy of this License. If the work during execution displays
|
||||||
|
copyright notices, you must include the copyright notice for the Library among
|
||||||
|
them, as well as a reference directing the user to the copy of this License.
|
||||||
|
Also, you must do one of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding machine-readable source
|
||||||
|
code for the Library including whatever changes were used in the work (which
|
||||||
|
must be distributed under Sections 1 and 2 above); and, if the work is an
|
||||||
|
executable linked with the Library, with the complete machine-readable "work
|
||||||
|
that uses the Library", as object code and/or source code, so that the user
|
||||||
|
can modify the Library and then relink to produce a modified executable containing
|
||||||
|
the modified Library. (It is understood that the user who changes the contents
|
||||||
|
of definitions files in the Library will not necessarily be able to recompile
|
||||||
|
the application to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Accompany the work with a written offer, valid for at least three years,
|
||||||
|
to give the same user the materials specified in Subsection 6a, above, for
|
||||||
|
a charge no more than the cost of performing this distribution.
|
||||||
|
|
||||||
|
c) If distribution of the work is made by offering access to copy from a designated
|
||||||
|
place, offer equivalent access to copy the above specified materials from
|
||||||
|
the same place.
|
||||||
|
|
||||||
|
d) Verify that the user has already received a copy of these materials or
|
||||||
|
that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the Library" must
|
||||||
|
include any data and utility programs needed for reproducing the executable
|
||||||
|
from it. However, as a special exception, the source code distributed need
|
||||||
|
not include anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the operating
|
||||||
|
system on which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license restrictions of
|
||||||
|
other proprietary libraries that do not normally accompany the operating system.
|
||||||
|
Such a contradiction means you cannot use both them and the Library together
|
||||||
|
in an executable that you distribute.
|
||||||
|
|
||||||
|
7. 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 not covered by
|
||||||
|
this License, and distribute such a combined library, provided that the separate
|
||||||
|
distribution of the work based on the Library and of the other library facilities
|
||||||
|
is otherwise permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the
|
||||||
|
Library, uncombined with any other library facilities. This must be distributed
|
||||||
|
under the terms of the Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact that part of
|
||||||
|
it is a work based on the Library, and explaining where to find the accompanying
|
||||||
|
uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute the Library
|
||||||
|
except as expressly provided under this License. Any attempt otherwise to
|
||||||
|
copy, modify, sublicense, link with, or distribute the Library is void, and
|
||||||
|
will automatically terminate your rights under this License. However, parties
|
||||||
|
who have received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Library or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Library
|
||||||
|
(or any work based on the Library), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the Library),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute, link with or modify the Library subject to these terms
|
||||||
|
and conditions. You may not impose any further restrictions on the recipients'
|
||||||
|
exercise of the rights granted herein. You are not responsible for enforcing
|
||||||
|
compliance by third parties to this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Library at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Library
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Library under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the Library 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 specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Library does not specify a license version number, you may choose any version
|
||||||
|
ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free programs
|
||||||
|
whose distribution conditions are incompatible with these, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free Software
|
||||||
|
Foundation, write to the Free Software Foundation; we sometimes make exceptions
|
||||||
|
for this. Our decision will be guided by the two goals of preserving the free
|
||||||
|
status of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest possible
|
||||||
|
use to the public, we recommend making it free software that everyone can
|
||||||
|
redistribute and change. You can do so by permitting redistribution under
|
||||||
|
these terms (or, alternatively, under the terms of the ordinary General Public
|
||||||
|
License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively convey
|
||||||
|
the exclusion of warranty; and each file should have at least the "copyright"
|
||||||
|
line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
one line to give the library's name and an idea of what it does.
|
||||||
|
|
||||||
|
Copyright (C) year name of author
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU Library General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This library 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 Library General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public License
|
||||||
|
along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the library, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||||
|
|
||||||
|
the library `Frob' (a library for tweaking knobs) written
|
||||||
|
|
||||||
|
by James Random Hacker.
|
||||||
|
|
||||||
|
signature of Ty Coon, 1 April 1990
|
||||||
|
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
MIT License Copyright (c) <year> <copyright holders>
|
||||||
|
|
||||||
|
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 (including the next
|
||||||
|
paragraph) 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.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
|
||||||
|
|
||||||
|
Unicode Data Files include all data files under the directories http://www.unicode.org/Public/,
|
||||||
|
http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/,
|
||||||
|
http://www.unicode.org/ivd/data/, and http://www.unicode.org/utility/trac/browser/.
|
||||||
|
|
||||||
|
Unicode Data Files do not include PDF online code charts under the directory
|
||||||
|
http://www.unicode.org/Public/.
|
||||||
|
|
||||||
|
Software includes any source code published in the Unicode Standard or under
|
||||||
|
the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/,
|
||||||
|
http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/,
|
||||||
|
and http://www.unicode.org/utility/trac/browser/.
|
||||||
|
|
||||||
|
NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING,
|
||||||
|
INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"),
|
||||||
|
AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND
|
||||||
|
BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE,
|
||||||
|
DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
|
||||||
|
|
||||||
|
COPYRIGHT AND PERMISSION NOTICE Copyright © 1991-2016 Unicode, Inc. All rights
|
||||||
|
reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of the Unicode data files and any associated documentation (the "Data Files")
|
||||||
|
or Unicode software and any associated documentation (the "Software") to deal
|
||||||
|
in the Data Files or Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, and/or sell copies
|
||||||
|
of the Data Files or Software, and to permit persons to whom the Data Files
|
||||||
|
or Software are furnished to do so, provided that either
|
||||||
|
|
||||||
|
(a) this copyright and permission notice appear with all copies of the Data
|
||||||
|
Files or Software, or
|
||||||
|
|
||||||
|
(b) this copyright and permission notice appear in associated Documentation.
|
||||||
|
|
||||||
|
THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
|
||||||
|
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
|
||||||
|
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
|
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of a copyright holder shall not
|
||||||
|
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||||
|
in these Data Files or Software without prior written authorization of the
|
||||||
|
copyright holder.
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
Apache License
|
||||||
|
|
||||||
|
Version 2.0, January 2004
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
|
||||||
|
AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, and distribution
|
||||||
|
as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, "control" means (i) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity, whether
|
||||||
|
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
|
||||||
|
of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
|
||||||
|
granted by this License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical transformation
|
||||||
|
or translation of a Source form, including but not limited to compiled object
|
||||||
|
code, generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or Object form,
|
||||||
|
made available under the License, as indicated by a copyright notice that
|
||||||
|
is included in or attached to the work (an example is provided in the Appendix
|
||||||
|
below).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object form,
|
||||||
|
that is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative
|
||||||
|
Works shall not include works that remain separable from, or merely link (or
|
||||||
|
bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative
|
||||||
|
Works thereof, that is intentionally submitted to Licensor for inclusion in
|
||||||
|
the Work by the copyright owner or by an individual or Legal Entity authorized
|
||||||
|
to submit on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
"submitted" means any form of electronic, verbal, or written communication
|
||||||
|
sent to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor
|
||||||
|
for the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as "Not a Contribution."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently incorporated
|
||||||
|
within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of this
|
||||||
|
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
|
||||||
|
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
|
||||||
|
the Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of this License,
|
||||||
|
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
||||||
|
license to make, have made, use, offer to sell, sell, import, and otherwise
|
||||||
|
transfer the Work, where such license applies only to those patent claims
|
||||||
|
licensable by such Contributor that are necessarily infringed by their Contribution(s)
|
||||||
|
alone or by combination of their Contribution(s) with the Work to which such
|
||||||
|
Contribution(s) was submitted. If You institute patent litigation against
|
||||||
|
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that the Work or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses granted to You
|
||||||
|
under this License for that Work shall terminate as of the date such litigation
|
||||||
|
is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the Work or
|
||||||
|
Derivative Works thereof in any medium, with or without modifications, and
|
||||||
|
in Source or Object form, provided that You meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or Derivative Works a copy
|
||||||
|
of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices stating that
|
||||||
|
You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source
|
||||||
|
form of the Work, excluding those notices that do not pertain to any part
|
||||||
|
of the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its distribution,
|
||||||
|
then any Derivative Works that You distribute must include a readable copy
|
||||||
|
of the attribution notices contained within such NOTICE file, excluding those
|
||||||
|
notices that do not pertain to any part of the Derivative Works, in at least
|
||||||
|
one of the following places: within a NOTICE text file distributed as part
|
||||||
|
of the Derivative Works; within the Source form or documentation, if provided
|
||||||
|
along with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works
|
||||||
|
that You distribute, alongside or as an addendum to the NOTICE text from the
|
||||||
|
Work, provided that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction,
|
||||||
|
or distribution of Your modifications, or for any such Derivative Works as
|
||||||
|
a whole, provided Your use, reproduction, and distribution of the Work otherwise
|
||||||
|
complies with the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, any
|
||||||
|
Contribution intentionally submitted for inclusion in the Work by You to the
|
||||||
|
Licensor shall be under the terms and conditions of this License, without
|
||||||
|
any additional terms or conditions. Notwithstanding the above, nothing herein
|
||||||
|
shall supersede or modify the terms of any separate license agreement you
|
||||||
|
may have executed with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade names,
|
||||||
|
trademarks, service marks, or product names of the Licensor, except as required
|
||||||
|
for reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
|
||||||
|
in writing, Licensor provides the Work (and each Contributor provides its
|
||||||
|
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied, including, without limitation, any warranties
|
||||||
|
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
|
||||||
|
of using or redistributing the Work and assume any risks associated with Your
|
||||||
|
exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory, whether
|
||||||
|
in tort (including negligence), contract, or otherwise, unless required by
|
||||||
|
applicable law (such as deliberate and grossly negligent acts) or agreed to
|
||||||
|
in writing, shall any Contributor be liable to You for damages, including
|
||||||
|
any direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character arising as a result of this License or out of the use or inability
|
||||||
|
to use the Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all other commercial
|
||||||
|
damages or losses), even if such Contributor has been advised of the possibility
|
||||||
|
of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing the Work
|
||||||
|
or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
||||||
|
acceptance of support, warranty, indemnity, or other liability obligations
|
||||||
|
and/or rights consistent with this License. However, in accepting such obligations,
|
||||||
|
You may act only on Your own behalf and on Your sole responsibility, not on
|
||||||
|
behalf of any other Contributor, and only if You agree to indemnify, defend,
|
||||||
|
and hold each Contributor harmless for any liability incurred by, or claims
|
||||||
|
asserted against, such Contributor by reason of your accepting any such warranty
|
||||||
|
or additional liability. END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
|
||||||
|
information. (Don't include the brackets!) The text should be enclosed in
|
||||||
|
the appropriate comment syntax for the file format. We also recommend that
|
||||||
|
a file or class name and description of purpose be included on the same "printed
|
||||||
|
page" as the copyright notice for easier identification within third-party
|
||||||
|
archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
http://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.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
Copyright (c) <year> <owner>. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public License is intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users. This General Public License applies to
|
||||||
|
most of the Free Software Foundation's software and to any other program whose
|
||||||
|
authors commit to using it. (Some other Free Software Foundation software
|
||||||
|
is covered by the GNU Lesser General Public License instead.) You can apply
|
||||||
|
it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our
|
||||||
|
General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish), that you receive source code or can get it if you want it, that you
|
||||||
|
can change the software or use pieces of it in new free programs; and that
|
||||||
|
you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid anyone to
|
||||||
|
deny you these rights or to ask you to surrender the rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or
|
||||||
|
for a fee, you must give the recipients all the rights that you have. You
|
||||||
|
must make sure that they, too, receive or can get the source code. And you
|
||||||
|
must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and (2)
|
||||||
|
offer you this license which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain that
|
||||||
|
everyone understands that there is no warranty for this free software. If
|
||||||
|
the software is modified by someone else and passed on, we want its recipients
|
||||||
|
to know that what they have is not the original, so that any problems introduced
|
||||||
|
by others will not reflect on the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software patents. We
|
||||||
|
wish to avoid the danger that redistributors of a free program will individually
|
||||||
|
obtain patent licenses, in effect making the program proprietary. To prevent
|
||||||
|
this, we have made it clear that any patent must be licensed for everyone's
|
||||||
|
free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains a notice
|
||||||
|
placed by the copyright holder saying it may be distributed under the terms
|
||||||
|
of this General Public License. The "Program", below, refers to any such program
|
||||||
|
or work, and a "work based on the Program" means either the Program or any
|
||||||
|
derivative work under copyright law: that is to say, a work containing the
|
||||||
|
Program or a portion of it, either verbatim or with modifications and/or translated
|
||||||
|
into another language. (Hereinafter, translation is included without limitation
|
||||||
|
in the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running the Program
|
||||||
|
is not restricted, and the output from the Program is covered only if its
|
||||||
|
contents constitute a work based on the Program (independent of having been
|
||||||
|
made by running the Program). Whether that is true depends on what the Program
|
||||||
|
does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's source code
|
||||||
|
as you receive it, in any medium, provided that you conspicuously and appropriately
|
||||||
|
publish on each copy an appropriate copyright notice and disclaimer of warranty;
|
||||||
|
keep intact all the notices that refer to this License and to the absence
|
||||||
|
of any warranty; and give any other recipients of the Program a copy of this
|
||||||
|
License along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion of it,
|
||||||
|
thus forming a work based on the Program, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in whole or
|
||||||
|
in part contains or is derived from the Program or any part thereof, to be
|
||||||
|
licensed as a whole at no charge to all third parties under the terms of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively when run,
|
||||||
|
you must cause it, when started running for such interactive use in the most
|
||||||
|
ordinary way, to print or display an announcement including an appropriate
|
||||||
|
copyright notice and a notice that there is no warranty (or else, saying that
|
||||||
|
you provide a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this License.
|
||||||
|
(Exception: if the Program itself is interactive but does not normally print
|
||||||
|
such an announcement, your work based on the Program is not required to print
|
||||||
|
an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Program, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Program, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program with
|
||||||
|
the Program (or with a work based on the Program) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it, under Section
|
||||||
|
2) in object code or executable form under the terms of Sections 1 and 2 above
|
||||||
|
provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable source code,
|
||||||
|
which must be distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three years, to give
|
||||||
|
any third party, for a charge no more than your cost of physically performing
|
||||||
|
source distribution, a complete machine-readable copy of the corresponding
|
||||||
|
source code, to be distributed under the terms of Sections 1 and 2 above on
|
||||||
|
a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer to distribute
|
||||||
|
corresponding source code. (This alternative is allowed only for noncommercial
|
||||||
|
distribution and only if you received the program in object code or executable
|
||||||
|
form with such an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for making
|
||||||
|
modifications to it. For an executable work, complete source code means all
|
||||||
|
the source code for all modules it contains, plus any associated interface
|
||||||
|
definition files, plus the scripts used to control compilation and installation
|
||||||
|
of the executable. However, as a special exception, the source code distributed
|
||||||
|
need not include anything that is normally distributed (in either source or
|
||||||
|
binary form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component itself
|
||||||
|
accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering access to
|
||||||
|
copy from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place counts as distribution of the source code,
|
||||||
|
even though third parties are not compelled to copy the source along with
|
||||||
|
the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program except
|
||||||
|
as expressly provided under this License. Any attempt otherwise to copy, modify,
|
||||||
|
sublicense or distribute the Program is void, and will automatically terminate
|
||||||
|
your rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses terminated
|
||||||
|
so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Program or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Program
|
||||||
|
(or any work based on the Program), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the Program),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute or modify the Program subject to these terms and conditions.
|
||||||
|
You may not impose any further restrictions on the recipients' exercise of
|
||||||
|
the rights granted herein. You are not responsible for enforcing compliance
|
||||||
|
by third parties to this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Program at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Program
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply and
|
||||||
|
the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system, which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Program under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the 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 Program specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Program does not specify a version number of this License, you may choose
|
||||||
|
any version ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free programs
|
||||||
|
whose distribution conditions are different, write to the author to ask for
|
||||||
|
permission. For software which is copyrighted by the Free Software Foundation,
|
||||||
|
write to the Free Software Foundation; we sometimes make exceptions for this.
|
||||||
|
Our decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing and reuse
|
||||||
|
of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest possible
|
||||||
|
use to the public, the best way to achieve this is to make it free software
|
||||||
|
which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest to attach
|
||||||
|
them to the start of each source file to most effectively convey the exclusion
|
||||||
|
of warranty; and each file should have at least the "copyright" line and a
|
||||||
|
pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and an idea of what it does.>
|
||||||
|
|
||||||
|
Copyright (C)< yyyy> <name of author>
|
||||||
|
|
||||||
|
This program 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 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program 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
|
||||||
|
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||||
|
Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this when
|
||||||
|
it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
|
||||||
|
with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software,
|
||||||
|
and you are welcome to redistribute it under certain conditions; type `show
|
||||||
|
c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may be
|
||||||
|
called something other than `show w' and `show c'; they could even be mouse-clicks
|
||||||
|
or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision'
|
||||||
|
(which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon >, 1 April 1989 Ty Coon, President of Vice This General
|
||||||
|
Public License does not permit incorporating your program into proprietary
|
||||||
|
programs. If your program is a subroutine library, you may consider it more
|
||||||
|
useful to permit linking proprietary applications with the library. If this
|
||||||
|
is what you want to do, use the GNU Lesser General Public License instead
|
||||||
|
of this License.
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public License is intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users. This General Public License applies to
|
||||||
|
most of the Free Software Foundation's software and to any other program whose
|
||||||
|
authors commit to using it. (Some other Free Software Foundation software
|
||||||
|
is covered by the GNU Lesser General Public License instead.) You can apply
|
||||||
|
it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our
|
||||||
|
General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish), that you receive source code or can get it if you want it, that you
|
||||||
|
can change the software or use pieces of it in new free programs; and that
|
||||||
|
you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid anyone to
|
||||||
|
deny you these rights or to ask you to surrender the rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or
|
||||||
|
for a fee, you must give the recipients all the rights that you have. You
|
||||||
|
must make sure that they, too, receive or can get the source code. And you
|
||||||
|
must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and (2)
|
||||||
|
offer you this license which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain that
|
||||||
|
everyone understands that there is no warranty for this free software. If
|
||||||
|
the software is modified by someone else and passed on, we want its recipients
|
||||||
|
to know that what they have is not the original, so that any problems introduced
|
||||||
|
by others will not reflect on the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software patents. We
|
||||||
|
wish to avoid the danger that redistributors of a free program will individually
|
||||||
|
obtain patent licenses, in effect making the program proprietary. To prevent
|
||||||
|
this, we have made it clear that any patent must be licensed for everyone's
|
||||||
|
free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains a notice
|
||||||
|
placed by the copyright holder saying it may be distributed under the terms
|
||||||
|
of this General Public License. The "Program", below, refers to any such program
|
||||||
|
or work, and a "work based on the Program" means either the Program or any
|
||||||
|
derivative work under copyright law: that is to say, a work containing the
|
||||||
|
Program or a portion of it, either verbatim or with modifications and/or translated
|
||||||
|
into another language. (Hereinafter, translation is included without limitation
|
||||||
|
in the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running the Program
|
||||||
|
is not restricted, and the output from the Program is covered only if its
|
||||||
|
contents constitute a work based on the Program (independent of having been
|
||||||
|
made by running the Program). Whether that is true depends on what the Program
|
||||||
|
does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's source code
|
||||||
|
as you receive it, in any medium, provided that you conspicuously and appropriately
|
||||||
|
publish on each copy an appropriate copyright notice and disclaimer of warranty;
|
||||||
|
keep intact all the notices that refer to this License and to the absence
|
||||||
|
of any warranty; and give any other recipients of the Program a copy of this
|
||||||
|
License along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion of it,
|
||||||
|
thus forming a work based on the Program, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in whole or
|
||||||
|
in part contains or is derived from the Program or any part thereof, to be
|
||||||
|
licensed as a whole at no charge to all third parties under the terms of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively when run,
|
||||||
|
you must cause it, when started running for such interactive use in the most
|
||||||
|
ordinary way, to print or display an announcement including an appropriate
|
||||||
|
copyright notice and a notice that there is no warranty (or else, saying that
|
||||||
|
you provide a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this License.
|
||||||
|
(Exception: if the Program itself is interactive but does not normally print
|
||||||
|
such an announcement, your work based on the Program is not required to print
|
||||||
|
an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Program, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Program, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program with
|
||||||
|
the Program (or with a work based on the Program) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it, under Section
|
||||||
|
2) in object code or executable form under the terms of Sections 1 and 2 above
|
||||||
|
provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable source code,
|
||||||
|
which must be distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three years, to give
|
||||||
|
any third party, for a charge no more than your cost of physically performing
|
||||||
|
source distribution, a complete machine-readable copy of the corresponding
|
||||||
|
source code, to be distributed under the terms of Sections 1 and 2 above on
|
||||||
|
a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer to distribute
|
||||||
|
corresponding source code. (This alternative is allowed only for noncommercial
|
||||||
|
distribution and only if you received the program in object code or executable
|
||||||
|
form with such an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for making
|
||||||
|
modifications to it. For an executable work, complete source code means all
|
||||||
|
the source code for all modules it contains, plus any associated interface
|
||||||
|
definition files, plus the scripts used to control compilation and installation
|
||||||
|
of the executable. However, as a special exception, the source code distributed
|
||||||
|
need not include anything that is normally distributed (in either source or
|
||||||
|
binary form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component itself
|
||||||
|
accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering access to
|
||||||
|
copy from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place counts as distribution of the source code,
|
||||||
|
even though third parties are not compelled to copy the source along with
|
||||||
|
the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program except
|
||||||
|
as expressly provided under this License. Any attempt otherwise to copy, modify,
|
||||||
|
sublicense or distribute the Program is void, and will automatically terminate
|
||||||
|
your rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses terminated
|
||||||
|
so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Program or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Program
|
||||||
|
(or any work based on the Program), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the Program),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute or modify the Program subject to these terms and conditions.
|
||||||
|
You may not impose any further restrictions on the recipients' exercise of
|
||||||
|
the rights granted herein. You are not responsible for enforcing compliance
|
||||||
|
by third parties to this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Program at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Program
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply and
|
||||||
|
the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system, which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Program under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the 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 Program specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Program does not specify a version number of this License, you may choose
|
||||||
|
any version ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free programs
|
||||||
|
whose distribution conditions are different, write to the author to ask for
|
||||||
|
permission. For software which is copyrighted by the Free Software Foundation,
|
||||||
|
write to the Free Software Foundation; we sometimes make exceptions for this.
|
||||||
|
Our decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing and reuse
|
||||||
|
of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest possible
|
||||||
|
use to the public, the best way to achieve this is to make it free software
|
||||||
|
which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest to attach
|
||||||
|
them to the start of each source file to most effectively convey the exclusion
|
||||||
|
of warranty; and each file should have at least the "copyright" line and a
|
||||||
|
pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and an idea of what it does.>
|
||||||
|
|
||||||
|
Copyright (C) <yyyy> <name of author>
|
||||||
|
|
||||||
|
This program 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 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program 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
|
||||||
|
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||||
|
Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this when
|
||||||
|
it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
|
||||||
|
with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software,
|
||||||
|
and you are welcome to redistribute it under certain conditions; type `show
|
||||||
|
c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may be
|
||||||
|
called something other than `show w' and `show c'; they could even be mouse-clicks
|
||||||
|
or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision'
|
||||||
|
(which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General
|
||||||
|
Public License does not permit incorporating your program into proprietary
|
||||||
|
programs. If your program is a subroutine library, you may consider it more
|
||||||
|
useful to permit linking proprietary applications with the library. If this
|
||||||
|
is what you want to do, use the GNU Lesser General Public License instead
|
||||||
|
of this License.
|
||||||
@@ -0,0 +1,625 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for software and
|
||||||
|
other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed to take
|
||||||
|
away your freedom to share and change the works. By contrast, the GNU General
|
||||||
|
Public License is intended to guarantee your freedom to share and change all
|
||||||
|
versions of a program--to make sure it remains free software for all its users.
|
||||||
|
We, the Free Software Foundation, use the GNU General Public License for most
|
||||||
|
of our software; it applies also to any other work released this way by its
|
||||||
|
authors. You can apply it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our
|
||||||
|
General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for them if you wish), that
|
||||||
|
you receive source code or can get it if you want it, that you can change
|
||||||
|
the software or use pieces of it in new free programs, and that you know you
|
||||||
|
can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you these rights
|
||||||
|
or asking you to surrender the rights. Therefore, you have certain responsibilities
|
||||||
|
if you distribute copies of the software, or if you modify it: responsibilities
|
||||||
|
to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or
|
||||||
|
for a fee, you must pass on to the recipients the same freedoms that you received.
|
||||||
|
You must make sure that they, too, receive or can get the source code. And
|
||||||
|
you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps: (1) assert
|
||||||
|
copyright on the software, and (2) offer you this License giving you legal
|
||||||
|
permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains that
|
||||||
|
there is no warranty for this free software. For both users' and authors'
|
||||||
|
sake, the GPL requires that modified versions be marked as changed, so that
|
||||||
|
their problems will not be attributed erroneously to authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run modified
|
||||||
|
versions of the software inside them, although the manufacturer can do so.
|
||||||
|
This is fundamentally incompatible with the aim of protecting users' freedom
|
||||||
|
to change the software. The systematic pattern of such abuse occurs in the
|
||||||
|
area of products for individuals to use, which is precisely where it is most
|
||||||
|
unacceptable. Therefore, we have designed this version of the GPL to prohibit
|
||||||
|
the practice for those products. If such problems arise substantially in other
|
||||||
|
domains, we stand ready to extend this provision to those domains in future
|
||||||
|
versions of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents. States
|
||||||
|
should not allow patents to restrict development and use of software on general-purpose
|
||||||
|
computers, but in those that do, we wish to avoid the special danger that
|
||||||
|
patents applied to a free program could make it effectively proprietary. To
|
||||||
|
prevent this, the GPL assures that patents cannot be used to render the program
|
||||||
|
non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of works,
|
||||||
|
such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this License.
|
||||||
|
Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals
|
||||||
|
or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work in
|
||||||
|
a fashion requiring copyright permission, other than the making of an exact
|
||||||
|
copy. The resulting work is called a "modified version" of the earlier work
|
||||||
|
or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based on the
|
||||||
|
Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without permission,
|
||||||
|
would make you directly or secondarily liable for infringement under applicable
|
||||||
|
copyright law, except executing it on a computer or modifying a private copy.
|
||||||
|
Propagation includes copying, distribution (with or without modification),
|
||||||
|
making available to the public, and in some countries other activities as
|
||||||
|
well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other parties
|
||||||
|
to make or receive copies. Mere interaction with a user through a computer
|
||||||
|
network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices" to the
|
||||||
|
extent that it includes a convenient and prominently visible feature that
|
||||||
|
(1) displays an appropriate copyright notice, and (2) tells the user that
|
||||||
|
there is no warranty for the work (except to the extent that warranties are
|
||||||
|
provided), that licensees may convey the work under this License, and how
|
||||||
|
to view a copy of this License. If the interface presents a list of user commands
|
||||||
|
or options, such as a menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work for making
|
||||||
|
modifications to it. "Object code" means any non-source form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official standard
|
||||||
|
defined by a recognized standards body, or, in the case of interfaces specified
|
||||||
|
for a particular programming language, one that is widely used among developers
|
||||||
|
working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other than
|
||||||
|
the work as a whole, that (a) is included in the normal form of packaging
|
||||||
|
a Major Component, but which is not part of that Major Component, and (b)
|
||||||
|
serves only to enable use of the work with that Major Component, or to implement
|
||||||
|
a Standard Interface for which an implementation is available to the public
|
||||||
|
in source code form. A "Major Component", in this context, means a major essential
|
||||||
|
component (kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to produce
|
||||||
|
the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all the source
|
||||||
|
code needed to generate, install, and (for an executable work) run the object
|
||||||
|
code and to modify the work, including scripts to control those activities.
|
||||||
|
However, it does not include the work's System Libraries, or general-purpose
|
||||||
|
tools or generally available free programs which are used unmodified in performing
|
||||||
|
those activities but which are not part of the work. For example, Corresponding
|
||||||
|
Source includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically linked
|
||||||
|
subprograms that the work is specifically designed to require, such as by
|
||||||
|
intimate data communication or control flow between those subprograms and
|
||||||
|
other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users can regenerate
|
||||||
|
automatically from other parts of the Corresponding Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of copyright
|
||||||
|
on the Program, and are irrevocable provided the stated conditions are met.
|
||||||
|
This License explicitly affirms your unlimited permission to run the unmodified
|
||||||
|
Program. The output from running a covered work is covered by this License
|
||||||
|
only if the output, given its content, constitutes a covered work. This License
|
||||||
|
acknowledges your rights of fair use or other equivalent, as provided by copyright
|
||||||
|
law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not convey, without
|
||||||
|
conditions so long as your license otherwise remains in force. You may convey
|
||||||
|
covered works to others for the sole purpose of having them make modifications
|
||||||
|
exclusively for you, or provide you with facilities for running those works,
|
||||||
|
provided that you comply with the terms of this License in conveying all material
|
||||||
|
for which you do not control copyright. Those thus making or running the covered
|
||||||
|
works for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of your copyrighted
|
||||||
|
material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under the conditions
|
||||||
|
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological measure
|
||||||
|
under any applicable law fulfilling obligations under article 11 of the WIPO
|
||||||
|
copyright treaty adopted on 20 December 1996, or similar laws prohibiting
|
||||||
|
or restricting circumvention of such measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid circumvention
|
||||||
|
of technological measures to the extent such circumvention is effected by
|
||||||
|
exercising rights under this License with respect to the covered work, and
|
||||||
|
you disclaim any intention to limit operation or modification of the work
|
||||||
|
as a means of enforcing, against the work's users, your or third parties'
|
||||||
|
legal rights to forbid circumvention of technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you receive
|
||||||
|
it, in any medium, provided that you conspicuously and appropriately publish
|
||||||
|
on each copy an appropriate copyright notice; keep intact all notices stating
|
||||||
|
that this License and any non-permissive terms added in accord with section
|
||||||
|
7 apply to the code; keep intact all notices of the absence of any warranty;
|
||||||
|
and give all recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey, and you
|
||||||
|
may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to produce
|
||||||
|
it from the Program, in the form of source code under the terms of section
|
||||||
|
4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified it, and
|
||||||
|
giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is released under
|
||||||
|
this License and any conditions added under section 7. This requirement modifies
|
||||||
|
the requirement in section 4 to "keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this License to anyone
|
||||||
|
who comes into possession of a copy. This License will therefore apply, along
|
||||||
|
with any applicable section 7 additional terms, to the whole of the work,
|
||||||
|
and all its parts, regardless of how they are packaged. This License gives
|
||||||
|
no permission to license the work in any other way, but it does not invalidate
|
||||||
|
such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display Appropriate
|
||||||
|
Legal Notices; however, if the Program has interactive interfaces that do
|
||||||
|
not display Appropriate Legal Notices, your work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent works,
|
||||||
|
which are not by their nature extensions of the covered work, and which are
|
||||||
|
not combined with it such as to form a larger program, in or on a volume of
|
||||||
|
a storage or distribution medium, is called an "aggregate" if the compilation
|
||||||
|
and its resulting copyright are not used to limit the access or legal rights
|
||||||
|
of the compilation's users beyond what the individual works permit. Inclusion
|
||||||
|
of a covered work in an aggregate does not cause this License to apply to
|
||||||
|
the other parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms of sections
|
||||||
|
4 and 5, provided that you also convey the machine-readable Corresponding
|
||||||
|
Source under the terms of this License, in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product (including
|
||||||
|
a physical distribution medium), accompanied by the Corresponding Source fixed
|
||||||
|
on a durable physical medium customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product (including
|
||||||
|
a physical distribution medium), accompanied by a written offer, valid for
|
||||||
|
at least three years and valid for as long as you offer spare parts or customer
|
||||||
|
support for that product model, to give anyone who possesses the object code
|
||||||
|
either (1) a copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical medium customarily
|
||||||
|
used for software interchange, for a price no more than your reasonable cost
|
||||||
|
of physically performing this conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the written
|
||||||
|
offer to provide the Corresponding Source. This alternative is allowed only
|
||||||
|
occasionally and noncommercially, and only if you received the object code
|
||||||
|
with such an offer, in accord with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated place (gratis
|
||||||
|
or for a charge), and offer equivalent access to the Corresponding Source
|
||||||
|
in the same way through the same place at no further charge. You need not
|
||||||
|
require recipients to copy the Corresponding Source along with the object
|
||||||
|
code. If the place to copy the object code is a network server, the Corresponding
|
||||||
|
Source may be on a different server (operated by you or a third party) that
|
||||||
|
supports equivalent copying facilities, provided you maintain clear directions
|
||||||
|
next to the object code saying where to find the Corresponding Source. Regardless
|
||||||
|
of what server hosts the Corresponding Source, you remain obligated to ensure
|
||||||
|
that it is available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided you inform
|
||||||
|
other peers where the object code and Corresponding Source of the work are
|
||||||
|
being offered to the general public at no charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded from
|
||||||
|
the Corresponding Source as a System Library, need not be included in conveying
|
||||||
|
the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any tangible
|
||||||
|
personal property which is normally used for personal, family, or household
|
||||||
|
purposes, or (2) anything designed or sold for incorporation into a dwelling.
|
||||||
|
In determining whether a product is a consumer product, doubtful cases shall
|
||||||
|
be resolved in favor of coverage. For a particular product received by a particular
|
||||||
|
user, "normally used" refers to a typical or common use of that class of product,
|
||||||
|
regardless of the status of the particular user or of the way in which the
|
||||||
|
particular user actually uses, or expects or is expected to use, the product.
|
||||||
|
A product is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent the
|
||||||
|
only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods, procedures,
|
||||||
|
authorization keys, or other information required to install and execute modified
|
||||||
|
versions of a covered work in that User Product from a modified version of
|
||||||
|
its Corresponding Source. The information must suffice to ensure that the
|
||||||
|
continued functioning of the modified object code is in no case prevented
|
||||||
|
or interfered with solely because modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or specifically
|
||||||
|
for use in, a User Product, and the conveying occurs as part of a transaction
|
||||||
|
in which the right of possession and use of the User Product is transferred
|
||||||
|
to the recipient in perpetuity or for a fixed term (regardless of how the
|
||||||
|
transaction is characterized), the Corresponding Source conveyed under this
|
||||||
|
section must be accompanied by the Installation Information. But this requirement
|
||||||
|
does not apply if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has been installed
|
||||||
|
in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a requirement
|
||||||
|
to continue to provide support service, warranty, or updates for a work that
|
||||||
|
has been modified or installed by the recipient, or for the User Product in
|
||||||
|
which it has been modified or installed. Access to a network may be denied
|
||||||
|
when the modification itself materially and adversely affects the operation
|
||||||
|
of the network or violates the rules and protocols for communication across
|
||||||
|
the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided, in accord
|
||||||
|
with this section must be in a format that is publicly documented (and with
|
||||||
|
an implementation available to the public in source code form), and must require
|
||||||
|
no special password or key for unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this License
|
||||||
|
by making exceptions from one or more of its conditions. Additional permissions
|
||||||
|
that are applicable to the entire Program shall be treated as though they
|
||||||
|
were included in this License, to the extent that they are valid under applicable
|
||||||
|
law. If additional permissions apply only to part of the Program, that part
|
||||||
|
may be used separately under those permissions, but the entire Program remains
|
||||||
|
governed by this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option remove any
|
||||||
|
additional permissions from that copy, or from any part of it. (Additional
|
||||||
|
permissions may be written to require their own removal in certain cases when
|
||||||
|
you modify the work.) You may place additional permissions on material, added
|
||||||
|
by you to a covered work, for which you have or can give appropriate copyright
|
||||||
|
permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you add
|
||||||
|
to a covered work, you may (if authorized by the copyright holders of that
|
||||||
|
material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the terms of
|
||||||
|
sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or author
|
||||||
|
attributions in that material or in the Appropriate Legal Notices displayed
|
||||||
|
by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or requiring
|
||||||
|
that modified versions of such material be marked in reasonable ways as different
|
||||||
|
from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or authors
|
||||||
|
of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some trade names,
|
||||||
|
trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that material by
|
||||||
|
anyone who conveys the material (or modified versions of it) with contractual
|
||||||
|
assumptions of liability to the recipient, for any liability that these contractual
|
||||||
|
assumptions directly impose on those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further restrictions"
|
||||||
|
within the meaning of section 10. If the Program as you received it, or any
|
||||||
|
part of it, contains a notice stating that it is governed by this License
|
||||||
|
along with a term that is a further restriction, you may remove that term.
|
||||||
|
If a license document contains a further restriction but permits relicensing
|
||||||
|
or conveying under this License, you may add to a covered work material governed
|
||||||
|
by the terms of that license document, provided that the further restriction
|
||||||
|
does not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you must place,
|
||||||
|
in the relevant source files, a statement of the additional terms that apply
|
||||||
|
to those files, or a notice indicating where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the form
|
||||||
|
of a separately written license, or stated as exceptions; the above requirements
|
||||||
|
apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly provided
|
||||||
|
under this License. Any attempt otherwise to propagate or modify it is void,
|
||||||
|
and will automatically terminate your rights under this License (including
|
||||||
|
any patent licenses granted under the third paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your license from
|
||||||
|
a particular copyright holder is reinstated (a) provisionally, unless and
|
||||||
|
until the copyright holder explicitly and finally terminates your license,
|
||||||
|
and (b) permanently, if the copyright holder fails to notify you of the violation
|
||||||
|
by some reasonable means prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is reinstated permanently
|
||||||
|
if the copyright holder notifies you of the violation by some reasonable means,
|
||||||
|
this is the first time you have received notice of violation of this License
|
||||||
|
(for any work) from that copyright holder, and you cure the violation prior
|
||||||
|
to 30 days after your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the licenses
|
||||||
|
of parties who have received copies or rights from you under this License.
|
||||||
|
If your rights have been terminated and not permanently reinstated, you do
|
||||||
|
not qualify to receive new licenses for the same material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or run a copy
|
||||||
|
of the Program. Ancillary propagation of a covered work occurring solely as
|
||||||
|
a consequence of using peer-to-peer transmission to receive a copy likewise
|
||||||
|
does not require acceptance. However, nothing other than this License grants
|
||||||
|
you permission to propagate or modify any covered work. These actions infringe
|
||||||
|
copyright if you do not accept this License. Therefore, by modifying or propagating
|
||||||
|
a covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically receives
|
||||||
|
a license from the original licensors, to run, modify and propagate that work,
|
||||||
|
subject to this License. You are not responsible for enforcing compliance
|
||||||
|
by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an organization,
|
||||||
|
or substantially all assets of one, or subdividing an organization, or merging
|
||||||
|
organizations. If propagation of a covered work results from an entity transaction,
|
||||||
|
each party to that transaction who receives a copy of the work also receives
|
||||||
|
whatever licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the Corresponding
|
||||||
|
Source of the work from the predecessor in interest, if the predecessor has
|
||||||
|
it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the rights
|
||||||
|
granted or affirmed under this License. For example, you may not impose a
|
||||||
|
license fee, royalty, or other charge for exercise of rights granted under
|
||||||
|
this License, and you may not initiate litigation (including a cross-claim
|
||||||
|
or counterclaim in a lawsuit) alleging that any patent claim is infringed
|
||||||
|
by making, using, selling, offering for sale, or importing the Program or
|
||||||
|
any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this License
|
||||||
|
of the Program or a work on which the Program is based. The work thus licensed
|
||||||
|
is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims owned or controlled
|
||||||
|
by the contributor, whether already acquired or hereafter acquired, that would
|
||||||
|
be infringed by some manner, permitted by this License, of making, using,
|
||||||
|
or selling its contributor version, but do not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of the contributor
|
||||||
|
version. For purposes of this definition, "control" includes the right to
|
||||||
|
grant patent sublicenses in a manner consistent with the requirements of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free patent
|
||||||
|
license under the contributor's essential patent claims, to make, use, sell,
|
||||||
|
offer for sale, import and otherwise run, modify and propagate the contents
|
||||||
|
of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express agreement
|
||||||
|
or commitment, however denominated, not to enforce a patent (such as an express
|
||||||
|
permission to practice a patent or covenant not to sue for patent infringement).
|
||||||
|
To "grant" such a patent license to a party means to make such an agreement
|
||||||
|
or commitment not to enforce a patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license, and the
|
||||||
|
Corresponding Source of the work is not available for anyone to copy, free
|
||||||
|
of charge and under the terms of this License, through a publicly available
|
||||||
|
network server or other readily accessible means, then you must either (1)
|
||||||
|
cause the Corresponding Source to be so available, or (2) arrange to deprive
|
||||||
|
yourself of the benefit of the patent license for this particular work, or
|
||||||
|
(3) arrange, in a manner consistent with the requirements of this License,
|
||||||
|
to extend the patent license to downstream recipients. "Knowingly relying"
|
||||||
|
means you have actual knowledge that, but for the patent license, your conveying
|
||||||
|
the covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that country
|
||||||
|
that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or arrangement,
|
||||||
|
you convey, or propagate by procuring conveyance of, a covered work, and grant
|
||||||
|
a patent license to some of the parties receiving the covered work authorizing
|
||||||
|
them to use, propagate, modify or convey a specific copy of the covered work,
|
||||||
|
then the patent license you grant is automatically extended to all recipients
|
||||||
|
of the covered work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within the scope
|
||||||
|
of its coverage, prohibits the exercise of, or is conditioned on the non-exercise
|
||||||
|
of one or more of the rights that are specifically granted under this License.
|
||||||
|
You may not convey a covered work if you are a party to an arrangement with
|
||||||
|
a third party that is in the business of distributing software, under which
|
||||||
|
you make payment to the third party based on the extent of your activity of
|
||||||
|
conveying the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory patent
|
||||||
|
license (a) in connection with copies of the covered work conveyed by you
|
||||||
|
(or copies made from those copies), or (b) primarily for and in connection
|
||||||
|
with specific products or compilations that contain the covered work, unless
|
||||||
|
you entered into that arrangement, or that patent license was granted, prior
|
||||||
|
to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting any implied
|
||||||
|
license or other defenses to infringement that may otherwise be available
|
||||||
|
to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or otherwise)
|
||||||
|
that contradict the conditions of this License, they do not excuse you from
|
||||||
|
the conditions of this License. If you cannot convey a covered work so as
|
||||||
|
to satisfy simultaneously your obligations under this License and any other
|
||||||
|
pertinent obligations, then as a consequence you may not convey it at all.
|
||||||
|
For example, if you agree to terms that obligate you to collect a royalty
|
||||||
|
for further conveying from those to whom you convey the Program, the only
|
||||||
|
way you could satisfy both those terms and this License would be to refrain
|
||||||
|
entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have permission to
|
||||||
|
link or combine any covered work with a work licensed under version 3 of the
|
||||||
|
GNU Affero General Public License into a single combined work, and to convey
|
||||||
|
the resulting work. The terms of this License will continue to apply to the
|
||||||
|
part which is the covered work, but the special requirements of the GNU Affero
|
||||||
|
General Public License, section 13, concerning interaction through a network
|
||||||
|
will apply to the combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of the
|
||||||
|
GNU 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 Program specifies
|
||||||
|
that a certain numbered version of the GNU General Public License "or any
|
||||||
|
later version" applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that numbered version or of any later version published
|
||||||
|
by the Free Software Foundation. If the Program does not specify a version
|
||||||
|
number of the GNU General Public License, you may choose any version ever
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future versions of
|
||||||
|
the GNU General Public License can be used, that proxy's public statement
|
||||||
|
of acceptance of a version permanently authorizes you to choose that version
|
||||||
|
for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different permissions. However,
|
||||||
|
no additional obligations are imposed on any author or copyright holder as
|
||||||
|
a result of your choosing to follow a later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
|
||||||
|
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
|
||||||
|
PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
|
||||||
|
CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
|
||||||
|
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM
|
||||||
|
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||||
|
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO
|
||||||
|
USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
|
||||||
|
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
|
||||||
|
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
|
||||||
|
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided above cannot
|
||||||
|
be given local legal effect according to their terms, reviewing courts shall
|
||||||
|
apply local law that most closely approximates an absolute waiver of all civil
|
||||||
|
liability in connection with the Program, unless a warranty or assumption
|
||||||
|
of liability accompanies a copy of the Program in return for a fee. END OF
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest possible
|
||||||
|
use to the public, the best way to achieve this is to make it free software
|
||||||
|
which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest to attach
|
||||||
|
them to the start of each source file to most effectively state the exclusion
|
||||||
|
of warranty; and each file should have at least the "copyright" line and a
|
||||||
|
pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program 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.
|
||||||
|
|
||||||
|
This program 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
|
||||||
|
this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short notice like
|
||||||
|
this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
|
||||||
|
This is free software, and you are welcome to redistribute it under certain
|
||||||
|
conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands might
|
||||||
|
be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary. For
|
||||||
|
more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General Public
|
||||||
|
License instead of this License. But first, please read <https://www.gnu.org/
|
||||||
|
licenses /why-not-lgpl.html>.
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
“This License” refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
|
||||||
|
|
||||||
|
“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.
|
||||||
|
|
||||||
|
To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
|
||||||
|
|
||||||
|
A “covered work” means either the unmodified Program or a work based on the Program.
|
||||||
|
|
||||||
|
To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.
|
||||||
|
|
||||||
|
A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
|
||||||
|
|
||||||
|
A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
|
||||||
|
|
||||||
|
“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.
|
||||||
|
|
||||||
|
A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
|
||||||
|
|
||||||
|
A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program 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.
|
||||||
|
|
||||||
|
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <https://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts as the
|
||||||
|
successor of the GNU Library Public License, version 2, hence the version
|
||||||
|
number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public Licenses are intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some specially
|
||||||
|
designated software packages--typically libraries--of the Free Software Foundation
|
||||||
|
and other authors who decide to use it. You can use it too, but we suggest
|
||||||
|
you first think carefully about whether this license or the ordinary General
|
||||||
|
Public License is the better strategy to use in any particular case, based
|
||||||
|
on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use, not price.
|
||||||
|
Our General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish); that you receive source code or can get it if you want it; that you
|
||||||
|
can change the software and use pieces of it in new free programs; and that
|
||||||
|
you are informed that you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid distributors
|
||||||
|
to deny you these rights or to ask you to surrender these rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis or for
|
||||||
|
a fee, you must give the recipients all the rights that we gave you. You must
|
||||||
|
make sure that they, too, receive or can get the source code. If you link
|
||||||
|
other code with the library, you must provide complete object files to the
|
||||||
|
recipients, so that they can relink them with the library after making changes
|
||||||
|
to the library and recompiling it. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the library,
|
||||||
|
and (2) we offer you this license, which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that there is no
|
||||||
|
warranty for the free library. Also, if the library is modified by someone
|
||||||
|
else and passed on, the recipients should know that what they have is not
|
||||||
|
the original version, so that the original author's reputation will not be
|
||||||
|
affected by problems that might be introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of any free
|
||||||
|
program. We wish to make sure that a company cannot effectively restrict the
|
||||||
|
users of a free program by obtaining a restrictive license from a patent holder.
|
||||||
|
Therefore, we insist that any patent license obtained for a version of the
|
||||||
|
library must be consistent with the full freedom of use specified in this
|
||||||
|
license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||||
|
General Public License. This license, the GNU Lesser General Public License,
|
||||||
|
applies to certain designated libraries, and is quite different from the ordinary
|
||||||
|
General Public License. We use this license for certain libraries in order
|
||||||
|
to permit linking those libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using a shared
|
||||||
|
library, the combination of the two is legally speaking a combined work, a
|
||||||
|
derivative of the original library. The ordinary General Public License therefore
|
||||||
|
permits such linking only if the entire combination fits its criteria of freedom.
|
||||||
|
The Lesser General Public License permits more lax criteria for linking other
|
||||||
|
code with the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it does Less
|
||||||
|
to protect the user's freedom than the ordinary General Public License. It
|
||||||
|
also provides other free software developers Less of an advantage over competing
|
||||||
|
non-free programs. These disadvantages are the reason we use the ordinary
|
||||||
|
General Public License for many libraries. However, the Lesser license provides
|
||||||
|
advantages in certain special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to encourage the
|
||||||
|
widest possible use of a certain library, so that it becomes a de-facto standard.
|
||||||
|
To achieve this, non-free programs must be allowed to use the library. A more
|
||||||
|
frequent case is that a free library does the same job as widely used non-free
|
||||||
|
libraries. In this case, there is little to gain by limiting the free library
|
||||||
|
to free software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free programs
|
||||||
|
enables a greater number of people to use a large body of free software. For
|
||||||
|
example, permission to use the GNU C Library in non-free programs enables
|
||||||
|
many more people to use the whole GNU operating system, as well as its variant,
|
||||||
|
the GNU/Linux operating system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the users'
|
||||||
|
freedom, it does ensure that the user of a program that is linked with the
|
||||||
|
Library has the freedom and the wherewithal to run that program using a modified
|
||||||
|
version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow. Pay close attention to the difference between a "work based on the
|
||||||
|
library" and a "work that uses the library". The former contains code derived
|
||||||
|
from the library, whereas the latter must be combined with the library in
|
||||||
|
order to run.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other program
|
||||||
|
which contains a notice placed by the copyright holder or other authorized
|
||||||
|
party saying it may be distributed under the terms of this Lesser General
|
||||||
|
Public License (also called "this License"). Each licensee is addressed as
|
||||||
|
"you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data prepared
|
||||||
|
so as to be conveniently linked with application programs (which use some
|
||||||
|
of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work which has
|
||||||
|
been distributed under these terms. A "work based on the Library" means either
|
||||||
|
the Library or any derivative work under copyright law: that is to say, a
|
||||||
|
work containing the Library or a portion of it, either verbatim or with modifications
|
||||||
|
and/or translated straightforwardly into another language. (Hereinafter, translation
|
||||||
|
is included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for making modifications
|
||||||
|
to it. For a library, complete source code means all the source code for all
|
||||||
|
modules it contains, plus any associated interface definition files, plus
|
||||||
|
the scripts used to control compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running a program
|
||||||
|
using the Library is not restricted, and output from such a program is covered
|
||||||
|
only if its contents constitute a work based on the Library (independent of
|
||||||
|
the use of the Library in a tool for writing it). Whether that is true depends
|
||||||
|
on what the Library does and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's complete source
|
||||||
|
code as you receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice and disclaimer
|
||||||
|
of warranty; keep intact all the notices that refer to this License and to
|
||||||
|
the absence of any warranty; and distribute a copy of this License along with
|
||||||
|
the Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion of it,
|
||||||
|
thus forming a work based on the Library, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no charge to all
|
||||||
|
third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a table of
|
||||||
|
data to be supplied by an application program that uses the facility, other
|
||||||
|
than as an argument passed when the facility is invoked, then you must make
|
||||||
|
a good faith effort to ensure that, in the event an application does not supply
|
||||||
|
such function or table, the facility still operates, and performs whatever
|
||||||
|
part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has a purpose
|
||||||
|
that is entirely well-defined independent of the application. Therefore, Subsection
|
||||||
|
2d requires that any application-supplied function or table used by this function
|
||||||
|
must be optional: if the application does not supply it, the square root function
|
||||||
|
must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Library, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Library, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library with
|
||||||
|
the Library (or with a work based on the Library) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public License
|
||||||
|
instead of this License to a given copy of the Library. To do this, you must
|
||||||
|
alter all the notices that refer to this License, so that they refer to the
|
||||||
|
ordinary GNU General Public License, version 2, instead of to this License.
|
||||||
|
(If a newer version than version 2 of the ordinary GNU General Public License
|
||||||
|
has appeared, then you can specify that version instead if you wish.) Do not
|
||||||
|
make any other change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for that copy,
|
||||||
|
so the ordinary GNU General Public License applies to all subsequent copies
|
||||||
|
and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of the Library
|
||||||
|
into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or derivative of
|
||||||
|
it, under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you accompany it with the complete corresponding
|
||||||
|
machine-readable source code, which must be distributed under the terms of
|
||||||
|
Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy from a designated
|
||||||
|
place, then offering equivalent access to copy the source code from the same
|
||||||
|
place satisfies the requirement to distribute the source code, even though
|
||||||
|
third parties are not compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the Library, but
|
||||||
|
is designed to work with the Library by being compiled or linked with it,
|
||||||
|
is called a "work that uses the Library". Such a work, in isolation, is not
|
||||||
|
a derivative work of the Library, and therefore falls outside the scope of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library creates an
|
||||||
|
executable that is a derivative of the Library (because it contains portions
|
||||||
|
of the Library), rather than a "work that uses the library". The executable
|
||||||
|
is therefore covered by this License. Section 6 states terms for distribution
|
||||||
|
of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file that
|
||||||
|
is part of the Library, the object code for the work may be a derivative work
|
||||||
|
of the Library even though the source code is not. Whether this is true is
|
||||||
|
especially significant if the work can be linked without the Library, or if
|
||||||
|
the work is itself a library. The threshold for this to be true is not precisely
|
||||||
|
defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data structure layouts
|
||||||
|
and accessors, and small macros and small inline functions (ten lines or less
|
||||||
|
in length), then the use of the object file is unrestricted, regardless of
|
||||||
|
whether it is legally a derivative work. (Executables containing this object
|
||||||
|
code plus portions of the Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||||
|
the object code for the work under the terms of Section 6. Any executables
|
||||||
|
containing that work also fall under Section 6, whether or not they are linked
|
||||||
|
directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or link a "work
|
||||||
|
that uses the Library" with the Library to produce a work containing portions
|
||||||
|
of the Library, and distribute that work under terms of your choice, provided
|
||||||
|
that the terms permit modification of the work for the customer's own use
|
||||||
|
and reverse engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
You must supply a copy of this License. If the work during execution displays
|
||||||
|
copyright notices, you must include the copyright notice for the Library among
|
||||||
|
them, as well as a reference directing the user to the copy of this License.
|
||||||
|
Also, you must do one of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding machine-readable source
|
||||||
|
code for the Library including whatever changes were used in the work (which
|
||||||
|
must be distributed under Sections 1 and 2 above); and, if the work is an
|
||||||
|
executable linked with the Library, with the complete machine-readable "work
|
||||||
|
that uses the Library", as object code and/or source code, so that the user
|
||||||
|
can modify the Library and then relink to produce a modified executable containing
|
||||||
|
the modified Library. (It is understood that the user who changes the contents
|
||||||
|
of definitions files in the Library will not necessarily be able to recompile
|
||||||
|
the application to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the Library. A
|
||||||
|
suitable mechanism is one that (1) uses at run time a copy of the library
|
||||||
|
already present on the user's computer system, rather than copying library
|
||||||
|
functions into the executable, and (2) will operate properly with a modified
|
||||||
|
version of the library, if the user installs one, as long as the modified
|
||||||
|
version is interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at least three years,
|
||||||
|
to give the same user the materials specified in Subsection 6a, above, for
|
||||||
|
a charge no more than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy from a designated
|
||||||
|
place, offer equivalent access to copy the above specified materials from
|
||||||
|
the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these materials or
|
||||||
|
that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the Library" must
|
||||||
|
include any data and utility programs needed for reproducing the executable
|
||||||
|
from it. However, as a special exception, the materials to be distributed
|
||||||
|
need not include anything that is normally distributed (in either source or
|
||||||
|
binary form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component itself
|
||||||
|
accompanies the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license restrictions of
|
||||||
|
other proprietary libraries that do not normally accompany the operating system.
|
||||||
|
Such a contradiction means you cannot use both them and the Library together
|
||||||
|
in an executable that you distribute.
|
||||||
|
|
||||||
|
7. 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 not covered by
|
||||||
|
this License, and distribute such a combined library, provided that the separate
|
||||||
|
distribution of the work based on the Library and of the other library facilities
|
||||||
|
is otherwise permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the
|
||||||
|
Library, uncombined with any other library facilities. This must be distributed
|
||||||
|
under the terms of the Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact that part of
|
||||||
|
it is a work based on the Library, and explaining where to find the accompanying
|
||||||
|
uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute the Library
|
||||||
|
except as expressly provided under this License. Any attempt otherwise to
|
||||||
|
copy, modify, sublicense, link with, or distribute the Library is void, and
|
||||||
|
will automatically terminate your rights under this License. However, parties
|
||||||
|
who have received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Library or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Library
|
||||||
|
(or any work based on the Library), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the Library),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute, link with or modify the Library subject to these terms
|
||||||
|
and conditions. You may not impose any further restrictions on the recipients'
|
||||||
|
exercise of the rights granted herein. You are not responsible for enforcing
|
||||||
|
compliance by third parties with this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Library at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Library
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Library under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the 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 specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Library does not specify a license version number, you may choose any version
|
||||||
|
ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free programs
|
||||||
|
whose distribution conditions are incompatible with these, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free Software
|
||||||
|
Foundation, write to the Free Software Foundation; we sometimes make exceptions
|
||||||
|
for this. Our decision will be guided by the two goals of preserving the free
|
||||||
|
status of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest possible
|
||||||
|
use to the public, we recommend making it free software that everyone can
|
||||||
|
redistribute and change. You can do so by permitting redistribution under
|
||||||
|
these terms (or, alternatively, under the terms of the ordinary General Public
|
||||||
|
License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively convey
|
||||||
|
the exclusion of warranty; and each file should have at least the "copyright"
|
||||||
|
line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
< one line to give the library's name and an idea of what it does. >
|
||||||
|
|
||||||
|
Copyright (C) < year > < name of author >
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2.1 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This library 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 Lesser General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this library; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information
|
||||||
|
on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the library, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||||
|
|
||||||
|
the library `Frob' (a library for tweaking knobs) written
|
||||||
|
|
||||||
|
by James Random Hacker.
|
||||||
|
|
||||||
|
< signature of Ty Coon > , 1 April 1990
|
||||||
|
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.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.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
This library 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) at any later version that is
|
||||||
|
accepted by the membership of KDE e.V. (or its successor
|
||||||
|
approved by the membership of KDE e.V.), which shall act as a
|
||||||
|
proxy as defined in Section 14 of version 3 of the license.
|
||||||
|
|
||||||
|
This program 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.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3 of the license or (at your option) any later version
|
||||||
|
that is accepted by the membership of KDE e.V. (or its successor
|
||||||
|
approved by the membership of KDE e.V.), which shall act as a
|
||||||
|
proxy as defined in Section 6 of version 3 of the license.
|
||||||
|
|
||||||
|
This program 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.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) <year> <copyright holders>
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
Copyright (c) <year> <owner>.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
||||||
@@ -0,0 +1,468 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts as the
|
||||||
|
successor of the GNU Library Public License, version 2, hence the version
|
||||||
|
number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public Licenses are intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some specially
|
||||||
|
designated software packages--typically libraries--of the Free Software Foundation
|
||||||
|
and other authors who decide to use it. You can use it too, but we suggest
|
||||||
|
you first think carefully about whether this license or the ordinary General
|
||||||
|
Public License is the better strategy to use in any particular case, based
|
||||||
|
on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use, not price.
|
||||||
|
Our General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish); that you receive source code or can get it if you want it; that you
|
||||||
|
can change the software and use pieces of it in new free programs; and that
|
||||||
|
you are informed that you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid distributors
|
||||||
|
to deny you these rights or to ask you to surrender these rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis or for
|
||||||
|
a fee, you must give the recipients all the rights that we gave you. You must
|
||||||
|
make sure that they, too, receive or can get the source code. If you link
|
||||||
|
other code with the library, you must provide complete object files to the
|
||||||
|
recipients, so that they can relink them with the library after making changes
|
||||||
|
to the library and recompiling it. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the library,
|
||||||
|
and (2) we offer you this license, which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that there is no
|
||||||
|
warranty for the free library. Also, if the library is modified by someone
|
||||||
|
else and passed on, the recipients should know that what they have is not
|
||||||
|
the original version, so that the original author's reputation will not be
|
||||||
|
affected by problems that might be introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of any free
|
||||||
|
program. We wish to make sure that a company cannot effectively restrict the
|
||||||
|
users of a free program by obtaining a restrictive license from a patent holder.
|
||||||
|
Therefore, we insist that any patent license obtained for a version of the
|
||||||
|
library must be consistent with the full freedom of use specified in this
|
||||||
|
license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||||
|
General Public License. This license, the GNU Lesser General Public License,
|
||||||
|
applies to certain designated libraries, and is quite different from the ordinary
|
||||||
|
General Public License. We use this license for certain libraries in order
|
||||||
|
to permit linking those libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using a shared
|
||||||
|
library, the combination of the two is legally speaking a combined work, a
|
||||||
|
derivative of the original library. The ordinary General Public License therefore
|
||||||
|
permits such linking only if the entire combination fits its criteria of freedom.
|
||||||
|
The Lesser General Public License permits more lax criteria for linking other
|
||||||
|
code with the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it does Less
|
||||||
|
to protect the user's freedom than the ordinary General Public License. It
|
||||||
|
also provides other free software developers Less of an advantage over competing
|
||||||
|
non-free programs. These disadvantages are the reason we use the ordinary
|
||||||
|
General Public License for many libraries. However, the Lesser license provides
|
||||||
|
advantages in certain special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to encourage the
|
||||||
|
widest possible use of a certain library, so that it becomes a de-facto standard.
|
||||||
|
To achieve this, non-free programs must be allowed to use the library. A more
|
||||||
|
frequent case is that a free library does the same job as widely used non-free
|
||||||
|
libraries. In this case, there is little to gain by limiting the free library
|
||||||
|
to free software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free programs
|
||||||
|
enables a greater number of people to use a large body of free software. For
|
||||||
|
example, permission to use the GNU C Library in non-free programs enables
|
||||||
|
many more people to use the whole GNU operating system, as well as its variant,
|
||||||
|
the GNU/Linux operating system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the users'
|
||||||
|
freedom, it does ensure that the user of a program that is linked with the
|
||||||
|
Library has the freedom and the wherewithal to run that program using a modified
|
||||||
|
version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow. Pay close attention to the difference between a "work based on the
|
||||||
|
library" and a "work that uses the library". The former contains code derived
|
||||||
|
from the library, whereas the latter must be combined with the library in
|
||||||
|
order to run.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other program
|
||||||
|
which contains a notice placed by the copyright holder or other authorized
|
||||||
|
party saying it may be distributed under the terms of this Lesser General
|
||||||
|
Public License (also called "this License"). Each licensee is addressed as
|
||||||
|
"you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data prepared
|
||||||
|
so as to be conveniently linked with application programs (which use some
|
||||||
|
of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work which has
|
||||||
|
been distributed under these terms. A "work based on the Library" means either
|
||||||
|
the Library or any derivative work under copyright law: that is to say, a
|
||||||
|
work containing the Library or a portion of it, either verbatim or with modifications
|
||||||
|
and/or translated straightforwardly into another language. (Hereinafter, translation
|
||||||
|
is included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for making modifications
|
||||||
|
to it. For a library, complete source code means all the source code for all
|
||||||
|
modules it contains, plus any associated interface definition files, plus
|
||||||
|
the scripts used to control compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running a program
|
||||||
|
using the Library is not restricted, and output from such a program is covered
|
||||||
|
only if its contents constitute a work based on the Library (independent of
|
||||||
|
the use of the Library in a tool for writing it). Whether that is true depends
|
||||||
|
on what the Library does and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's complete source
|
||||||
|
code as you receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice and disclaimer
|
||||||
|
of warranty; keep intact all the notices that refer to this License and to
|
||||||
|
the absence of any warranty; and distribute a copy of this License along with
|
||||||
|
the Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion of it,
|
||||||
|
thus forming a work based on the Library, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no charge to all
|
||||||
|
third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a table of
|
||||||
|
data to be supplied by an application program that uses the facility, other
|
||||||
|
than as an argument passed when the facility is invoked, then you must make
|
||||||
|
a good faith effort to ensure that, in the event an application does not supply
|
||||||
|
such function or table, the facility still operates, and performs whatever
|
||||||
|
part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has a purpose
|
||||||
|
that is entirely well-defined independent of the application. Therefore, Subsection
|
||||||
|
2d requires that any application-supplied function or table used by this function
|
||||||
|
must be optional: if the application does not supply it, the square root function
|
||||||
|
must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Library, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Library, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library with
|
||||||
|
the Library (or with a work based on the Library) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public License
|
||||||
|
instead of this License to a given copy of the Library. To do this, you must
|
||||||
|
alter all the notices that refer to this License, so that they refer to the
|
||||||
|
ordinary GNU General Public License, version 2, instead of to this License.
|
||||||
|
(If a newer version than version 2 of the ordinary GNU General Public License
|
||||||
|
has appeared, then you can specify that version instead if you wish.) Do not
|
||||||
|
make any other change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for that copy,
|
||||||
|
so the ordinary GNU General Public License applies to all subsequent copies
|
||||||
|
and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of the Library
|
||||||
|
into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or derivative of
|
||||||
|
it, under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you accompany it with the complete corresponding
|
||||||
|
machine-readable source code, which must be distributed under the terms of
|
||||||
|
Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy from a designated
|
||||||
|
place, then offering equivalent access to copy the source code from the same
|
||||||
|
place satisfies the requirement to distribute the source code, even though
|
||||||
|
third parties are not compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the Library, but
|
||||||
|
is designed to work with the Library by being compiled or linked with it,
|
||||||
|
is called a "work that uses the Library". Such a work, in isolation, is not
|
||||||
|
a derivative work of the Library, and therefore falls outside the scope of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library creates an
|
||||||
|
executable that is a derivative of the Library (because it contains portions
|
||||||
|
of the Library), rather than a "work that uses the library". The executable
|
||||||
|
is therefore covered by this License. Section 6 states terms for distribution
|
||||||
|
of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file that
|
||||||
|
is part of the Library, the object code for the work may be a derivative work
|
||||||
|
of the Library even though the source code is not. Whether this is true is
|
||||||
|
especially significant if the work can be linked without the Library, or if
|
||||||
|
the work is itself a library. The threshold for this to be true is not precisely
|
||||||
|
defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data structure layouts
|
||||||
|
and accessors, and small macros and small inline functions (ten lines or less
|
||||||
|
in length), then the use of the object file is unrestricted, regardless of
|
||||||
|
whether it is legally a derivative work. (Executables containing this object
|
||||||
|
code plus portions of the Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||||
|
the object code for the work under the terms of Section 6. Any executables
|
||||||
|
containing that work also fall under Section 6, whether or not they are linked
|
||||||
|
directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or link a "work
|
||||||
|
that uses the Library" with the Library to produce a work containing portions
|
||||||
|
of the Library, and distribute that work under terms of your choice, provided
|
||||||
|
that the terms permit modification of the work for the customer's own use
|
||||||
|
and reverse engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
You must supply a copy of this License. If the work during execution displays
|
||||||
|
copyright notices, you must include the copyright notice for the Library among
|
||||||
|
them, as well as a reference directing the user to the copy of this License.
|
||||||
|
Also, you must do one of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding machine-readable source
|
||||||
|
code for the Library including whatever changes were used in the work (which
|
||||||
|
must be distributed under Sections 1 and 2 above); and, if the work is an
|
||||||
|
executable linked with the Library, with the complete machine-readable "work
|
||||||
|
that uses the Library", as object code and/or source code, so that the user
|
||||||
|
can modify the Library and then relink to produce a modified executable containing
|
||||||
|
the modified Library. (It is understood that the user who changes the contents
|
||||||
|
of definitions files in the Library will not necessarily be able to recompile
|
||||||
|
the application to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the Library. A
|
||||||
|
suitable mechanism is one that (1) uses at run time a copy of the library
|
||||||
|
already present on the user's computer system, rather than copying library
|
||||||
|
functions into the executable, and (2) will operate properly with a modified
|
||||||
|
version of the library, if the user installs one, as long as the modified
|
||||||
|
version is interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at least three years,
|
||||||
|
to give the same user the materials specified in Subsection 6a, above, for
|
||||||
|
a charge no more than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy from a designated
|
||||||
|
place, offer equivalent access to copy the above specified materials from
|
||||||
|
the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these materials or
|
||||||
|
that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the Library" must
|
||||||
|
include any data and utility programs needed for reproducing the executable
|
||||||
|
from it. However, as a special exception, the materials to be distributed
|
||||||
|
need not include anything that is normally distributed (in either source or
|
||||||
|
binary form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component itself
|
||||||
|
accompanies the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license restrictions of
|
||||||
|
other proprietary libraries that do not normally accompany the operating system.
|
||||||
|
Such a contradiction means you cannot use both them and the Library together
|
||||||
|
in an executable that you distribute.
|
||||||
|
|
||||||
|
7. 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 not covered by
|
||||||
|
this License, and distribute such a combined library, provided that the separate
|
||||||
|
distribution of the work based on the Library and of the other library facilities
|
||||||
|
is otherwise permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the
|
||||||
|
Library, uncombined with any other library facilities. This must be distributed
|
||||||
|
under the terms of the Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact that part of
|
||||||
|
it is a work based on the Library, and explaining where to find the accompanying
|
||||||
|
uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute the Library
|
||||||
|
except as expressly provided under this License. Any attempt otherwise to
|
||||||
|
copy, modify, sublicense, link with, or distribute the Library is void, and
|
||||||
|
will automatically terminate your rights under this License. However, parties
|
||||||
|
who have received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Library or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Library
|
||||||
|
(or any work based on the Library), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the Library),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute, link with or modify the Library subject to these terms
|
||||||
|
and conditions. You may not impose any further restrictions on the recipients'
|
||||||
|
exercise of the rights granted herein. You are not responsible for enforcing
|
||||||
|
compliance by third parties with this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Library at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Library
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Library under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the 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 specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Library does not specify a license version number, you may choose any version
|
||||||
|
ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free programs
|
||||||
|
whose distribution conditions are incompatible with these, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free Software
|
||||||
|
Foundation, write to the Free Software Foundation; we sometimes make exceptions
|
||||||
|
for this. Our decision will be guided by the two goals of preserving the free
|
||||||
|
status of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest possible
|
||||||
|
use to the public, we recommend making it free software that everyone can
|
||||||
|
redistribute and change. You can do so by permitting redistribution under
|
||||||
|
these terms (or, alternatively, under the terms of the ordinary General Public
|
||||||
|
License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively convey
|
||||||
|
the exclusion of warranty; and each file should have at least the "copyright"
|
||||||
|
line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and an idea of what it does.>
|
||||||
|
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2.1 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This library 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 Lesser General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this library; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the library, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||||
|
|
||||||
|
the library `Frob' (a library for tweaking knobs) written
|
||||||
|
|
||||||
|
by James Random Hacker.
|
||||||
|
|
||||||
|
< signature of Ty Coon > , 1 April 1990
|
||||||
|
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
libFakeKey
|
||||||
|
|
||||||
|
A simple library for converting utf8 chars into 'fake' keypresses.
|
||||||
|
|
||||||
|
Uses ideas from Fontconfig, libvirtkeys.c, keysym2ucs.c and dasher.
|
||||||
|
|
||||||
|
Authored By Matthew Allum <amllum@openedhand.com>
|
||||||
|
|
||||||
|
Copyright (C) 2004 OpenedHand
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the
|
||||||
|
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA.
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the library GPL. It is numbered 2 because
|
||||||
|
it goes with version 2 of the ordinary GPL.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public Licenses are intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Library General Public License, applies to some specially
|
||||||
|
designated Free Software Foundation software, and to any other libraries whose
|
||||||
|
authors decide to use it. You can use it for your libraries, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our
|
||||||
|
General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish), that you receive source code or can get it if you want it, that you
|
||||||
|
can change the software or use pieces of it in new free programs; and that
|
||||||
|
you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid anyone to
|
||||||
|
deny you these rights or to ask you to surrender the rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the library, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis or for
|
||||||
|
a fee, you must give the recipients all the rights that we gave you. You must
|
||||||
|
make sure that they, too, receive or can get the source code. If you link
|
||||||
|
a program with the library, you must provide complete object files to the
|
||||||
|
recipients so that they can relink them with the library, after making changes
|
||||||
|
to the library and recompiling it. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Our method of protecting your rights has two steps: (1) copyright the library,
|
||||||
|
and (2) offer you this license which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the library.
|
||||||
|
|
||||||
|
Also, for each distributor's protection, we want to make certain that everyone
|
||||||
|
understands that there is no warranty for this free library. If the library
|
||||||
|
is modified by someone else and passed on, we want its recipients to know
|
||||||
|
that what they have is not the original version, so that any problems introduced
|
||||||
|
by others will not reflect on the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software patents. We
|
||||||
|
wish to avoid the danger that companies distributing free software will individually
|
||||||
|
obtain patent licenses, thus in effect transforming the program into proprietary
|
||||||
|
software. To prevent this, we have made it clear that any patent must be licensed
|
||||||
|
for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||||
|
General Public License, which was designed for utility programs. This license,
|
||||||
|
the GNU Library General Public License, applies to certain designated libraries.
|
||||||
|
This license is quite different from the ordinary one; be sure to read it
|
||||||
|
in full, and don't assume that anything in it is the same as in the ordinary
|
||||||
|
license.
|
||||||
|
|
||||||
|
The reason we have a separate public license for some libraries is that they
|
||||||
|
blur the distinction we usually make between modifying or adding to a program
|
||||||
|
and simply using it. Linking a program with a library, without changing the
|
||||||
|
library, is in some sense simply using the library, and is analogous to running
|
||||||
|
a utility program or application program. However, in a textual and legal
|
||||||
|
sense, the linked executable is a combined work, a derivative of the original
|
||||||
|
library, and the ordinary General Public License treats it as such.
|
||||||
|
|
||||||
|
Because of this blurred distinction, using the ordinary General Public License
|
||||||
|
for libraries did not effectively promote software sharing, because most developers
|
||||||
|
did not use the libraries. We concluded that weaker conditions might promote
|
||||||
|
sharing better.
|
||||||
|
|
||||||
|
However, unrestricted linking of non-free programs would deprive the users
|
||||||
|
of those programs of all benefit from the free status of the libraries themselves.
|
||||||
|
This Library General Public License is intended to permit developers of non-free
|
||||||
|
programs to use free libraries, while preserving your freedom as a user of
|
||||||
|
such programs to change the free libraries that are incorporated in them.
|
||||||
|
(We have not seen how to achieve this as regards changes in header files,
|
||||||
|
but we have achieved it as regards changes in the actual functions of the
|
||||||
|
Library.) The hope is that this will lead to faster development of free libraries.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow. Pay close attention to the difference between a "work based on the
|
||||||
|
library" and a "work that uses the library". The former contains code derived
|
||||||
|
from the library, while the latter only works together with the library.
|
||||||
|
|
||||||
|
Note that it is possible for a library to be covered by the ordinary General
|
||||||
|
Public License rather than by this special one.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library which contains a
|
||||||
|
notice placed by the copyright holder or other authorized party saying it
|
||||||
|
may be distributed under the terms of this Library General Public License
|
||||||
|
(also called "this License"). Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data prepared
|
||||||
|
so as to be conveniently linked with application programs (which use some
|
||||||
|
of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work which has
|
||||||
|
been distributed under these terms. A "work based on the Library" means either
|
||||||
|
the Library or any derivative work under copyright law: that is to say, a
|
||||||
|
work containing the Library or a portion of it, either verbatim or with modifications
|
||||||
|
and/or translated straightforwardly into another language. (Hereinafter, translation
|
||||||
|
is included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for making modifications
|
||||||
|
to it. For a library, complete source code means all the source code for all
|
||||||
|
modules it contains, plus any associated interface definition files, plus
|
||||||
|
the scripts used to control compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running a program
|
||||||
|
using the Library is not restricted, and output from such a program is covered
|
||||||
|
only if its contents constitute a work based on the Library (independent of
|
||||||
|
the use of the Library in a tool for writing it). Whether that is true depends
|
||||||
|
on what the Library does and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's complete source
|
||||||
|
code as you receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice and disclaimer
|
||||||
|
of warranty; keep intact all the notices that refer to this License and to
|
||||||
|
the absence of any warranty; and distribute a copy of this License along with
|
||||||
|
the Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion of it,
|
||||||
|
thus forming a work based on the Library, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no charge to all
|
||||||
|
third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a table of
|
||||||
|
data to be supplied by an application program that uses the facility, other
|
||||||
|
than as an argument passed when the facility is invoked, then you must make
|
||||||
|
a good faith effort to ensure that, in the event an application does not supply
|
||||||
|
such function or table, the facility still operates, and performs whatever
|
||||||
|
part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has a purpose
|
||||||
|
that is entirely well-defined independent of the application. Therefore, Subsection
|
||||||
|
2d requires that any application-supplied function or table used by this function
|
||||||
|
must be optional: if the application does not supply it, the square root function
|
||||||
|
must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Library, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Library, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library with
|
||||||
|
the Library (or with a work based on the Library) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public License
|
||||||
|
instead of this License to a given copy of the Library. To do this, you must
|
||||||
|
alter all the notices that refer to this License, so that they refer to the
|
||||||
|
ordinary GNU General Public License, version 2, instead of to this License.
|
||||||
|
(If a newer version than version 2 of the ordinary GNU General Public License
|
||||||
|
has appeared, then you can specify that version instead if you wish.) Do not
|
||||||
|
make any other change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for that copy,
|
||||||
|
so the ordinary GNU General Public License applies to all subsequent copies
|
||||||
|
and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of the Library
|
||||||
|
into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or derivative of
|
||||||
|
it, under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you accompany it with the complete corresponding
|
||||||
|
machine-readable source code, which must be distributed under the terms of
|
||||||
|
Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy from a designated
|
||||||
|
place, then offering equivalent access to copy the source code from the same
|
||||||
|
place satisfies the requirement to distribute the source code, even though
|
||||||
|
third parties are not compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the Library, but
|
||||||
|
is designed to work with the Library by being compiled or linked with it,
|
||||||
|
is called a "work that uses the Library". Such a work, in isolation, is not
|
||||||
|
a derivative work of the Library, and therefore falls outside the scope of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library creates an
|
||||||
|
executable that is a derivative of the Library (because it contains portions
|
||||||
|
of the Library), rather than a "work that uses the library". The executable
|
||||||
|
is therefore covered by this License. Section 6 states terms for distribution
|
||||||
|
of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file that
|
||||||
|
is part of the Library, the object code for the work may be a derivative work
|
||||||
|
of the Library even though the source code is not. Whether this is true is
|
||||||
|
especially significant if the work can be linked without the Library, or if
|
||||||
|
the work is itself a library. The threshold for this to be true is not precisely
|
||||||
|
defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data structure layouts
|
||||||
|
and accessors, and small macros and small inline functions (ten lines or less
|
||||||
|
in length), then the use of the object file is unrestricted, regardless of
|
||||||
|
whether it is legally a derivative work. (Executables containing this object
|
||||||
|
code plus portions of the Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||||
|
the object code for the work under the terms of Section 6. Any executables
|
||||||
|
containing that work also fall under Section 6, whether or not they are linked
|
||||||
|
directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also compile or link a "work
|
||||||
|
that uses the Library" with the Library to produce a work containing portions
|
||||||
|
of the Library, and distribute that work under terms of your choice, provided
|
||||||
|
that the terms permit modification of the work for the customer's own use
|
||||||
|
and reverse engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
You must supply a copy of this License. If the work during execution displays
|
||||||
|
copyright notices, you must include the copyright notice for the Library among
|
||||||
|
them, as well as a reference directing the user to the copy of this License.
|
||||||
|
Also, you must do one of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding machine-readable source
|
||||||
|
code for the Library including whatever changes were used in the work (which
|
||||||
|
must be distributed under Sections 1 and 2 above); and, if the work is an
|
||||||
|
executable linked with the Library, with the complete machine-readable "work
|
||||||
|
that uses the Library", as object code and/or source code, so that the user
|
||||||
|
can modify the Library and then relink to produce a modified executable containing
|
||||||
|
the modified Library. (It is understood that the user who changes the contents
|
||||||
|
of definitions files in the Library will not necessarily be able to recompile
|
||||||
|
the application to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Accompany the work with a written offer, valid for at least three years,
|
||||||
|
to give the same user the materials specified in Subsection 6a, above, for
|
||||||
|
a charge no more than the cost of performing this distribution.
|
||||||
|
|
||||||
|
c) If distribution of the work is made by offering access to copy from a designated
|
||||||
|
place, offer equivalent access to copy the above specified materials from
|
||||||
|
the same place.
|
||||||
|
|
||||||
|
d) Verify that the user has already received a copy of these materials or
|
||||||
|
that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the Library" must
|
||||||
|
include any data and utility programs needed for reproducing the executable
|
||||||
|
from it. However, as a special exception, the source code distributed need
|
||||||
|
not include anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the operating
|
||||||
|
system on which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license restrictions of
|
||||||
|
other proprietary libraries that do not normally accompany the operating system.
|
||||||
|
Such a contradiction means you cannot use both them and the Library together
|
||||||
|
in an executable that you distribute.
|
||||||
|
|
||||||
|
7. 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 not covered by
|
||||||
|
this License, and distribute such a combined library, provided that the separate
|
||||||
|
distribution of the work based on the Library and of the other library facilities
|
||||||
|
is otherwise permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the
|
||||||
|
Library, uncombined with any other library facilities. This must be distributed
|
||||||
|
under the terms of the Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact that part of
|
||||||
|
it is a work based on the Library, and explaining where to find the accompanying
|
||||||
|
uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute the Library
|
||||||
|
except as expressly provided under this License. Any attempt otherwise to
|
||||||
|
copy, modify, sublicense, link with, or distribute the Library is void, and
|
||||||
|
will automatically terminate your rights under this License. However, parties
|
||||||
|
who have received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Library or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Library
|
||||||
|
(or any work based on the Library), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the Library),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute, link with or modify the Library subject to these terms
|
||||||
|
and conditions. You may not impose any further restrictions on the recipients'
|
||||||
|
exercise of the rights granted herein. You are not responsible for enforcing
|
||||||
|
compliance by third parties to this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Library at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Library
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Library under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the Library 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 specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Library does not specify a license version number, you may choose any version
|
||||||
|
ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free programs
|
||||||
|
whose distribution conditions are incompatible with these, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free Software
|
||||||
|
Foundation, write to the Free Software Foundation; we sometimes make exceptions
|
||||||
|
for this. Our decision will be guided by the two goals of preserving the free
|
||||||
|
status of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest possible
|
||||||
|
use to the public, we recommend making it free software that everyone can
|
||||||
|
redistribute and change. You can do so by permitting redistribution under
|
||||||
|
these terms (or, alternatively, under the terms of the ordinary General Public
|
||||||
|
License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively convey
|
||||||
|
the exclusion of warranty; and each file should have at least the "copyright"
|
||||||
|
line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
one line to give the library's name and an idea of what it does.
|
||||||
|
|
||||||
|
Copyright (C) year name of author
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU Library General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This library 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 Library General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public License
|
||||||
|
along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the library, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||||
|
|
||||||
|
the library `Frob' (a library for tweaking knobs) written
|
||||||
|
|
||||||
|
by James Random Hacker.
|
||||||
|
|
||||||
|
signature of Ty Coon, 1 April 1990
|
||||||
|
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
Copyright (c) <year> <owner>. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public License is intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users. This General Public License applies to
|
||||||
|
most of the Free Software Foundation's software and to any other program whose
|
||||||
|
authors commit to using it. (Some other Free Software Foundation software
|
||||||
|
is covered by the GNU Lesser General Public License instead.) You can apply
|
||||||
|
it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our
|
||||||
|
General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish), that you receive source code or can get it if you want it, that you
|
||||||
|
can change the software or use pieces of it in new free programs; and that
|
||||||
|
you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid anyone to
|
||||||
|
deny you these rights or to ask you to surrender the rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or
|
||||||
|
for a fee, you must give the recipients all the rights that you have. You
|
||||||
|
must make sure that they, too, receive or can get the source code. And you
|
||||||
|
must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and (2)
|
||||||
|
offer you this license which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain that
|
||||||
|
everyone understands that there is no warranty for this free software. If
|
||||||
|
the software is modified by someone else and passed on, we want its recipients
|
||||||
|
to know that what they have is not the original, so that any problems introduced
|
||||||
|
by others will not reflect on the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software patents. We
|
||||||
|
wish to avoid the danger that redistributors of a free program will individually
|
||||||
|
obtain patent licenses, in effect making the program proprietary. To prevent
|
||||||
|
this, we have made it clear that any patent must be licensed for everyone's
|
||||||
|
free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains a notice
|
||||||
|
placed by the copyright holder saying it may be distributed under the terms
|
||||||
|
of this General Public License. The "Program", below, refers to any such program
|
||||||
|
or work, and a "work based on the Program" means either the Program or any
|
||||||
|
derivative work under copyright law: that is to say, a work containing the
|
||||||
|
Program or a portion of it, either verbatim or with modifications and/or translated
|
||||||
|
into another language. (Hereinafter, translation is included without limitation
|
||||||
|
in the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running the Program
|
||||||
|
is not restricted, and the output from the Program is covered only if its
|
||||||
|
contents constitute a work based on the Program (independent of having been
|
||||||
|
made by running the Program). Whether that is true depends on what the Program
|
||||||
|
does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's source code
|
||||||
|
as you receive it, in any medium, provided that you conspicuously and appropriately
|
||||||
|
publish on each copy an appropriate copyright notice and disclaimer of warranty;
|
||||||
|
keep intact all the notices that refer to this License and to the absence
|
||||||
|
of any warranty; and give any other recipients of the Program a copy of this
|
||||||
|
License along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion of it,
|
||||||
|
thus forming a work based on the Program, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in whole or
|
||||||
|
in part contains or is derived from the Program or any part thereof, to be
|
||||||
|
licensed as a whole at no charge to all third parties under the terms of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively when run,
|
||||||
|
you must cause it, when started running for such interactive use in the most
|
||||||
|
ordinary way, to print or display an announcement including an appropriate
|
||||||
|
copyright notice and a notice that there is no warranty (or else, saying that
|
||||||
|
you provide a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this License.
|
||||||
|
(Exception: if the Program itself is interactive but does not normally print
|
||||||
|
such an announcement, your work based on the Program is not required to print
|
||||||
|
an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Program, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Program, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program with
|
||||||
|
the Program (or with a work based on the Program) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it, under Section
|
||||||
|
2) in object code or executable form under the terms of Sections 1 and 2 above
|
||||||
|
provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable source code,
|
||||||
|
which must be distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three years, to give
|
||||||
|
any third party, for a charge no more than your cost of physically performing
|
||||||
|
source distribution, a complete machine-readable copy of the corresponding
|
||||||
|
source code, to be distributed under the terms of Sections 1 and 2 above on
|
||||||
|
a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer to distribute
|
||||||
|
corresponding source code. (This alternative is allowed only for noncommercial
|
||||||
|
distribution and only if you received the program in object code or executable
|
||||||
|
form with such an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for making
|
||||||
|
modifications to it. For an executable work, complete source code means all
|
||||||
|
the source code for all modules it contains, plus any associated interface
|
||||||
|
definition files, plus the scripts used to control compilation and installation
|
||||||
|
of the executable. However, as a special exception, the source code distributed
|
||||||
|
need not include anything that is normally distributed (in either source or
|
||||||
|
binary form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component itself
|
||||||
|
accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering access to
|
||||||
|
copy from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place counts as distribution of the source code,
|
||||||
|
even though third parties are not compelled to copy the source along with
|
||||||
|
the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program except
|
||||||
|
as expressly provided under this License. Any attempt otherwise to copy, modify,
|
||||||
|
sublicense or distribute the Program is void, and will automatically terminate
|
||||||
|
your rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses terminated
|
||||||
|
so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Program or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Program
|
||||||
|
(or any work based on the Program), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the Program),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute or modify the Program subject to these terms and conditions.
|
||||||
|
You may not impose any further restrictions on the recipients' exercise of
|
||||||
|
the rights granted herein. You are not responsible for enforcing compliance
|
||||||
|
by third parties to this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Program at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Program
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply and
|
||||||
|
the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system, which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Program under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the 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 Program specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Program does not specify a version number of this License, you may choose
|
||||||
|
any version ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free programs
|
||||||
|
whose distribution conditions are different, write to the author to ask for
|
||||||
|
permission. For software which is copyrighted by the Free Software Foundation,
|
||||||
|
write to the Free Software Foundation; we sometimes make exceptions for this.
|
||||||
|
Our decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing and reuse
|
||||||
|
of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest possible
|
||||||
|
use to the public, the best way to achieve this is to make it free software
|
||||||
|
which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest to attach
|
||||||
|
them to the start of each source file to most effectively convey the exclusion
|
||||||
|
of warranty; and each file should have at least the "copyright" line and a
|
||||||
|
pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and an idea of what it does.>
|
||||||
|
|
||||||
|
Copyright (C)< yyyy> <name of author>
|
||||||
|
|
||||||
|
This program 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 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program 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
|
||||||
|
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
||||||
|
Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this when
|
||||||
|
it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
|
||||||
|
with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software,
|
||||||
|
and you are welcome to redistribute it under certain conditions; type `show
|
||||||
|
c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may be
|
||||||
|
called something other than `show w' and `show c'; they could even be mouse-clicks
|
||||||
|
or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision'
|
||||||
|
(which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon >, 1 April 1989 Ty Coon, President of Vice This General
|
||||||
|
Public License does not permit incorporating your program into proprietary
|
||||||
|
programs. If your program is a subroutine library, you may consider it more
|
||||||
|
useful to permit linking proprietary applications with the library. If this
|
||||||
|
is what you want to do, use the GNU Lesser General Public License instead
|
||||||
|
of this License.
|
||||||
@@ -0,0 +1,625 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for software and
|
||||||
|
other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed to take
|
||||||
|
away your freedom to share and change the works. By contrast, the GNU General
|
||||||
|
Public License is intended to guarantee your freedom to share and change all
|
||||||
|
versions of a program--to make sure it remains free software for all its users.
|
||||||
|
We, the Free Software Foundation, use the GNU General Public License for most
|
||||||
|
of our software; it applies also to any other work released this way by its
|
||||||
|
authors. You can apply it to your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not price. Our
|
||||||
|
General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for them if you wish), that
|
||||||
|
you receive source code or can get it if you want it, that you can change
|
||||||
|
the software or use pieces of it in new free programs, and that you know you
|
||||||
|
can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you these rights
|
||||||
|
or asking you to surrender the rights. Therefore, you have certain responsibilities
|
||||||
|
if you distribute copies of the software, or if you modify it: responsibilities
|
||||||
|
to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether gratis or
|
||||||
|
for a fee, you must pass on to the recipients the same freedoms that you received.
|
||||||
|
You must make sure that they, too, receive or can get the source code. And
|
||||||
|
you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps: (1) assert
|
||||||
|
copyright on the software, and (2) offer you this License giving you legal
|
||||||
|
permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains that
|
||||||
|
there is no warranty for this free software. For both users' and authors'
|
||||||
|
sake, the GPL requires that modified versions be marked as changed, so that
|
||||||
|
their problems will not be attributed erroneously to authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run modified
|
||||||
|
versions of the software inside them, although the manufacturer can do so.
|
||||||
|
This is fundamentally incompatible with the aim of protecting users' freedom
|
||||||
|
to change the software. The systematic pattern of such abuse occurs in the
|
||||||
|
area of products for individuals to use, which is precisely where it is most
|
||||||
|
unacceptable. Therefore, we have designed this version of the GPL to prohibit
|
||||||
|
the practice for those products. If such problems arise substantially in other
|
||||||
|
domains, we stand ready to extend this provision to those domains in future
|
||||||
|
versions of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents. States
|
||||||
|
should not allow patents to restrict development and use of software on general-purpose
|
||||||
|
computers, but in those that do, we wish to avoid the special danger that
|
||||||
|
patents applied to a free program could make it effectively proprietary. To
|
||||||
|
prevent this, the GPL assures that patents cannot be used to render the program
|
||||||
|
non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of works,
|
||||||
|
such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this License.
|
||||||
|
Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals
|
||||||
|
or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work in
|
||||||
|
a fashion requiring copyright permission, other than the making of an exact
|
||||||
|
copy. The resulting work is called a "modified version" of the earlier work
|
||||||
|
or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based on the
|
||||||
|
Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without permission,
|
||||||
|
would make you directly or secondarily liable for infringement under applicable
|
||||||
|
copyright law, except executing it on a computer or modifying a private copy.
|
||||||
|
Propagation includes copying, distribution (with or without modification),
|
||||||
|
making available to the public, and in some countries other activities as
|
||||||
|
well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other parties
|
||||||
|
to make or receive copies. Mere interaction with a user through a computer
|
||||||
|
network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices" to the
|
||||||
|
extent that it includes a convenient and prominently visible feature that
|
||||||
|
(1) displays an appropriate copyright notice, and (2) tells the user that
|
||||||
|
there is no warranty for the work (except to the extent that warranties are
|
||||||
|
provided), that licensees may convey the work under this License, and how
|
||||||
|
to view a copy of this License. If the interface presents a list of user commands
|
||||||
|
or options, such as a menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work for making
|
||||||
|
modifications to it. "Object code" means any non-source form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official standard
|
||||||
|
defined by a recognized standards body, or, in the case of interfaces specified
|
||||||
|
for a particular programming language, one that is widely used among developers
|
||||||
|
working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other than
|
||||||
|
the work as a whole, that (a) is included in the normal form of packaging
|
||||||
|
a Major Component, but which is not part of that Major Component, and (b)
|
||||||
|
serves only to enable use of the work with that Major Component, or to implement
|
||||||
|
a Standard Interface for which an implementation is available to the public
|
||||||
|
in source code form. A "Major Component", in this context, means a major essential
|
||||||
|
component (kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to produce
|
||||||
|
the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all the source
|
||||||
|
code needed to generate, install, and (for an executable work) run the object
|
||||||
|
code and to modify the work, including scripts to control those activities.
|
||||||
|
However, it does not include the work's System Libraries, or general-purpose
|
||||||
|
tools or generally available free programs which are used unmodified in performing
|
||||||
|
those activities but which are not part of the work. For example, Corresponding
|
||||||
|
Source includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically linked
|
||||||
|
subprograms that the work is specifically designed to require, such as by
|
||||||
|
intimate data communication or control flow between those subprograms and
|
||||||
|
other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users can regenerate
|
||||||
|
automatically from other parts of the Corresponding Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of copyright
|
||||||
|
on the Program, and are irrevocable provided the stated conditions are met.
|
||||||
|
This License explicitly affirms your unlimited permission to run the unmodified
|
||||||
|
Program. The output from running a covered work is covered by this License
|
||||||
|
only if the output, given its content, constitutes a covered work. This License
|
||||||
|
acknowledges your rights of fair use or other equivalent, as provided by copyright
|
||||||
|
law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not convey, without
|
||||||
|
conditions so long as your license otherwise remains in force. You may convey
|
||||||
|
covered works to others for the sole purpose of having them make modifications
|
||||||
|
exclusively for you, or provide you with facilities for running those works,
|
||||||
|
provided that you comply with the terms of this License in conveying all material
|
||||||
|
for which you do not control copyright. Those thus making or running the covered
|
||||||
|
works for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of your copyrighted
|
||||||
|
material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under the conditions
|
||||||
|
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological measure
|
||||||
|
under any applicable law fulfilling obligations under article 11 of the WIPO
|
||||||
|
copyright treaty adopted on 20 December 1996, or similar laws prohibiting
|
||||||
|
or restricting circumvention of such measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid circumvention
|
||||||
|
of technological measures to the extent such circumvention is effected by
|
||||||
|
exercising rights under this License with respect to the covered work, and
|
||||||
|
you disclaim any intention to limit operation or modification of the work
|
||||||
|
as a means of enforcing, against the work's users, your or third parties'
|
||||||
|
legal rights to forbid circumvention of technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you receive
|
||||||
|
it, in any medium, provided that you conspicuously and appropriately publish
|
||||||
|
on each copy an appropriate copyright notice; keep intact all notices stating
|
||||||
|
that this License and any non-permissive terms added in accord with section
|
||||||
|
7 apply to the code; keep intact all notices of the absence of any warranty;
|
||||||
|
and give all recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey, and you
|
||||||
|
may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to produce
|
||||||
|
it from the Program, in the form of source code under the terms of section
|
||||||
|
4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified it, and
|
||||||
|
giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is released under
|
||||||
|
this License and any conditions added under section 7. This requirement modifies
|
||||||
|
the requirement in section 4 to "keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this License to anyone
|
||||||
|
who comes into possession of a copy. This License will therefore apply, along
|
||||||
|
with any applicable section 7 additional terms, to the whole of the work,
|
||||||
|
and all its parts, regardless of how they are packaged. This License gives
|
||||||
|
no permission to license the work in any other way, but it does not invalidate
|
||||||
|
such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display Appropriate
|
||||||
|
Legal Notices; however, if the Program has interactive interfaces that do
|
||||||
|
not display Appropriate Legal Notices, your work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent works,
|
||||||
|
which are not by their nature extensions of the covered work, and which are
|
||||||
|
not combined with it such as to form a larger program, in or on a volume of
|
||||||
|
a storage or distribution medium, is called an "aggregate" if the compilation
|
||||||
|
and its resulting copyright are not used to limit the access or legal rights
|
||||||
|
of the compilation's users beyond what the individual works permit. Inclusion
|
||||||
|
of a covered work in an aggregate does not cause this License to apply to
|
||||||
|
the other parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms of sections
|
||||||
|
4 and 5, provided that you also convey the machine-readable Corresponding
|
||||||
|
Source under the terms of this License, in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product (including
|
||||||
|
a physical distribution medium), accompanied by the Corresponding Source fixed
|
||||||
|
on a durable physical medium customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product (including
|
||||||
|
a physical distribution medium), accompanied by a written offer, valid for
|
||||||
|
at least three years and valid for as long as you offer spare parts or customer
|
||||||
|
support for that product model, to give anyone who possesses the object code
|
||||||
|
either (1) a copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical medium customarily
|
||||||
|
used for software interchange, for a price no more than your reasonable cost
|
||||||
|
of physically performing this conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the written
|
||||||
|
offer to provide the Corresponding Source. This alternative is allowed only
|
||||||
|
occasionally and noncommercially, and only if you received the object code
|
||||||
|
with such an offer, in accord with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated place (gratis
|
||||||
|
or for a charge), and offer equivalent access to the Corresponding Source
|
||||||
|
in the same way through the same place at no further charge. You need not
|
||||||
|
require recipients to copy the Corresponding Source along with the object
|
||||||
|
code. If the place to copy the object code is a network server, the Corresponding
|
||||||
|
Source may be on a different server (operated by you or a third party) that
|
||||||
|
supports equivalent copying facilities, provided you maintain clear directions
|
||||||
|
next to the object code saying where to find the Corresponding Source. Regardless
|
||||||
|
of what server hosts the Corresponding Source, you remain obligated to ensure
|
||||||
|
that it is available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided you inform
|
||||||
|
other peers where the object code and Corresponding Source of the work are
|
||||||
|
being offered to the general public at no charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded from
|
||||||
|
the Corresponding Source as a System Library, need not be included in conveying
|
||||||
|
the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any tangible
|
||||||
|
personal property which is normally used for personal, family, or household
|
||||||
|
purposes, or (2) anything designed or sold for incorporation into a dwelling.
|
||||||
|
In determining whether a product is a consumer product, doubtful cases shall
|
||||||
|
be resolved in favor of coverage. For a particular product received by a particular
|
||||||
|
user, "normally used" refers to a typical or common use of that class of product,
|
||||||
|
regardless of the status of the particular user or of the way in which the
|
||||||
|
particular user actually uses, or expects or is expected to use, the product.
|
||||||
|
A product is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent the
|
||||||
|
only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods, procedures,
|
||||||
|
authorization keys, or other information required to install and execute modified
|
||||||
|
versions of a covered work in that User Product from a modified version of
|
||||||
|
its Corresponding Source. The information must suffice to ensure that the
|
||||||
|
continued functioning of the modified object code is in no case prevented
|
||||||
|
or interfered with solely because modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or specifically
|
||||||
|
for use in, a User Product, and the conveying occurs as part of a transaction
|
||||||
|
in which the right of possession and use of the User Product is transferred
|
||||||
|
to the recipient in perpetuity or for a fixed term (regardless of how the
|
||||||
|
transaction is characterized), the Corresponding Source conveyed under this
|
||||||
|
section must be accompanied by the Installation Information. But this requirement
|
||||||
|
does not apply if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has been installed
|
||||||
|
in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a requirement
|
||||||
|
to continue to provide support service, warranty, or updates for a work that
|
||||||
|
has been modified or installed by the recipient, or for the User Product in
|
||||||
|
which it has been modified or installed. Access to a network may be denied
|
||||||
|
when the modification itself materially and adversely affects the operation
|
||||||
|
of the network or violates the rules and protocols for communication across
|
||||||
|
the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided, in accord
|
||||||
|
with this section must be in a format that is publicly documented (and with
|
||||||
|
an implementation available to the public in source code form), and must require
|
||||||
|
no special password or key for unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this License
|
||||||
|
by making exceptions from one or more of its conditions. Additional permissions
|
||||||
|
that are applicable to the entire Program shall be treated as though they
|
||||||
|
were included in this License, to the extent that they are valid under applicable
|
||||||
|
law. If additional permissions apply only to part of the Program, that part
|
||||||
|
may be used separately under those permissions, but the entire Program remains
|
||||||
|
governed by this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option remove any
|
||||||
|
additional permissions from that copy, or from any part of it. (Additional
|
||||||
|
permissions may be written to require their own removal in certain cases when
|
||||||
|
you modify the work.) You may place additional permissions on material, added
|
||||||
|
by you to a covered work, for which you have or can give appropriate copyright
|
||||||
|
permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you add
|
||||||
|
to a covered work, you may (if authorized by the copyright holders of that
|
||||||
|
material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the terms of
|
||||||
|
sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or author
|
||||||
|
attributions in that material or in the Appropriate Legal Notices displayed
|
||||||
|
by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or requiring
|
||||||
|
that modified versions of such material be marked in reasonable ways as different
|
||||||
|
from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or authors
|
||||||
|
of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some trade names,
|
||||||
|
trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that material by
|
||||||
|
anyone who conveys the material (or modified versions of it) with contractual
|
||||||
|
assumptions of liability to the recipient, for any liability that these contractual
|
||||||
|
assumptions directly impose on those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further restrictions"
|
||||||
|
within the meaning of section 10. If the Program as you received it, or any
|
||||||
|
part of it, contains a notice stating that it is governed by this License
|
||||||
|
along with a term that is a further restriction, you may remove that term.
|
||||||
|
If a license document contains a further restriction but permits relicensing
|
||||||
|
or conveying under this License, you may add to a covered work material governed
|
||||||
|
by the terms of that license document, provided that the further restriction
|
||||||
|
does not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you must place,
|
||||||
|
in the relevant source files, a statement of the additional terms that apply
|
||||||
|
to those files, or a notice indicating where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the form
|
||||||
|
of a separately written license, or stated as exceptions; the above requirements
|
||||||
|
apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly provided
|
||||||
|
under this License. Any attempt otherwise to propagate or modify it is void,
|
||||||
|
and will automatically terminate your rights under this License (including
|
||||||
|
any patent licenses granted under the third paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your license from
|
||||||
|
a particular copyright holder is reinstated (a) provisionally, unless and
|
||||||
|
until the copyright holder explicitly and finally terminates your license,
|
||||||
|
and (b) permanently, if the copyright holder fails to notify you of the violation
|
||||||
|
by some reasonable means prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is reinstated permanently
|
||||||
|
if the copyright holder notifies you of the violation by some reasonable means,
|
||||||
|
this is the first time you have received notice of violation of this License
|
||||||
|
(for any work) from that copyright holder, and you cure the violation prior
|
||||||
|
to 30 days after your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the licenses
|
||||||
|
of parties who have received copies or rights from you under this License.
|
||||||
|
If your rights have been terminated and not permanently reinstated, you do
|
||||||
|
not qualify to receive new licenses for the same material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or run a copy
|
||||||
|
of the Program. Ancillary propagation of a covered work occurring solely as
|
||||||
|
a consequence of using peer-to-peer transmission to receive a copy likewise
|
||||||
|
does not require acceptance. However, nothing other than this License grants
|
||||||
|
you permission to propagate or modify any covered work. These actions infringe
|
||||||
|
copyright if you do not accept this License. Therefore, by modifying or propagating
|
||||||
|
a covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically receives
|
||||||
|
a license from the original licensors, to run, modify and propagate that work,
|
||||||
|
subject to this License. You are not responsible for enforcing compliance
|
||||||
|
by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an organization,
|
||||||
|
or substantially all assets of one, or subdividing an organization, or merging
|
||||||
|
organizations. If propagation of a covered work results from an entity transaction,
|
||||||
|
each party to that transaction who receives a copy of the work also receives
|
||||||
|
whatever licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the Corresponding
|
||||||
|
Source of the work from the predecessor in interest, if the predecessor has
|
||||||
|
it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the rights
|
||||||
|
granted or affirmed under this License. For example, you may not impose a
|
||||||
|
license fee, royalty, or other charge for exercise of rights granted under
|
||||||
|
this License, and you may not initiate litigation (including a cross-claim
|
||||||
|
or counterclaim in a lawsuit) alleging that any patent claim is infringed
|
||||||
|
by making, using, selling, offering for sale, or importing the Program or
|
||||||
|
any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this License
|
||||||
|
of the Program or a work on which the Program is based. The work thus licensed
|
||||||
|
is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims owned or controlled
|
||||||
|
by the contributor, whether already acquired or hereafter acquired, that would
|
||||||
|
be infringed by some manner, permitted by this License, of making, using,
|
||||||
|
or selling its contributor version, but do not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of the contributor
|
||||||
|
version. For purposes of this definition, "control" includes the right to
|
||||||
|
grant patent sublicenses in a manner consistent with the requirements of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free patent
|
||||||
|
license under the contributor's essential patent claims, to make, use, sell,
|
||||||
|
offer for sale, import and otherwise run, modify and propagate the contents
|
||||||
|
of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express agreement
|
||||||
|
or commitment, however denominated, not to enforce a patent (such as an express
|
||||||
|
permission to practice a patent or covenant not to sue for patent infringement).
|
||||||
|
To "grant" such a patent license to a party means to make such an agreement
|
||||||
|
or commitment not to enforce a patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license, and the
|
||||||
|
Corresponding Source of the work is not available for anyone to copy, free
|
||||||
|
of charge and under the terms of this License, through a publicly available
|
||||||
|
network server or other readily accessible means, then you must either (1)
|
||||||
|
cause the Corresponding Source to be so available, or (2) arrange to deprive
|
||||||
|
yourself of the benefit of the patent license for this particular work, or
|
||||||
|
(3) arrange, in a manner consistent with the requirements of this License,
|
||||||
|
to extend the patent license to downstream recipients. "Knowingly relying"
|
||||||
|
means you have actual knowledge that, but for the patent license, your conveying
|
||||||
|
the covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that country
|
||||||
|
that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or arrangement,
|
||||||
|
you convey, or propagate by procuring conveyance of, a covered work, and grant
|
||||||
|
a patent license to some of the parties receiving the covered work authorizing
|
||||||
|
them to use, propagate, modify or convey a specific copy of the covered work,
|
||||||
|
then the patent license you grant is automatically extended to all recipients
|
||||||
|
of the covered work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within the scope
|
||||||
|
of its coverage, prohibits the exercise of, or is conditioned on the non-exercise
|
||||||
|
of one or more of the rights that are specifically granted under this License.
|
||||||
|
You may not convey a covered work if you are a party to an arrangement with
|
||||||
|
a third party that is in the business of distributing software, under which
|
||||||
|
you make payment to the third party based on the extent of your activity of
|
||||||
|
conveying the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory patent
|
||||||
|
license (a) in connection with copies of the covered work conveyed by you
|
||||||
|
(or copies made from those copies), or (b) primarily for and in connection
|
||||||
|
with specific products or compilations that contain the covered work, unless
|
||||||
|
you entered into that arrangement, or that patent license was granted, prior
|
||||||
|
to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting any implied
|
||||||
|
license or other defenses to infringement that may otherwise be available
|
||||||
|
to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or otherwise)
|
||||||
|
that contradict the conditions of this License, they do not excuse you from
|
||||||
|
the conditions of this License. If you cannot convey a covered work so as
|
||||||
|
to satisfy simultaneously your obligations under this License and any other
|
||||||
|
pertinent obligations, then as a consequence you may not convey it at all.
|
||||||
|
For example, if you agree to terms that obligate you to collect a royalty
|
||||||
|
for further conveying from those to whom you convey the Program, the only
|
||||||
|
way you could satisfy both those terms and this License would be to refrain
|
||||||
|
entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have permission to
|
||||||
|
link or combine any covered work with a work licensed under version 3 of the
|
||||||
|
GNU Affero General Public License into a single combined work, and to convey
|
||||||
|
the resulting work. The terms of this License will continue to apply to the
|
||||||
|
part which is the covered work, but the special requirements of the GNU Affero
|
||||||
|
General Public License, section 13, concerning interaction through a network
|
||||||
|
will apply to the combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of the
|
||||||
|
GNU 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 Program specifies
|
||||||
|
that a certain numbered version of the GNU General Public License "or any
|
||||||
|
later version" applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that numbered version or of any later version published
|
||||||
|
by the Free Software Foundation. If the Program does not specify a version
|
||||||
|
number of the GNU General Public License, you may choose any version ever
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future versions of
|
||||||
|
the GNU General Public License can be used, that proxy's public statement
|
||||||
|
of acceptance of a version permanently authorizes you to choose that version
|
||||||
|
for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different permissions. However,
|
||||||
|
no additional obligations are imposed on any author or copyright holder as
|
||||||
|
a result of your choosing to follow a later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
|
||||||
|
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM
|
||||||
|
PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
|
||||||
|
CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
|
||||||
|
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM
|
||||||
|
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||||
|
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO
|
||||||
|
USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
|
||||||
|
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
|
||||||
|
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
|
||||||
|
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided above cannot
|
||||||
|
be given local legal effect according to their terms, reviewing courts shall
|
||||||
|
apply local law that most closely approximates an absolute waiver of all civil
|
||||||
|
liability in connection with the Program, unless a warranty or assumption
|
||||||
|
of liability accompanies a copy of the Program in return for a fee. END OF
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest possible
|
||||||
|
use to the public, the best way to achieve this is to make it free software
|
||||||
|
which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest to attach
|
||||||
|
them to the start of each source file to most effectively state the exclusion
|
||||||
|
of warranty; and each file should have at least the "copyright" line and a
|
||||||
|
pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program 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.
|
||||||
|
|
||||||
|
This program 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
|
||||||
|
this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short notice like
|
||||||
|
this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
|
||||||
|
This is free software, and you are welcome to redistribute it under certain
|
||||||
|
conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands might
|
||||||
|
be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary. For
|
||||||
|
more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General Public
|
||||||
|
License instead of this License. But first, please read <https://www.gnu.org/
|
||||||
|
licenses /why-not-lgpl.html>.
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts as the
|
||||||
|
successor of the GNU Library Public License, version 2, hence the version
|
||||||
|
number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public Licenses are intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some specially
|
||||||
|
designated software packages--typically libraries--of the Free Software Foundation
|
||||||
|
and other authors who decide to use it. You can use it too, but we suggest
|
||||||
|
you first think carefully about whether this license or the ordinary General
|
||||||
|
Public License is the better strategy to use in any particular case, based
|
||||||
|
on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use, not price.
|
||||||
|
Our General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish); that you receive source code or can get it if you want it; that you
|
||||||
|
can change the software and use pieces of it in new free programs; and that
|
||||||
|
you are informed that you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid distributors
|
||||||
|
to deny you these rights or to ask you to surrender these rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis or for
|
||||||
|
a fee, you must give the recipients all the rights that we gave you. You must
|
||||||
|
make sure that they, too, receive or can get the source code. If you link
|
||||||
|
other code with the library, you must provide complete object files to the
|
||||||
|
recipients, so that they can relink them with the library after making changes
|
||||||
|
to the library and recompiling it. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the library,
|
||||||
|
and (2) we offer you this license, which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that there is no
|
||||||
|
warranty for the free library. Also, if the library is modified by someone
|
||||||
|
else and passed on, the recipients should know that what they have is not
|
||||||
|
the original version, so that the original author's reputation will not be
|
||||||
|
affected by problems that might be introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of any free
|
||||||
|
program. We wish to make sure that a company cannot effectively restrict the
|
||||||
|
users of a free program by obtaining a restrictive license from a patent holder.
|
||||||
|
Therefore, we insist that any patent license obtained for a version of the
|
||||||
|
library must be consistent with the full freedom of use specified in this
|
||||||
|
license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||||
|
General Public License. This license, the GNU Lesser General Public License,
|
||||||
|
applies to certain designated libraries, and is quite different from the ordinary
|
||||||
|
General Public License. We use this license for certain libraries in order
|
||||||
|
to permit linking those libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using a shared
|
||||||
|
library, the combination of the two is legally speaking a combined work, a
|
||||||
|
derivative of the original library. The ordinary General Public License therefore
|
||||||
|
permits such linking only if the entire combination fits its criteria of freedom.
|
||||||
|
The Lesser General Public License permits more lax criteria for linking other
|
||||||
|
code with the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it does Less
|
||||||
|
to protect the user's freedom than the ordinary General Public License. It
|
||||||
|
also provides other free software developers Less of an advantage over competing
|
||||||
|
non-free programs. These disadvantages are the reason we use the ordinary
|
||||||
|
General Public License for many libraries. However, the Lesser license provides
|
||||||
|
advantages in certain special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to encourage the
|
||||||
|
widest possible use of a certain library, so that it becomes a de-facto standard.
|
||||||
|
To achieve this, non-free programs must be allowed to use the library. A more
|
||||||
|
frequent case is that a free library does the same job as widely used non-free
|
||||||
|
libraries. In this case, there is little to gain by limiting the free library
|
||||||
|
to free software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free programs
|
||||||
|
enables a greater number of people to use a large body of free software. For
|
||||||
|
example, permission to use the GNU C Library in non-free programs enables
|
||||||
|
many more people to use the whole GNU operating system, as well as its variant,
|
||||||
|
the GNU/Linux operating system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the users'
|
||||||
|
freedom, it does ensure that the user of a program that is linked with the
|
||||||
|
Library has the freedom and the wherewithal to run that program using a modified
|
||||||
|
version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow. Pay close attention to the difference between a "work based on the
|
||||||
|
library" and a "work that uses the library". The former contains code derived
|
||||||
|
from the library, whereas the latter must be combined with the library in
|
||||||
|
order to run.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other program
|
||||||
|
which contains a notice placed by the copyright holder or other authorized
|
||||||
|
party saying it may be distributed under the terms of this Lesser General
|
||||||
|
Public License (also called "this License"). Each licensee is addressed as
|
||||||
|
"you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data prepared
|
||||||
|
so as to be conveniently linked with application programs (which use some
|
||||||
|
of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work which has
|
||||||
|
been distributed under these terms. A "work based on the Library" means either
|
||||||
|
the Library or any derivative work under copyright law: that is to say, a
|
||||||
|
work containing the Library or a portion of it, either verbatim or with modifications
|
||||||
|
and/or translated straightforwardly into another language. (Hereinafter, translation
|
||||||
|
is included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for making modifications
|
||||||
|
to it. For a library, complete source code means all the source code for all
|
||||||
|
modules it contains, plus any associated interface definition files, plus
|
||||||
|
the scripts used to control compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running a program
|
||||||
|
using the Library is not restricted, and output from such a program is covered
|
||||||
|
only if its contents constitute a work based on the Library (independent of
|
||||||
|
the use of the Library in a tool for writing it). Whether that is true depends
|
||||||
|
on what the Library does and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's complete source
|
||||||
|
code as you receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice and disclaimer
|
||||||
|
of warranty; keep intact all the notices that refer to this License and to
|
||||||
|
the absence of any warranty; and distribute a copy of this License along with
|
||||||
|
the Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion of it,
|
||||||
|
thus forming a work based on the Library, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no charge to all
|
||||||
|
third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a table of
|
||||||
|
data to be supplied by an application program that uses the facility, other
|
||||||
|
than as an argument passed when the facility is invoked, then you must make
|
||||||
|
a good faith effort to ensure that, in the event an application does not supply
|
||||||
|
such function or table, the facility still operates, and performs whatever
|
||||||
|
part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has a purpose
|
||||||
|
that is entirely well-defined independent of the application. Therefore, Subsection
|
||||||
|
2d requires that any application-supplied function or table used by this function
|
||||||
|
must be optional: if the application does not supply it, the square root function
|
||||||
|
must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Library, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Library, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library with
|
||||||
|
the Library (or with a work based on the Library) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public License
|
||||||
|
instead of this License to a given copy of the Library. To do this, you must
|
||||||
|
alter all the notices that refer to this License, so that they refer to the
|
||||||
|
ordinary GNU General Public License, version 2, instead of to this License.
|
||||||
|
(If a newer version than version 2 of the ordinary GNU General Public License
|
||||||
|
has appeared, then you can specify that version instead if you wish.) Do not
|
||||||
|
make any other change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for that copy,
|
||||||
|
so the ordinary GNU General Public License applies to all subsequent copies
|
||||||
|
and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of the Library
|
||||||
|
into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or derivative of
|
||||||
|
it, under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you accompany it with the complete corresponding
|
||||||
|
machine-readable source code, which must be distributed under the terms of
|
||||||
|
Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy from a designated
|
||||||
|
place, then offering equivalent access to copy the source code from the same
|
||||||
|
place satisfies the requirement to distribute the source code, even though
|
||||||
|
third parties are not compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the Library, but
|
||||||
|
is designed to work with the Library by being compiled or linked with it,
|
||||||
|
is called a "work that uses the Library". Such a work, in isolation, is not
|
||||||
|
a derivative work of the Library, and therefore falls outside the scope of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library creates an
|
||||||
|
executable that is a derivative of the Library (because it contains portions
|
||||||
|
of the Library), rather than a "work that uses the library". The executable
|
||||||
|
is therefore covered by this License. Section 6 states terms for distribution
|
||||||
|
of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file that
|
||||||
|
is part of the Library, the object code for the work may be a derivative work
|
||||||
|
of the Library even though the source code is not. Whether this is true is
|
||||||
|
especially significant if the work can be linked without the Library, or if
|
||||||
|
the work is itself a library. The threshold for this to be true is not precisely
|
||||||
|
defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data structure layouts
|
||||||
|
and accessors, and small macros and small inline functions (ten lines or less
|
||||||
|
in length), then the use of the object file is unrestricted, regardless of
|
||||||
|
whether it is legally a derivative work. (Executables containing this object
|
||||||
|
code plus portions of the Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||||
|
the object code for the work under the terms of Section 6. Any executables
|
||||||
|
containing that work also fall under Section 6, whether or not they are linked
|
||||||
|
directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or link a "work
|
||||||
|
that uses the Library" with the Library to produce a work containing portions
|
||||||
|
of the Library, and distribute that work under terms of your choice, provided
|
||||||
|
that the terms permit modification of the work for the customer's own use
|
||||||
|
and reverse engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
You must supply a copy of this License. If the work during execution displays
|
||||||
|
copyright notices, you must include the copyright notice for the Library among
|
||||||
|
them, as well as a reference directing the user to the copy of this License.
|
||||||
|
Also, you must do one of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding machine-readable source
|
||||||
|
code for the Library including whatever changes were used in the work (which
|
||||||
|
must be distributed under Sections 1 and 2 above); and, if the work is an
|
||||||
|
executable linked with the Library, with the complete machine-readable "work
|
||||||
|
that uses the Library", as object code and/or source code, so that the user
|
||||||
|
can modify the Library and then relink to produce a modified executable containing
|
||||||
|
the modified Library. (It is understood that the user who changes the contents
|
||||||
|
of definitions files in the Library will not necessarily be able to recompile
|
||||||
|
the application to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the Library. A
|
||||||
|
suitable mechanism is one that (1) uses at run time a copy of the library
|
||||||
|
already present on the user's computer system, rather than copying library
|
||||||
|
functions into the executable, and (2) will operate properly with a modified
|
||||||
|
version of the library, if the user installs one, as long as the modified
|
||||||
|
version is interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at least three years,
|
||||||
|
to give the same user the materials specified in Subsection 6a, above, for
|
||||||
|
a charge no more than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy from a designated
|
||||||
|
place, offer equivalent access to copy the above specified materials from
|
||||||
|
the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these materials or
|
||||||
|
that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the Library" must
|
||||||
|
include any data and utility programs needed for reproducing the executable
|
||||||
|
from it. However, as a special exception, the materials to be distributed
|
||||||
|
need not include anything that is normally distributed (in either source or
|
||||||
|
binary form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component itself
|
||||||
|
accompanies the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license restrictions of
|
||||||
|
other proprietary libraries that do not normally accompany the operating system.
|
||||||
|
Such a contradiction means you cannot use both them and the Library together
|
||||||
|
in an executable that you distribute.
|
||||||
|
|
||||||
|
7. 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 not covered by
|
||||||
|
this License, and distribute such a combined library, provided that the separate
|
||||||
|
distribution of the work based on the Library and of the other library facilities
|
||||||
|
is otherwise permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the
|
||||||
|
Library, uncombined with any other library facilities. This must be distributed
|
||||||
|
under the terms of the Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact that part of
|
||||||
|
it is a work based on the Library, and explaining where to find the accompanying
|
||||||
|
uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute the Library
|
||||||
|
except as expressly provided under this License. Any attempt otherwise to
|
||||||
|
copy, modify, sublicense, link with, or distribute the Library is void, and
|
||||||
|
will automatically terminate your rights under this License. However, parties
|
||||||
|
who have received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Library or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Library
|
||||||
|
(or any work based on the Library), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the Library),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute, link with or modify the Library subject to these terms
|
||||||
|
and conditions. You may not impose any further restrictions on the recipients'
|
||||||
|
exercise of the rights granted herein. You are not responsible for enforcing
|
||||||
|
compliance by third parties with this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Library at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Library
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Library under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the 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 specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Library does not specify a license version number, you may choose any version
|
||||||
|
ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free programs
|
||||||
|
whose distribution conditions are incompatible with these, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free Software
|
||||||
|
Foundation, write to the Free Software Foundation; we sometimes make exceptions
|
||||||
|
for this. Our decision will be guided by the two goals of preserving the free
|
||||||
|
status of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest possible
|
||||||
|
use to the public, we recommend making it free software that everyone can
|
||||||
|
redistribute and change. You can do so by permitting redistribution under
|
||||||
|
these terms (or, alternatively, under the terms of the ordinary General Public
|
||||||
|
License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively convey
|
||||||
|
the exclusion of warranty; and each file should have at least the "copyright"
|
||||||
|
line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
< one line to give the library's name and an idea of what it does. >
|
||||||
|
|
||||||
|
Copyright (C) < year > < name of author >
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2.1 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This library 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 Lesser General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this library; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information
|
||||||
|
on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the library, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||||
|
|
||||||
|
the library `Frob' (a library for tweaking knobs) written
|
||||||
|
|
||||||
|
by James Random Hacker.
|
||||||
|
|
||||||
|
< signature of Ty Coon > , 1 April 1990
|
||||||
|
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.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.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
This library 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) at any later version that is
|
||||||
|
accepted by the membership of KDE e.V. (or its successor
|
||||||
|
approved by the membership of KDE e.V.), which shall act as a
|
||||||
|
proxy as defined in Section 14 of version 3 of the license.
|
||||||
|
|
||||||
|
This program 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.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3 of the license or (at your option) any later version
|
||||||
|
that is accepted by the membership of KDE e.V. (or its successor
|
||||||
|
approved by the membership of KDE e.V.), which shall act as a
|
||||||
|
proxy as defined in Section 6 of version 3 of the license.
|
||||||
|
|
||||||
|
This program 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.
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts as the
|
||||||
|
successor of the GNU Library Public License, version 2, hence the version
|
||||||
|
number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your freedom to share
|
||||||
|
and change it. By contrast, the GNU General Public Licenses are intended to
|
||||||
|
guarantee your freedom to share and change free software--to make sure the
|
||||||
|
software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some specially
|
||||||
|
designated software packages--typically libraries--of the Free Software Foundation
|
||||||
|
and other authors who decide to use it. You can use it too, but we suggest
|
||||||
|
you first think carefully about whether this license or the ordinary General
|
||||||
|
Public License is the better strategy to use in any particular case, based
|
||||||
|
on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use, not price.
|
||||||
|
Our General Public Licenses are designed to make sure that you have the freedom
|
||||||
|
to distribute copies of free software (and charge for this service if you
|
||||||
|
wish); that you receive source code or can get it if you want it; that you
|
||||||
|
can change the software and use pieces of it in new free programs; and that
|
||||||
|
you are informed that you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid distributors
|
||||||
|
to deny you these rights or to ask you to surrender these rights. These restrictions
|
||||||
|
translate to certain responsibilities for you if you distribute copies of
|
||||||
|
the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis or for
|
||||||
|
a fee, you must give the recipients all the rights that we gave you. You must
|
||||||
|
make sure that they, too, receive or can get the source code. If you link
|
||||||
|
other code with the library, you must provide complete object files to the
|
||||||
|
recipients, so that they can relink them with the library after making changes
|
||||||
|
to the library and recompiling it. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the library,
|
||||||
|
and (2) we offer you this license, which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that there is no
|
||||||
|
warranty for the free library. Also, if the library is modified by someone
|
||||||
|
else and passed on, the recipients should know that what they have is not
|
||||||
|
the original version, so that the original author's reputation will not be
|
||||||
|
affected by problems that might be introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of any free
|
||||||
|
program. We wish to make sure that a company cannot effectively restrict the
|
||||||
|
users of a free program by obtaining a restrictive license from a patent holder.
|
||||||
|
Therefore, we insist that any patent license obtained for a version of the
|
||||||
|
library must be consistent with the full freedom of use specified in this
|
||||||
|
license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||||
|
General Public License. This license, the GNU Lesser General Public License,
|
||||||
|
applies to certain designated libraries, and is quite different from the ordinary
|
||||||
|
General Public License. We use this license for certain libraries in order
|
||||||
|
to permit linking those libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using a shared
|
||||||
|
library, the combination of the two is legally speaking a combined work, a
|
||||||
|
derivative of the original library. The ordinary General Public License therefore
|
||||||
|
permits such linking only if the entire combination fits its criteria of freedom.
|
||||||
|
The Lesser General Public License permits more lax criteria for linking other
|
||||||
|
code with the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it does Less
|
||||||
|
to protect the user's freedom than the ordinary General Public License. It
|
||||||
|
also provides other free software developers Less of an advantage over competing
|
||||||
|
non-free programs. These disadvantages are the reason we use the ordinary
|
||||||
|
General Public License for many libraries. However, the Lesser license provides
|
||||||
|
advantages in certain special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to encourage the
|
||||||
|
widest possible use of a certain library, so that it becomes a de-facto standard.
|
||||||
|
To achieve this, non-free programs must be allowed to use the library. A more
|
||||||
|
frequent case is that a free library does the same job as widely used non-free
|
||||||
|
libraries. In this case, there is little to gain by limiting the free library
|
||||||
|
to free software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free programs
|
||||||
|
enables a greater number of people to use a large body of free software. For
|
||||||
|
example, permission to use the GNU C Library in non-free programs enables
|
||||||
|
many more people to use the whole GNU operating system, as well as its variant,
|
||||||
|
the GNU/Linux operating system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the users'
|
||||||
|
freedom, it does ensure that the user of a program that is linked with the
|
||||||
|
Library has the freedom and the wherewithal to run that program using a modified
|
||||||
|
version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and modification
|
||||||
|
follow. Pay close attention to the difference between a "work based on the
|
||||||
|
library" and a "work that uses the library". The former contains code derived
|
||||||
|
from the library, whereas the latter must be combined with the library in
|
||||||
|
order to run.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other program
|
||||||
|
which contains a notice placed by the copyright holder or other authorized
|
||||||
|
party saying it may be distributed under the terms of this Lesser General
|
||||||
|
Public License (also called "this License"). Each licensee is addressed as
|
||||||
|
"you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data prepared
|
||||||
|
so as to be conveniently linked with application programs (which use some
|
||||||
|
of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work which has
|
||||||
|
been distributed under these terms. A "work based on the Library" means either
|
||||||
|
the Library or any derivative work under copyright law: that is to say, a
|
||||||
|
work containing the Library or a portion of it, either verbatim or with modifications
|
||||||
|
and/or translated straightforwardly into another language. (Hereinafter, translation
|
||||||
|
is included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for making modifications
|
||||||
|
to it. For a library, complete source code means all the source code for all
|
||||||
|
modules it contains, plus any associated interface definition files, plus
|
||||||
|
the scripts used to control compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not covered
|
||||||
|
by this License; they are outside its scope. The act of running a program
|
||||||
|
using the Library is not restricted, and output from such a program is covered
|
||||||
|
only if its contents constitute a work based on the Library (independent of
|
||||||
|
the use of the Library in a tool for writing it). Whether that is true depends
|
||||||
|
on what the Library does and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's complete source
|
||||||
|
code as you receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice and disclaimer
|
||||||
|
of warranty; keep intact all the notices that refer to this License and to
|
||||||
|
the absence of any warranty; and distribute a copy of this License along with
|
||||||
|
the Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and you
|
||||||
|
may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion of it,
|
||||||
|
thus forming a work based on the Library, and copy and distribute such modifications
|
||||||
|
or work under the terms of Section 1 above, provided that you also meet all
|
||||||
|
of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices stating that
|
||||||
|
you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no charge to all
|
||||||
|
third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a table of
|
||||||
|
data to be supplied by an application program that uses the facility, other
|
||||||
|
than as an argument passed when the facility is invoked, then you must make
|
||||||
|
a good faith effort to ensure that, in the event an application does not supply
|
||||||
|
such function or table, the facility still operates, and performs whatever
|
||||||
|
part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has a purpose
|
||||||
|
that is entirely well-defined independent of the application. Therefore, Subsection
|
||||||
|
2d requires that any application-supplied function or table used by this function
|
||||||
|
must be optional: if the application does not supply it, the square root function
|
||||||
|
must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If identifiable
|
||||||
|
sections of that work are not derived from the Library, and can be reasonably
|
||||||
|
considered independent and separate works in themselves, then this License,
|
||||||
|
and its terms, do not apply to those sections when you distribute them as
|
||||||
|
separate works. But when you distribute the same sections as part of a whole
|
||||||
|
which is a work based on the Library, the distribution of the whole must be
|
||||||
|
on the terms of this License, whose permissions for other licensees extend
|
||||||
|
to the entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest your
|
||||||
|
rights to work written entirely by you; rather, the intent is to exercise
|
||||||
|
the right to control the distribution of derivative or collective works based
|
||||||
|
on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library with
|
||||||
|
the Library (or with a work based on the Library) on a volume of a storage
|
||||||
|
or distribution medium does not bring the other work under the scope of this
|
||||||
|
License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public License
|
||||||
|
instead of this License to a given copy of the Library. To do this, you must
|
||||||
|
alter all the notices that refer to this License, so that they refer to the
|
||||||
|
ordinary GNU General Public License, version 2, instead of to this License.
|
||||||
|
(If a newer version than version 2 of the ordinary GNU General Public License
|
||||||
|
has appeared, then you can specify that version instead if you wish.) Do not
|
||||||
|
make any other change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for that copy,
|
||||||
|
so the ordinary GNU General Public License applies to all subsequent copies
|
||||||
|
and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of the Library
|
||||||
|
into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or derivative of
|
||||||
|
it, under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you accompany it with the complete corresponding
|
||||||
|
machine-readable source code, which must be distributed under the terms of
|
||||||
|
Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy from a designated
|
||||||
|
place, then offering equivalent access to copy the source code from the same
|
||||||
|
place satisfies the requirement to distribute the source code, even though
|
||||||
|
third parties are not compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the Library, but
|
||||||
|
is designed to work with the Library by being compiled or linked with it,
|
||||||
|
is called a "work that uses the Library". Such a work, in isolation, is not
|
||||||
|
a derivative work of the Library, and therefore falls outside the scope of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library creates an
|
||||||
|
executable that is a derivative of the Library (because it contains portions
|
||||||
|
of the Library), rather than a "work that uses the library". The executable
|
||||||
|
is therefore covered by this License. Section 6 states terms for distribution
|
||||||
|
of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file that
|
||||||
|
is part of the Library, the object code for the work may be a derivative work
|
||||||
|
of the Library even though the source code is not. Whether this is true is
|
||||||
|
especially significant if the work can be linked without the Library, or if
|
||||||
|
the work is itself a library. The threshold for this to be true is not precisely
|
||||||
|
defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data structure layouts
|
||||||
|
and accessors, and small macros and small inline functions (ten lines or less
|
||||||
|
in length), then the use of the object file is unrestricted, regardless of
|
||||||
|
whether it is legally a derivative work. (Executables containing this object
|
||||||
|
code plus portions of the Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||||
|
the object code for the work under the terms of Section 6. Any executables
|
||||||
|
containing that work also fall under Section 6, whether or not they are linked
|
||||||
|
directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or link a "work
|
||||||
|
that uses the Library" with the Library to produce a work containing portions
|
||||||
|
of the Library, and distribute that work under terms of your choice, provided
|
||||||
|
that the terms permit modification of the work for the customer's own use
|
||||||
|
and reverse engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
You must supply a copy of this License. If the work during execution displays
|
||||||
|
copyright notices, you must include the copyright notice for the Library among
|
||||||
|
them, as well as a reference directing the user to the copy of this License.
|
||||||
|
Also, you must do one of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding machine-readable source
|
||||||
|
code for the Library including whatever changes were used in the work (which
|
||||||
|
must be distributed under Sections 1 and 2 above); and, if the work is an
|
||||||
|
executable linked with the Library, with the complete machine-readable "work
|
||||||
|
that uses the Library", as object code and/or source code, so that the user
|
||||||
|
can modify the Library and then relink to produce a modified executable containing
|
||||||
|
the modified Library. (It is understood that the user who changes the contents
|
||||||
|
of definitions files in the Library will not necessarily be able to recompile
|
||||||
|
the application to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the Library. A
|
||||||
|
suitable mechanism is one that (1) uses at run time a copy of the library
|
||||||
|
already present on the user's computer system, rather than copying library
|
||||||
|
functions into the executable, and (2) will operate properly with a modified
|
||||||
|
version of the library, if the user installs one, as long as the modified
|
||||||
|
version is interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at least three years,
|
||||||
|
to give the same user the materials specified in Subsection 6a, above, for
|
||||||
|
a charge no more than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy from a designated
|
||||||
|
place, offer equivalent access to copy the above specified materials from
|
||||||
|
the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these materials or
|
||||||
|
that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the Library" must
|
||||||
|
include any data and utility programs needed for reproducing the executable
|
||||||
|
from it. However, as a special exception, the materials to be distributed
|
||||||
|
need not include anything that is normally distributed (in either source or
|
||||||
|
binary form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component itself
|
||||||
|
accompanies the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license restrictions of
|
||||||
|
other proprietary libraries that do not normally accompany the operating system.
|
||||||
|
Such a contradiction means you cannot use both them and the Library together
|
||||||
|
in an executable that you distribute.
|
||||||
|
|
||||||
|
7. 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 not covered by
|
||||||
|
this License, and distribute such a combined library, provided that the separate
|
||||||
|
distribution of the work based on the Library and of the other library facilities
|
||||||
|
is otherwise permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the
|
||||||
|
Library, uncombined with any other library facilities. This must be distributed
|
||||||
|
under the terms of the Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact that part of
|
||||||
|
it is a work based on the Library, and explaining where to find the accompanying
|
||||||
|
uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute the Library
|
||||||
|
except as expressly provided under this License. Any attempt otherwise to
|
||||||
|
copy, modify, sublicense, link with, or distribute the Library is void, and
|
||||||
|
will automatically terminate your rights under this License. However, parties
|
||||||
|
who have received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not signed
|
||||||
|
it. However, nothing else grants you permission to modify or distribute the
|
||||||
|
Library or its derivative works. These actions are prohibited by law if you
|
||||||
|
do not accept this License. Therefore, by modifying or distributing the Library
|
||||||
|
(or any work based on the Library), you indicate your acceptance of this License
|
||||||
|
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the Library),
|
||||||
|
the recipient automatically receives a license from the original licensor
|
||||||
|
to copy, distribute, link with or modify the Library subject to these terms
|
||||||
|
and conditions. You may not impose any further restrictions on the recipients'
|
||||||
|
exercise of the rights granted herein. You are not responsible for enforcing
|
||||||
|
compliance by third parties with this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent infringement
|
||||||
|
or for any other reason (not limited to patent issues), conditions are imposed
|
||||||
|
on you (whether by court order, agreement or otherwise) that contradict the
|
||||||
|
conditions of this License, they do not excuse you from the conditions of
|
||||||
|
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||||
|
obligations under this License and any other pertinent obligations, then as
|
||||||
|
a consequence you may not distribute the Library at all. For example, if a
|
||||||
|
patent license would not permit royalty-free redistribution of the Library
|
||||||
|
by all those who receive copies directly or indirectly through you, then the
|
||||||
|
only way you could satisfy both it and this License would be to refrain entirely
|
||||||
|
from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any patents
|
||||||
|
or other property right claims or to contest validity of any such claims;
|
||||||
|
this section has the sole purpose of protecting the integrity of the free
|
||||||
|
software distribution system which is implemented by public license practices.
|
||||||
|
Many people have made generous contributions to the wide range of software
|
||||||
|
distributed through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing to
|
||||||
|
distribute software through any other system and a licensee cannot impose
|
||||||
|
that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to be a
|
||||||
|
consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in certain
|
||||||
|
countries either by patents or by copyrighted interfaces, the original copyright
|
||||||
|
holder who places the Library under this License may add an explicit geographical
|
||||||
|
distribution limitation excluding those countries, so that distribution is
|
||||||
|
permitted only in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the 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 specifies
|
||||||
|
a version number of this License which applies to it and "any later version",
|
||||||
|
you have the option of following the terms and conditions either of that version
|
||||||
|
or of any later version published by the Free Software Foundation. If the
|
||||||
|
Library does not specify a license version number, you may choose any version
|
||||||
|
ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free programs
|
||||||
|
whose distribution conditions are incompatible with these, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free Software
|
||||||
|
Foundation, write to the Free Software Foundation; we sometimes make exceptions
|
||||||
|
for this. Our decision will be guided by the two goals of preserving the free
|
||||||
|
status of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||||
|
THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||||
|
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
|
||||||
|
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||||
|
OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||||
|
THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||||
|
OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||||
|
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||||
|
OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
|
||||||
|
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest possible
|
||||||
|
use to the public, we recommend making it free software that everyone can
|
||||||
|
redistribute and change. You can do so by permitting redistribution under
|
||||||
|
these terms (or, alternatively, under the terms of the ordinary General Public
|
||||||
|
License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively convey
|
||||||
|
the exclusion of warranty; and each file should have at least the "copyright"
|
||||||
|
line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
< one line to give the library's name and an idea of what it does. >
|
||||||
|
|
||||||
|
Copyright (C) < year > < name of author >
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2.1 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
This library 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 Lesser General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this library; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information
|
||||||
|
on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the library, if necessary. Here
|
||||||
|
is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||||
|
|
||||||
|
the library `Frob' (a library for tweaking knobs) written
|
||||||
|
|
||||||
|
by James Random Hacker.
|
||||||
|
|
||||||
|
< signature of Ty Coon > , 1 April 1990
|
||||||
|
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.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.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3 of the license or (at your option) any later version
|
||||||
|
that is accepted by the membership of KDE e.V. (or its successor
|
||||||
|
approved by the membership of KDE e.V.), which shall act as a
|
||||||
|
proxy as defined in Section 6 of version 3 of the license.
|
||||||
|
|
||||||
|
This program 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.
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# KDE Connect for the Steam Frame
|
||||||
|
|
||||||
|
Frame Control's keyboard and trackpad work through KDE Connect running on the
|
||||||
|
Frame. The Frame doesn't come with it, so Frame Control ships it: the six
|
||||||
|
packages below, unchanged, exactly as Valve builds them for the Frame's
|
||||||
|
SteamOS (aarch64, packaged by "GitLab CI Package Builder
|
||||||
|
<ci-package-builder-1@steamos.cloud>"). On first use, Frame Control copies them
|
||||||
|
to the Frame over SSH and unpacks them into
|
||||||
|
`~/.local/share/frame-control/kdeconnect`.
|
||||||
|
|
||||||
|
They are separate programs and libraries under their own licences, listed
|
||||||
|
below; their licence texts are in `LICENSES/<package>/`. Frame Control itself
|
||||||
|
is MIT-licensed. It doesn't link to or include any of this code: it starts
|
||||||
|
`kdeconnectd` and talks to it over KDE Connect's network protocol, as a phone
|
||||||
|
would.
|
||||||
|
|
||||||
|
| Package | Version (Valve's build) | Licence | Complete source (Valve's source package) | Upstream |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| kdeconnect 24.02.2-1 | 24.02.2-1 | GPL-2.0-only or GPL-3.0-only or any later version accepted by KDE e.V. (LicenseRef-KDE-Accepted-GPL); some files LGPL-2.1/3.0, MIT, BSD-3-Clause, Apache-2.0, CC0-1.0 | [kdeconnect-24.02.2-1.src.tar.gz](https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/kdeconnect-24.02.2-1.src.tar.gz) | [KDE](https://download.kde.org/stable/release-service/24.02.2/src/kdeconnect-kde-24.02.2.tar.xz) |
|
||||||
|
| kcontacts 6.1.0-1 | 1:6.1.0-1 | LGPL-2.0-or-later; some files MIT, BSD-3-Clause, CC0-1.0, Unicode-DFS-2016 | [kcontacts-6.1.0-1.src.tar.gz](https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/kcontacts-6.1.0-1.src.tar.gz) | [KDE](https://download.kde.org/stable/frameworks/6.1/kcontacts-6.1.0.tar.xz) |
|
||||||
|
| kpeople 6.1.0-1 | 6.1.0-1 | LGPL-2.1-or-later; some files BSD-3-Clause, CC0-1.0 | [kpeople-6.1.0-1.src.tar.gz](https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/kpeople-6.1.0-1.src.tar.gz) | [KDE](https://download.kde.org/stable/frameworks/6.1/kpeople-6.1.0.tar.xz) |
|
||||||
|
| modemmanager-qt 6.1.0-1 | 6.1.0-1 | LGPL-2.1-only or LGPL-3.0-only (or later accepted by KDE e.V.); some files BSD-3-Clause, CC0-1.0 | [modemmanager-qt-6.1.0-1.src.tar.gz](https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/modemmanager-qt-6.1.0-1.src.tar.gz) | [KDE](https://download.kde.org/stable/frameworks/6.1/modemmanager-qt-6.1.0.tar.xz) |
|
||||||
|
| pulseaudio-qt 1.4.0-3 | 1.4.0-3 | LGPL-2.1-only or LGPL-3.0-only (or later accepted by KDE e.V.) | [pulseaudio-qt-1.4.0-3.src.tar.gz](https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/pulseaudio-qt-1.4.0-3.src.tar.gz) | [KDE](https://download.kde.org/stable/pulseaudio-qt/pulseaudio-qt-1.4.0.tar.xz) |
|
||||||
|
| libfakekey 0.3-2 | 0.3-2 | LGPL-2.0-or-later (Copyright © 2004 OpenedHand) | [libfakekey-0.3-2.src.tar.gz](https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/libfakekey-0.3-2.src.tar.gz) | [Yocto Project git, tag 0.3](https://git.yoctoproject.org/libfakekey/tag/?h=0.3) |
|
||||||
|
|
||||||
|
## Source code
|
||||||
|
|
||||||
|
Each source package is Valve's, copied unchanged: the upstream release
|
||||||
|
tarball (with its signature where upstream signs it), the PKGBUILD Valve built
|
||||||
|
from, and the signing keys. None of them carry patches. Frame Control
|
||||||
|
publishes them next to the binaries, in the
|
||||||
|
[kdeconnect-frame-24.02.2-1 release](https://github.com/saphid/frame-control/releases/tag/kdeconnect-frame-24.02.2-1),
|
||||||
|
so the source is available from the same place as the binaries for as long as
|
||||||
|
Frame Control distributes them. `packages.json` pins each binary and source
|
||||||
|
package by SHA-256.
|
||||||
|
|
||||||
|
If a link ever stops working, open an issue at
|
||||||
|
https://github.com/saphid/frame-control/issues and we'll send you the source.
|
||||||
|
|
||||||
|
## Replacing them
|
||||||
|
|
||||||
|
These are ordinary files in your home folder on the Frame: you may replace
|
||||||
|
them with your own builds, and Frame Control will use whatever `kdeconnectd`
|
||||||
|
is at `~/.local/share/frame-control/kdeconnect/root/usr/lib/kdeconnectd`
|
||||||
|
(or the Frame's own `/usr/lib/kdeconnectd`, if SteamOS ever ships one).
|
||||||
|
Deleting `root/.frame-control-packages` there makes Frame Control copy its
|
||||||
|
bundled build back on next use.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Download the KDE Connect packages Frame Control ships for the Frame into
|
||||||
|
frame/kdeconnect/packages, checking each against its pinned SHA-256
|
||||||
|
(packages.json). Already-downloaded files that match are kept.
|
||||||
|
|
||||||
|
ios/scripts/make_frame_bundle.py runs this; app/build/fetch-deps.js does the same
|
||||||
|
in Node. Usage: python3 frame/kdeconnect/fetch.py
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
PACKAGES = HERE / "packages"
|
||||||
|
|
||||||
|
|
||||||
|
def manifest():
|
||||||
|
return json.loads((HERE / "packages.json").read_text())
|
||||||
|
|
||||||
|
|
||||||
|
def sha256(data):
|
||||||
|
return hashlib.sha256(data).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def fetch():
|
||||||
|
m = manifest()
|
||||||
|
PACKAGES.mkdir(exist_ok=True)
|
||||||
|
for p in m["packages"]:
|
||||||
|
path = PACKAGES / p["file"]
|
||||||
|
if path.is_file() and sha256(path.read_bytes()) == p["sha256"]:
|
||||||
|
continue
|
||||||
|
url = m["release"] + p["file"]
|
||||||
|
with urllib.request.urlopen(url, timeout=120) as r:
|
||||||
|
data = r.read()
|
||||||
|
if sha256(data) != p["sha256"]:
|
||||||
|
raise SystemExit(f"checksum mismatch for {url}: {sha256(data)}")
|
||||||
|
tmp = path.with_suffix(".part")
|
||||||
|
tmp.write_bytes(data)
|
||||||
|
tmp.replace(path)
|
||||||
|
wanted = {p["file"] for p in m["packages"]}
|
||||||
|
for old in PACKAGES.iterdir():
|
||||||
|
if old.name not in wanted:
|
||||||
|
old.unlink()
|
||||||
|
return [PACKAGES / p["file"] for p in m["packages"]]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for path in fetch():
|
||||||
|
print(path.relative_to(HERE.parent.parent), file=sys.stderr)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
"about": "KDE Connect for the Steam Frame, as Valve builds it for the Frame's SteamOS (aarch64), and the libraries it needs that the Frame lacks. See NOTICE.md.",
|
||||||
|
"release": "https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "kdeconnect",
|
||||||
|
"version": "24.02.2-1",
|
||||||
|
"file": "kdeconnect-24.02.2-1-aarch64.pkg.tar.zst",
|
||||||
|
"sha256": "4cd88498f3dcd9563009f84022aeb9b60344b8afbcd1d3c5aeddc4d09727ef9d",
|
||||||
|
"size": 1390996,
|
||||||
|
"licenses": [
|
||||||
|
"GPL-2.0-or-later"
|
||||||
|
],
|
||||||
|
"source": "https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/kdeconnect-24.02.2-1.src.tar.gz",
|
||||||
|
"source_sha256": "6e4b8e51f71a5492178231fa94d6cb858d7e752c51ea2b02208179f370e0733d",
|
||||||
|
"upstream": "https://download.kde.org/stable/release-service/24.02.2/src/kdeconnect-kde-24.02.2.tar.xz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kcontacts",
|
||||||
|
"version": "6.1.0-1",
|
||||||
|
"file": "kcontacts-6.1.0-1-aarch64.pkg.tar.zst",
|
||||||
|
"sha256": "247c9dd8f8b781e69f818c96170756556a0b0bb75187ad88627217a5f924f90a",
|
||||||
|
"size": 838848,
|
||||||
|
"licenses": [
|
||||||
|
"LGPL-2.0-or-later"
|
||||||
|
],
|
||||||
|
"source": "https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/kcontacts-6.1.0-1.src.tar.gz",
|
||||||
|
"source_sha256": "b3a1058e0eb13f6221de23835a128b166885d3b5f2a2126deafd62e556853316",
|
||||||
|
"upstream": "https://download.kde.org/stable/frameworks/6.1/kcontacts-6.1.0.tar.xz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kpeople",
|
||||||
|
"version": "6.1.0-1",
|
||||||
|
"file": "kpeople-6.1.0-1-aarch64.pkg.tar.zst",
|
||||||
|
"sha256": "68d1e680f1fa6b25bc1e8c9d9b2ab3bfe53a74c2b507da0f437a82c58a542942",
|
||||||
|
"size": 464354,
|
||||||
|
"licenses": [
|
||||||
|
"LGPL-2.1-or-later"
|
||||||
|
],
|
||||||
|
"source": "https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/kpeople-6.1.0-1.src.tar.gz",
|
||||||
|
"source_sha256": "3ad93d2709dbea0b3ca4fbcfbd6052f739aff3d6053de03347593d1261bd5268",
|
||||||
|
"upstream": "https://download.kde.org/stable/frameworks/6.1/kpeople-6.1.0.tar.xz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "modemmanager-qt",
|
||||||
|
"version": "6.1.0-1",
|
||||||
|
"file": "modemmanager-qt-6.1.0-1-aarch64.pkg.tar.zst",
|
||||||
|
"sha256": "8e918c0c2c84ed4bb2383f3de9288a2cf25cc52a26d749b63df962a3d2cc6d73",
|
||||||
|
"size": 627059,
|
||||||
|
"licenses": [
|
||||||
|
"LGPL-2.1-only",
|
||||||
|
"LGPL-3.0-only"
|
||||||
|
],
|
||||||
|
"source": "https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/modemmanager-qt-6.1.0-1.src.tar.gz",
|
||||||
|
"source_sha256": "4004dfe8d537399e6a7a0cea0b0ec8a4c995ab34cc75a15fb97a12c315555b09",
|
||||||
|
"upstream": "https://download.kde.org/stable/frameworks/6.1/modemmanager-qt-6.1.0.tar.xz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pulseaudio-qt",
|
||||||
|
"version": "1.4.0-3",
|
||||||
|
"file": "pulseaudio-qt-1.4.0-3-aarch64.pkg.tar.zst",
|
||||||
|
"sha256": "b57b91b3cc2b6e80e69a80a7cd93ea0dc4f22b5d54a5ac25540c854e31de92ce",
|
||||||
|
"size": 303766,
|
||||||
|
"licenses": [
|
||||||
|
"LGPL-2.1-only",
|
||||||
|
"LGPL-3.0-only"
|
||||||
|
],
|
||||||
|
"source": "https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/pulseaudio-qt-1.4.0-3.src.tar.gz",
|
||||||
|
"source_sha256": "032490884260eeb5a66d98215926fa7ae7d678f6bf93cafe4be19c0cfa5459da",
|
||||||
|
"upstream": "https://download.kde.org/stable/pulseaudio-qt/pulseaudio-qt-1.4.0.tar.xz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "libfakekey",
|
||||||
|
"version": "0.3-2",
|
||||||
|
"file": "libfakekey-0.3-2-aarch64.pkg.tar.zst",
|
||||||
|
"sha256": "a7563b1dcc3702f9ab9c7c07587fdc312119461c545ae5aee693e6a829e8206a",
|
||||||
|
"size": 6804,
|
||||||
|
"licenses": [
|
||||||
|
"LGPL-2.0-or-later"
|
||||||
|
],
|
||||||
|
"source": "https://github.com/saphid/frame-control/releases/download/kdeconnect-frame-24.02.2-1/libfakekey-0.3-2.src.tar.gz",
|
||||||
|
"source_sha256": "0e8e989660b80ec2fe2569bde5710102841bd76f981acdb1e4b72d4d8d2b4da3",
|
||||||
|
"upstream": "https://git.yoctoproject.org/libfakekey/tag/?h=0.3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Preserve upstream headers byte-for-byte, including their existing whitespace.
|
||||||
|
vendor/** -whitespace
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build-*/
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
project(FrameXrCompat LANGUAGES CXX)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
add_library(compat_logic INTERFACE)
|
||||||
|
target_include_directories(compat_logic INTERFACE . vendor)
|
||||||
|
if(ANDROID)
|
||||||
|
add_library(XrApiLayer_FRAME_compat SHARED layer.cpp)
|
||||||
|
target_link_libraries(XrApiLayer_FRAME_compat PRIVATE compat_logic log)
|
||||||
|
target_compile_definitions(XrApiLayer_FRAME_compat PRIVATE XR_USE_PLATFORM_ANDROID XR_NO_PROTOTYPES)
|
||||||
|
target_compile_options(XrApiLayer_FRAME_compat PRIVATE -O2 -fvisibility=hidden -Wall -Wextra -Werror)
|
||||||
|
target_link_options(XrApiLayer_FRAME_compat PRIVATE -Wl,-z,max-page-size=16384 -Wl,--no-undefined -Wl,--exclude-libs,ALL -Wl,-s)
|
||||||
|
else()
|
||||||
|
enable_testing()
|
||||||
|
add_executable(compat_tests tests/compat_tests.cpp)
|
||||||
|
target_link_libraries(compat_tests PRIVATE compat_logic)
|
||||||
|
target_compile_options(compat_tests PRIVATE -Wall -Wextra -Werror)
|
||||||
|
add_test(NAME compat_logic COMMAND compat_tests)
|
||||||
|
add_executable(dispatch_tests tests/dispatch_tests.cpp)
|
||||||
|
target_link_libraries(dispatch_tests PRIVATE compat_logic)
|
||||||
|
target_include_directories(dispatch_tests PRIVATE tests/shims)
|
||||||
|
target_compile_definitions(dispatch_tests PRIVATE XR_USE_PLATFORM_ANDROID XR_NO_PROTOTYPES)
|
||||||
|
target_compile_options(dispatch_tests PRIVATE -Wall -Wextra -Werror)
|
||||||
|
add_test(NAME layer_dispatch COMMAND dispatch_tests)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
# Frame OpenXR compatibility API layer
|
||||||
|
|
||||||
|
`XR_APILAYER_FRAME_compat` is an APK-local implicit API layer for Steam Frame's
|
||||||
|
OpenXR 1.0 Android runtime. It translates selected 1.1 functionality; it is not
|
||||||
|
a conformant replacement for a complete 1.1 runtime. Device facts are in
|
||||||
|
[../../docs/vr-apks.md](../../docs/vr-apks.md). No headset was contacted during
|
||||||
|
implementation. Loading, Valve-layer coexistence, controller usability, and
|
||||||
|
Wolvic startup/rendering on Lepton remain **unverified on device**.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
- The negotiated `createApiLayerInstance` hook handles `xrCreateInstance`.
|
||||||
|
API requests >= 1.1 become `XR_API_VERSION_1_0` (1.0.63 with these headers),
|
||||||
|
rather than carrying an arbitrary application's patch into the 1.0 request.
|
||||||
|
Requests below 1.1 are unchanged. The loader may reject future API versions
|
||||||
|
before any layer runs.
|
||||||
|
- Enumerate downstream extensions through the **next layer's** GIPA before
|
||||||
|
creation. Only advertised promoted extensions are added, with duplicates
|
||||||
|
removed. All other application extensions remain, except the two stubs below.
|
||||||
|
Log added, stubbed and missing extensions with tag `FrameXrCompat`.
|
||||||
|
- Missing `XR_KHR_android_thread_settings` is advertised and accepted;
|
||||||
|
`xrSetAndroidApplicationThreadKHR` logs and returns success without changing
|
||||||
|
thread scheduling. Missing `XR_OCULUS_android_session_state_enable` is
|
||||||
|
advertised and accepted, with no commands or additional session behavior.
|
||||||
|
If downstream supports either extension, preserve it and its real commands.
|
||||||
|
Stubbing these capabilities is intentionally limited to accepting the app's
|
||||||
|
request; it does not provide Meta services.
|
||||||
|
- The manifest's `instance_extensions` makes both stubs visible during the
|
||||||
|
loader's pre-instance enumeration and validation. The layer also exposes
|
||||||
|
an enumeration implementation with count/capacity handling. Before a next
|
||||||
|
dispatch is available it can enumerate only its own extensions; normal app
|
||||||
|
global enumeration is performed by the loader, merging runtime and manifest
|
||||||
|
entries. See [loader_core.cpp L106–145](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_core.cpp#L106)
|
||||||
|
and [manifest parsing L549–558](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L549).
|
||||||
|
- Unknown missing extensions are never silently removed. Khronos may reject
|
||||||
|
them **before** entering the layer and name them in `OpenXR-Loader` logs
|
||||||
|
([loader_instance.cpp L149–176](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_instance.cpp#L149)).
|
||||||
|
If they reach this layer, it logs their names and preserves them for the
|
||||||
|
downstream failure.
|
||||||
|
- `xrLocateSpaces` dispatches to `xrLocateSpacesKHR` per session/instance.
|
||||||
|
This is the only command added in 1.1. The three relevant structure types
|
||||||
|
(`SPACES_LOCATE_INFO`, `SPACE_LOCATIONS`, `SPACE_VELOCITIES`) and their KHR
|
||||||
|
versions are **numeric aliases**, and the structs are typedef aliases in
|
||||||
|
these headers. Explicit type conversion of local input/output copies is
|
||||||
|
therefore an identity conversion; the velocity chain passes through without
|
||||||
|
mutating its types or linkage. Output arrays remain shared and receive the
|
||||||
|
runtime's results, including on error; caller struct headers remain intact.
|
||||||
|
- With palm pose enabled, rewrite both hand paths ending in
|
||||||
|
`/input/grip_surface/pose` to `/input/palm_ext/pose` in `xrStringToPath` and
|
||||||
|
in binding suggestions (including paths obtained before this layer rewrote
|
||||||
|
them). Unknown paths are left alone.
|
||||||
|
- `xrGetInstanceProperties` and unrelated commands pass through. The real
|
||||||
|
runtime's identity is not spoofed. Dispatch tables and session ownership are
|
||||||
|
locked, and removed after successful destruction. Downstream calls occur
|
||||||
|
outside the lock.
|
||||||
|
|
||||||
|
## Spec basis and interaction profiles
|
||||||
|
|
||||||
|
The pinned [1.1 promotions appendix, versions.adoc L16–156](https://github.com/KhronosGroup/OpenXR-Docs/blob/release-1.1.63/specification/sources/chapters/versions.adoc#L16)
|
||||||
|
includes a generated promotion list. Its source is the
|
||||||
|
[1.1.63 XML registry](https://github.com/KhronosGroup/OpenXR-Docs/blob/release-1.1.63/specification/registry/xr.xml),
|
||||||
|
selecting `extension[@promotedto='XR_VERSION_1_1']`. The full list implemented:
|
||||||
|
|
||||||
|
| Promoted extension | Handling |
|
||||||
|
| --- | --- |
|
||||||
|
| `XR_KHR_locate_spaces` | Enable if advertised; core command alias |
|
||||||
|
| `XR_EXT_local_floor` | Enable if advertised; reference-space enum is an alias |
|
||||||
|
| `XR_EXT_uuid` | Enable if advertised; type alias, no commands |
|
||||||
|
| `XR_EXT_palm_pose` | Enable if advertised; rewrite grip-surface paths |
|
||||||
|
| `XR_VARJO_quad_views` | Enable if advertised; view-configuration enum alias, support remains optional |
|
||||||
|
| `XR_EXT_samsung_odyssey_controller` | Enable if advertised |
|
||||||
|
| `XR_EXT_hp_mixed_reality_controller` | Enable if advertised |
|
||||||
|
| `XR_HTC_vive_cosmos_controller_interaction` | Enable if advertised |
|
||||||
|
| `XR_HTC_vive_focus3_controller_interaction` | Enable if advertised |
|
||||||
|
| `XR_ML_ml2_controller_interaction` | Enable if advertised |
|
||||||
|
| `XR_FB_touch_controller_pro` | Enable if advertised; old profile still usable |
|
||||||
|
| `XR_META_touch_controller_plus` | Enable if advertised; old profile still usable |
|
||||||
|
| `XR_BD_controller_interaction` | Enable if advertised |
|
||||||
|
| `XR_KHR_maintenance1` | Enable if advertised (included in the pinned registry's promotion list) |
|
||||||
|
|
||||||
|
`XR_EXT_hand_interaction` is **not** promoted to 1.1. Preserve it if requested;
|
||||||
|
it is not auto-enabled merely because the runtime advertises it. The parent
|
||||||
|
provided its availability; the device notes list extensions non-exhaustively.
|
||||||
|
|
||||||
|
The registry's `XR_VERSION_1_1` feature introduces 13 profile paths. For
|
||||||
|
Samsung Odyssey, HP mixed reality, HTC Cosmos/Focus3, ML2 and the three
|
||||||
|
ByteDance Pico profiles, drop a whole suggestion call with success when the
|
||||||
|
corresponding extension is absent. Keep it when the extension is enabled.
|
||||||
|
Drop the five new Meta paths: `touch_pro_controller`, `touch_plus_controller`,
|
||||||
|
`touch_controller_rift_cv1`, `touch_controller_quest_1_rift_s`, and
|
||||||
|
`touch_controller_quest_2`. The verified runtime notes do not list these;
|
||||||
|
Pro/Plus promotion also renames several components, so the old extension's
|
||||||
|
presence alone does not prove support for the new profile. This layer does
|
||||||
|
not implement those component conversions. Existing old Pro/Plus profile
|
||||||
|
paths are passed through if the app uses them.
|
||||||
|
|
||||||
|
Preserve the documented `oculus/touch_controller`, `khr/simple_controller`,
|
||||||
|
`valve/frame_controller` and original PC profile paths. The notes' "usual PC
|
||||||
|
controllers" is not a complete enumerated list: availability of promoted PC
|
||||||
|
profiles is inferred from the runtime's extension advertisement, not invented
|
||||||
|
from that phrase. OpenXR has no general profile enumeration API. Dropping
|
||||||
|
suggestions prevents an unsupported 1.1 profile from aborting initialization;
|
||||||
|
it does not create bindings, so the app must also suggest a supported fallback.
|
||||||
|
|
||||||
|
## Headers and licensing
|
||||||
|
|
||||||
|
The required public headers (`openxr.h`, `openxr_platform.h`,
|
||||||
|
`openxr_platform_defines.h`) are unmodified from
|
||||||
|
[OpenXR-SDK release-1.1.63](https://github.com/KhronosGroup/OpenXR-SDK/tree/release-1.1.63/include/openxr),
|
||||||
|
commit [f2448a8](https://github.com/KhronosGroup/OpenXR-SDK/commit/f2448a8797c85814aa892efc1ab8707900fbcc78).
|
||||||
|
The requested filename `loader_interfaces.h` no longer exists in 1.1 SDK
|
||||||
|
releases: negotiation was ratified and moved to `openxr_loader_negotiation.h`
|
||||||
|
in 1.0.33 ([spec history L166–186](https://github.com/KhronosGroup/OpenXR-Docs/blob/release-1.1.63/specification/sources/chapters/versions.adoc#L166)).
|
||||||
|
To retain that requested interface filename, the layer uses the unmodified,
|
||||||
|
ABI-compatible [last legacy header from SDK-Source release-1.0.32](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.0.32/src/common/loader_interfaces.h).
|
||||||
|
Only these four headers are vendored. They offer Apache-2.0 OR MIT; this
|
||||||
|
vendoring uses Apache-2.0, reproduced in [vendor/LICENSE](vendor/LICENSE).
|
||||||
|
|
||||||
|
## Android discovery and coexistence
|
||||||
|
|
||||||
|
**Minimum asset-discovery loader release: 1.0.25 (2022-09-02).** Its
|
||||||
|
[release commit, 15c3d8e](https://github.com/KhronosGroup/OpenXR-SDK-Source/commit/15c3d8eb99994e5365d6b6f96eefaf4c51a65a9d)
|
||||||
|
explicitly announces APK-packaged API layers;
|
||||||
|
[manifest_file.cpp L665–723](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.0.25/src/loader/manifest_file.cpp#L665)
|
||||||
|
implements discovery, and [L934–940](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.0.25/src/loader/manifest_file.cpp#L934)
|
||||||
|
adds asset manifests after filesystem manifests. This is not a 1.1-only feature.
|
||||||
|
|
||||||
|
**For an app requesting API 1.1, use a 1.1 loader (first release 1.1.36) or
|
||||||
|
newer.** A 1.0 loader rejects the request before the layer can downgrade it;
|
||||||
|
see [loader_core.cpp L217–230](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_core.cpp#L217).
|
||||||
|
The runtime can remain 1.0.
|
||||||
|
|
||||||
|
Source inspection at release-1.1.63 confirms:
|
||||||
|
|
||||||
|
- [manifest_file.cpp L720–783](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L720)
|
||||||
|
uses the initialized Android asset manager and scans
|
||||||
|
`openxr/1/api_layers/implicit.d/` for JSON. The application must initialize
|
||||||
|
the loader with its Android context (`xrInitializeLoaderKHR`). In the APK,
|
||||||
|
the entry is `assets/openxr/1/api_layers/implicit.d/XrApiLayer_FRAME_compat.json`.
|
||||||
|
- A bare `library_path`, as used here, is **not explicitly concatenated** with
|
||||||
|
`nativeLibraryDir`. [L878–900](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L878)
|
||||||
|
leaves a bare name for normal dynamic-linker search in the application's
|
||||||
|
namespace (including its native library directory). Relative paths with a
|
||||||
|
slash use [LocateLibraryInAssets L943–952](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L943),
|
||||||
|
which resolves against `GetAndroidNativeLibraryDir()`.
|
||||||
|
[loader_init_data.cpp L87–96](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/loader_init_data.cpp#L87)
|
||||||
|
obtains the asset manager and `ApplicationInfo.nativeLibraryDir` via JNI.
|
||||||
|
Wolvic's supplied manifest has `android:extractNativeLibs="true"`.
|
||||||
|
- [android_utilities.cpp L267–322](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/android_utilities.cpp#L267)
|
||||||
|
handles runtime broker discovery, not APK layer discovery. Its
|
||||||
|
`native_lib_dir + so_filename` and `dlopen` refer to the runtime package.
|
||||||
|
They must not be confused with the app's layer library resolution.
|
||||||
|
- [manifest_file.cpp L1008–1023](https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/release-1.1.63/src/loader/manifest_file.cpp#L1008)
|
||||||
|
retains filesystem manifests and then appends asset manifests. This leaves
|
||||||
|
`/vendor`'s `XR_APILAYER_VALVE_fdm_injection` discoverable. This layer advances
|
||||||
|
`nextInfo` exactly once, uses next-layer GIPA, preserves the rest of the
|
||||||
|
create-info chain and never opens `vrclient.so` directly. No environment
|
||||||
|
layer-list override or assumed ordering is needed.
|
||||||
|
|
||||||
|
`DISABLE_FRAME_XR_COMPAT` is the manifest's disable environment variable.
|
||||||
|
Leave it unset to activate the implicit layer. No `enable_environment` is
|
||||||
|
required.
|
||||||
|
|
||||||
|
## Rebuild and verify
|
||||||
|
|
||||||
|
NDK **r30 / 30.0.16248370**, installed at
|
||||||
|
`~/Library/Android/ndk/30.0.16248370`, was the latest stable/LTS package shown
|
||||||
|
by the [Android download page](https://developer.android.com/ndk/downloads)
|
||||||
|
on 2026-09-28. Downloaded
|
||||||
|
[android-ndk-r30-darwin.dmg](https://dl.google.com/android/repository/android-ndk-r30-darwin.dmg)
|
||||||
|
(1,072,970,961 bytes). The page publishes SHA-1, not SHA-256:
|
||||||
|
|
||||||
|
```
|
||||||
|
expected: 48591224b6657f46eebbc9d95d4af09bbed8d107
|
||||||
|
actual: 48591224b6657f46eebbc9d95d4af09bbed8d107 (PASS)
|
||||||
|
```
|
||||||
|
|
||||||
|
Mounted read-only with `hdiutil`; copied
|
||||||
|
`AndroidNDK16248370.app/Contents/NDK` into that version directory. Despite the
|
||||||
|
`darwin-x86_64` toolchain directory name, clang is universal arm64/x86_64 and
|
||||||
|
ran on this Apple Silicon Mac.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
frame/openxr-compat/build.sh
|
||||||
|
# Or set ANDROID_NDK_HOME to an installed NDK.
|
||||||
|
cmake -S frame/openxr-compat -B frame/openxr-compat/build-host -G Ninja
|
||||||
|
cmake --build frame/openxr-compat/build-host
|
||||||
|
ctest --test-dir frame/openxr-compat/build-host --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
The script produces arm64-v8a / android-24, C++17, `-O2`, static libc++, hidden
|
||||||
|
internal symbols, stripped output, and `-Wl,-z,max-page-size=16384`.
|
||||||
|
The committed prebuilt is `prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so`.
|
||||||
|
Its SHA-256 is recorded below with the APK checks.
|
||||||
|
|
||||||
|
[Host test output](evidence/ctest.txt): two tests passed, covering pure logic
|
||||||
|
and the actual layer with a fake next layer and host-only log/JNI shims.
|
||||||
|
[Full llvm-readelf -d -s -l output](evidence/readelf.txt) records three LOAD
|
||||||
|
segments aligned `0x4000`, only `xrNegotiateLoaderApiLayerInterface` exported,
|
||||||
|
and only `libc.so`, `libm.so`, `libdl.so`, `liblog.so` as NEEDED libraries.
|
||||||
|
All 46 undefined imports are versioned `@LIBC` except `__android_log_print`.
|
||||||
|
There is no `libc++_shared.so` dependency and no unstripped `.symtab`.
|
||||||
|
|
||||||
|
No independent model review was run: the task explicitly prohibits delegation.
|
||||||
|
No `CODING_STANDARDS.md` exists in this worktree or the primary worktree.
|
||||||
|
|
||||||
|
## Wolvic injection artifact (Mac only)
|
||||||
|
|
||||||
|
Copied `/tmp/vrapk/wq-dec` to `/tmp/vrapk/wq-layer-dec`; retained its existing
|
||||||
|
LAUNCHER fix and native-library extraction setting. Added only:
|
||||||
|
|
||||||
|
```
|
||||||
|
assets/openxr/1/api_layers/implicit.d/XrApiLayer_FRAME_compat.json
|
||||||
|
lib/arm64-v8a/libXrApiLayer_FRAME_compat.so
|
||||||
|
```
|
||||||
|
|
||||||
|
Built using `/tmp/vrapk/jdk/Contents/Home/bin/java` and
|
||||||
|
`/tmp/vrapk/apktool_3.0.3.jar`, then signed in place using
|
||||||
|
`/tmp/vrapk/uber-apk-signer-1.3.0.jar --overwrite` (embedded debug certificate):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/apktool_3.0.3.jar \
|
||||||
|
b /tmp/vrapk/wq-layer-dec -o /tmp/vrapk/wolvic-quest-compat.apk
|
||||||
|
/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/uber-apk-signer-1.3.0.jar \
|
||||||
|
-a /tmp/vrapk/wolvic-quest-compat.apk --overwrite
|
||||||
|
/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/uber-apk-signer-1.3.0.jar \
|
||||||
|
-a /tmp/vrapk/wolvic-quest-compat.apk --onlyVerify
|
||||||
|
```
|
||||||
|
|
||||||
|
Final APK: `/tmp/vrapk/wolvic-quest-compat.apk` (not committed).
|
||||||
|
The bundled `lib/arm64-v8a/libopenxr_loader.so` is byte-for-byte unchanged.
|
||||||
|
Its strings include both `openxr/1/api_layers/implicit.d/` and
|
||||||
|
`openxr/1/api_layers/explicit.d/`, `AddManifestFilesAndroid` error messages,
|
||||||
|
and `xrLocateSpaces`. Thus it contains APK asset discovery and evidence of
|
||||||
|
1.1 command support. Its exact upstream release number was not established
|
||||||
|
from the binary; runtime execution of those paths remains unverified.
|
||||||
|
|
||||||
|
Prebuilt library SHA-256:
|
||||||
|
|
||||||
|
```
|
||||||
|
479d31c374f137906e03f73209b581d65d7e6a41b8476e6425fa55a6aa40bd68
|
||||||
|
```
|
||||||
|
|
||||||
|
APK SHA-256:
|
||||||
|
|
||||||
|
```
|
||||||
|
355a681625e38b71563dcffecb031799eff27894d6ab910a659bace057e82c1f
|
||||||
|
```
|
||||||
|
|
||||||
|
Final `--onlyVerify` exited 0: zip alignment verified, v2/v3 signatures
|
||||||
|
verified, one APK processed and zero errors. ZIP readback confirmed that the
|
||||||
|
injected library and manifest match the committed inputs, the loader is
|
||||||
|
unchanged, and the binary Android manifest retains the LAUNCHER category.
|
||||||
|
See [APK verification evidence](evidence/apk-verification.txt).
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"file_format_version": "1.0.0",
|
||||||
|
"api_layer": {
|
||||||
|
"name": "XR_APILAYER_FRAME_compat",
|
||||||
|
"library_path": "libXrApiLayer_FRAME_compat.so",
|
||||||
|
"api_version": "1.1",
|
||||||
|
"implementation_version": "1",
|
||||||
|
"description": "Steam Frame OpenXR 1.1 to 1.0 compatibility",
|
||||||
|
"disable_environment": "DISABLE_FRAME_XR_COMPAT",
|
||||||
|
"instance_extensions": [
|
||||||
|
{
|
||||||
|
"name": "XR_KHR_android_thread_settings",
|
||||||
|
"extension_version": "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "XR_OCULUS_android_session_state_enable",
|
||||||
|
"extension_version": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||||
|
ndk=${ANDROID_NDK_HOME:-"$HOME/Library/Android/ndk/30.0.16248370"}
|
||||||
|
cmake -S "$here" -B "$here/build-android" -G Ninja \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="$ndk/build/cmake/android.toolchain.cmake" \
|
||||||
|
-DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-24 \
|
||||||
|
-DANDROID_STL=c++_static -DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build "$here/build-android"
|
||||||
|
mkdir -p "$here/prebuilt/arm64-v8a"
|
||||||
|
cp "$here/build-android/libXrApiLayer_FRAME_compat.so" "$here/prebuilt/arm64-v8a/"
|
||||||
|
shasum -a 256 "$here/prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so"
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace frame {
|
||||||
|
using Names = std::vector<std::string>;
|
||||||
|
inline constexpr const char* layerName = "XR_APILAYER_FRAME_compat";
|
||||||
|
inline constexpr const char* stubs[] = {"XR_KHR_android_thread_settings", "XR_OCULUS_android_session_state_enable"};
|
||||||
|
// OpenXR-Docs release-1.1.63 registry: promotedto="XR_VERSION_1_1".
|
||||||
|
inline constexpr const char* promotions[] = {
|
||||||
|
"XR_KHR_locate_spaces", "XR_EXT_local_floor", "XR_EXT_uuid", "XR_EXT_palm_pose",
|
||||||
|
"XR_VARJO_quad_views", "XR_EXT_samsung_odyssey_controller", "XR_EXT_hp_mixed_reality_controller",
|
||||||
|
"XR_HTC_vive_cosmos_controller_interaction", "XR_HTC_vive_focus3_controller_interaction",
|
||||||
|
"XR_ML_ml2_controller_interaction", "XR_FB_touch_controller_pro", "XR_META_touch_controller_plus",
|
||||||
|
"XR_BD_controller_interaction", "XR_KHR_maintenance1"};
|
||||||
|
inline bool has(const Names& names, const std::string& n) {
|
||||||
|
return std::find(names.begin(), names.end(), n) != names.end();
|
||||||
|
}
|
||||||
|
inline bool downgrade(XrVersion v) { return v >= XR_MAKE_VERSION(1, 1, 0); }
|
||||||
|
inline XrVersion runtimeVersion(XrVersion v) { return downgrade(v) ? XR_API_VERSION_1_0 : v; }
|
||||||
|
struct ExtensionPlan { Names downstream, stubbed, missing, added; };
|
||||||
|
inline ExtensionPlan extensions(const Names& requested, const Names& available, bool promote) {
|
||||||
|
ExtensionPlan p;
|
||||||
|
for (const auto& n : requested) {
|
||||||
|
if (!has(available, n) && (n == stubs[0] || n == stubs[1])) p.stubbed.push_back(n);
|
||||||
|
else {
|
||||||
|
if (!has(p.downstream, n)) p.downstream.push_back(n);
|
||||||
|
if (!has(available, n)) p.missing.push_back(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (promote) for (auto n : promotions) if (has(available, n) && !has(p.downstream, n)) {
|
||||||
|
p.downstream.push_back(n); p.added.push_back(n);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
// 1.1's grip_surface is XR_EXT_palm_pose's palm_ext renamed (same pose), not the grip pose.
|
||||||
|
inline std::string rewritePath(std::string p, bool palm) {
|
||||||
|
const std::string suffix = "/input/grip_surface/pose";
|
||||||
|
if (palm && (p == "/user/hand/left" + suffix || p == "/user/hand/right" + suffix))
|
||||||
|
p.replace(p.size() - suffix.size(), suffix.size(), "/input/palm_ext/pose");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
// Profiles added to 1.1. The three legacy Meta splits have no 1.0 extension.
|
||||||
|
// The renamed Pro/Plus profiles have different component semantics; do not
|
||||||
|
// claim the old extension implements the new profile merely because it exists.
|
||||||
|
inline bool dropProfile(const std::string& p, const Names& enabled) {
|
||||||
|
for (auto name : {"touch_pro_controller", "touch_plus_controller", "touch_controller_rift_cv1",
|
||||||
|
"touch_controller_quest_1_rift_s", "touch_controller_quest_2"})
|
||||||
|
if (p == std::string("/interaction_profiles/meta/") + name) return true;
|
||||||
|
const char* profiles[][2] = {
|
||||||
|
{"samsung/odyssey_controller", "XR_EXT_samsung_odyssey_controller"},
|
||||||
|
{"hp/mixed_reality_controller", "XR_EXT_hp_mixed_reality_controller"},
|
||||||
|
{"htc/vive_cosmos_controller", "XR_HTC_vive_cosmos_controller_interaction"},
|
||||||
|
{"htc/vive_focus3_controller", "XR_HTC_vive_focus3_controller_interaction"},
|
||||||
|
{"ml/ml2_controller", "XR_ML_ml2_controller_interaction"},
|
||||||
|
{"bytedance/pico_neo3_controller", "XR_BD_controller_interaction"},
|
||||||
|
{"bytedance/pico4_controller", "XR_BD_controller_interaction"},
|
||||||
|
{"bytedance/pico_g3_controller", "XR_BD_controller_interaction"}};
|
||||||
|
for (auto& entry : profiles) if (p == std::string("/interaction_profiles/") + entry[0])
|
||||||
|
return !has(enabled, entry[1]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline XrStructureType extensionType(XrStructureType t) {
|
||||||
|
switch (t) {
|
||||||
|
case XR_TYPE_SPACES_LOCATE_INFO: return XR_TYPE_SPACES_LOCATE_INFO_KHR;
|
||||||
|
case XR_TYPE_SPACE_LOCATIONS: return XR_TYPE_SPACE_LOCATIONS_KHR;
|
||||||
|
case XR_TYPE_SPACE_VELOCITIES: return XR_TYPE_SPACE_VELOCITIES_KHR;
|
||||||
|
default: return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline XrResult locate(PFN_xrLocateSpacesKHR next, XrSession session,
|
||||||
|
const XrSpacesLocateInfo* info, XrSpaceLocations* locations) {
|
||||||
|
if (!info || !locations) return XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
static_assert(XR_TYPE_SPACE_VELOCITIES == XR_TYPE_SPACE_VELOCITIES_KHR);
|
||||||
|
static_assert(XR_TYPE_SPACE_LOCATIONS == XR_TYPE_SPACE_LOCATIONS_KHR);
|
||||||
|
static_assert(XR_TYPE_SPACES_LOCATE_INFO == XR_TYPE_SPACES_LOCATE_INFO_KHR);
|
||||||
|
auto in = *info;
|
||||||
|
auto out = *locations;
|
||||||
|
in.type = extensionType(in.type); out.type = extensionType(out.type);
|
||||||
|
// The enums and typedefs are exact aliases. All chained velocities,
|
||||||
|
// including unknown next structures, can be forwarded without mutation.
|
||||||
|
const auto result = next(session, &in, &out);
|
||||||
|
locations->locationCount = out.locationCount;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
2026-09-28, Mac only; no device connection.
|
||||||
|
APK: /tmp/vrapk/wolvic-quest-compat.apk
|
||||||
|
SHA-256: 355a681625e38b71563dcffecb031799eff27894d6ab910a659bace057e82c1f
|
||||||
|
|
||||||
|
Command:
|
||||||
|
/tmp/vrapk/jdk/Contents/Home/bin/java -jar /tmp/vrapk/uber-apk-signer-1.3.0.jar -a /tmp/vrapk/wolvic-quest-compat.apk --onlyVerify
|
||||||
|
Exit status: 0
|
||||||
|
Relevant output:
|
||||||
|
- zipalign verified
|
||||||
|
- signature verified [v2, v3]
|
||||||
|
Successfully processed 1 APKs and 0 errors in 0.63 seconds.
|
||||||
|
|
||||||
|
ZIP readback checks: PASS
|
||||||
|
- Injected JSON identical to frame/openxr-compat/XrApiLayer_FRAME_compat.json.
|
||||||
|
- Injected SO identical to prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so.
|
||||||
|
- Existing libopenxr_loader.so identical to /tmp/vrapk/wq-dec version.
|
||||||
|
- Binary AndroidManifest.xml contains android.intent.category.LAUNCHER.
|
||||||
|
|
||||||
|
Loader strings present:
|
||||||
|
openxr/1/api_layers/implicit.d/
|
||||||
|
openxr/1/api_layers/explicit.d/
|
||||||
|
ApiLayerManifestFile::AddManifestFilesAndroid unable to open asset
|
||||||
|
xrLocateSpaces
|
||||||
|
|
||||||
|
NDK: 30.0.16248370 (r30)
|
||||||
|
DMG size: 1072970961 bytes
|
||||||
|
Download-page SHA-1: 48591224b6657f46eebbc9d95d4af09bbed8d107
|
||||||
|
Measured SHA-1: 48591224b6657f46eebbc9d95d4af09bbed8d107
|
||||||
|
SHA check: PASS
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Test project /Users/saphid/projects/steam-frame-xrlayer/frame/openxr-compat/build-host
|
||||||
|
Start 1: compat_logic
|
||||||
|
1/2 Test #1: compat_logic ..................... Passed 0.01 sec
|
||||||
|
Start 2: layer_dispatch
|
||||||
|
2/2 Test #2: layer_dispatch ................... Passed 0.42 sec
|
||||||
|
|
||||||
|
100% tests passed out of 2
|
||||||
|
|
||||||
|
Total Test time (real) = 0.43 sec
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
|
||||||
|
Elf file type is DYN (Shared object file)
|
||||||
|
Entry point 0x0
|
||||||
|
There are 9 program headers, starting at offset 64
|
||||||
|
|
||||||
|
Program Headers:
|
||||||
|
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
|
||||||
|
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0001f8 0x0001f8 R 0x8
|
||||||
|
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0169e0 0x0169e0 R E 0x4000
|
||||||
|
LOAD 0x0169e0 0x000000000001a9e0 0x000000000001a9e0 0x000a90 0x001620 RW 0x4000
|
||||||
|
LOAD 0x017470 0x000000000001f470 0x000000000001f470 0x000068 0x000bc0 RW 0x4000
|
||||||
|
DYNAMIC 0x017048 0x000000000001b048 0x000000000001b048 0x0001c0 0x0001c0 RW 0x8
|
||||||
|
GNU_RELRO 0x0169e0 0x000000000001a9e0 0x000000000001a9e0 0x000a90 0x001620 R 0x1
|
||||||
|
GNU_EH_FRAME 0x004fb4 0x0000000000004fb4 0x0000000000004fb4 0x00070c 0x00070c R 0x4
|
||||||
|
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x0
|
||||||
|
NOTE 0x000238 0x0000000000000238 0x0000000000000238 0x0000bc 0x0000bc R 0x4
|
||||||
|
|
||||||
|
Section to Segment mapping:
|
||||||
|
Segment Sections...
|
||||||
|
00
|
||||||
|
01 .note.android.ident .note.gnu.build-id .dynsym .gnu.version .gnu.version_r .gnu.hash .dynstr .rela.dyn .rela.plt .gcc_except_table .rodata .eh_frame_hdr .eh_frame .text __lcxx_override .plt
|
||||||
|
02 .data.rel.ro .fini_array .init_array .dynamic .got .got.plt .relro_padding
|
||||||
|
03 .data .bss
|
||||||
|
04 .dynamic
|
||||||
|
05 .data.rel.ro .fini_array .init_array .dynamic .got .got.plt .relro_padding
|
||||||
|
06 .eh_frame_hdr
|
||||||
|
07
|
||||||
|
08 .note.android.ident .note.gnu.build-id
|
||||||
|
None .comment .shstrtab
|
||||||
|
Dynamic section at offset 0x17048 contains 28 entries:
|
||||||
|
Tag Type Name/Value
|
||||||
|
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
|
||||||
|
0x0000000000000001 (NEEDED) Shared library: [libm.so]
|
||||||
|
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
|
||||||
|
0x0000000000000001 (NEEDED) Shared library: [libc.so]
|
||||||
|
0x000000000000000e (SONAME) Library soname: [libXrApiLayer_FRAME_compat.so]
|
||||||
|
0x000000000000001e (FLAGS) BIND_NOW
|
||||||
|
0x000000006ffffffb (FLAGS_1) NOW
|
||||||
|
0x0000000000000007 (RELA) 0xae8
|
||||||
|
0x0000000000000008 (RELASZ) 5424 (bytes)
|
||||||
|
0x0000000000000009 (RELAENT) 24 (bytes)
|
||||||
|
0x000000006ffffff9 (RELACOUNT) 225
|
||||||
|
0x0000000000000017 (JMPREL) 0x2018
|
||||||
|
0x0000000000000002 (PLTRELSZ) 1080 (bytes)
|
||||||
|
0x0000000000000003 (PLTGOT) 0x1b2f0
|
||||||
|
0x0000000000000014 (PLTREL) RELA
|
||||||
|
0x0000000000000006 (SYMTAB) 0x2f8
|
||||||
|
0x000000000000000b (SYMENT) 24 (bytes)
|
||||||
|
0x0000000000000005 (STRTAB) 0x838
|
||||||
|
0x000000000000000a (STRSZ) 681 (bytes)
|
||||||
|
0x000000006ffffef5 (GNU_HASH) 0x818
|
||||||
|
0x0000000000000019 (INIT_ARRAY) 0x1b030
|
||||||
|
0x000000000000001b (INIT_ARRAYSZ) 24 (bytes)
|
||||||
|
0x000000000000001a (FINI_ARRAY) 0x1b020
|
||||||
|
0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
|
||||||
|
0x000000006ffffff0 (VERSYM) 0x778
|
||||||
|
0x000000006ffffffe (VERNEED) 0x7d8
|
||||||
|
0x000000006fffffff (VERNEEDNUM) 2
|
||||||
|
0x0000000000000000 (NULL) 0x0
|
||||||
|
|
||||||
|
Symbol table '.dynsym' contains 48 entries:
|
||||||
|
Num: Value Size Type Bind Vis Ndx Name
|
||||||
|
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
|
||||||
|
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@LIBC
|
||||||
|
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@LIBC
|
||||||
|
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strcmp@LIBC
|
||||||
|
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __android_log_print
|
||||||
|
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@LIBC
|
||||||
|
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memmove@LIBC
|
||||||
|
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@LIBC
|
||||||
|
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@LIBC
|
||||||
|
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __strcpy_chk@LIBC
|
||||||
|
10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memset@LIBC
|
||||||
|
11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcmp@LIBC
|
||||||
|
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_lock@LIBC
|
||||||
|
13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_unlock@LIBC
|
||||||
|
14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno@LIBC
|
||||||
|
15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND snprintf@LIBC
|
||||||
|
16: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_destroy@LIBC
|
||||||
|
17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strerror_r@LIBC
|
||||||
|
18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort@LIBC
|
||||||
|
19: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_getspecific@LIBC
|
||||||
|
20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_setspecific@LIBC
|
||||||
|
21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_key_create@LIBC
|
||||||
|
22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND syscall@LIBC
|
||||||
|
23: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND __sF@LIBC
|
||||||
|
24: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fwrite@LIBC
|
||||||
|
25: 0000000000000000 0 FUNC GLOBAL DEFAULT UND vfprintf@LIBC
|
||||||
|
26: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputc@LIBC
|
||||||
|
27: 0000000000000000 0 FUNC GLOBAL DEFAULT UND vasprintf@LIBC
|
||||||
|
28: 0000000000000000 0 FUNC GLOBAL DEFAULT UND android_set_abort_message@LIBC
|
||||||
|
29: 0000000000000000 0 FUNC GLOBAL DEFAULT UND openlog@LIBC
|
||||||
|
30: 0000000000000000 0 FUNC GLOBAL DEFAULT UND syslog@LIBC
|
||||||
|
31: 0000000000000000 0 FUNC GLOBAL DEFAULT UND closelog@LIBC
|
||||||
|
32: 0000000000000000 0 FUNC GLOBAL DEFAULT UND malloc@LIBC
|
||||||
|
33: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@LIBC
|
||||||
|
34: 0000000000000000 0 FUNC GLOBAL DEFAULT UND posix_memalign@LIBC
|
||||||
|
35: 0000000000000000 0 FUNC GLOBAL DEFAULT UND realloc@LIBC
|
||||||
|
36: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_once@LIBC
|
||||||
|
37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_key_delete@LIBC
|
||||||
|
38: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getauxval@LIBC
|
||||||
|
39: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __system_property_get@LIBC
|
||||||
|
40: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strncmp@LIBC
|
||||||
|
41: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fprintf@LIBC
|
||||||
|
42: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fflush@LIBC
|
||||||
|
43: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_rwlock_wrlock@LIBC
|
||||||
|
44: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_rwlock_unlock@LIBC
|
||||||
|
45: 0000000000000000 0 FUNC GLOBAL DEFAULT UND dl_iterate_phdr@LIBC
|
||||||
|
46: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_rwlock_rdlock@LIBC
|
||||||
|
47: 00000000000081a0 284 FUNC GLOBAL DEFAULT 14 xrNegotiateLoaderApiLayerInterface
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
#include "compat_logic.h"
|
||||||
|
#include <loader_interfaces.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include <openxr/openxr_platform.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "FrameXrCompat", __VA_ARGS__)
|
||||||
|
#define GUARD_END catch (const std::bad_alloc&) { return XR_ERROR_OUT_OF_MEMORY; } catch (...) { return XR_ERROR_RUNTIME_FAILURE; }
|
||||||
|
namespace {
|
||||||
|
struct Dispatch {
|
||||||
|
PFN_xrGetInstanceProcAddr gipa{};
|
||||||
|
PFN_xrDestroyInstance destroy{};
|
||||||
|
PFN_xrCreateSession createSession{};
|
||||||
|
PFN_xrDestroySession destroySession{};
|
||||||
|
PFN_xrLocateSpacesKHR locate{};
|
||||||
|
PFN_xrStringToPath stringToPath{};
|
||||||
|
PFN_xrPathToString pathToString{};
|
||||||
|
PFN_xrSuggestInteractionProfileBindings suggest{};
|
||||||
|
PFN_xrSetAndroidApplicationThreadKHR thread{};
|
||||||
|
PFN_xrRequestDisplayRefreshRateFB refresh{};
|
||||||
|
frame::Names enabled, stubbed;
|
||||||
|
XrInstance instance{};
|
||||||
|
bool promote{}, palm{};
|
||||||
|
};
|
||||||
|
std::mutex mutex;
|
||||||
|
std::unordered_map<XrInstance, std::shared_ptr<Dispatch>> instances;
|
||||||
|
std::unordered_map<XrSession, std::shared_ptr<Dispatch>> sessions;
|
||||||
|
// No runtime library is opened here: every call goes through the next layer.
|
||||||
|
PFN_xrEnumerateInstanceExtensionProperties enumerateNext{};
|
||||||
|
template<class T> T proc(PFN_xrGetInstanceProcAddr gipa, XrInstance i, const char* name) {
|
||||||
|
PFN_xrVoidFunction f{};
|
||||||
|
if (XR_FAILED(gipa(i, name, &f))) return nullptr;
|
||||||
|
return reinterpret_cast<T>(f);
|
||||||
|
}
|
||||||
|
std::shared_ptr<Dispatch> get(XrInstance i) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
auto it = instances.find(i); return it == instances.end() ? nullptr : it->second;
|
||||||
|
}
|
||||||
|
std::shared_ptr<Dispatch> get(XrSession s) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
auto it = sessions.find(s); return it == sessions.end() ? nullptr : it->second;
|
||||||
|
}
|
||||||
|
XrResult properties(PFN_xrEnumerateInstanceExtensionProperties fn,
|
||||||
|
std::vector<XrExtensionProperties>& out) {
|
||||||
|
if (!fn) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||||
|
for (int attempt = 0; attempt < 4; ++attempt) {
|
||||||
|
uint32_t n{};
|
||||||
|
auto r = fn(nullptr, 0, &n, nullptr);
|
||||||
|
if (XR_FAILED(r)) return r;
|
||||||
|
out.assign(n, {XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, 0});
|
||||||
|
if (!n) return XR_SUCCESS;
|
||||||
|
r = fn(nullptr, n, &n, out.data());
|
||||||
|
if (r == XR_ERROR_SIZE_INSUFFICIENT) continue;
|
||||||
|
if (XR_FAILED(r)) return r;
|
||||||
|
out.resize(n); return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
return XR_ERROR_RUNTIME_FAILURE;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL enumerate(const char* layer, uint32_t capacity, uint32_t* count,
|
||||||
|
XrExtensionProperties* output) try {
|
||||||
|
if (!count || (capacity && !output)) return XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
const bool own = layer && std::strcmp(layer, frame::layerName) == 0;
|
||||||
|
PFN_xrEnumerateInstanceExtensionProperties next;
|
||||||
|
{ std::lock_guard<std::mutex> lock(mutex); next = enumerateNext; }
|
||||||
|
if (layer && *layer && !own) return next ? next(layer, capacity, count, output) : XR_ERROR_API_LAYER_NOT_PRESENT;
|
||||||
|
std::vector<XrExtensionProperties> all;
|
||||||
|
if (!own && next) {
|
||||||
|
auto r = properties(next, all); if (XR_FAILED(r)) return r;
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < 2; ++i) {
|
||||||
|
bool found = false;
|
||||||
|
for (auto& e : all) if (!std::strcmp(e.extensionName, frame::stubs[i])) found = true;
|
||||||
|
if (!found) {
|
||||||
|
XrExtensionProperties p{XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, i == 0 ? XR_KHR_android_thread_settings_SPEC_VERSION : 1u};
|
||||||
|
std::strcpy(p.extensionName, frame::stubs[i]); all.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*count = static_cast<uint32_t>(all.size());
|
||||||
|
LOG("enumerate extensions: %u (includes stubs)", *count);
|
||||||
|
if (!capacity) return XR_SUCCESS;
|
||||||
|
if (capacity < all.size()) return XR_ERROR_SIZE_INSUFFICIENT;
|
||||||
|
for (size_t i = 0; i < all.size(); ++i) {
|
||||||
|
if (output[i].type != XR_TYPE_EXTENSION_PROPERTIES) return XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
std::strcpy(output[i].extensionName, all[i].extensionName);
|
||||||
|
output[i].extensionVersion = all[i].extensionVersion;
|
||||||
|
}
|
||||||
|
return XR_SUCCESS;
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL destroyInstance(XrInstance i) try {
|
||||||
|
auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
auto r = d->destroy(i);
|
||||||
|
if (XR_SUCCEEDED(r)) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
for (auto it = sessions.begin(); it != sessions.end();) {
|
||||||
|
if (it->second == d) it = sessions.erase(it); else ++it;
|
||||||
|
}
|
||||||
|
instances.erase(i);
|
||||||
|
if (instances.empty()) enumerateNext = nullptr;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL createSession(XrInstance i, const XrSessionCreateInfo* info, XrSession* s) try {
|
||||||
|
auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
auto r = d->createSession(i, info, s);
|
||||||
|
if (XR_SUCCEEDED(r)) {
|
||||||
|
try { std::lock_guard<std::mutex> lock(mutex); sessions.emplace(*s, d); }
|
||||||
|
catch (...) { d->destroySession(*s); *s = XR_NULL_HANDLE; throw; }
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL destroySession(XrSession s) try {
|
||||||
|
auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
auto r = d->destroySession(s);
|
||||||
|
if (XR_SUCCEEDED(r)) { std::lock_guard<std::mutex> lock(mutex); sessions.erase(s); }
|
||||||
|
return r;
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL locateSpaces(XrSession s, const XrSpacesLocateInfo* in, XrSpaceLocations* out) try {
|
||||||
|
auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
if (!d->locate) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||||
|
static std::once_flag logged; // called every frame
|
||||||
|
std::call_once(logged, [] { LOG("xrLocateSpaces -> xrLocateSpacesKHR (core/KHR type aliases)"); });
|
||||||
|
return frame::locate(d->locate, s, in, out);
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL threadSettings(XrSession s, XrAndroidThreadTypeKHR type, uint32_t tid) try {
|
||||||
|
auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
if (frame::has(d->stubbed, frame::stubs[0])) {
|
||||||
|
LOG("stub xrSetAndroidApplicationThreadKHR type=%d tid=%u -> XR_SUCCESS (no scheduling change)", type, tid);
|
||||||
|
return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
return d->thread ? d->thread(s, type, tid) : XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||||
|
} GUARD_END
|
||||||
|
// SteamVR offers only the current refresh rate; Quest apps ask for 72/90/120 Hz
|
||||||
|
// and abort on the error, so keep the current rate and report success.
|
||||||
|
XrResult XRAPI_CALL requestRefreshRate(XrSession s, float hz) try {
|
||||||
|
auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
auto r = d->refresh(s, hz);
|
||||||
|
if (r == XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB) {
|
||||||
|
LOG("xrRequestDisplayRefreshRateFB %.1f Hz unsupported; keeping the current rate", hz);
|
||||||
|
return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL stringToPath(XrInstance i, const char* path, XrPath* out) try {
|
||||||
|
auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
if (!path) return XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
auto rewritten = frame::rewritePath(path, d->palm);
|
||||||
|
if (rewritten != path) LOG("path %s -> %s", path, rewritten.c_str());
|
||||||
|
return d->stringToPath(i, rewritten.c_str(), out);
|
||||||
|
} GUARD_END
|
||||||
|
XrResult pathString(const Dispatch& d, XrPath path, std::string& out) {
|
||||||
|
uint32_t n{};
|
||||||
|
auto r = d.pathToString(d.instance, path, 0, &n, nullptr);
|
||||||
|
if (XR_FAILED(r)) return r;
|
||||||
|
std::vector<char> text(n);
|
||||||
|
r = d.pathToString(d.instance, path, n, &n, text.data());
|
||||||
|
if (XR_SUCCEEDED(r)) out.assign(text.data());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL suggest(XrInstance i, const XrInteractionProfileSuggestedBinding* info) try {
|
||||||
|
auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
if (!info || (info->countSuggestedBindings && !info->suggestedBindings)) return XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
std::string profile;
|
||||||
|
auto r = pathString(*d, info->interactionProfile, profile);
|
||||||
|
if (XR_FAILED(r)) return r;
|
||||||
|
if (d->promote && frame::dropProfile(profile, d->enabled)) {
|
||||||
|
LOG("drop unsupported 1.1 interaction profile %s (%u suggestions)", profile.c_str(), info->countSuggestedBindings);
|
||||||
|
return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
auto copy = *info;
|
||||||
|
std::vector<XrActionSuggestedBinding> bindings;
|
||||||
|
for (uint32_t index = 0; index < info->countSuggestedBindings; ++index) {
|
||||||
|
auto b = info->suggestedBindings[index];
|
||||||
|
std::string path;
|
||||||
|
r = pathString(*d, b.binding, path); if (XR_FAILED(r)) return r;
|
||||||
|
auto rewritten = frame::rewritePath(path, d->palm);
|
||||||
|
if (rewritten != path) {
|
||||||
|
LOG("binding %s -> %s", path.c_str(), rewritten.c_str());
|
||||||
|
r = d->stringToPath(i, rewritten.c_str(), &b.binding); if (XR_FAILED(r)) return r;
|
||||||
|
}
|
||||||
|
bindings.push_back(b);
|
||||||
|
}
|
||||||
|
copy.suggestedBindings = bindings.data();
|
||||||
|
return d->suggest(i, ©);
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL gipa(XrInstance, const char*, PFN_xrVoidFunction*);
|
||||||
|
XrResult XRAPI_CALL createLayer(const XrInstanceCreateInfo* info, const XrApiLayerCreateInfo* layer,
|
||||||
|
XrInstance* instance) try {
|
||||||
|
if (!info || !instance || !layer || layer->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO ||
|
||||||
|
layer->structVersion != XR_API_LAYER_CREATE_INFO_STRUCT_VERSION || layer->structSize != sizeof(*layer) ||
|
||||||
|
!layer->nextInfo || layer->nextInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO ||
|
||||||
|
layer->nextInfo->structVersion != XR_API_LAYER_NEXT_INFO_STRUCT_VERSION ||
|
||||||
|
layer->nextInfo->structSize != sizeof(XrApiLayerNextInfo) ||
|
||||||
|
!layer->nextInfo->nextGetInstanceProcAddr || !layer->nextInfo->nextCreateApiLayerInstance ||
|
||||||
|
(info->enabledExtensionCount && !info->enabledExtensionNames)) return XR_ERROR_INITIALIZATION_FAILED;
|
||||||
|
*instance = XR_NULL_HANDLE;
|
||||||
|
auto next = layer->nextInfo->nextGetInstanceProcAddr;
|
||||||
|
auto enumFn = proc<PFN_xrEnumerateInstanceExtensionProperties>(next, XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties");
|
||||||
|
std::vector<XrExtensionProperties> available;
|
||||||
|
auto r = properties(enumFn, available); if (XR_FAILED(r)) { LOG("runtime extension enumeration failed: %d", r); return r; }
|
||||||
|
frame::Names requested, supported;
|
||||||
|
for (auto& p : available) supported.emplace_back(p.extensionName);
|
||||||
|
for (uint32_t n = 0; n < info->enabledExtensionCount; ++n) {
|
||||||
|
if (!info->enabledExtensionNames[n]) return XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
requested.emplace_back(info->enabledExtensionNames[n]);
|
||||||
|
}
|
||||||
|
auto d = std::make_shared<Dispatch>();
|
||||||
|
d->promote = frame::downgrade(info->applicationInfo.apiVersion);
|
||||||
|
auto plan = frame::extensions(requested, supported, d->promote);
|
||||||
|
for (auto& n : plan.added) LOG("enable promoted extension %s", n.c_str());
|
||||||
|
for (auto& n : plan.stubbed) LOG("emulate missing extension %s; remove downstream", n.c_str());
|
||||||
|
for (auto& n : plan.missing) LOG("unsupported extension %s retained; runtime must reject", n.c_str());
|
||||||
|
auto copy = *info;
|
||||||
|
copy.applicationInfo.apiVersion = frame::runtimeVersion(info->applicationInfo.apiVersion);
|
||||||
|
if (d->promote) LOG("API %u.%u.%u -> 1.0.%u", unsigned(XR_VERSION_MAJOR(info->applicationInfo.apiVersion)),
|
||||||
|
unsigned(XR_VERSION_MINOR(info->applicationInfo.apiVersion)), unsigned(XR_VERSION_PATCH(info->applicationInfo.apiVersion)),
|
||||||
|
unsigned(XR_VERSION_PATCH(copy.applicationInfo.apiVersion)));
|
||||||
|
std::vector<const char*> names;
|
||||||
|
for (auto& n : plan.downstream) names.push_back(n.c_str());
|
||||||
|
copy.enabledExtensionCount = static_cast<uint32_t>(names.size()); copy.enabledExtensionNames = names.data();
|
||||||
|
auto chain = *layer; chain.nextInfo = layer->nextInfo->next;
|
||||||
|
r = layer->nextInfo->nextCreateApiLayerInstance(©, &chain, instance);
|
||||||
|
if (XR_FAILED(r)) { LOG("downstream xrCreateInstance -> %d", r); return r; }
|
||||||
|
d->gipa = next; d->instance = *instance;
|
||||||
|
d->destroy = proc<PFN_xrDestroyInstance>(next, *instance, "xrDestroyInstance");
|
||||||
|
// A successful runtime instance must expose xrDestroyInstance.
|
||||||
|
if (!d->destroy) { // nothing could ever destroy it, so don't hand it out
|
||||||
|
LOG("invalid downstream: missing xrDestroyInstance"); *instance = XR_NULL_HANDLE; return XR_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
d->enabled = std::move(plan.downstream); d->stubbed = std::move(plan.stubbed);
|
||||||
|
d->palm = frame::has(d->enabled, "XR_EXT_palm_pose");
|
||||||
|
#define LOAD(field, name) d->field = proc<PFN_##name>(next, *instance, #name)
|
||||||
|
LOAD(createSession, xrCreateSession); LOAD(destroySession, xrDestroySession);
|
||||||
|
LOAD(stringToPath, xrStringToPath); LOAD(pathToString, xrPathToString);
|
||||||
|
LOAD(suggest, xrSuggestInteractionProfileBindings);
|
||||||
|
if (frame::has(d->enabled, "XR_KHR_locate_spaces")) LOAD(locate, xrLocateSpacesKHR);
|
||||||
|
if (frame::has(d->enabled, frame::stubs[0])) LOAD(thread, xrSetAndroidApplicationThreadKHR);
|
||||||
|
if (frame::has(d->enabled, "XR_FB_display_refresh_rate")) LOAD(refresh, xrRequestDisplayRefreshRateFB);
|
||||||
|
#undef LOAD
|
||||||
|
if (!d->createSession || !d->destroySession || !d->stringToPath || !d->pathToString || !d->suggest) {
|
||||||
|
d->destroy(*instance); *instance = XR_NULL_HANDLE; return XR_ERROR_INITIALIZATION_FAILED;
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
instances.emplace(*instance, d); enumerateNext = enumFn;
|
||||||
|
} catch (...) { d->destroy(*instance); *instance = XR_NULL_HANDLE; throw; }
|
||||||
|
LOG("created instance; next-layer dispatch retained (Valve layer coexists)");
|
||||||
|
return r;
|
||||||
|
} GUARD_END
|
||||||
|
XrResult XRAPI_CALL gipa(XrInstance i, const char* name, PFN_xrVoidFunction* out) try {
|
||||||
|
if (!name || !out) return XR_ERROR_VALIDATION_FAILURE;
|
||||||
|
*out = nullptr;
|
||||||
|
#define RETURN_PROC(n, fn) if (!std::strcmp(name, n)) { *out = reinterpret_cast<PFN_xrVoidFunction>(fn); return XR_SUCCESS; }
|
||||||
|
RETURN_PROC("xrGetInstanceProcAddr", gipa)
|
||||||
|
RETURN_PROC("xrCreateApiLayerInstance", createLayer)
|
||||||
|
RETURN_PROC("xrEnumerateInstanceExtensionProperties", enumerate)
|
||||||
|
auto d = get(i); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||||
|
RETURN_PROC("xrDestroyInstance", destroyInstance)
|
||||||
|
RETURN_PROC("xrCreateSession", createSession)
|
||||||
|
RETURN_PROC("xrDestroySession", destroySession)
|
||||||
|
RETURN_PROC("xrStringToPath", stringToPath)
|
||||||
|
RETURN_PROC("xrSuggestInteractionProfileBindings", suggest)
|
||||||
|
if (d->locate && d->promote) { RETURN_PROC("xrLocateSpaces", locateSpaces) }
|
||||||
|
if (frame::has(d->stubbed, frame::stubs[0])) { RETURN_PROC("xrSetAndroidApplicationThreadKHR", threadSettings) }
|
||||||
|
if (d->refresh) { RETURN_PROC("xrRequestDisplayRefreshRateFB", requestRefreshRate) }
|
||||||
|
#undef RETURN_PROC
|
||||||
|
// Includes xrGetInstanceProperties: report the actual runtime's identity.
|
||||||
|
return d->gipa(i, name, out);
|
||||||
|
} GUARD_END
|
||||||
|
} // namespace
|
||||||
|
extern "C" __attribute__((visibility("default"))) XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface(
|
||||||
|
const XrNegotiateLoaderInfo* loader, const char* name, XrNegotiateApiLayerRequest* request) {
|
||||||
|
if (!loader || !name || !request || std::strcmp(name, frame::layerName) ||
|
||||||
|
loader->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO ||
|
||||||
|
loader->structVersion != XR_LOADER_INFO_STRUCT_VERSION || loader->structSize != sizeof(*loader) ||
|
||||||
|
request->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST ||
|
||||||
|
request->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || request->structSize != sizeof(*request) ||
|
||||||
|
loader->minInterfaceVersion > 1 || loader->maxInterfaceVersion < 1 ||
|
||||||
|
loader->minApiVersion > XR_CURRENT_API_VERSION || loader->maxApiVersion < XR_MAKE_VERSION(1, 1, 0))
|
||||||
|
return XR_ERROR_INITIALIZATION_FAILED;
|
||||||
|
request->layerInterfaceVersion = 1;
|
||||||
|
request->layerApiVersion = std::min<XrVersion>(XR_CURRENT_API_VERSION, loader->maxApiVersion);
|
||||||
|
request->getInstanceProcAddr = gipa; request->createApiLayerInstance = createLayer;
|
||||||
|
LOG("negotiated interface 1, API 1.1");
|
||||||
|
return XR_SUCCESS;
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,70 @@
|
|||||||
|
#include "compat_logic.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <type_traits>
|
||||||
|
#define CHECK(expr) do { if (!(expr)) { std::cerr << __LINE__ << ": " #expr "\n"; std::exit(1); } } while (false)
|
||||||
|
namespace {
|
||||||
|
void* sentinel = reinterpret_cast<void*>(0x1234);
|
||||||
|
XrResult XRAPI_CALL fakeLocate(XrSession, const XrSpacesLocateInfo* in, XrSpaceLocations* out) {
|
||||||
|
CHECK(in->type == XR_TYPE_SPACES_LOCATE_INFO_KHR);
|
||||||
|
CHECK(out->type == XR_TYPE_SPACE_LOCATIONS_KHR);
|
||||||
|
CHECK(in->next == sentinel);
|
||||||
|
auto* velocities = static_cast<XrSpaceVelocitiesKHR*>(out->next);
|
||||||
|
CHECK(velocities->type == XR_TYPE_SPACE_VELOCITIES_KHR);
|
||||||
|
CHECK(velocities->next == sentinel);
|
||||||
|
CHECK(in->spaceCount == 1 && in->time == 123);
|
||||||
|
out->locations[0].pose.position.x = 42;
|
||||||
|
velocities->velocities[0].linearVelocity.y = 7;
|
||||||
|
return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int main() {
|
||||||
|
using namespace frame;
|
||||||
|
CHECK(!downgrade(XR_MAKE_VERSION(1, 0, 99)));
|
||||||
|
CHECK(runtimeVersion(XR_MAKE_VERSION(1, 0, 42)) == XR_MAKE_VERSION(1, 0, 42));
|
||||||
|
CHECK(runtimeVersion(XR_MAKE_VERSION(1, 1, 0)) == XR_API_VERSION_1_0);
|
||||||
|
CHECK(runtimeVersion(XR_MAKE_VERSION(1, 1, 999)) == XR_API_VERSION_1_0);
|
||||||
|
CHECK(runtimeVersion(XR_MAKE_VERSION(2, 0, 0)) == XR_API_VERSION_1_0);
|
||||||
|
Names available{"XR_KHR_locate_spaces", "XR_EXT_palm_pose", "XR_EXT_hand_interaction", "XR_VARJO_quad_views"};
|
||||||
|
Names requested{stubs[0], stubs[1], "XR_MISSING_test", "XR_EXT_palm_pose"};
|
||||||
|
auto p = extensions(requested, available, true);
|
||||||
|
CHECK(p.stubbed.size() == 2 && p.missing == Names{"XR_MISSING_test"});
|
||||||
|
CHECK(has(p.downstream, "XR_MISSING_test"));
|
||||||
|
CHECK(has(p.downstream, "XR_KHR_locate_spaces") && has(p.downstream, "XR_VARJO_quad_views"));
|
||||||
|
CHECK(!has(p.downstream, "XR_EXT_hand_interaction"));
|
||||||
|
CHECK(!has(p.downstream, "XR_EXT_local_floor"));
|
||||||
|
CHECK(std::count(p.downstream.begin(), p.downstream.end(), "XR_EXT_palm_pose") == 1);
|
||||||
|
available.push_back(stubs[0]);
|
||||||
|
CHECK(extensions(requested, available, true).stubbed == Names{stubs[1]});
|
||||||
|
CHECK(extensions({}, available, false).downstream.empty());
|
||||||
|
CHECK(rewritePath("/user/hand/left/input/grip_surface/pose", true) == "/user/hand/left/input/palm_ext/pose");
|
||||||
|
CHECK(rewritePath("/user/hand/right/input/grip_surface/pose", true) == "/user/hand/right/input/palm_ext/pose");
|
||||||
|
CHECK(rewritePath("/user/hand/left/input/grip_surface/pose", false) == "/user/hand/left/input/grip_surface/pose");
|
||||||
|
CHECK(rewritePath("/user/hand/left/input/grip_surface/pose/extra", true) == "/user/hand/left/input/grip_surface/pose/extra");
|
||||||
|
CHECK(rewritePath("/user/hand/left/input/grip/pose", true) == "/user/hand/left/input/grip/pose");
|
||||||
|
CHECK(dropProfile("/interaction_profiles/meta/touch_pro_controller", {}));
|
||||||
|
CHECK(dropProfile("/interaction_profiles/meta/touch_controller_quest_2", {}));
|
||||||
|
CHECK(dropProfile("/interaction_profiles/hp/mixed_reality_controller", {}));
|
||||||
|
CHECK(!dropProfile("/interaction_profiles/hp/mixed_reality_controller", {"XR_EXT_hp_mixed_reality_controller"}));
|
||||||
|
CHECK(!dropProfile("/interaction_profiles/oculus/touch_controller", {}));
|
||||||
|
CHECK(!dropProfile("/interaction_profiles/khr/simple_controller", {}));
|
||||||
|
CHECK(!dropProfile("/interaction_profiles/valve/frame_controller", {}));
|
||||||
|
CHECK(!dropProfile("/interaction_profiles/ext/hand_interaction_ext", {}));
|
||||||
|
CHECK(extensionType(XR_TYPE_SPACES_LOCATE_INFO) == XR_TYPE_SPACES_LOCATE_INFO_KHR);
|
||||||
|
CHECK(extensionType(XR_TYPE_SPACE_LOCATIONS) == XR_TYPE_SPACE_LOCATIONS_KHR);
|
||||||
|
CHECK(extensionType(XR_TYPE_SPACE_VELOCITIES) == XR_TYPE_SPACE_VELOCITIES_KHR);
|
||||||
|
CHECK(extensionType(XR_TYPE_INSTANCE_CREATE_INFO) == XR_TYPE_INSTANCE_CREATE_INFO);
|
||||||
|
static_assert(std::is_same_v<XrSpaceVelocities, XrSpaceVelocitiesKHR>);
|
||||||
|
XrSpace space{};
|
||||||
|
XrSpaceLocationData data{};
|
||||||
|
XrSpaceVelocityData velocity{};
|
||||||
|
XrSpaceVelocities velocities{XR_TYPE_SPACE_VELOCITIES, sentinel, 1, &velocity};
|
||||||
|
XrSpaceLocations locations{XR_TYPE_SPACE_LOCATIONS, &velocities, 1, &data};
|
||||||
|
XrSpacesLocateInfo info{XR_TYPE_SPACES_LOCATE_INFO, sentinel, XR_NULL_HANDLE, 123, 1, &space};
|
||||||
|
CHECK(locate(fakeLocate, XR_NULL_HANDLE, &info, &locations) == XR_SUCCESS);
|
||||||
|
CHECK(data.pose.position.x == 42 && velocity.linearVelocity.y == 7);
|
||||||
|
CHECK(info.next == sentinel && locations.next == &velocities && velocities.next == sentinel);
|
||||||
|
CHECK(info.type == XR_TYPE_SPACES_LOCATE_INFO && locations.type == XR_TYPE_SPACE_LOCATIONS && velocities.type == XR_TYPE_SPACE_VELOCITIES);
|
||||||
|
CHECK(locate(fakeLocate, XR_NULL_HANDLE, nullptr, &locations) == XR_ERROR_VALIDATION_FAILURE);
|
||||||
|
std::cout << "All version, extension, path/profile and locate-chain checks passed\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
// Compile the actual layer with host-only JNI/logging shims, and a fake next
|
||||||
|
// layer. This exercises ABI routing without a runtime or a headset.
|
||||||
|
#include "../layer.cpp"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#define CHECK(expr) do { if (!(expr)) { std::cerr << __LINE__ << ": " #expr "\n"; std::exit(1); } } while (false)
|
||||||
|
namespace {
|
||||||
|
uintptr_t serial = 100;
|
||||||
|
frame::Names seenExtensions;
|
||||||
|
XrVersion seenVersion{};
|
||||||
|
XrApiLayerNextInfo* seenNext{};
|
||||||
|
int suggestionCalls{}, locateCalls{}, realThreadCalls{};
|
||||||
|
bool nativeThread{};
|
||||||
|
std::unordered_map<XrPath, std::string> paths;
|
||||||
|
XrResult XRAPI_CALL fakeEnumerate(const char*, uint32_t capacity, uint32_t* count, XrExtensionProperties* out) {
|
||||||
|
const char* extensions[] = {"XR_KHR_locate_spaces", "XR_EXT_palm_pose", frame::stubs[0]};
|
||||||
|
*count = nativeThread ? 3 : 2;
|
||||||
|
if (!capacity) return XR_SUCCESS;
|
||||||
|
if (capacity < *count) return XR_ERROR_SIZE_INSUFFICIENT;
|
||||||
|
for (uint32_t j = 0; j < *count; ++j) { std::strcpy(out[j].extensionName, extensions[j]); out[j].extensionVersion = 1; }
|
||||||
|
return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL fakeCreate(const XrInstanceCreateInfo* in, const XrApiLayerCreateInfo* layer, XrInstance* i) {
|
||||||
|
seenVersion = in->applicationInfo.apiVersion; seenNext = layer->nextInfo;
|
||||||
|
seenExtensions.clear();
|
||||||
|
for (uint32_t j = 0; j < in->enabledExtensionCount; ++j) seenExtensions.emplace_back(in->enabledExtensionNames[j]);
|
||||||
|
if (frame::has(seenExtensions, "XR_MISSING_test")) return XR_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
|
*i = reinterpret_cast<XrInstance>(++serial); return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL fakeDestroy(XrInstance) { return XR_SUCCESS; }
|
||||||
|
XrResult XRAPI_CALL fakeCreateSession(XrInstance, const XrSessionCreateInfo*, XrSession* s) {
|
||||||
|
*s = reinterpret_cast<XrSession>(++serial); return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL fakeDestroySession(XrSession) { return XR_SUCCESS; }
|
||||||
|
XrResult XRAPI_CALL fakeString(XrInstance, const char* text, XrPath* p) {
|
||||||
|
*p = ++serial; paths[*p] = text; return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL fakePath(XrInstance, XrPath p, uint32_t cap, uint32_t* count, char* out) {
|
||||||
|
if (!paths.count(p)) return XR_ERROR_PATH_INVALID;
|
||||||
|
*count = static_cast<uint32_t>(paths[p].size() + 1);
|
||||||
|
if (!cap) return XR_SUCCESS;
|
||||||
|
if (cap < *count) return XR_ERROR_SIZE_INSUFFICIENT;
|
||||||
|
std::strcpy(out, paths[p].c_str()); return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL fakeSuggest(XrInstance, const XrInteractionProfileSuggestedBinding* info) {
|
||||||
|
++suggestionCalls;
|
||||||
|
if (info->countSuggestedBindings) CHECK(paths[info->suggestedBindings[0].binding] == "/user/hand/left/input/palm_ext/pose");
|
||||||
|
return XR_SUCCESS;
|
||||||
|
}
|
||||||
|
XrResult XRAPI_CALL fakeLocate(XrSession, const XrSpacesLocateInfo*, XrSpaceLocations*) { ++locateCalls; return XR_SUCCESS; }
|
||||||
|
XrResult XRAPI_CALL fakeThread(XrSession, XrAndroidThreadTypeKHR, uint32_t) { ++realThreadCalls; return XR_TIMEOUT_EXPIRED; }
|
||||||
|
XrResult XRAPI_CALL fakeProperties(XrInstance, XrInstanceProperties*) { return XR_SUCCESS; }
|
||||||
|
XrResult XRAPI_CALL fakeGipa(XrInstance, const char* n, PFN_xrVoidFunction* out) {
|
||||||
|
#define MAP(name, f) if (!std::strcmp(n, name)) { *out = reinterpret_cast<PFN_xrVoidFunction>(f); return XR_SUCCESS; }
|
||||||
|
MAP("xrEnumerateInstanceExtensionProperties", fakeEnumerate)
|
||||||
|
MAP("xrDestroyInstance", fakeDestroy)
|
||||||
|
MAP("xrCreateSession", fakeCreateSession) MAP("xrDestroySession", fakeDestroySession)
|
||||||
|
MAP("xrStringToPath", fakeString) MAP("xrPathToString", fakePath)
|
||||||
|
MAP("xrSuggestInteractionProfileBindings", fakeSuggest) MAP("xrLocateSpacesKHR", fakeLocate)
|
||||||
|
MAP("xrSetAndroidApplicationThreadKHR", fakeThread) MAP("xrGetInstanceProperties", fakeProperties)
|
||||||
|
#undef MAP
|
||||||
|
*out = nullptr; return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int main() {
|
||||||
|
XrNegotiateLoaderInfo loader{XR_LOADER_INTERFACE_STRUCT_LOADER_INFO, 1, sizeof(XrNegotiateLoaderInfo), 1, 1,
|
||||||
|
XR_MAKE_VERSION(1,0,0), XR_MAKE_VERSION(1,1023,4095)};
|
||||||
|
XrNegotiateApiLayerRequest request{};
|
||||||
|
request.structType = XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST; request.structVersion = 1; request.structSize = sizeof(request);
|
||||||
|
CHECK(xrNegotiateLoaderApiLayerInterface(&loader, frame::layerName, &request) == XR_SUCCESS);
|
||||||
|
CHECK(request.getInstanceProcAddr && request.createApiLayerInstance);
|
||||||
|
loader.maxApiVersion = XR_MAKE_VERSION(1,1,0);
|
||||||
|
CHECK(xrNegotiateLoaderApiLayerInterface(&loader, frame::layerName, &request) == XR_SUCCESS);
|
||||||
|
CHECK(request.layerApiVersion == loader.maxApiVersion);
|
||||||
|
loader.maxApiVersion = XR_MAKE_VERSION(1,0,99);
|
||||||
|
CHECK(xrNegotiateLoaderApiLayerInterface(&loader, frame::layerName, &request) == XR_ERROR_INITIALIZATION_FAILED);
|
||||||
|
loader.maxApiVersion = XR_MAKE_VERSION(1,1023,4095);
|
||||||
|
CHECK(xrNegotiateLoaderApiLayerInterface(&loader, "wrong", &request) == XR_ERROR_INITIALIZATION_FAILED);
|
||||||
|
XrApiLayerNextInfo tail{};
|
||||||
|
XrApiLayerNextInfo next{XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO, 1, sizeof(XrApiLayerNextInfo), {}, fakeGipa, fakeCreate, &tail};
|
||||||
|
XrApiLayerCreateInfo layer{XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO, 1, sizeof(XrApiLayerCreateInfo), nullptr, {}, &next};
|
||||||
|
XrInstanceCreateInfo info{XR_TYPE_INSTANCE_CREATE_INFO, nullptr, 0, {}, 0, nullptr, 2, frame::stubs};
|
||||||
|
info.applicationInfo.apiVersion = XR_MAKE_VERSION(1,1,999);
|
||||||
|
XrInstance a{}, b{};
|
||||||
|
CHECK(createLayer(&info, &layer, &a) == XR_SUCCESS);
|
||||||
|
CHECK(seenVersion == XR_API_VERSION_1_0 && info.applicationInfo.apiVersion == XR_MAKE_VERSION(1,1,999));
|
||||||
|
CHECK(seenNext == &tail && layer.nextInfo == &next);
|
||||||
|
CHECK(!frame::has(seenExtensions, frame::stubs[0]) && frame::has(seenExtensions, "XR_KHR_locate_spaces"));
|
||||||
|
XrSession sa{}, sb{};
|
||||||
|
CHECK(createSession(a, nullptr, &sa) == XR_SUCCESS);
|
||||||
|
CHECK(threadSettings(sa, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_SUCCESS);
|
||||||
|
nativeThread = true;
|
||||||
|
CHECK(createLayer(&info, &layer, &b) == XR_SUCCESS);
|
||||||
|
CHECK(frame::has(seenExtensions, frame::stubs[0]));
|
||||||
|
CHECK(createSession(b, nullptr, &sb) == XR_SUCCESS);
|
||||||
|
PFN_xrVoidFunction fn{};
|
||||||
|
CHECK(gipa(b, "xrSetAndroidApplicationThreadKHR", &fn) == XR_SUCCESS);
|
||||||
|
CHECK(reinterpret_cast<PFN_xrSetAndroidApplicationThreadKHR>(fn)(sb, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_TIMEOUT_EXPIRED);
|
||||||
|
CHECK(realThreadCalls == 1);
|
||||||
|
CHECK(threadSettings(sa, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_SUCCESS);
|
||||||
|
CHECK(gipa(a, "xrGetInstanceProperties", &fn) == XR_SUCCESS && fn == reinterpret_cast<PFN_xrVoidFunction>(fakeProperties));
|
||||||
|
CHECK(gipa(a, "xrLocateSpaces", &fn) == XR_SUCCESS);
|
||||||
|
XrSpacesLocateInfo locateInfo{XR_TYPE_SPACES_LOCATE_INFO, nullptr, XR_NULL_HANDLE, 1, 0, nullptr};
|
||||||
|
XrSpaceLocations locations{XR_TYPE_SPACE_LOCATIONS, nullptr, 0, nullptr};
|
||||||
|
CHECK(reinterpret_cast<PFN_xrLocateSpaces>(fn)(sa, &locateInfo, &locations) == XR_SUCCESS && locateCalls == 1);
|
||||||
|
XrPath binding{}, profile{};
|
||||||
|
CHECK(stringToPath(a, "/user/hand/left/input/grip_surface/pose", &binding) == XR_SUCCESS);
|
||||||
|
CHECK(paths[binding] == "/user/hand/left/input/palm_ext/pose");
|
||||||
|
fakeString(a, "/user/hand/left/input/grip_surface/pose", &binding); // preexisting path: rewrite in suggest too
|
||||||
|
fakeString(a, "/interaction_profiles/oculus/touch_controller", &profile);
|
||||||
|
XrActionSuggestedBinding action{XR_NULL_HANDLE, binding};
|
||||||
|
XrInteractionProfileSuggestedBinding suggested{XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING, nullptr, profile, 1, &action};
|
||||||
|
CHECK(suggest(a, &suggested) == XR_SUCCESS && suggestionCalls == 1);
|
||||||
|
CHECK(paths[action.binding] == "/user/hand/left/input/grip_surface/pose");
|
||||||
|
fakeString(a, "/interaction_profiles/meta/touch_pro_controller", &suggested.interactionProfile);
|
||||||
|
CHECK(suggest(a, &suggested) == XR_SUCCESS && suggestionCalls == 1);
|
||||||
|
uint32_t count{};
|
||||||
|
CHECK(enumerate(frame::layerName, 0, &count, nullptr) == XR_SUCCESS && count == 2);
|
||||||
|
XrExtensionProperties props[2]{{XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, 0}, {XR_TYPE_EXTENSION_PROPERTIES, nullptr, {}, 0}};
|
||||||
|
CHECK(enumerate(frame::layerName, 1, &count, props) == XR_ERROR_SIZE_INSUFFICIENT);
|
||||||
|
CHECK(enumerate(frame::layerName, 2, &count, props) == XR_SUCCESS && count == 2);
|
||||||
|
CHECK(enumerate(nullptr, 0, &count, nullptr) == XR_SUCCESS && count == 4);
|
||||||
|
const char* missing = "XR_MISSING_test";
|
||||||
|
info.enabledExtensionCount = 1; info.enabledExtensionNames = &missing;
|
||||||
|
XrInstance failed{};
|
||||||
|
CHECK(createLayer(&info, &layer, &failed) == XR_ERROR_EXTENSION_NOT_PRESENT && failed == XR_NULL_HANDLE);
|
||||||
|
CHECK(destroySession(sa) == XR_SUCCESS);
|
||||||
|
CHECK(threadSettings(sa, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) == XR_ERROR_HANDLE_INVALID);
|
||||||
|
CHECK(destroyInstance(a) == XR_SUCCESS);
|
||||||
|
CHECK(get(sb) && !get(a));
|
||||||
|
CHECK(destroyInstance(b) == XR_SUCCESS && !get(sb));
|
||||||
|
CHECK(instances.empty() && sessions.empty() && enumerateNext == nullptr);
|
||||||
|
std::cout << "Negotiation, next-layer chaining, multi-instance dispatch, stubs, forwarding and cleanup passed\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
// Host-only logcat replacement. Android builds use the NDK header and liblog.
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
#define ANDROID_LOG_INFO 4
|
||||||
|
inline int __android_log_print(int, const char*, const char* fmt, ...) {
|
||||||
|
va_list args; va_start(args, fmt); auto r = std::vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args); std::fputc('\n', stderr); return r;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
// Only the opaque jobject declaration is needed by openxr_platform.h.
|
||||||
|
typedef void* jobject;
|
||||||
Vendored
+208
@@ -0,0 +1,208 @@
|
|||||||
|
Apache License
|
||||||
|
|
||||||
|
Version 2.0, January 2004
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
|
||||||
|
AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, and distribution
|
||||||
|
as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of this definition, "control" means (i) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity, whether
|
||||||
|
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
|
||||||
|
of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
|
||||||
|
granted by this License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical transformation
|
||||||
|
or translation of a Source form, including but not limited to compiled object
|
||||||
|
code, generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or Object form,
|
||||||
|
made available under the License, as indicated by a copyright notice that
|
||||||
|
is included in or attached to the work (an example is provided in the Appendix
|
||||||
|
below).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object form,
|
||||||
|
that is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of this License, Derivative
|
||||||
|
Works shall not include works that remain separable from, or merely link (or
|
||||||
|
bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative
|
||||||
|
Works thereof, that is intentionally submitted to Licensor for inclusion in
|
||||||
|
the Work by the copyright owner or by an individual or Legal Entity authorized
|
||||||
|
to submit on behalf of the copyright owner. For the purposes of this definition,
|
||||||
|
"submitted" means any form of electronic, verbal, or written communication
|
||||||
|
sent to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor
|
||||||
|
for the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as "Not a Contribution."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently incorporated
|
||||||
|
within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of this
|
||||||
|
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
|
||||||
|
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
|
||||||
|
the Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of this License,
|
||||||
|
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
||||||
|
license to make, have made, use, offer to sell, sell, import, and otherwise
|
||||||
|
transfer the Work, where such license applies only to those patent claims
|
||||||
|
licensable by such Contributor that are necessarily infringed by their Contribution(s)
|
||||||
|
alone or by combination of their Contribution(s) with the Work to which such
|
||||||
|
Contribution(s) was submitted. If You institute patent litigation against
|
||||||
|
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that the Work or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses granted to You
|
||||||
|
under this License for that Work shall terminate as of the date such litigation
|
||||||
|
is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the Work or
|
||||||
|
Derivative Works thereof in any medium, with or without modifications, and
|
||||||
|
in Source or Object form, provided that You meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or Derivative Works a copy
|
||||||
|
of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices stating that
|
||||||
|
You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source
|
||||||
|
form of the Work, excluding those notices that do not pertain to any part
|
||||||
|
of the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its distribution,
|
||||||
|
then any Derivative Works that You distribute must include a readable copy
|
||||||
|
of the attribution notices contained within such NOTICE file, excluding those
|
||||||
|
notices that do not pertain to any part of the Derivative Works, in at least
|
||||||
|
one of the following places: within a NOTICE text file distributed as part
|
||||||
|
of the Derivative Works; within the Source form or documentation, if provided
|
||||||
|
along with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works
|
||||||
|
that You distribute, alongside or as an addendum to the NOTICE text from the
|
||||||
|
Work, provided that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction,
|
||||||
|
or distribution of Your modifications, or for any such Derivative Works as
|
||||||
|
a whole, provided Your use, reproduction, and distribution of the Work otherwise
|
||||||
|
complies with the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, any
|
||||||
|
Contribution intentionally submitted for inclusion in the Work by You to the
|
||||||
|
Licensor shall be under the terms and conditions of this License, without
|
||||||
|
any additional terms or conditions. Notwithstanding the above, nothing herein
|
||||||
|
shall supersede or modify the terms of any separate license agreement you
|
||||||
|
may have executed with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade names,
|
||||||
|
trademarks, service marks, or product names of the Licensor, except as required
|
||||||
|
for reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
|
||||||
|
in writing, Licensor provides the Work (and each Contributor provides its
|
||||||
|
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied, including, without limitation, any warranties
|
||||||
|
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
|
||||||
|
of using or redistributing the Work and assume any risks associated with Your
|
||||||
|
exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory, whether
|
||||||
|
in tort (including negligence), contract, or otherwise, unless required by
|
||||||
|
applicable law (such as deliberate and grossly negligent acts) or agreed to
|
||||||
|
in writing, shall any Contributor be liable to You for damages, including
|
||||||
|
any direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character arising as a result of this License or out of the use or inability
|
||||||
|
to use the Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all other commercial
|
||||||
|
damages or losses), even if such Contributor has been advised of the possibility
|
||||||
|
of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing the Work
|
||||||
|
or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
||||||
|
acceptance of support, warranty, indemnity, or other liability obligations
|
||||||
|
and/or rights consistent with this License. However, in accepting such obligations,
|
||||||
|
You may act only on Your own behalf and on Your sole responsibility, not on
|
||||||
|
behalf of any other Contributor, and only if You agree to indemnify, defend,
|
||||||
|
and hold each Contributor harmless for any liability incurred by, or claims
|
||||||
|
asserted against, such Contributor by reason of your accepting any such warranty
|
||||||
|
or additional liability. END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
|
||||||
|
information. (Don't include the brackets!) The text should be enclosed in
|
||||||
|
the appropriate comment syntax for the file format. We also recommend that
|
||||||
|
a file or class name and description of purpose be included on the same "printed
|
||||||
|
page" as the copyright notice for easier identification within third-party
|
||||||
|
archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
http://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.
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
// Copyright (c) 2017-2023, The Khronos Group Inc.
|
||||||
|
// Copyright (c) 2017 Valve Corporation
|
||||||
|
// Copyright (c) 2017 LunarG, Inc.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
//
|
||||||
|
// Initial Author: Mark Young <marky@lunarg.com>
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Forward declare.
|
||||||
|
typedef struct XrApiLayerCreateInfo XrApiLayerCreateInfo;
|
||||||
|
|
||||||
|
// Function pointer prototype for the xrCreateApiLayerInstance function used in place of xrCreateInstance.
|
||||||
|
// This function allows us to pass special API layer information to each layer during the process of creating an Instance.
|
||||||
|
typedef XrResult(XRAPI_PTR *PFN_xrCreateApiLayerInstance)(const XrInstanceCreateInfo *info,
|
||||||
|
const XrApiLayerCreateInfo *apiLayerInfo, XrInstance *instance);
|
||||||
|
|
||||||
|
// Loader/API Layer Interface versions
|
||||||
|
// 1 - First version, introduces negotiation structure and functions
|
||||||
|
#define XR_CURRENT_LOADER_API_LAYER_VERSION 1
|
||||||
|
|
||||||
|
// Loader/Runtime Interface versions
|
||||||
|
// 1 - First version, introduces negotiation structure and functions
|
||||||
|
#define XR_CURRENT_LOADER_RUNTIME_VERSION 1
|
||||||
|
|
||||||
|
// Version negotiation values
|
||||||
|
typedef enum XrLoaderInterfaceStructs {
|
||||||
|
XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED = 0,
|
||||||
|
XR_LOADER_INTERFACE_STRUCT_LOADER_INFO,
|
||||||
|
XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST,
|
||||||
|
XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST,
|
||||||
|
XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO,
|
||||||
|
XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO,
|
||||||
|
} XrLoaderInterfaceStructs;
|
||||||
|
|
||||||
|
#define XR_LOADER_INFO_STRUCT_VERSION 1
|
||||||
|
typedef struct XrNegotiateLoaderInfo {
|
||||||
|
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_LOADER_INFO
|
||||||
|
uint32_t structVersion; // XR_LOADER_INFO_STRUCT_VERSION
|
||||||
|
size_t structSize; // sizeof(XrNegotiateLoaderInfo)
|
||||||
|
uint32_t minInterfaceVersion;
|
||||||
|
uint32_t maxInterfaceVersion;
|
||||||
|
XrVersion minApiVersion;
|
||||||
|
XrVersion maxApiVersion;
|
||||||
|
} XrNegotiateLoaderInfo;
|
||||||
|
|
||||||
|
#define XR_API_LAYER_INFO_STRUCT_VERSION 1
|
||||||
|
typedef struct XrNegotiateApiLayerRequest {
|
||||||
|
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST
|
||||||
|
uint32_t structVersion; // XR_API_LAYER_INFO_STRUCT_VERSION
|
||||||
|
size_t structSize; // sizeof(XrNegotiateApiLayerRequest)
|
||||||
|
uint32_t layerInterfaceVersion; // CURRENT_LOADER_API_LAYER_VERSION
|
||||||
|
XrVersion layerApiVersion;
|
||||||
|
PFN_xrGetInstanceProcAddr getInstanceProcAddr;
|
||||||
|
PFN_xrCreateApiLayerInstance createApiLayerInstance;
|
||||||
|
} XrNegotiateApiLayerRequest;
|
||||||
|
|
||||||
|
#define XR_RUNTIME_INFO_STRUCT_VERSION 1
|
||||||
|
typedef struct XrNegotiateRuntimeRequest {
|
||||||
|
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST
|
||||||
|
uint32_t structVersion; // XR_RUNTIME_INFO_STRUCT_VERSION
|
||||||
|
size_t structSize; // sizeof(XrNegotiateRuntimeRequest)
|
||||||
|
uint32_t runtimeInterfaceVersion; // CURRENT_LOADER_RUNTIME_VERSION
|
||||||
|
XrVersion runtimeApiVersion;
|
||||||
|
PFN_xrGetInstanceProcAddr getInstanceProcAddr;
|
||||||
|
} XrNegotiateRuntimeRequest;
|
||||||
|
|
||||||
|
// Function used to negotiate an interface betewen the loader and an API layer. Each library exposing one or
|
||||||
|
// more API layers needs to expose at least this function.
|
||||||
|
typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderApiLayerInterface)(const XrNegotiateLoaderInfo *loaderInfo,
|
||||||
|
const char *apiLayerName,
|
||||||
|
XrNegotiateApiLayerRequest *apiLayerRequest);
|
||||||
|
|
||||||
|
// Function used to negotiate an interface betewen the loader and a runtime. Each runtime should expose
|
||||||
|
// at least this function.
|
||||||
|
typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderRuntimeInterface)(const XrNegotiateLoaderInfo *loaderInfo,
|
||||||
|
XrNegotiateRuntimeRequest *runtimeRequest);
|
||||||
|
|
||||||
|
// Forward declare.
|
||||||
|
typedef struct XrApiLayerNextInfo XrApiLayerNextInfo;
|
||||||
|
|
||||||
|
#define XR_API_LAYER_NEXT_INFO_STRUCT_VERSION 1
|
||||||
|
struct XrApiLayerNextInfo {
|
||||||
|
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO
|
||||||
|
uint32_t structVersion; // XR_API_LAYER_NEXT_INFO_STRUCT_VERSION
|
||||||
|
size_t structSize; // sizeof(XrApiLayerNextInfo)
|
||||||
|
char layerName[XR_MAX_API_LAYER_NAME_SIZE]; // Name of API layer which should receive this info
|
||||||
|
PFN_xrGetInstanceProcAddr nextGetInstanceProcAddr; // Pointer to next API layer's xrGetInstanceProcAddr
|
||||||
|
PFN_xrCreateApiLayerInstance nextCreateApiLayerInstance; // Pointer to next API layer's xrCreateApiLayerInstance
|
||||||
|
XrApiLayerNextInfo *next; // Pointer to the next API layer info in the sequence
|
||||||
|
};
|
||||||
|
|
||||||
|
#define XR_API_LAYER_MAX_SETTINGS_PATH_SIZE 512
|
||||||
|
#define XR_API_LAYER_CREATE_INFO_STRUCT_VERSION 1
|
||||||
|
typedef struct XrApiLayerCreateInfo {
|
||||||
|
XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO
|
||||||
|
uint32_t structVersion; // XR_API_LAYER_CREATE_INFO_STRUCT_VERSION
|
||||||
|
size_t structSize; // sizeof(XrApiLayerCreateInfo)
|
||||||
|
void *loaderInstance; // Pointer to the LoaderInstance class
|
||||||
|
char settings_file_location[XR_API_LAYER_MAX_SETTINGS_PATH_SIZE]; // Location to the found settings file (or empty '\0')
|
||||||
|
XrApiLayerNextInfo *nextInfo; // Pointer to the next API layer's Info
|
||||||
|
} XrApiLayerCreateInfo;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
+14911
File diff suppressed because it is too large.
Load diff
+819
@@ -0,0 +1,819 @@
|
|||||||
|
#ifndef OPENXR_PLATFORM_H_
|
||||||
|
#define OPENXR_PLATFORM_H_ 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Copyright 2017-2026 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This header is generated from the Khronos OpenXR XML API Registry.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "openxr.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
// XR_KHR_android_thread_settings is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_android_thread_settings 1
|
||||||
|
#define XR_KHR_android_thread_settings_SPEC_VERSION 6
|
||||||
|
#define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings"
|
||||||
|
|
||||||
|
typedef enum XrAndroidThreadTypeKHR {
|
||||||
|
XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR = 1,
|
||||||
|
XR_ANDROID_THREAD_TYPE_APPLICATION_WORKER_KHR = 2,
|
||||||
|
XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR = 3,
|
||||||
|
XR_ANDROID_THREAD_TYPE_RENDERER_WORKER_KHR = 4,
|
||||||
|
XR_ANDROID_THREAD_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
|
||||||
|
} XrAndroidThreadTypeKHR;
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrSetAndroidApplicationThreadKHR)(XrSession session, XrAndroidThreadTypeKHR threadType, uint32_t threadId);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR(
|
||||||
|
XrSession session,
|
||||||
|
XrAndroidThreadTypeKHR threadType,
|
||||||
|
uint32_t threadId);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
// XR_KHR_android_surface_swapchain is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_android_surface_swapchain 1
|
||||||
|
#define XR_KHR_android_surface_swapchain_SPEC_VERSION 4
|
||||||
|
#define XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME "XR_KHR_android_surface_swapchain"
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo* info, XrSwapchain* swapchain, jobject* surface);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR(
|
||||||
|
XrSession session,
|
||||||
|
const XrSwapchainCreateInfo* info,
|
||||||
|
XrSwapchain* swapchain,
|
||||||
|
jobject* surface);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
// XR_KHR_android_create_instance is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_android_create_instance 1
|
||||||
|
#define XR_KHR_android_create_instance_SPEC_VERSION 3
|
||||||
|
#define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance"
|
||||||
|
// XrInstanceCreateInfoAndroidKHR extends XrInstanceCreateInfo
|
||||||
|
typedef struct XrInstanceCreateInfoAndroidKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
void* XR_MAY_ALIAS applicationVM;
|
||||||
|
void* XR_MAY_ALIAS applicationActivity;
|
||||||
|
} XrInstanceCreateInfoAndroidKHR;
|
||||||
|
|
||||||
|
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
|
||||||
|
// XR_KHR_vulkan_swapchain_format_list is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_vulkan_swapchain_format_list 1
|
||||||
|
#define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 5
|
||||||
|
#define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list"
|
||||||
|
// XrVulkanSwapchainFormatListCreateInfoKHR extends XrSwapchainCreateInfo
|
||||||
|
typedef struct XrVulkanSwapchainFormatListCreateInfoKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
uint32_t viewFormatCount;
|
||||||
|
const VkFormat* viewFormats;
|
||||||
|
} XrVulkanSwapchainFormatListCreateInfoKHR;
|
||||||
|
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_OPENGL
|
||||||
|
|
||||||
|
// XR_KHR_opengl_enable is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_opengl_enable 1
|
||||||
|
#define XR_KHR_opengl_enable_SPEC_VERSION 12
|
||||||
|
#define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable"
|
||||||
|
#ifdef XR_USE_PLATFORM_WIN32
|
||||||
|
// XrGraphicsBindingOpenGLWin32KHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingOpenGLWin32KHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
HDC hDC;
|
||||||
|
HGLRC hGLRC;
|
||||||
|
} XrGraphicsBindingOpenGLWin32KHR;
|
||||||
|
#endif // XR_USE_PLATFORM_WIN32
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_XLIB
|
||||||
|
// XrGraphicsBindingOpenGLXlibKHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingOpenGLXlibKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
Display* xDisplay;
|
||||||
|
uint32_t visualid;
|
||||||
|
GLXFBConfig glxFBConfig;
|
||||||
|
GLXDrawable glxDrawable;
|
||||||
|
GLXContext glxContext;
|
||||||
|
} XrGraphicsBindingOpenGLXlibKHR;
|
||||||
|
#endif // XR_USE_PLATFORM_XLIB
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_XCB
|
||||||
|
// XrGraphicsBindingOpenGLXcbKHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingOpenGLXcbKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
xcb_connection_t* connection;
|
||||||
|
uint32_t screenNumber;
|
||||||
|
xcb_glx_fbconfig_t fbconfigid;
|
||||||
|
xcb_visualid_t visualid;
|
||||||
|
xcb_glx_drawable_t glxDrawable;
|
||||||
|
xcb_glx_context_t glxContext;
|
||||||
|
} XrGraphicsBindingOpenGLXcbKHR;
|
||||||
|
#endif // XR_USE_PLATFORM_XCB
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_WAYLAND
|
||||||
|
// XrGraphicsBindingOpenGLWaylandKHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingOpenGLWaylandKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
struct wl_display* display;
|
||||||
|
} XrGraphicsBindingOpenGLWaylandKHR;
|
||||||
|
#endif // XR_USE_PLATFORM_WAYLAND
|
||||||
|
|
||||||
|
typedef struct XrSwapchainImageOpenGLKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
uint32_t image;
|
||||||
|
} XrSwapchainImageOpenGLKHR;
|
||||||
|
|
||||||
|
typedef struct XrGraphicsRequirementsOpenGLKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
XrVersion minApiVersionSupported;
|
||||||
|
XrVersion maxApiVersionSupported;
|
||||||
|
} XrGraphicsRequirementsOpenGLKHR;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_OPENGL */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
|
||||||
|
|
||||||
|
// XR_KHR_opengl_es_enable is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_opengl_es_enable 1
|
||||||
|
#define XR_KHR_opengl_es_enable_SPEC_VERSION 10
|
||||||
|
#define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable"
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
// XrGraphicsBindingOpenGLESAndroidKHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingOpenGLESAndroidKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
EGLDisplay display;
|
||||||
|
EGLConfig config;
|
||||||
|
EGLContext context;
|
||||||
|
} XrGraphicsBindingOpenGLESAndroidKHR;
|
||||||
|
#endif // XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
typedef struct XrSwapchainImageOpenGLESKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
uint32_t image;
|
||||||
|
} XrSwapchainImageOpenGLESKHR;
|
||||||
|
|
||||||
|
typedef struct XrGraphicsRequirementsOpenGLESKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
XrVersion minApiVersionSupported;
|
||||||
|
XrVersion maxApiVersionSupported;
|
||||||
|
} XrGraphicsRequirementsOpenGLESKHR;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLESGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
|
||||||
|
// XR_KHR_vulkan_enable is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_vulkan_enable 1
|
||||||
|
#define XR_KHR_vulkan_enable_SPEC_VERSION 10
|
||||||
|
#define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable"
|
||||||
|
// XrGraphicsBindingVulkanKHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingVulkanKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
VkInstance instance;
|
||||||
|
VkPhysicalDevice physicalDevice;
|
||||||
|
VkDevice device;
|
||||||
|
uint32_t queueFamilyIndex;
|
||||||
|
uint32_t queueIndex;
|
||||||
|
} XrGraphicsBindingVulkanKHR;
|
||||||
|
|
||||||
|
typedef struct XrSwapchainImageVulkanKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
VkImage image;
|
||||||
|
} XrSwapchainImageVulkanKHR;
|
||||||
|
|
||||||
|
typedef struct XrGraphicsRequirementsVulkanKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
XrVersion minApiVersionSupported;
|
||||||
|
XrVersion maxApiVersionSupported;
|
||||||
|
} XrGraphicsRequirementsVulkanKHR;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanInstanceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanDeviceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDeviceKHR)(XrInstance instance, XrSystemId systemId, VkInstance vkInstance, VkPhysicalDevice* vkPhysicalDevice);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanInstanceExtensionsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
uint32_t bufferCapacityInput,
|
||||||
|
uint32_t* bufferCountOutput,
|
||||||
|
char* buffer);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanDeviceExtensionsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
uint32_t bufferCapacityInput,
|
||||||
|
uint32_t* bufferCountOutput,
|
||||||
|
char* buffer);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsDeviceKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
VkInstance vkInstance,
|
||||||
|
VkPhysicalDevice* vkPhysicalDevice);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
|
||||||
|
// XR_KHR_D3D11_enable is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_D3D11_enable 1
|
||||||
|
#define XR_KHR_D3D11_enable_SPEC_VERSION 11
|
||||||
|
#define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable"
|
||||||
|
// XrGraphicsBindingD3D11KHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingD3D11KHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
ID3D11Device* device;
|
||||||
|
} XrGraphicsBindingD3D11KHR;
|
||||||
|
|
||||||
|
typedef struct XrSwapchainImageD3D11KHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
ID3D11Texture2D* texture;
|
||||||
|
} XrSwapchainImageD3D11KHR;
|
||||||
|
|
||||||
|
typedef struct XrGraphicsRequirementsD3D11KHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
LUID adapterLuid;
|
||||||
|
D3D_FEATURE_LEVEL minFeatureLevel;
|
||||||
|
} XrGraphicsRequirementsD3D11KHR;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetD3D11GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_D3D11 */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D12
|
||||||
|
|
||||||
|
// XR_KHR_D3D12_enable is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_D3D12_enable 1
|
||||||
|
#define XR_KHR_D3D12_enable_SPEC_VERSION 11
|
||||||
|
#define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable"
|
||||||
|
// XrGraphicsBindingD3D12KHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingD3D12KHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
ID3D12Device* device;
|
||||||
|
ID3D12CommandQueue* queue;
|
||||||
|
} XrGraphicsBindingD3D12KHR;
|
||||||
|
|
||||||
|
typedef struct XrSwapchainImageD3D12KHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
ID3D12Resource* texture;
|
||||||
|
} XrSwapchainImageD3D12KHR;
|
||||||
|
|
||||||
|
typedef struct XrGraphicsRequirementsD3D12KHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
LUID adapterLuid;
|
||||||
|
D3D_FEATURE_LEVEL minFeatureLevel;
|
||||||
|
} XrGraphicsRequirementsD3D12KHR;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetD3D12GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_D3D12 */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_METAL
|
||||||
|
|
||||||
|
// XR_KHR_metal_enable is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_metal_enable 1
|
||||||
|
#define XR_KHR_metal_enable_SPEC_VERSION 3
|
||||||
|
#define XR_KHR_METAL_ENABLE_EXTENSION_NAME "XR_KHR_metal_enable"
|
||||||
|
// XrGraphicsBindingMetalKHR extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingMetalKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
void* XR_MAY_ALIAS commandQueue;
|
||||||
|
} XrGraphicsBindingMetalKHR;
|
||||||
|
|
||||||
|
typedef struct XrSwapchainImageMetalKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
void* XR_MAY_ALIAS texture;
|
||||||
|
} XrSwapchainImageMetalKHR;
|
||||||
|
|
||||||
|
typedef struct XrGraphicsRequirementsMetalKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
void* XR_MAY_ALIAS metalDevice;
|
||||||
|
} XrGraphicsRequirementsMetalKHR;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetMetalGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsMetalKHR* graphicsRequirements);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetMetalGraphicsRequirementsKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
XrGraphicsRequirementsMetalKHR* graphicsRequirements);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_METAL */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_WIN32
|
||||||
|
|
||||||
|
// XR_KHR_win32_convert_performance_counter_time is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_win32_convert_performance_counter_time 1
|
||||||
|
#define XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION 1
|
||||||
|
#define XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME "XR_KHR_win32_convert_performance_counter_time"
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrConvertWin32PerformanceCounterToTimeKHR)(XrInstance instance, const LARGE_INTEGER* performanceCounter, XrTime* time);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToWin32PerformanceCounterKHR)(XrInstance instance, XrTime time, LARGE_INTEGER* performanceCounter);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrConvertWin32PerformanceCounterToTimeKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
const LARGE_INTEGER* performanceCounter,
|
||||||
|
XrTime* time);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrTime time,
|
||||||
|
LARGE_INTEGER* performanceCounter);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_PLATFORM_WIN32 */
|
||||||
|
|
||||||
|
#ifdef XR_USE_TIMESPEC
|
||||||
|
|
||||||
|
// XR_KHR_convert_timespec_time is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_convert_timespec_time 1
|
||||||
|
#define XR_KHR_convert_timespec_time_SPEC_VERSION 1
|
||||||
|
#define XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME "XR_KHR_convert_timespec_time"
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimespecTimeToTimeKHR)(XrInstance instance, const struct timespec* timespecTime, XrTime* time);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToTimespecTimeKHR)(XrInstance instance, XrTime time, struct timespec* timespecTime);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimespecTimeToTimeKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
const struct timespec* timespecTime,
|
||||||
|
XrTime* time);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrTime time,
|
||||||
|
struct timespec* timespecTime);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_TIMESPEC */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
// XR_KHR_loader_init_android is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_loader_init_android 1
|
||||||
|
#define XR_KHR_loader_init_android_SPEC_VERSION 1
|
||||||
|
#define XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME "XR_KHR_loader_init_android"
|
||||||
|
typedef struct XrLoaderInitInfoAndroidKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
void* XR_MAY_ALIAS applicationVM;
|
||||||
|
void* XR_MAY_ALIAS applicationContext;
|
||||||
|
} XrLoaderInitInfoAndroidKHR;
|
||||||
|
|
||||||
|
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
|
||||||
|
// XR_KHR_vulkan_enable2 is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_KHR_vulkan_enable2 1
|
||||||
|
#define XR_KHR_vulkan_enable2_SPEC_VERSION 4
|
||||||
|
#define XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME "XR_KHR_vulkan_enable2"
|
||||||
|
typedef XrFlags64 XrVulkanInstanceCreateFlagsKHR;
|
||||||
|
|
||||||
|
// Flag bits for XrVulkanInstanceCreateFlagsKHR
|
||||||
|
|
||||||
|
typedef XrFlags64 XrVulkanDeviceCreateFlagsKHR;
|
||||||
|
|
||||||
|
// Flag bits for XrVulkanDeviceCreateFlagsKHR
|
||||||
|
|
||||||
|
typedef struct XrVulkanInstanceCreateInfoKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
XrSystemId systemId;
|
||||||
|
XrVulkanInstanceCreateFlagsKHR createFlags;
|
||||||
|
PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
|
||||||
|
const VkInstanceCreateInfo* vulkanCreateInfo;
|
||||||
|
const VkAllocationCallbacks* vulkanAllocator;
|
||||||
|
} XrVulkanInstanceCreateInfoKHR;
|
||||||
|
|
||||||
|
typedef struct XrVulkanDeviceCreateInfoKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
XrSystemId systemId;
|
||||||
|
XrVulkanDeviceCreateFlagsKHR createFlags;
|
||||||
|
PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr;
|
||||||
|
VkPhysicalDevice vulkanPhysicalDevice;
|
||||||
|
const VkDeviceCreateInfo* vulkanCreateInfo;
|
||||||
|
const VkAllocationCallbacks* vulkanAllocator;
|
||||||
|
} XrVulkanDeviceCreateInfoKHR;
|
||||||
|
|
||||||
|
typedef XrGraphicsBindingVulkanKHR XrGraphicsBindingVulkan2KHR;
|
||||||
|
|
||||||
|
typedef struct XrVulkanGraphicsDeviceGetInfoKHR {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
XrSystemId systemId;
|
||||||
|
VkInstance vulkanInstance;
|
||||||
|
} XrVulkanGraphicsDeviceGetInfoKHR;
|
||||||
|
|
||||||
|
typedef XrSwapchainImageVulkanKHR XrSwapchainImageVulkan2KHR;
|
||||||
|
|
||||||
|
typedef XrGraphicsRequirementsVulkanKHR XrGraphicsRequirementsVulkan2KHR;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanInstanceKHR)(XrInstance instance, const XrVulkanInstanceCreateInfoKHR* createInfo, VkInstance* vulkanInstance, VkResult* vulkanResult);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrCreateVulkanDeviceKHR)(XrInstance instance, const XrVulkanDeviceCreateInfoKHR* createInfo, VkDevice* vulkanDevice, VkResult* vulkanResult);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDevice2KHR)(XrInstance instance, const XrVulkanGraphicsDeviceGetInfoKHR* getInfo, VkPhysicalDevice* vulkanPhysicalDevice);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirements2KHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrCreateVulkanInstanceKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
const XrVulkanInstanceCreateInfoKHR* createInfo,
|
||||||
|
VkInstance* vulkanInstance,
|
||||||
|
VkResult* vulkanResult);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrCreateVulkanDeviceKHR(
|
||||||
|
XrInstance instance,
|
||||||
|
const XrVulkanDeviceCreateInfoKHR* createInfo,
|
||||||
|
VkDevice* vulkanDevice,
|
||||||
|
VkResult* vulkanResult);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsDevice2KHR(
|
||||||
|
XrInstance instance,
|
||||||
|
const XrVulkanGraphicsDeviceGetInfoKHR* getInfo,
|
||||||
|
VkPhysicalDevice* vulkanPhysicalDevice);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR(
|
||||||
|
XrInstance instance,
|
||||||
|
XrSystemId systemId,
|
||||||
|
XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_EGL
|
||||||
|
|
||||||
|
// XR_MNDX_egl_enable is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_MNDX_egl_enable 1
|
||||||
|
#define XR_MNDX_egl_enable_SPEC_VERSION 2
|
||||||
|
#define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable"
|
||||||
|
typedef PFN_xrVoidFunction (*PFN_xrEglGetProcAddressMNDX)(const char *name);
|
||||||
|
// XrGraphicsBindingEGLMNDX extends XrSessionCreateInfo
|
||||||
|
typedef struct XrGraphicsBindingEGLMNDX {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
PFN_xrEglGetProcAddressMNDX getProcAddress;
|
||||||
|
EGLDisplay display;
|
||||||
|
EGLConfig config;
|
||||||
|
EGLContext context;
|
||||||
|
} XrGraphicsBindingEGLMNDX;
|
||||||
|
|
||||||
|
#endif /* XR_USE_PLATFORM_EGL */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_WIN32
|
||||||
|
|
||||||
|
// XR_MSFT_perception_anchor_interop is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_MSFT_perception_anchor_interop 1
|
||||||
|
#define XR_MSFT_perception_anchor_interop_SPEC_VERSION 1
|
||||||
|
#define XR_MSFT_PERCEPTION_ANCHOR_INTEROP_EXTENSION_NAME "XR_MSFT_perception_anchor_interop"
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFromPerceptionAnchorMSFT)(XrSession session, IUnknown* perceptionAnchor, XrSpatialAnchorMSFT* anchor);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrTryGetPerceptionAnchorFromSpatialAnchorMSFT)(XrSession session, XrSpatialAnchorMSFT anchor, IUnknown** perceptionAnchor);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFromPerceptionAnchorMSFT(
|
||||||
|
XrSession session,
|
||||||
|
IUnknown* perceptionAnchor,
|
||||||
|
XrSpatialAnchorMSFT* anchor);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT(
|
||||||
|
XrSession session,
|
||||||
|
XrSpatialAnchorMSFT anchor,
|
||||||
|
IUnknown** perceptionAnchor);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_PLATFORM_WIN32 */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_WIN32
|
||||||
|
|
||||||
|
// XR_MSFT_holographic_window_attachment is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_MSFT_holographic_window_attachment 1
|
||||||
|
#define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1
|
||||||
|
#define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment"
|
||||||
|
#ifdef XR_USE_PLATFORM_WIN32
|
||||||
|
// XrHolographicWindowAttachmentMSFT extends XrSessionCreateInfo
|
||||||
|
typedef struct XrHolographicWindowAttachmentMSFT {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
IUnknown* holographicSpace;
|
||||||
|
IUnknown* coreWindow;
|
||||||
|
} XrHolographicWindowAttachmentMSFT;
|
||||||
|
#endif // XR_USE_PLATFORM_WIN32
|
||||||
|
|
||||||
|
#endif /* XR_USE_PLATFORM_WIN32 */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
// XR_FB_android_surface_swapchain_create is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_FB_android_surface_swapchain_create 1
|
||||||
|
#define XR_FB_android_surface_swapchain_create_SPEC_VERSION 1
|
||||||
|
#define XR_FB_ANDROID_SURFACE_SWAPCHAIN_CREATE_EXTENSION_NAME "XR_FB_android_surface_swapchain_create"
|
||||||
|
typedef XrFlags64 XrAndroidSurfaceSwapchainFlagsFB;
|
||||||
|
|
||||||
|
// Flag bits for XrAndroidSurfaceSwapchainFlagsFB
|
||||||
|
static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_SYNCHRONOUS_BIT_FB = 0x00000001;
|
||||||
|
static const XrAndroidSurfaceSwapchainFlagsFB XR_ANDROID_SURFACE_SWAPCHAIN_USE_TIMESTAMPS_BIT_FB = 0x00000002;
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
// XrAndroidSurfaceSwapchainCreateInfoFB extends XrSwapchainCreateInfo
|
||||||
|
typedef struct XrAndroidSurfaceSwapchainCreateInfoFB {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
XrAndroidSurfaceSwapchainFlagsFB createFlags;
|
||||||
|
} XrAndroidSurfaceSwapchainCreateInfoFB;
|
||||||
|
#endif // XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ML
|
||||||
|
|
||||||
|
// XR_ML_compat is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_ML_compat 1
|
||||||
|
#define XR_ML_compat_SPEC_VERSION 1
|
||||||
|
#define XR_ML_COMPAT_EXTENSION_NAME "XR_ML_compat"
|
||||||
|
typedef struct XrCoordinateSpaceCreateInfoML {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
MLCoordinateFrameUID cfuid;
|
||||||
|
XrPosef poseInCoordinateSpace;
|
||||||
|
} XrCoordinateSpaceCreateInfoML;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrCreateSpaceFromCoordinateFrameUIDML)(XrSession session, const XrCoordinateSpaceCreateInfoML *createInfo, XrSpace* space);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceFromCoordinateFrameUIDML(
|
||||||
|
XrSession session,
|
||||||
|
const XrCoordinateSpaceCreateInfoML * createInfo,
|
||||||
|
XrSpace* space);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_PLATFORM_ML */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_WIN32
|
||||||
|
|
||||||
|
// XR_OCULUS_audio_device_guid is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_OCULUS_audio_device_guid 1
|
||||||
|
#define XR_OCULUS_audio_device_guid_SPEC_VERSION 1
|
||||||
|
#define XR_OCULUS_AUDIO_DEVICE_GUID_EXTENSION_NAME "XR_OCULUS_audio_device_guid"
|
||||||
|
#define XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS 128
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetAudioOutputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrGetAudioInputDeviceGuidOculus)(XrInstance instance, wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioOutputDeviceGuidOculus(
|
||||||
|
XrInstance instance,
|
||||||
|
wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioInputDeviceGuidOculus(
|
||||||
|
XrInstance instance,
|
||||||
|
wchar_t buffer[XR_MAX_AUDIO_DEVICE_STR_SIZE_OCULUS]);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_PLATFORM_WIN32 */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
|
||||||
|
// XR_FB_foveation_vulkan is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_FB_foveation_vulkan 1
|
||||||
|
#define XR_FB_foveation_vulkan_SPEC_VERSION 1
|
||||||
|
#define XR_FB_FOVEATION_VULKAN_EXTENSION_NAME "XR_FB_foveation_vulkan"
|
||||||
|
// XrSwapchainImageFoveationVulkanFB extends XrSwapchainImageVulkanKHR
|
||||||
|
typedef struct XrSwapchainImageFoveationVulkanFB {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
VkImage image;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
} XrSwapchainImageFoveationVulkanFB;
|
||||||
|
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
// XR_FB_swapchain_update_state_android_surface is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_FB_swapchain_update_state_android_surface 1
|
||||||
|
#define XR_FB_swapchain_update_state_android_surface_SPEC_VERSION 1
|
||||||
|
#define XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME "XR_FB_swapchain_update_state_android_surface"
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
typedef struct XrSwapchainStateAndroidSurfaceDimensionsFB {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
} XrSwapchainStateAndroidSurfaceDimensionsFB;
|
||||||
|
#endif // XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
|
||||||
|
|
||||||
|
// XR_FB_swapchain_update_state_opengl_es is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_FB_swapchain_update_state_opengl_es 1
|
||||||
|
#define XR_FB_swapchain_update_state_opengl_es_SPEC_VERSION 1
|
||||||
|
#define XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME "XR_FB_swapchain_update_state_opengl_es"
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
|
||||||
|
typedef struct XrSwapchainStateSamplerOpenGLESFB {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
EGLenum minFilter;
|
||||||
|
EGLenum magFilter;
|
||||||
|
EGLenum wrapModeS;
|
||||||
|
EGLenum wrapModeT;
|
||||||
|
EGLenum swizzleRed;
|
||||||
|
EGLenum swizzleGreen;
|
||||||
|
EGLenum swizzleBlue;
|
||||||
|
EGLenum swizzleAlpha;
|
||||||
|
float maxAnisotropy;
|
||||||
|
XrColor4f borderColor;
|
||||||
|
} XrSwapchainStateSamplerOpenGLESFB;
|
||||||
|
#endif // XR_USE_GRAPHICS_API_OPENGL_ES
|
||||||
|
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
|
||||||
|
// XR_FB_swapchain_update_state_vulkan is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_FB_swapchain_update_state_vulkan 1
|
||||||
|
#define XR_FB_swapchain_update_state_vulkan_SPEC_VERSION 1
|
||||||
|
#define XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME "XR_FB_swapchain_update_state_vulkan"
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
typedef struct XrSwapchainStateSamplerVulkanFB {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
VkFilter minFilter;
|
||||||
|
VkFilter magFilter;
|
||||||
|
VkSamplerMipmapMode mipmapMode;
|
||||||
|
VkSamplerAddressMode wrapModeS;
|
||||||
|
VkSamplerAddressMode wrapModeT;
|
||||||
|
VkComponentSwizzle swizzleRed;
|
||||||
|
VkComponentSwizzle swizzleGreen;
|
||||||
|
VkComponentSwizzle swizzleBlue;
|
||||||
|
VkComponentSwizzle swizzleAlpha;
|
||||||
|
float maxAnisotropy;
|
||||||
|
XrColor4f borderColor;
|
||||||
|
} XrSwapchainStateSamplerVulkanFB;
|
||||||
|
#endif // XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||||
|
|
||||||
|
// XR_META_vulkan_swapchain_create_info is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_META_vulkan_swapchain_create_info 1
|
||||||
|
#define XR_META_vulkan_swapchain_create_info_SPEC_VERSION 1
|
||||||
|
#define XR_META_VULKAN_SWAPCHAIN_CREATE_INFO_EXTENSION_NAME "XR_META_vulkan_swapchain_create_info"
|
||||||
|
// XrVulkanSwapchainCreateInfoMETA extends XrSwapchainCreateInfo
|
||||||
|
typedef struct XrVulkanSwapchainCreateInfoMETA {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
VkImageCreateFlags additionalCreateFlags;
|
||||||
|
VkImageUsageFlags additionalUsageFlags;
|
||||||
|
} XrVulkanSwapchainCreateInfoMETA;
|
||||||
|
|
||||||
|
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||||
|
|
||||||
|
#ifdef XR_USE_PLATFORM_ANDROID
|
||||||
|
|
||||||
|
// XR_ANDROID_anchor_sharing_export is a preprocessor guard. Do not pass it to API calls.
|
||||||
|
#define XR_ANDROID_anchor_sharing_export 1
|
||||||
|
#define XR_ANDROID_anchor_sharing_export_SPEC_VERSION 1
|
||||||
|
#define XR_ANDROID_ANCHOR_SHARING_EXPORT_EXTENSION_NAME "XR_ANDROID_anchor_sharing_export"
|
||||||
|
typedef struct XrAnchorSharingInfoANDROID {
|
||||||
|
XrStructureType type;
|
||||||
|
const void* XR_MAY_ALIAS next;
|
||||||
|
XrSpace anchor;
|
||||||
|
} XrAnchorSharingInfoANDROID;
|
||||||
|
|
||||||
|
typedef struct XrAnchorSharingTokenANDROID {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
struct AIBinder* token;
|
||||||
|
} XrAnchorSharingTokenANDROID;
|
||||||
|
|
||||||
|
// XrSystemAnchorSharingExportPropertiesANDROID extends XrSystemProperties
|
||||||
|
typedef struct XrSystemAnchorSharingExportPropertiesANDROID {
|
||||||
|
XrStructureType type;
|
||||||
|
void* XR_MAY_ALIAS next;
|
||||||
|
XrBool32 supportsAnchorSharingExport;
|
||||||
|
} XrSystemAnchorSharingExportPropertiesANDROID;
|
||||||
|
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrShareAnchorANDROID)(XrSession session, const XrAnchorSharingInfoANDROID* sharingInfo, XrAnchorSharingTokenANDROID* anchorToken);
|
||||||
|
typedef XrResult (XRAPI_PTR *PFN_xrUnshareAnchorANDROID)(XrSession session, XrSpace anchor);
|
||||||
|
|
||||||
|
#ifndef XR_NO_PROTOTYPES
|
||||||
|
#ifdef XR_EXTENSION_PROTOTYPES
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrShareAnchorANDROID(
|
||||||
|
XrSession session,
|
||||||
|
const XrAnchorSharingInfoANDROID* sharingInfo,
|
||||||
|
XrAnchorSharingTokenANDROID* anchorToken);
|
||||||
|
|
||||||
|
XRAPI_ATTR XrResult XRAPI_CALL xrUnshareAnchorANDROID(
|
||||||
|
XrSession session,
|
||||||
|
XrSpace anchor);
|
||||||
|
#endif /* XR_EXTENSION_PROTOTYPES */
|
||||||
|
#endif /* !XR_NO_PROTOTYPES */
|
||||||
|
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
** Copyright (c) 2017-2026 The Khronos Group Inc.
|
||||||
|
**
|
||||||
|
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPENXR_PLATFORM_DEFINES_H_
|
||||||
|
#define OPENXR_PLATFORM_DEFINES_H_ 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Platform-specific calling convention macros.
|
||||||
|
*
|
||||||
|
* Platforms should define these so that OpenXR clients call OpenXR functions
|
||||||
|
* with the same calling conventions that the OpenXR implementation expects.
|
||||||
|
*
|
||||||
|
* XRAPI_ATTR - Placed before the return type in function declarations.
|
||||||
|
* Useful for C++11 and GCC/Clang-style function attribute syntax.
|
||||||
|
* XRAPI_CALL - Placed after the return type in function declarations.
|
||||||
|
* Useful for MSVC-style calling convention syntax.
|
||||||
|
* XRAPI_PTR - Placed between the '(' and '*' in function pointer types.
|
||||||
|
*
|
||||||
|
* Function declaration: XRAPI_ATTR void XRAPI_CALL xrFunction(void);
|
||||||
|
* Function pointer type: typedef void (XRAPI_PTR *PFN_xrFunction)(void);
|
||||||
|
*/
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define XRAPI_ATTR
|
||||||
|
// On Windows, functions use the stdcall convention
|
||||||
|
#define XRAPI_CALL __stdcall
|
||||||
|
#define XRAPI_PTR XRAPI_CALL
|
||||||
|
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
|
||||||
|
#error "API not supported for the 'armeabi' NDK ABI"
|
||||||
|
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
|
||||||
|
// On Android 32-bit ARM targets, functions use the "hardfloat"
|
||||||
|
// calling convention, i.e. float parameters are passed in registers. This
|
||||||
|
// is true even if the rest of the application passes floats on the stack,
|
||||||
|
// as it does by default when compiling for the armeabi-v7a NDK ABI.
|
||||||
|
#define XRAPI_ATTR __attribute__((pcs("aapcs-vfp")))
|
||||||
|
#define XRAPI_CALL
|
||||||
|
#define XRAPI_PTR XRAPI_ATTR
|
||||||
|
#else
|
||||||
|
// On other platforms, use the default calling convention
|
||||||
|
#define XRAPI_ATTR
|
||||||
|
#define XRAPI_CALL
|
||||||
|
#define XRAPI_PTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#if !defined(XR_NO_STDINT_H)
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||||
|
typedef signed __int8 int8_t;
|
||||||
|
typedef unsigned __int8 uint8_t;
|
||||||
|
typedef signed __int16 int16_t;
|
||||||
|
typedef unsigned __int16 uint16_t;
|
||||||
|
typedef signed __int32 int32_t;
|
||||||
|
typedef unsigned __int32 uint32_t;
|
||||||
|
typedef signed __int64 int64_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
#endif // !defined( XR_NO_STDINT_H )
|
||||||
|
|
||||||
|
// XR_PTR_SIZE (in bytes)
|
||||||
|
#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
|
||||||
|
#define XR_PTR_SIZE 8
|
||||||
|
#else
|
||||||
|
#define XR_PTR_SIZE 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Needed so we can use clang __has_feature portably.
|
||||||
|
#if !defined(XR_COMPILER_HAS_FEATURE)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define XR_COMPILER_HAS_FEATURE(x) __has_feature(x)
|
||||||
|
#else
|
||||||
|
#define XR_COMPILER_HAS_FEATURE(x) 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Identifies if the current compiler has C++11 support enabled.
|
||||||
|
// Does not by itself identify if any given C++11 feature is present.
|
||||||
|
#if !defined(XR_CPP11_ENABLED) && defined(__cplusplus)
|
||||||
|
#if defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
#define XR_CPP11_ENABLED 1
|
||||||
|
#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
|
||||||
|
#define XR_CPP11_ENABLED 1
|
||||||
|
#elif (__cplusplus >= 201103L) // 201103 is the first C++11 version.
|
||||||
|
#define XR_CPP11_ENABLED 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Identifies if the current compiler supports C++11 nullptr.
|
||||||
|
#if !defined(XR_CPP_NULLPTR_SUPPORTED)
|
||||||
|
#if defined(XR_CPP11_ENABLED) && \
|
||||||
|
((defined(__clang__) && XR_COMPILER_HAS_FEATURE(cxx_nullptr)) || \
|
||||||
|
(defined(__GNUC__) && (((__GNUC__ * 1000) + __GNUC_MINOR__) >= 4006)) || \
|
||||||
|
(defined(_MSC_VER) && (_MSC_VER >= 1600)) || \
|
||||||
|
(defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403)))
|
||||||
|
#define XR_CPP_NULLPTR_SUPPORTED 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(XR_CPP_NULLPTR_SUPPORTED)
|
||||||
|
#define XR_CPP_NULLPTR_SUPPORTED 0
|
||||||
|
#endif // !defined(XR_CPP_NULLPTR_SUPPORTED)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "mkdir -p \"${DERIVED_FILE_DIR}\"\npython3 \"${SRCROOT}/scripts/make_frame_bundle.py\" \"${DERIVED_FILE_DIR}/frame-bundle.tar.gz\" > \"${DERIVED_FILE_DIR}/frame-bundle.version\"\nmkdir -p \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\ncp \"${DERIVED_FILE_DIR}/frame-bundle.tar.gz\" \"${DERIVED_FILE_DIR}/frame-bundle.version\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\"\n";
|
shellScript = "set -e\nmkdir -p \"${DERIVED_FILE_DIR}\"\n# Written aside and moved into place together, so a failed download can't leave a stale pair.\npython3 \"${SRCROOT}/scripts/make_frame_bundle.py\" \"${DERIVED_FILE_DIR}/frame-bundle.new.tar.gz\" > \"${DERIVED_FILE_DIR}/frame-bundle.new.version\"\nmv \"${DERIVED_FILE_DIR}/frame-bundle.new.tar.gz\" \"${DERIVED_FILE_DIR}/frame-bundle.tar.gz\"\nmv \"${DERIVED_FILE_DIR}/frame-bundle.new.version\" \"${DERIVED_FILE_DIR}/frame-bundle.version\"\nmkdir -p \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\ncp \"${DERIVED_FILE_DIR}/frame-bundle.tar.gz\" \"${DERIVED_FILE_DIR}/frame-bundle.version\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\"\n";
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Citadel
|
import Citadel
|
||||||
|
import CryptoKit
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
import UIKit
|
||||||
@@ -33,6 +34,11 @@ final class AppModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var deviceName: String { UIDevice.current.userInterfaceIdiom == .pad ? "iPad" : "iPhone" }
|
var deviceName: String { UIDevice.current.userInterfaceIdiom == .pad ? "iPad" : "iPhone" }
|
||||||
|
/// Stable per install: a hash of this device's SSH key, which is made once and kept in the Keychain.
|
||||||
|
var clientID: String {
|
||||||
|
let digest = SHA256.hash(data: Data(authorizedKeysLine.utf8))
|
||||||
|
return deviceName.lowercased() + "-" + digest.prefix(6).map { String(format: "%02x", $0) }.joined()
|
||||||
|
}
|
||||||
private var hostKey: String? { UserDefaults.standard.string(forKey: Self.hostKeyKey) }
|
private var hostKey: String? { UserDefaults.standard.string(forKey: Self.hostKeyKey) }
|
||||||
|
|
||||||
// MARK: pairing
|
// MARK: pairing
|
||||||
@@ -167,7 +173,7 @@ final class AppModel: ObservableObject {
|
|||||||
guard current() else { throw CancellationError() }
|
guard current() else { throw CancellationError() }
|
||||||
step("Starting Frame Control on the headset")
|
step("Starting Frame Control on the headset")
|
||||||
let key = Self.randomKey()
|
let key = Self.randomKey()
|
||||||
let server = try await HeadsetServer.start(in: dir, over: l, key: key, device: deviceName)
|
let server = try await HeadsetServer.start(in: dir, over: l, key: key, device: deviceName, client: clientID)
|
||||||
guard current() else { throw CancellationError() }
|
guard current() else { throw CancellationError() }
|
||||||
let f = try await PortForwarder.start(over: l, to: server.port)
|
let f = try await PortForwarder.start(over: l, to: server.port)
|
||||||
forwarder = f
|
forwarder = f
|
||||||
|
|||||||
@@ -80,8 +80,10 @@ final class HeadsetServer: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the server in dir and waits for it to say which port it took.
|
/// Starts the server in dir and waits for it to say which port it took.
|
||||||
static func start(in dir: String, over link: FrameLink, key: String, device: String) async throws -> HeadsetServer {
|
/// client is a stable id for this install (keyboard-and-trackpad pairing is kept per client).
|
||||||
|
static func start(in dir: String, over link: FrameLink, key: String, device: String, client: String) async throws -> HeadsetServer {
|
||||||
let command = "cd \(dir) && FRAME_LOCAL=1 FRAME_UI_KEY=\(key) FRAME_DEVICE=\(shellQuote(device)) "
|
let command = "cd \(dir) && FRAME_LOCAL=1 FRAME_UI_KEY=\(key) FRAME_DEVICE=\(shellQuote(device)) "
|
||||||
|
+ "FRAME_CLIENT=\(shellQuote(client)) "
|
||||||
+ "exec python3 -I -u -B \"$PWD/ui/server.py\" --port 0 --exit-on-eof 2>&1"
|
+ "exec python3 -I -u -B \"$PWD/ui/server.py\" --port 0 --exit-on-eof 2>&1"
|
||||||
let stream = try await link.client.executeCommandStream(command)
|
let stream = try await link.client.executeCommandStream(command)
|
||||||
let box = PortWaiter()
|
let box = PortWaiter()
|
||||||
|
|||||||
+5
-1
@@ -52,8 +52,12 @@ targets:
|
|||||||
- name: Pack the Frame bundle
|
- name: Pack the Frame bundle
|
||||||
# The server, headset helpers and catalogue, as the app copies them to the Frame.
|
# The server, headset helpers and catalogue, as the app copies them to the Frame.
|
||||||
script: |
|
script: |
|
||||||
|
set -e
|
||||||
mkdir -p "${DERIVED_FILE_DIR}"
|
mkdir -p "${DERIVED_FILE_DIR}"
|
||||||
python3 "${SRCROOT}/scripts/make_frame_bundle.py" "${DERIVED_FILE_DIR}/frame-bundle.tar.gz" > "${DERIVED_FILE_DIR}/frame-bundle.version"
|
# Written aside and moved into place together, so a failed download can't leave a stale pair.
|
||||||
|
python3 "${SRCROOT}/scripts/make_frame_bundle.py" "${DERIVED_FILE_DIR}/frame-bundle.new.tar.gz" > "${DERIVED_FILE_DIR}/frame-bundle.new.version"
|
||||||
|
mv "${DERIVED_FILE_DIR}/frame-bundle.new.tar.gz" "${DERIVED_FILE_DIR}/frame-bundle.tar.gz"
|
||||||
|
mv "${DERIVED_FILE_DIR}/frame-bundle.new.version" "${DERIVED_FILE_DIR}/frame-bundle.version"
|
||||||
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||||
cp "${DERIVED_FILE_DIR}/frame-bundle.tar.gz" "${DERIVED_FILE_DIR}/frame-bundle.version" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/"
|
cp "${DERIVED_FILE_DIR}/frame-bundle.tar.gz" "${DERIVED_FILE_DIR}/frame-bundle.version" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/"
|
||||||
basedOnDependencyAnalysis: false
|
basedOnDependencyAnalysis: false
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Pack what Frame Control's server needs to run on the Frame itself (the files the
|
"""Pack what Frame Control's server needs to run on the Frame itself (the files the
|
||||||
desktop app ships, plus ui/local-bin) into one reproducible .tar.gz.
|
desktop app ships, plus ui/local-bin) into one reproducible .tar.gz. That includes
|
||||||
|
KDE Connect for the keyboard and trackpad (frame/kdeconnect), downloaded first and
|
||||||
|
checked against its pinned SHA-256s.
|
||||||
|
|
||||||
The iPhone app copies it to ~/.cache/frame-control/<version> on the Frame and
|
The iPhone app copies it to ~/.cache/frame-control/<version> on the Frame and
|
||||||
starts ui/server.py there. <version> is the SHA-256 of the archive, so a new
|
starts ui/server.py there. <version> is the SHA-256 of the archive, so a new
|
||||||
@@ -17,7 +19,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
PATTERNS = ["ui/*.py", "ui/*.html", "ui/local-bin/*", "scripts/*.sh", "frame/android/*.sh", "frame/android/*.py",
|
PATTERNS = ["ui/*.py", "ui/*.html", "ui/local-bin/*", "scripts/*.sh", "frame/android/*.sh", "frame/android/*.py",
|
||||||
"frame/devkit-utils/**/*", "apk-catalog/*.py", "apk-catalog/pins.json", "apk-catalog/site/apps.js"]
|
"frame/devkit-utils/**/*", "apk-catalog/*.py", "apk-catalog/pins.json", "apk-catalog/site/apps.js",
|
||||||
|
"frame/kdeconnect/packages.json", "frame/kdeconnect/NOTICE.md", "frame/kdeconnect/LICENSES/*/*",
|
||||||
|
"frame/kdeconnect/packages/*.pkg.tar.zst", "LICENSE", "THIRD_PARTY_NOTICES.md"]
|
||||||
|
|
||||||
|
|
||||||
def files():
|
def files():
|
||||||
@@ -47,6 +51,9 @@ def build():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) != 2:
|
if len(sys.argv) != 2:
|
||||||
sys.exit(__doc__)
|
sys.exit(__doc__)
|
||||||
|
sys.path.insert(0, str(ROOT / "frame" / "kdeconnect"))
|
||||||
|
import fetch
|
||||||
|
fetch.fetch()
|
||||||
data = build()
|
data = build()
|
||||||
Path(sys.argv[1]).write_bytes(data)
|
Path(sys.argv[1]).write_bytes(data)
|
||||||
print(hashlib.sha256(data).hexdigest()[:16])
|
print(hashlib.sha256(data).hexdigest()[:16])
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// The page's input queue, run in node against the real functions from ui/index.html.
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
const src = readFileSync(new URL("../../ui/index.html", import.meta.url), "utf8");
|
||||||
|
const grab = name => src.match(new RegExp(`(?:const ${name} = [^\\n]*\\n)|((?:async )?function ${name}\\([\\s\\S]*?\\n}\\n)`))[0];
|
||||||
|
const code = ["isMove", "padSend", "padKeep", "padFlush"].map(grab).join("");
|
||||||
|
const fail = msg => { console.log("FAIL " + msg); process.exit(1); };
|
||||||
|
|
||||||
|
function page(api) {
|
||||||
|
const pad = { state: "ready", queue: [], sending: false };
|
||||||
|
const shown = [];
|
||||||
|
const fns = new Function("pad", "api", "padShow", code + "; return { padSend, padFlush };")(pad, api, s => { shown.push(s); pad.state = s.state; });
|
||||||
|
return { pad, shown, ...fns };
|
||||||
|
}
|
||||||
|
|
||||||
|
// While a request is in flight, events queue by these rules.
|
||||||
|
{
|
||||||
|
const { pad, padSend } = page(() => new Promise(() => {})); // a request that never returns
|
||||||
|
const send = (state, e) => { pad.state = state; padSend(e); };
|
||||||
|
send("ready", { singlehold: true }); // goes out, and stays in flight
|
||||||
|
send("error", { dx: 3 }); // dropped: a stale move while broken
|
||||||
|
send("error", { singlerelease: true }); // kept
|
||||||
|
send("off", { singlerelease: true }); // off: nothing was held
|
||||||
|
send("pairing", { key: "a" }); // kept while reconnecting
|
||||||
|
send("pairing", { dx: 1 }); // dropped
|
||||||
|
for (let i = 0; i < 250; i++) send("pairing", { key: "x" });
|
||||||
|
send("pairing", { singlerelease: true }); // kept past the 200 cap
|
||||||
|
const q = pad.queue;
|
||||||
|
if (!(q[0].singlerelease && q[1].key === "a" && q.at(-1).singlerelease && !q.some(e => "dx" in e)
|
||||||
|
&& q.filter(e => e.singlerelease).length === 2)) fail("queueing " + JSON.stringify(q.slice(0, 3)) + " len " + q.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A request that fails, or reaches a link that had dropped, keeps its keys and releases.
|
||||||
|
for (const [label, api] of [["request failed", async () => { throw new Error("offline"); }],
|
||||||
|
["not sent", async () => ({ state: "starting", sent: false })]]) {
|
||||||
|
const { pad, padSend, shown } = page(api);
|
||||||
|
padSend({ dx: 5 }); // sent and lost: a move, fine to drop
|
||||||
|
await new Promise(r => setTimeout(r, 0));
|
||||||
|
pad.state = "ready";
|
||||||
|
padSend({ singlerelease: true });
|
||||||
|
padSend({ dx: 7 }); // queued while that request is in flight: stale once it fails
|
||||||
|
padSend({ key: "b" });
|
||||||
|
await new Promise(r => setTimeout(r, 0));
|
||||||
|
const q = pad.queue;
|
||||||
|
if (!(q.some(e => e.singlerelease) && q.some(e => e.key === "b") && !q.some(e => "dx" in e)))
|
||||||
|
fail(label + ": " + JSON.stringify(q));
|
||||||
|
if (!shown.length || shown.at(-1).state === "ready") fail(label + ": state not shown");
|
||||||
|
}
|
||||||
|
console.log("queue rules ok");
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
"""Local-only VR manifest, raw ZIP and independent signature checks."""
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'ui'))
|
||||||
|
import frame_apk
|
||||||
|
import frame_apk_vr as vr
|
||||||
|
import frame_apk_sign as signing
|
||||||
|
import frame_android
|
||||||
|
from test_frame_apk import pool
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT = 'android.intent.category.DEFAULT'
|
||||||
|
|
||||||
|
|
||||||
|
def manifest(utf8=False, launcher=False, category=None, samsung=False, split=False, alias=False, splash=False):
|
||||||
|
strings = ['manifest', 'package', 'org.test.vr', 'application', 'activity', 'intent-filter',
|
||||||
|
'action', 'category', 'name', vr.MAIN, category or next(iter(sorted(vr.VR))),
|
||||||
|
vr.LAUNCHER, 'http://schemas.android.com/apk/res/android', 'meta-data', 'value',
|
||||||
|
'com.samsung.android.vr.application.mode', 'vr_only', 'activity-alias', DEFAULT, next(iter(sorted(vr.VR))), 'targetActivity', '.Splash', '.Game']
|
||||||
|
def start(tag, attrs=()):
|
||||||
|
body = struct.pack('<IIHHHHHH', 0xffffffff, strings.index(tag), 20, 20, len(attrs), 0, 0, 0)
|
||||||
|
for name, value in attrs:
|
||||||
|
ns = 0xffffffff if name == 'package' else 12
|
||||||
|
body += struct.pack('<IIIHBBI', ns, strings.index(name), strings.index(value), 8, 0, 3, strings.index(value))
|
||||||
|
return struct.pack('<HHIII', 0x102, 16, 16 + len(body), 1, 0xffffffff) + body
|
||||||
|
def end(tag):
|
||||||
|
return struct.pack('<HHIIIII', 0x103, 16, 24, 1, 0xffffffff, 0xffffffff, strings.index(tag))
|
||||||
|
def leaf(tag, attrs):
|
||||||
|
return start(tag, attrs) + end(tag)
|
||||||
|
b = pool(strings, utf8) + start('manifest', [('package', 'org.test.vr')]) + start('application')
|
||||||
|
if samsung:
|
||||||
|
b += leaf('meta-data', [('name', strings[15]), ('value', 'vr_only')])
|
||||||
|
if splash: # a helper activity with its own MAIN filter, ahead of the game's
|
||||||
|
b += start('activity', [('name', '.Splash')]) + start('intent-filter') + leaf('action', [('name', vr.MAIN)])
|
||||||
|
b += leaf('category', [('name', DEFAULT)]) + end('intent-filter') + end('activity')
|
||||||
|
b += start('activity', [('name', '.Game')] if splash else []) + start('intent-filter') + leaf('action', [('name', vr.MAIN)])
|
||||||
|
b += leaf('category', [('name', strings[10])])
|
||||||
|
if split:
|
||||||
|
b += end('intent-filter') + start('intent-filter')
|
||||||
|
if launcher:
|
||||||
|
b += leaf('category', [('name', vr.LAUNCHER)])
|
||||||
|
b += end('intent-filter') + end('activity')
|
||||||
|
if alias: # Godot 4: LAUNCHER only on an alias of the activity ('vr' or 'flat')
|
||||||
|
b += start('activity-alias', [('targetActivity', '.Game')] if splash else []) + start('intent-filter') + leaf('action', [('name', vr.MAIN)])
|
||||||
|
if alias == 'vr':
|
||||||
|
b += leaf('category', [('name', next(iter(sorted(vr.VR))))])
|
||||||
|
b += leaf('category', [('name', vr.LAUNCHER)])
|
||||||
|
b += end('intent-filter') + end('activity-alias')
|
||||||
|
b += end('application') + end('manifest')
|
||||||
|
return struct.pack('<HHI', 3, 8, len(b) + 8) + b
|
||||||
|
|
||||||
|
|
||||||
|
class VRTests(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.tmp = tempfile.TemporaryDirectory()
|
||||||
|
cls.keypath = Path(cls.tmp.name) / 'key.json'
|
||||||
|
cls.key = signing.signing_key(cls.keypath)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.tmp.cleanup()
|
||||||
|
|
||||||
|
def test_manifest_patch(self):
|
||||||
|
for utf8 in (False, True):
|
||||||
|
for category in vr.VR:
|
||||||
|
original = manifest(utf8, category=category)
|
||||||
|
result = vr.add_launcher_category(original)
|
||||||
|
info, filters = vr.inspect(result)
|
||||||
|
self.assertTrue(info['launchable'])
|
||||||
|
self.assertTrue(info['vr'])
|
||||||
|
self.assertIn(vr.LAUNCHER, filters[0]['categories'])
|
||||||
|
self.assertEqual(struct.unpack_from('<I', result, 4)[0], len(result))
|
||||||
|
self.assertEqual(vr.add_launcher_category(result), result)
|
||||||
|
self.assertEqual(vr.add_launcher_category(manifest(utf8, True)), manifest(utf8, True))
|
||||||
|
|
||||||
|
def test_filter_boundaries(self):
|
||||||
|
self.assertFalse(vr.inspect(manifest(launcher=True, split=True))[0]['launchable'])
|
||||||
|
self.assertTrue(vr.inspect(vr.add_launcher_category(manifest(launcher=True, split=True)))[0]['launchable'])
|
||||||
|
|
||||||
|
def test_alias_launcher_is_not_enough(self):
|
||||||
|
# (manifest, still VR after patching)
|
||||||
|
cases = [(manifest(alias='vr'), True), # Godot 4 VR export
|
||||||
|
(manifest(alias='vr', category=DEFAULT), True), # VR category only on the alias
|
||||||
|
(manifest(alias='flat', category=DEFAULT), False)] # Godot 4 flat export
|
||||||
|
for original, is_vr in cases:
|
||||||
|
info, filters = vr.inspect(original)
|
||||||
|
self.assertFalse(info['launchable'])
|
||||||
|
self.assertTrue(info['repairable'])
|
||||||
|
self.assertEqual(len(filters), 1)
|
||||||
|
self.assertFalse(filters[0]['alias'])
|
||||||
|
result = vr.add_launcher_category(original)
|
||||||
|
info, _ = vr.inspect(result)
|
||||||
|
self.assertTrue(info['launchable'])
|
||||||
|
self.assertFalse(info['repairable'])
|
||||||
|
self.assertEqual(info['vr'], is_vr)
|
||||||
|
|
||||||
|
def test_alias_target_activity_is_patched(self):
|
||||||
|
original = manifest(alias='flat', category=DEFAULT, splash=True)
|
||||||
|
info, filters = vr.inspect(original)
|
||||||
|
self.assertTrue(info['repairable'])
|
||||||
|
self.assertEqual(filters[0]['activity'], 'org.test.vr.Game')
|
||||||
|
owner, launchers = None, []
|
||||||
|
for tag, attrs in frame_apk.manifest_elements(vr.add_launcher_category(original)):
|
||||||
|
if tag in ('activity', 'activity-alias'):
|
||||||
|
owner = (tag, attrs.get('name', (0, 0, None))[2])
|
||||||
|
if tag == 'category' and attrs['name'][2] == vr.LAUNCHER:
|
||||||
|
launchers.append(owner)
|
||||||
|
self.assertEqual(launchers, [('activity', '.Game'), ('activity-alias', None)])
|
||||||
|
|
||||||
|
def test_alias_with_launchable_activity_is_left_alone(self):
|
||||||
|
original = manifest(launcher=True, alias='vr')
|
||||||
|
info, _ = vr.inspect(original)
|
||||||
|
self.assertTrue(info['launchable'])
|
||||||
|
self.assertFalse(info['repairable'])
|
||||||
|
self.assertEqual(vr.add_launcher_category(original), original)
|
||||||
|
|
||||||
|
def test_patch_alias_apk(self):
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
src, dst = Path(d) / 'in.apk', Path(d) / 'out.apk'
|
||||||
|
with zipfile.ZipFile(src, 'w') as z:
|
||||||
|
z.writestr('AndroidManifest.xml', manifest(alias='flat', category=DEFAULT))
|
||||||
|
with patch.object(signing, 'signing_key', return_value=self.key):
|
||||||
|
result = frame_android.patch(src, dst)
|
||||||
|
self.assertEqual(result['patched'], ['launcher'])
|
||||||
|
self.assertTrue(frame_apk.apk_info(dst)['launchable'])
|
||||||
|
self.assertTrue(signing.verify(dst))
|
||||||
|
|
||||||
|
def test_styled_pool(self):
|
||||||
|
for utf8 in (False, True):
|
||||||
|
p = bytearray(pool(['styled'], utf8))
|
||||||
|
old_start = struct.unpack_from('<I', p, 20)[0]
|
||||||
|
p[old_start:old_start] = struct.pack('<I', 0)
|
||||||
|
style_start = len(p)
|
||||||
|
p += struct.pack('<III', 0, 0, 2) + b'\xff' * 12
|
||||||
|
struct.pack_into('<I', p, 4, len(p))
|
||||||
|
struct.pack_into('<I', p, 16, (0x100 if utf8 else 0) | 1)
|
||||||
|
struct.pack_into('<I', p, 12, 1)
|
||||||
|
struct.pack_into('<II', p, 20, old_start + 4, style_start)
|
||||||
|
result, idx = vr._append_string(bytes(p), vr.LAUNCHER)
|
||||||
|
self.assertEqual(frame_apk._string_pool(result, 0), ['styled', vr.LAUNCHER])
|
||||||
|
new_style = struct.unpack_from('<I', result, 24)[0]
|
||||||
|
self.assertEqual(result[new_style:], p[style_start:])
|
||||||
|
self.assertEqual(len(result) % 4, 0)
|
||||||
|
|
||||||
|
def test_detection(self):
|
||||||
|
names = {'lib/arm64-v8a/' + n for n in ('libvrapi.so', 'libopenxr_loader.so', 'libovrplatformloader.so')}
|
||||||
|
info = vr.detection(manifest(category='android.intent.category.LAUNCHER'), names)
|
||||||
|
self.assertTrue(info['vr'])
|
||||||
|
self.assertTrue(info['launchable'])
|
||||||
|
self.assertEqual(len(info['vr_issues']), 3)
|
||||||
|
self.assertFalse(vr.detection(manifest(category=vr.LAUNCHER), set())['vr'])
|
||||||
|
self.assertTrue(vr.detection(manifest(category=vr.LAUNCHER, samsung=True), set())['vr'])
|
||||||
|
|
||||||
|
def test_key_cache(self):
|
||||||
|
with patch.object(signing, '_prime', side_effect=AssertionError('regenerated')):
|
||||||
|
self.assertEqual(signing.signing_key(self.keypath), self.key)
|
||||||
|
if os.name != 'nt':
|
||||||
|
self.assertEqual(self.keypath.stat().st_mode & 0o777, 0o600)
|
||||||
|
|
||||||
|
def test_repack_sign_tamper(self):
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
src, dst = Path(d) / 'in.apk', Path(d) / 'out.apk'
|
||||||
|
with zipfile.ZipFile(src, 'w') as z:
|
||||||
|
z.writestr('AndroidManifest.xml', manifest(), compress_type=8)
|
||||||
|
z.writestr('classes.dex', b'compress me' * 10000, compress_type=8)
|
||||||
|
z.writestr('resources.arsc', b'1234')
|
||||||
|
z.writestr('lib/arm64-v8a/libx.so', b'ELF' * 500000)
|
||||||
|
z.writestr('META-INF/OLD.RSA', b'old')
|
||||||
|
z.writestr('META-INF/MANIFEST.MF', b'old')
|
||||||
|
with patch.object(signing, 'signing_key', return_value=self.key):
|
||||||
|
result = frame_android.patch(src, dst, {'assets/layer.json': b'{}', 'lib/arm64-v8a/liblayer.so': b'layer'})
|
||||||
|
self.assertEqual(result['patched'], ['launcher'])
|
||||||
|
self.assertTrue(frame_apk.apk_info(dst)['launchable'])
|
||||||
|
self.assertTrue(signing.verify(dst))
|
||||||
|
def compressed(path, info):
|
||||||
|
data = path.read_bytes()
|
||||||
|
nl, el = struct.unpack_from('<HH', data, info.header_offset + 26)
|
||||||
|
start = info.header_offset + 30 + nl + el
|
||||||
|
return data[start:start + info.compress_size], start
|
||||||
|
with zipfile.ZipFile(src) as a, zipfile.ZipFile(dst) as b:
|
||||||
|
self.assertNotIn('META-INF/OLD.RSA', b.namelist())
|
||||||
|
self.assertNotIn('META-INF/MANIFEST.MF', b.namelist())
|
||||||
|
for i in b.infolist():
|
||||||
|
raw, start = compressed(dst, i)
|
||||||
|
if i.compress_type == 0:
|
||||||
|
self.assertEqual(start % (16384 if i.filename.endswith('.so') else 4), 0)
|
||||||
|
if i.filename in ('classes.dex', 'resources.arsc', 'lib/arm64-v8a/libx.so'):
|
||||||
|
self.assertEqual(raw, compressed(src, a.getinfo(i.filename))[0])
|
||||||
|
_, off = compressed(dst, b.getinfo('resources.arsc'))
|
||||||
|
original = dst.read_bytes()
|
||||||
|
data = bytearray(original)
|
||||||
|
eo, cd = signing._eocd(data)
|
||||||
|
size = struct.unpack_from('<Q', data, cd - 24)[0]
|
||||||
|
block_start = cd - size - 8
|
||||||
|
value = data[block_start + 20:cd - 24]
|
||||||
|
signer = signing._parts(signing._parts(value)[0])[0]
|
||||||
|
signed, signatures, pub = signing._parts(signer)
|
||||||
|
signature = signing._parts(signing._parts(signatures)[0][4:])[0]
|
||||||
|
sig_offset = data.index(signature, block_start)
|
||||||
|
data[sig_offset] ^= 1
|
||||||
|
dst.write_bytes(data)
|
||||||
|
with self.assertRaisesRegex(ValueError, 'RSA signature'):
|
||||||
|
signing.verify(dst)
|
||||||
|
data = bytearray(original)
|
||||||
|
data[off] ^= 1
|
||||||
|
dst.write_bytes(data)
|
||||||
|
with self.assertRaisesRegex(ValueError, 'digest'):
|
||||||
|
signing.verify(dst)
|
||||||
|
|
||||||
|
@unittest.skipUnless(Path('/usr/bin/openssl').exists(), 'openssl absent')
|
||||||
|
def test_openssl(self):
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
d = Path(d)
|
||||||
|
(d / 'cert.der').write_bytes(signing.certificate(self.key))
|
||||||
|
(d / 'message').write_bytes(b'independent signature check')
|
||||||
|
(d / 'signature').write_bytes(signing.rsa_sign(b'independent signature check', self.key))
|
||||||
|
def run(*args):
|
||||||
|
return subprocess.run(['/usr/bin/openssl', *args], check=True, capture_output=True).stdout
|
||||||
|
run('x509', '-inform', 'DER', '-in', str(d / 'cert.der'), '-out', str(d / 'cert.pem'))
|
||||||
|
run('verify', '-check_ss_sig', '-CAfile', str(d / 'cert.pem'), str(d / 'cert.pem'))
|
||||||
|
(d / 'pub.pem').write_bytes(run('x509', '-in', str(d / 'cert.pem'), '-pubkey', '-noout'))
|
||||||
|
output = run('dgst', '-sha256', '-verify', str(d / 'pub.pem'), '-signature', str(d / 'signature'), str(d / 'message'))
|
||||||
|
self.assertIn(b'Verified OK', output)
|
||||||
|
|
||||||
|
def test_install_auto_and_override(self):
|
||||||
|
base = {'package': 'org.test.vr', 'label': 'VR', 'abis': [], 'min_sdk': None,
|
||||||
|
'vr': True, 'vr_activity': True, 'launchable': False, 'repairable': True}
|
||||||
|
with patch.object(frame_android, 'apk_info', return_value=base), \
|
||||||
|
patch.object(frame_android, 'xr_compat_files', return_value={}), \
|
||||||
|
patch.object(frame_android, 'patch', return_value={'patched': ['launcher']}) as repair, \
|
||||||
|
patch.object(frame_android, '_install', return_value={}) as install:
|
||||||
|
frame_android.install('original.apk')
|
||||||
|
repair.assert_called_once()
|
||||||
|
self.assertFalse(install.call_args.args[3])
|
||||||
|
self.assertEqual(install.call_args.args[1]['patched'], ['launcher'])
|
||||||
|
frame_android.install('original.apk', flatscreen=True)
|
||||||
|
self.assertTrue(install.call_args.args[3])
|
||||||
|
|
||||||
|
def test_xr_compat_layer(self):
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
apk = Path(d) / 'a.apk'
|
||||||
|
with zipfile.ZipFile(apk, 'w') as z:
|
||||||
|
z.writestr('lib/arm64-v8a/libopenxr_loader.so', b'')
|
||||||
|
add = frame_android.xr_compat_files(str(apk))
|
||||||
|
self.assertEqual(set(add), set(frame_android.XR_COMPAT_FILES))
|
||||||
|
self.assertIn(b'XR_APILAYER_FRAME_compat', add['assets/openxr/1/api_layers/implicit.d/XrApiLayer_FRAME_compat.json'])
|
||||||
|
self.assertTrue(add['lib/arm64-v8a/libXrApiLayer_FRAME_compat.so'].startswith(b'\x7fELF'))
|
||||||
|
with zipfile.ZipFile(apk, 'a') as z: # already injected: nothing more to add
|
||||||
|
z.writestr('lib/arm64-v8a/libXrApiLayer_FRAME_compat.so', b'')
|
||||||
|
self.assertEqual(frame_android.xr_compat_files(str(apk)), {})
|
||||||
|
flat = Path(d) / 'flat.apk'
|
||||||
|
with zipfile.ZipFile(flat, 'w') as z:
|
||||||
|
z.writestr('classes.dex', b'')
|
||||||
|
self.assertEqual(frame_android.xr_compat_files(str(flat)), {})
|
||||||
|
|
||||||
|
def test_xr_compat_layer_missing(self):
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
apk = Path(d) / 'a.apk'
|
||||||
|
with zipfile.ZipFile(apk, 'w') as z:
|
||||||
|
z.writestr('lib/arm64-v8a/libopenxr_loader.so', b'')
|
||||||
|
with patch.object(frame_android, 'XR_COMPAT', d):
|
||||||
|
with self.assertRaisesRegex(frame_android.FrameError, 'build.sh'):
|
||||||
|
frame_android.xr_compat_files(str(apk))
|
||||||
|
|
||||||
|
def test_patch_rejects_corrupt_manifest_cleanly(self):
|
||||||
|
with patch.object(frame_android, 'apk_info', side_effect=struct.error('bad')):
|
||||||
|
with self.assertRaises(frame_android.FrameError):
|
||||||
|
frame_android.patch('x.apk', 'y.apk')
|
||||||
|
|
||||||
|
def test_install_adds_layer_to_vr_apps(self):
|
||||||
|
base = {'package': 'org.test.vr', 'label': 'VR', 'abis': [], 'min_sdk': None,
|
||||||
|
'vr': True, 'vr_activity': True, 'launchable': True, 'repairable': False}
|
||||||
|
layer = {'x': b''}
|
||||||
|
with patch.object(frame_android, 'apk_info', return_value=dict(base)), \
|
||||||
|
patch.object(frame_android, 'xr_compat_files', return_value=layer), \
|
||||||
|
patch.object(frame_android, 'patch', return_value={'patched': ['openxr-compat']}) as repair, \
|
||||||
|
patch.object(frame_android, '_install', return_value={}) as install:
|
||||||
|
frame_android.install('game.apk')
|
||||||
|
self.assertIs(repair.call_args.args[2], layer)
|
||||||
|
self.assertEqual(install.call_args.args[1]['patched'], ['openxr-compat'])
|
||||||
|
repair.reset_mock()
|
||||||
|
frame_android.install('game.apk', xr_compat=False) # launchable, no layer: install as is
|
||||||
|
repair.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,632 @@
|
|||||||
|
"""Keyboard and pointer: event checks, and the agent's KDE Connect protocol against a fake kdeconnectd.
|
||||||
|
|
||||||
|
The fake follows what KDE Connect 24.02 does (read from its source,
|
||||||
|
core/backends/lan/lanlinkprovider.cpp): it accepts the TCP connection, reads the
|
||||||
|
identity line, then starts TLS as the *client*.
|
||||||
|
|
||||||
|
Run: python3 -m unittest discover -s tests
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
sys.path.insert(0, str(ROOT / "ui"))
|
||||||
|
|
||||||
|
|
||||||
|
class InputEvents(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
import server
|
||||||
|
cls.server = server
|
||||||
|
|
||||||
|
def check(self, event):
|
||||||
|
return self.server.input_event(event)
|
||||||
|
|
||||||
|
def test_keeps_known_fields(self):
|
||||||
|
self.assertEqual(self.check({"dx": 3, "dy": -1.234}), {"dx": 3.0, "dy": -1.23})
|
||||||
|
self.assertEqual(self.check({"singleclick": True, "other": 1}), {"singleclick": True})
|
||||||
|
self.assertEqual(self.check({"key": "héllo", "shift": True}), {"key": "héllo", "shift": True})
|
||||||
|
self.assertEqual(self.check({"specialKey": 12, "ctrl": True}), {"specialKey": 12, "ctrl": True})
|
||||||
|
self.assertEqual(self.check({"scroll": True, "dy": 1}), {"scroll": True, "dy": 1.0})
|
||||||
|
|
||||||
|
def test_clamps_movement(self):
|
||||||
|
self.assertEqual(self.check({"dx": 1e9})["dx"], self.server.INPUT_MOVE_LIMIT)
|
||||||
|
self.assertEqual(self.check({"dy": -1e9})["dy"], -self.server.INPUT_MOVE_LIMIT)
|
||||||
|
|
||||||
|
def test_rejects_bad_events(self):
|
||||||
|
bad = [None, [], "a", {}, {"shift": True}, {"dx": "1"}, {"dx": True}, {"dx": float("nan")},
|
||||||
|
{"key": ""}, {"key": 5}, {"key": "x" * 501}, {"specialKey": 0}, {"specialKey": 33},
|
||||||
|
{"specialKey": True}, {"specialKey": 1.5}, {"singleclick": "yes"}]
|
||||||
|
for event in bad:
|
||||||
|
with self.assertRaises(self.server.Failure, msg=repr(event)):
|
||||||
|
self.check(event)
|
||||||
|
|
||||||
|
def test_send_while_link_is_down_says_not_sent(self):
|
||||||
|
# The page keeps unsent keys and clicks and sends them once the agent is ready again.
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
class Probe(self.server.InputAgent):
|
||||||
|
def start(self):
|
||||||
|
self.proc, self.status = SimpleNamespace(poll=lambda: None, stdin=None), {"state": "starting"}
|
||||||
|
|
||||||
|
agent = Probe()
|
||||||
|
agent.status, agent.proc = {"state": "ready"}, SimpleNamespace(poll=lambda: 255) # ssh has exited
|
||||||
|
self.assertEqual(agent.send([{"key": "x"}]), {"state": "starting", "sent": False})
|
||||||
|
|
||||||
|
def test_command_passes_client_quoted(self):
|
||||||
|
os.environ["FRAME_CLIENT"] = "test-client-1"
|
||||||
|
try:
|
||||||
|
cmd = self.server.InputAgent().command()
|
||||||
|
finally:
|
||||||
|
del os.environ["FRAME_CLIENT"]
|
||||||
|
self.assertTrue(cmd.startswith("python3 -u -c '"))
|
||||||
|
self.assertIn(" test-client-1 ", cmd)
|
||||||
|
cmd = self.server.InputAgent(packages=[("a.pkg.tar.zst", "ab")]).command("~/in/x")
|
||||||
|
self.assertTrue(cmd.endswith(""" '~/in/x' '[["a.pkg.tar.zst","ab"]]'"""), cmd)
|
||||||
|
|
||||||
|
def test_batch_limits(self):
|
||||||
|
with self.assertRaises(self.server.Failure):
|
||||||
|
self.server.remote_input({"events": "dx"})
|
||||||
|
with self.assertRaises(self.server.Failure):
|
||||||
|
self.server.remote_input({"events": [{"dx": 1}] * (self.server.INPUT_BATCH_LIMIT + 1)})
|
||||||
|
|
||||||
|
|
||||||
|
class Bundled(unittest.TestCase):
|
||||||
|
"""KDE Connect ships with Frame Control: the manifest, the notice, the copy to the Frame."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
import server
|
||||||
|
cls.server = server
|
||||||
|
cls.manifest = json.loads((ROOT / "frame/kdeconnect/packages.json").read_text())
|
||||||
|
|
||||||
|
def test_manifest_pins_every_package(self):
|
||||||
|
packages = self.manifest["packages"]
|
||||||
|
self.assertEqual({p["name"] for p in packages},
|
||||||
|
{"kdeconnect", "kcontacts", "kpeople", "libfakekey", "modemmanager-qt", "pulseaudio-qt"})
|
||||||
|
for p in packages:
|
||||||
|
self.assertRegex(p["sha256"], r"^[0-9a-f]{64}$")
|
||||||
|
self.assertTrue(p["file"].startswith(f"{p['name']}-{p['version']}-") and p["file"].endswith("-aarch64.pkg.tar.zst"), p)
|
||||||
|
self.assertTrue(p["source"].startswith(self.manifest["release"]), p)
|
||||||
|
self.assertRegex(p["source_sha256"], r"^[0-9a-f]{64}$")
|
||||||
|
self.assertTrue(self.manifest["release"].startswith("https://github.com/") and self.manifest["release"].endswith("/"))
|
||||||
|
self.assertNotIn("DO_NOT_SHARE", json.dumps(self.manifest))
|
||||||
|
|
||||||
|
def test_notice_names_each_version_and_its_licence_texts_ship(self):
|
||||||
|
notice = (ROOT / "frame/kdeconnect/NOTICE.md").read_text()
|
||||||
|
for p in self.manifest["packages"]:
|
||||||
|
self.assertIn(f"{p['name']} {p['version']}", notice)
|
||||||
|
self.assertIn(p["source"], notice)
|
||||||
|
self.assertIn(p["upstream"], notice)
|
||||||
|
for spdx in p["licenses"]:
|
||||||
|
self.assertTrue((ROOT / "frame/kdeconnect/LICENSES" / p["name"] / f"{spdx}.txt").is_file(), spdx)
|
||||||
|
self.assertIn("frame/kdeconnect/NOTICE.md", (ROOT / "THIRD_PARTY_NOTICES.md").read_text())
|
||||||
|
|
||||||
|
def test_about_dialog_has_the_licences(self):
|
||||||
|
titles = [n["title"] for n in self.server.licenses()]
|
||||||
|
self.assertIn("Frame Control (MIT)", titles)
|
||||||
|
self.assertIn("KDE Connect for the Frame", titles)
|
||||||
|
self.assertIn("kdeconnect: GPL-2.0-only", titles)
|
||||||
|
|
||||||
|
def test_both_apps_bundle_the_packages(self):
|
||||||
|
pkg = json.loads((ROOT / "app/package.json").read_text())["build"]["extraResources"]
|
||||||
|
kde = next(r for r in pkg if r["from"] == "../frame/kdeconnect")
|
||||||
|
self.assertIn("packages/*.pkg.tar.zst", kde["filter"])
|
||||||
|
self.assertIn("LICENSES/**/*", kde["filter"])
|
||||||
|
bundle = (ROOT / "ios/scripts/make_frame_bundle.py").read_text()
|
||||||
|
for pattern in ("frame/kdeconnect/packages/*.pkg.tar.zst", "frame/kdeconnect/LICENSES/*/*", "THIRD_PARTY_NOTICES.md"):
|
||||||
|
self.assertIn(pattern, bundle)
|
||||||
|
|
||||||
|
def fake_bundle(self, damaged=False):
|
||||||
|
folder = Path(tempfile.mkdtemp())
|
||||||
|
self.addCleanup(shutil.rmtree, folder)
|
||||||
|
(folder / "packages").mkdir()
|
||||||
|
data = {"a-1-1-aarch64.pkg.tar.zst": b"first", "b-2-1-aarch64.pkg.tar.zst": b"second"}
|
||||||
|
packages = []
|
||||||
|
for name, body in data.items():
|
||||||
|
(folder / "packages" / name).write_bytes(b"x" + body if damaged else body)
|
||||||
|
packages.append((name, __import__("hashlib").sha256(body).hexdigest()))
|
||||||
|
return folder, packages
|
||||||
|
|
||||||
|
def deliver(self, frame_has, damaged=False):
|
||||||
|
folder, packages = self.fake_bundle(damaged)
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def ssh(remote, stdin=None, timeout=30, text=True):
|
||||||
|
calls.append((remote, stdin))
|
||||||
|
answer = "yes\n" if remote.startswith("{ test -x") and frame_has else ""
|
||||||
|
return answer if text else answer.encode()
|
||||||
|
old = self.server.ssh, self.server.KDECONNECT, self.server.LOCAL
|
||||||
|
self.server.ssh, self.server.KDECONNECT, self.server.LOCAL = ssh, folder, False
|
||||||
|
try:
|
||||||
|
agent = self.server.InputAgent(packages=packages)
|
||||||
|
return agent.deliver(lambda m: calls.append(("report", m))), calls, packages
|
||||||
|
finally:
|
||||||
|
self.server.ssh, self.server.KDECONNECT, self.server.LOCAL = old
|
||||||
|
|
||||||
|
def test_copies_nothing_when_the_frame_has_them(self):
|
||||||
|
folder, calls, packages = self.deliver(frame_has=True)
|
||||||
|
self.assertEqual(folder, "")
|
||||||
|
self.assertEqual(len(calls), 1)
|
||||||
|
# Bytes: on Windows a text pipe would send CRLF and never match.
|
||||||
|
self.assertEqual(calls[0][1], "".join(f"{sha} {name}\n" for name, sha in packages).encode())
|
||||||
|
|
||||||
|
def test_copies_each_package_over_ssh(self):
|
||||||
|
folder, calls, packages = self.deliver(frame_has=False)
|
||||||
|
self.assertTrue(folder.startswith("~/.local/share/frame-control/kdeconnect/incoming/"))
|
||||||
|
copies = [c for c in calls if c[0] != "report" and "cat >" in c[0]]
|
||||||
|
self.assertEqual([c[1] for c in copies], [b"first", b"second"])
|
||||||
|
self.assertIn("a-1-1-aarch64.pkg.tar.zst.part", copies[0][0])
|
||||||
|
|
||||||
|
def test_refuses_a_damaged_bundle(self):
|
||||||
|
with self.assertRaises(self.server.Failure):
|
||||||
|
self.deliver(frame_has=False, damaged=True)
|
||||||
|
|
||||||
|
def test_a_failed_copy_is_removed(self):
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def ssh(remote, stdin=None, timeout=30, text=True):
|
||||||
|
calls.append(remote)
|
||||||
|
if "cat >" in remote and len([c for c in calls if "cat >" in c]) == 2:
|
||||||
|
raise self.server.Failure("Broken pipe")
|
||||||
|
return b"" if not text else ""
|
||||||
|
folder, packages = self.fake_bundle()
|
||||||
|
old = self.server.ssh, self.server.KDECONNECT, self.server.LOCAL
|
||||||
|
self.server.ssh, self.server.KDECONNECT, self.server.LOCAL = ssh, folder, False
|
||||||
|
try:
|
||||||
|
with self.assertRaises(self.server.Failure):
|
||||||
|
self.server.InputAgent(packages=packages).deliver(lambda m: None)
|
||||||
|
finally:
|
||||||
|
self.server.ssh, self.server.KDECONNECT, self.server.LOCAL = old
|
||||||
|
self.assertTrue(calls[-1].startswith("rm -rf .local/share/frame-control/kdeconnect/incoming/"), calls[-1])
|
||||||
|
|
||||||
|
def lifecycle(self, agent_lines, deliver=None):
|
||||||
|
"""An InputAgent whose ssh and agent are fakes. Returns it, the folders each launch
|
||||||
|
used, and the fake agents. A fake agent reports its lines, then keeps running
|
||||||
|
(unless its lines end with need-packages) until the test ends."""
|
||||||
|
launches, procs, done = [], [], threading.Event()
|
||||||
|
self.addCleanup(done.set)
|
||||||
|
|
||||||
|
class Agent(self.server.InputAgent):
|
||||||
|
def deliver(self, report, force=False):
|
||||||
|
if deliver:
|
||||||
|
deliver()
|
||||||
|
return "~/copied" if force else ""
|
||||||
|
|
||||||
|
def command(self, folder=""):
|
||||||
|
launches.append(folder)
|
||||||
|
return "agent"
|
||||||
|
|
||||||
|
class Proc:
|
||||||
|
stdin = None
|
||||||
|
|
||||||
|
def __init__(self, lines):
|
||||||
|
self.lines, self.ended = lines, threading.Event()
|
||||||
|
procs.append(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stdout(self):
|
||||||
|
yield from self.lines
|
||||||
|
if self.lines and b"need-packages" not in self.lines[-1]:
|
||||||
|
while not (done.is_set() or self.ended.is_set()):
|
||||||
|
self.ended.wait(0.02)
|
||||||
|
self.ended.set()
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
self.ended.wait(5)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def poll(self):
|
||||||
|
return 0 if self.ended.is_set() else None
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
self.ended.set()
|
||||||
|
|
||||||
|
def popen(*a, **k):
|
||||||
|
return Proc(agent_lines.pop(0) if agent_lines else [b'{"state": "ready"}\n'])
|
||||||
|
old = self.server.ensure_master, self.server.subprocess.Popen
|
||||||
|
self.server.ensure_master, self.server.subprocess.Popen = lambda: None, popen
|
||||||
|
self.addCleanup(lambda: (setattr(self.server, "ensure_master", old[0]),
|
||||||
|
setattr(self.server.subprocess, "Popen", old[1])))
|
||||||
|
agent = Agent(packages=[("a.pkg.tar.zst", "0" * 64)])
|
||||||
|
|
||||||
|
def settle(): # nothing of this test may still be launching when the next one starts
|
||||||
|
agent.stop()
|
||||||
|
for _ in range(250):
|
||||||
|
if agent.launching is None and all(p.ended.is_set() for p in procs):
|
||||||
|
return
|
||||||
|
time.sleep(0.02)
|
||||||
|
self.addCleanup(settle)
|
||||||
|
return agent, launches, procs
|
||||||
|
|
||||||
|
def wait_for(self, check):
|
||||||
|
for _ in range(250):
|
||||||
|
if check():
|
||||||
|
return
|
||||||
|
time.sleep(0.02)
|
||||||
|
self.fail("timed out")
|
||||||
|
|
||||||
|
def test_agent_asking_for_packages_gets_them_and_starts_again(self):
|
||||||
|
agent, launches, procs = self.lifecycle([[b'{"state": "need-packages"}\n'], [b'{"state": "ready"}\n']])
|
||||||
|
agent.start()
|
||||||
|
self.wait_for(lambda: agent.status == {"state": "ready"})
|
||||||
|
self.assertEqual(launches, ["", "~/copied"])
|
||||||
|
self.assertIs(agent.proc, procs[1])
|
||||||
|
|
||||||
|
def test_asking_twice_is_an_error_not_a_loop(self):
|
||||||
|
need = [b'{"state": "need-packages"}\n']
|
||||||
|
agent, launches, _ = self.lifecycle([need, list(need)])
|
||||||
|
agent.start()
|
||||||
|
self.wait_for(lambda: agent.status.get("state") == "error")
|
||||||
|
self.assertEqual(launches, ["", "~/copied"])
|
||||||
|
self.assertIn("didn't reach", agent.status["message"])
|
||||||
|
|
||||||
|
def test_start_after_stop_during_the_copy_still_starts(self):
|
||||||
|
gate, entered = threading.Event(), threading.Event()
|
||||||
|
agent, launches, procs = self.lifecycle([], deliver=lambda: (entered.set(), gate.wait(5)))
|
||||||
|
agent.start()
|
||||||
|
self.assertTrue(entered.wait(5))
|
||||||
|
agent.stop()
|
||||||
|
entered.clear()
|
||||||
|
agent.start() # while the first launch is still copying
|
||||||
|
self.assertTrue(entered.wait(5), "the second start didn't launch")
|
||||||
|
gate.set()
|
||||||
|
self.wait_for(lambda: agent.status == {"state": "ready"})
|
||||||
|
time.sleep(0.1)
|
||||||
|
# The stopped launch started no agent; the new one is the one in use.
|
||||||
|
self.assertEqual(len(procs), 1)
|
||||||
|
self.assertIs(agent.proc, procs[0])
|
||||||
|
agent.stop()
|
||||||
|
self.wait_for(lambda: all(p.ended.is_set() for p in procs))
|
||||||
|
|
||||||
|
def test_retry_and_a_new_start_never_run_two_agents(self):
|
||||||
|
# A start() landing just as an agent that asked for the packages exits: exactly one
|
||||||
|
# of it and the retry launches, and stop() ends everything. The new start is held
|
||||||
|
# in its copy until the retry has decided, so neither can see the other's agent.
|
||||||
|
first, decided = [], threading.Event()
|
||||||
|
agent, launches, procs = self.lifecycle([[b'{"state": "need-packages"}\n'], [b'{"state": "ready"}\n']])
|
||||||
|
watch, launch, deliver = agent._watch, agent._launch, agent.deliver
|
||||||
|
|
||||||
|
def racing_watch(proc, errors, retry=False):
|
||||||
|
wanted, heard = watch(proc, errors, retry)
|
||||||
|
if wanted and not first:
|
||||||
|
first.append(threading.current_thread())
|
||||||
|
agent.start() # lands between the agent exiting and the retry
|
||||||
|
return wanted, heard
|
||||||
|
|
||||||
|
def held_deliver(report, force=False):
|
||||||
|
if force:
|
||||||
|
decided.set() # the retry went ahead
|
||||||
|
elif first and threading.current_thread() is not first[0]:
|
||||||
|
decided.wait(5) # the new start waits until the retry has decided
|
||||||
|
return deliver(report, force)
|
||||||
|
|
||||||
|
def first_launch(generation, force=False):
|
||||||
|
try:
|
||||||
|
launch(generation, force)
|
||||||
|
finally:
|
||||||
|
if first and threading.current_thread() is first[0]:
|
||||||
|
decided.set() # the retry declined
|
||||||
|
agent._watch, agent._launch, agent.deliver = racing_watch, first_launch, held_deliver
|
||||||
|
agent.start()
|
||||||
|
# Both have decided once the new start has launched its agent (it always does).
|
||||||
|
self.wait_for(lambda: decided.is_set() and launches.count("") == 2 and len(procs) == len(launches))
|
||||||
|
self.wait_for(lambda: agent.status == {"state": "ready"})
|
||||||
|
self.assertEqual(sum(not p.ended.is_set() for p in procs), 1, launches)
|
||||||
|
agent.stop()
|
||||||
|
self.wait_for(lambda: all(p.ended.is_set() for p in procs))
|
||||||
|
|
||||||
|
def test_stopped_during_the_copy_removes_it(self):
|
||||||
|
gate, entered = threading.Event(), threading.Event()
|
||||||
|
agent, launches, procs = self.lifecycle([], deliver=lambda: (entered.set(), gate.wait(5)))
|
||||||
|
removed = []
|
||||||
|
agent.discard = removed.append
|
||||||
|
agent.deliver = lambda report, force=False: (entered.set(), gate.wait(5), "~/incoming/x")[2]
|
||||||
|
self.addCleanup(lambda: self.assertEqual(removed, ["~/incoming/x"]))
|
||||||
|
agent.start()
|
||||||
|
self.assertTrue(entered.wait(5))
|
||||||
|
agent.stop()
|
||||||
|
gate.set()
|
||||||
|
self.wait_for(lambda: removed == ["~/incoming/x"])
|
||||||
|
self.assertEqual(procs, []) # no agent started for it
|
||||||
|
|
||||||
|
def test_a_copy_whose_agent_never_starts_is_removed(self):
|
||||||
|
agent, launches, procs = self.lifecycle([[]]) # the agent dies before saying anything
|
||||||
|
removed = []
|
||||||
|
agent.discard = removed.append
|
||||||
|
agent.deliver = lambda report, force=False: "~/incoming/y"
|
||||||
|
agent.start()
|
||||||
|
self.wait_for(lambda: removed == ["~/incoming/y"])
|
||||||
|
|
||||||
|
def test_discard_only_touches_copies(self):
|
||||||
|
calls = []
|
||||||
|
old = self.server.ssh, self.server.LOCAL
|
||||||
|
self.server.ssh, self.server.LOCAL = (lambda remote, **k: calls.append(remote)), False
|
||||||
|
try:
|
||||||
|
agent = self.server.InputAgent(packages=[])
|
||||||
|
agent.discard("")
|
||||||
|
agent.discard("~/.local/share/frame-control/kdeconnect")
|
||||||
|
agent.discard("~/.local/share/frame-control/kdeconnect/incoming/ab12")
|
||||||
|
self.server.LOCAL = True
|
||||||
|
agent.discard("~/.local/share/frame-control/kdeconnect/incoming/ab12")
|
||||||
|
finally:
|
||||||
|
self.server.ssh, self.server.LOCAL = old
|
||||||
|
self.assertEqual(calls, ["rm -rf .local/share/frame-control/kdeconnect/incoming/ab12"])
|
||||||
|
|
||||||
|
def test_a_launch_error_leaves_it_ready_to_try_again(self):
|
||||||
|
# Popen fails (say, out of file handles) and so does removing the copy: the
|
||||||
|
# page gets the error and the next start launches.
|
||||||
|
agent, launches, procs = self.lifecycle([])
|
||||||
|
agent.deliver = lambda report, force=False: "~/.local/share/frame-control/kdeconnect/incoming/z"
|
||||||
|
old_popen, old_ssh = self.server.subprocess.Popen, self.server.ssh
|
||||||
|
self.server.subprocess.Popen = self.server.ssh = lambda *a, **k: (_ for _ in ()).throw(OSError(24, "Too many open files"))
|
||||||
|
try:
|
||||||
|
agent.start()
|
||||||
|
self.wait_for(lambda: agent.status.get("state") == "error")
|
||||||
|
finally:
|
||||||
|
self.server.subprocess.Popen, self.server.ssh = old_popen, old_ssh
|
||||||
|
self.assertIsNone(agent.launching)
|
||||||
|
agent.start()
|
||||||
|
self.wait_for(lambda: agent.status == {"state": "ready"})
|
||||||
|
|
||||||
|
def test_any_unexpected_error_still_reports_and_allows_a_retry(self):
|
||||||
|
agent, launches, procs = self.lifecycle([])
|
||||||
|
old = self.server.tempfile.TemporaryFile
|
||||||
|
self.server.tempfile.TemporaryFile = lambda: (_ for _ in ()).throw(OSError(24, "Too many open files"))
|
||||||
|
try:
|
||||||
|
agent.start()
|
||||||
|
self.wait_for(lambda: agent.status.get("state") == "error")
|
||||||
|
finally:
|
||||||
|
self.server.tempfile.TemporaryFile = old
|
||||||
|
self.assertIsNone(agent.launching)
|
||||||
|
agent.deliver = lambda report, force=False: (_ for _ in ()).throw(ValueError("something odd"))
|
||||||
|
agent.start()
|
||||||
|
self.wait_for(lambda: "something odd" in agent.status.get("message", ""))
|
||||||
|
self.assertIsNone(agent.launching)
|
||||||
|
|
||||||
|
def test_copies_go_to_a_folder_of_their_own(self):
|
||||||
|
first, _, _ = self.deliver(frame_has=False)
|
||||||
|
second, _, _ = self.deliver(frame_has=False)
|
||||||
|
self.assertNotEqual(first, second)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.platform == "win32", "the agent runs on the Frame (Linux)")
|
||||||
|
class AgentInstall(unittest.TestCase):
|
||||||
|
"""The agent unpacks what the server copied, after checking each SHA-256."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
import frame_input_agent
|
||||||
|
cls.agent = frame_input_agent
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.dir = Path(tempfile.mkdtemp())
|
||||||
|
self.addCleanup(shutil.rmtree, self.dir)
|
||||||
|
saved = self.agent.BASE, self.agent.ROOT, self.agent.stop_daemon, self.agent.say
|
||||||
|
self.addCleanup(lambda: setattr_all(self.agent, saved))
|
||||||
|
self.agent.BASE, self.agent.ROOT = self.dir / "base", self.dir / "base/root"
|
||||||
|
self.agent.stop_daemon, self.agent.say = lambda: None, lambda *a, **k: None
|
||||||
|
self.agent.BASE.mkdir()
|
||||||
|
|
||||||
|
def package(self):
|
||||||
|
src = self.dir / "src"
|
||||||
|
(src / "usr/lib").mkdir(parents=True)
|
||||||
|
(src / "usr/lib/kdeconnectd").write_text("#!/bin/sh\n")
|
||||||
|
out = self.dir / "incoming/kdeconnect-24.02.2-1-aarch64.pkg.tar.zst"
|
||||||
|
out.parent.mkdir()
|
||||||
|
if subprocess.run(["tar", "--zstd", "-cf", str(out), "-C", str(src), "usr"], capture_output=True).returncode:
|
||||||
|
self.skipTest("this tar can't write zstd")
|
||||||
|
return out, [(out.name, self.agent.sha256(out))]
|
||||||
|
|
||||||
|
def test_unpacks_and_stamps(self):
|
||||||
|
path, packages = self.package()
|
||||||
|
self.assertFalse(self.agent.installed(packages))
|
||||||
|
self.agent.install(path.parent, packages)
|
||||||
|
self.assertTrue((self.agent.ROOT / "usr/lib/kdeconnectd").is_file())
|
||||||
|
self.assertTrue(self.agent.installed(packages))
|
||||||
|
self.assertFalse(self.agent.installed([(path.name, "0" * 64)]))
|
||||||
|
|
||||||
|
def test_refuses_a_damaged_package(self):
|
||||||
|
path, packages = self.package()
|
||||||
|
with open(path, "ab") as f:
|
||||||
|
f.write(b"!")
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "damaged"):
|
||||||
|
self.agent.install(path.parent, packages)
|
||||||
|
self.assertFalse(self.agent.ROOT.exists())
|
||||||
|
|
||||||
|
def test_missing_package_and_empty_manifest(self):
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "didn't reach"):
|
||||||
|
self.agent.install(self.dir, [("nope.pkg.tar.zst", "0" * 64)])
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "doesn't include"):
|
||||||
|
self.agent.install(self.dir, [])
|
||||||
|
|
||||||
|
def test_leaves_another_devices_running_copy_alone(self):
|
||||||
|
# A different build is running for another device: use it, don't stop it to reinstall.
|
||||||
|
self.agent.listening, old = (lambda: True), self.agent.listening
|
||||||
|
self.agent.our_daemons, old_ours = (lambda: [123]), self.agent.our_daemons
|
||||||
|
try:
|
||||||
|
self.agent.ensure_daemon("", [("new.pkg.tar.zst", "1" * 64)])
|
||||||
|
finally:
|
||||||
|
self.agent.listening, self.agent.our_daemons = old, old_ours
|
||||||
|
self.assertFalse(self.agent.ROOT.exists())
|
||||||
|
|
||||||
|
def test_asks_for_packages_it_was_not_sent(self):
|
||||||
|
self.agent.listening, old = (lambda: False), self.agent.listening
|
||||||
|
try:
|
||||||
|
for folder in ("", str(self.dir / "gone")): # none sent, or already tidied away
|
||||||
|
with self.assertRaises(self.agent.NeedPackages):
|
||||||
|
self.agent.ensure_daemon(folder, [("new.pkg.tar.zst", "1" * 64)])
|
||||||
|
finally:
|
||||||
|
self.agent.listening = old
|
||||||
|
|
||||||
|
def test_restart_can_still_unpack_its_copy_then_tidies_it(self):
|
||||||
|
folder = self.agent.BASE / "incoming/abc"
|
||||||
|
folder.mkdir(parents=True)
|
||||||
|
seen = []
|
||||||
|
stubs = {"ensure_daemon": lambda f, p: seen.append(Path(f).is_dir()),
|
||||||
|
"identity": lambda c: ("id", "cert", "key"), "our_daemons": lambda: [123],
|
||||||
|
"connect": lambda *a: (_ for _ in ()).throw(OSError("no answer"))}
|
||||||
|
saved = {k: getattr(self.agent, k) for k in stubs}
|
||||||
|
self.addCleanup(lambda: [setattr(self.agent, k, v) for k, v in saved.items()])
|
||||||
|
for k, v in stubs.items():
|
||||||
|
setattr(self.agent, k, v)
|
||||||
|
self.assertEqual(self.agent.run("c", "n", str(folder), []), 1)
|
||||||
|
self.assertEqual(seen, [True, True]) # there for the first start and the restart
|
||||||
|
self.assertFalse(folder.exists())
|
||||||
|
|
||||||
|
def test_tidy_keeps_copies_in_use(self):
|
||||||
|
incoming = self.agent.BASE / "incoming"
|
||||||
|
held_dir = incoming / "held"
|
||||||
|
held_dir.mkdir(parents=True)
|
||||||
|
held = self.agent.hold_incoming(str(held_dir))
|
||||||
|
self.addCleanup(held.close)
|
||||||
|
os.utime(held_dir, (time.time() - 7200,) * 2)
|
||||||
|
self.agent.tidy_incoming("")
|
||||||
|
self.assertTrue(held_dir.is_dir())
|
||||||
|
|
||||||
|
def test_tidy_keeps_other_starts_copies(self):
|
||||||
|
incoming = self.agent.BASE / "incoming"
|
||||||
|
mine, theirs, stale = incoming / "mine", incoming / "theirs", incoming / "stale"
|
||||||
|
for d in (mine, theirs, stale):
|
||||||
|
d.mkdir(parents=True)
|
||||||
|
os.utime(stale, (time.time() - 7200,) * 2)
|
||||||
|
self.agent.tidy_incoming(str(mine))
|
||||||
|
self.assertEqual(sorted(p.name for p in incoming.iterdir()), ["theirs"])
|
||||||
|
|
||||||
|
def test_server_and_agent_agree_on_the_stamp(self):
|
||||||
|
import server
|
||||||
|
packages = [("a.pkg.tar.zst", "1" * 64), ("b.pkg.tar.zst", "2" * 64)]
|
||||||
|
self.assertEqual(server.kdeconnect_stamp(packages), self.agent.stamp(packages))
|
||||||
|
|
||||||
|
|
||||||
|
def setattr_all(module, saved):
|
||||||
|
module.BASE, module.ROOT, module.stop_daemon, module.say = saved
|
||||||
|
|
||||||
|
|
||||||
|
class FakeKdeConnect:
|
||||||
|
"""Just enough of kdeconnectd: pairs when asked and records remote-input packets."""
|
||||||
|
|
||||||
|
def __init__(self, paired=False):
|
||||||
|
self.listener = socket.socket()
|
||||||
|
self.listener.bind(("127.0.0.1", 0))
|
||||||
|
self.listener.listen(1)
|
||||||
|
self.port = self.listener.getsockname()[1]
|
||||||
|
self.paired, self.identity, self.received, self.pair_requests = paired, None, [], 0
|
||||||
|
threading.Thread(target=self.serve, daemon=True).start()
|
||||||
|
|
||||||
|
def serve(self):
|
||||||
|
conn, _ = self.listener.accept()
|
||||||
|
line = b""
|
||||||
|
while not line.endswith(b"\n"):
|
||||||
|
line += conn.recv(1)
|
||||||
|
self.identity = json.loads(line)
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||||
|
ctx.check_hostname, ctx.verify_mode = False, ssl.CERT_NONE
|
||||||
|
tls = ctx.wrap_socket(conn) # "Starting client ssl (but I'm the server TCP socket)"
|
||||||
|
tls.sendall(b'{"id": 1, "type": "kdeconnect.mousepad.keyboardstate", "body": {"state": true}}\n')
|
||||||
|
buf = b""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
chunk = tls.recv(65536)
|
||||||
|
except OSError:
|
||||||
|
return
|
||||||
|
if not chunk:
|
||||||
|
return
|
||||||
|
buf += chunk
|
||||||
|
while b"\n" in buf:
|
||||||
|
raw, buf = buf.split(b"\n", 1)
|
||||||
|
p = json.loads(raw)
|
||||||
|
if p["type"] == "kdeconnect.pair":
|
||||||
|
self.pair_requests += 1
|
||||||
|
elif p["type"] == "kdeconnect.mousepad.request" and self.paired:
|
||||||
|
self.received.append(p["body"])
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.platform == "win32", "the agent runs on the Frame (Linux)")
|
||||||
|
@unittest.skipUnless(shutil.which("openssl"), "needs openssl to make a certificate")
|
||||||
|
class AgentProtocol(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
import frame_input_agent
|
||||||
|
cls.agent = frame_input_agent
|
||||||
|
cls.dir = tempfile.TemporaryDirectory()
|
||||||
|
cls.cert, cls.key = Path(cls.dir.name, "cert.pem"), Path(cls.dir.name, "key.pem")
|
||||||
|
subprocess.run(["openssl", "req", "-x509", "-newkey", "rsa:2048", "-nodes", "-days", "1",
|
||||||
|
"-subj", "/CN=framecontrol_test", "-keyout", str(cls.key), "-out", str(cls.cert)],
|
||||||
|
check=True, capture_output=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.dir.cleanup()
|
||||||
|
|
||||||
|
def wait_for(self, check):
|
||||||
|
for _ in range(100):
|
||||||
|
if check():
|
||||||
|
return
|
||||||
|
time.sleep(0.02)
|
||||||
|
self.fail("timed out")
|
||||||
|
|
||||||
|
def test_pairs_then_forwards_events(self):
|
||||||
|
fake = FakeKdeConnect()
|
||||||
|
link = self.agent.Link("framecontrol_0123456789abcdef01234567", self.cert, self.key, port=fake.port)
|
||||||
|
link.pair(lambda: fake.paired, lambda: setattr(fake, "paired", fake.pair_requests > 0), timeout=5)
|
||||||
|
self.assertEqual(fake.pair_requests, 1)
|
||||||
|
ident = fake.identity["body"]
|
||||||
|
self.assertEqual(fake.identity["type"], "kdeconnect.identity")
|
||||||
|
self.assertEqual(ident["protocolVersion"], 7)
|
||||||
|
self.assertIn("kdeconnect.mousepad.request", ident["outgoingCapabilities"])
|
||||||
|
link.read(0.5)
|
||||||
|
self.assertTrue(link.keyboard)
|
||||||
|
for body in self.agent.events(b'[{"dx": 4, "dy": -2}, {"key": "hi"}]'):
|
||||||
|
link.send(body)
|
||||||
|
self.wait_for(lambda: len(fake.received) == 2)
|
||||||
|
self.assertEqual(fake.received, [{"dx": 4, "dy": -2}, {"key": "hi"}])
|
||||||
|
|
||||||
|
def test_already_paired_sends_no_pair_request(self):
|
||||||
|
# A pair request to a device that's already paired makes KDE Connect unpair it.
|
||||||
|
fake = FakeKdeConnect(paired=True)
|
||||||
|
link = self.agent.Link("framecontrol_0123456789abcdef01234567", self.cert, self.key, port=fake.port)
|
||||||
|
link.pair(lambda: True, lambda: self.fail("accepted a pairing that wasn't needed"))
|
||||||
|
link.send({"singleclick": True})
|
||||||
|
self.wait_for(lambda: fake.received == [{"singleclick": True}])
|
||||||
|
self.assertEqual(fake.pair_requests, 0)
|
||||||
|
|
||||||
|
def test_client_args_are_folder_safe(self):
|
||||||
|
old = sys.argv
|
||||||
|
try:
|
||||||
|
sys.argv = ["-c", "../../etc x", "Alex's Mac"]
|
||||||
|
self.assertEqual(self.agent.client_args(), ("etcx", "Frame Control (Alex's Mac)", "", []))
|
||||||
|
sys.argv = ["-c"]
|
||||||
|
self.assertEqual(self.agent.client_args(), ("default", "Frame Control", "", []))
|
||||||
|
sys.argv = ["-c", "mac", "Mac", "~/x", '[["a.pkg.tar.zst", "ab"]]']
|
||||||
|
self.assertEqual(self.agent.client_args()[2:], (os.path.expanduser("~/x"), [("a.pkg.tar.zst", "ab")]))
|
||||||
|
finally:
|
||||||
|
sys.argv = old
|
||||||
|
|
||||||
|
def test_events_parsing(self):
|
||||||
|
self.assertEqual(self.agent.events(b'{"dx": 1}'), [{"dx": 1}])
|
||||||
|
self.assertEqual(self.agent.events(b'[{"dx": 1}, 5, {}]'), [{"dx": 1}])
|
||||||
|
self.assertEqual(self.agent.events(b"not json"), [])
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(shutil.which("node"), "needs node")
|
||||||
|
class PageQueue(unittest.TestCase):
|
||||||
|
"""The page's input queue (tests/page/pad_queue.mjs runs the real functions from index.html)."""
|
||||||
|
|
||||||
|
def test_queue_rules(self):
|
||||||
|
r = subprocess.run(["node", str(ROOT / "tests" / "page" / "pad_queue.mjs")], capture_output=True, text=True)
|
||||||
|
self.assertEqual(r.returncode, 0, r.stdout + r.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"""APK sources: every place Frame Control can find and download APKs.
|
||||||
|
|
||||||
|
One module per source kind in this package. Each module exposes the same small
|
||||||
|
interface so ``search.py`` can query them all and show where every result came
|
||||||
|
from. Python stdlib only; must run on Python 3.9.
|
||||||
|
|
||||||
|
Module interface
|
||||||
|
----------------
|
||||||
|
KIND = 'sidequest' # stable id of the source kind
|
||||||
|
def sources() -> list[dict] # configured sources of this kind (a kind can have
|
||||||
|
# several, e.g. one per F-Droid-format repo)
|
||||||
|
def search(source, query, limit=50) -> list[dict] # Entry dicts, best first
|
||||||
|
def details(source, entry_id) -> dict # Entry with 'versions'
|
||||||
|
def download(source, entry_id, version_code=None) -> dict
|
||||||
|
# {'apk': local path, 'obb': [paths], 'sha256': hex or None, 'verified': bool}
|
||||||
|
# Raise SourceError with a user-readable message on failure.
|
||||||
|
|
||||||
|
Source dict
|
||||||
|
-----------
|
||||||
|
{'id': 'sidequest', 'kind': KIND, 'name': 'SideQuest', 'url': 'https://...',
|
||||||
|
'builtin': True, 'enabled': True, 'trust': 'official' | 'community' | 'user'}
|
||||||
|
|
||||||
|
Entry dict (missing facts are None, never guessed)
|
||||||
|
----------
|
||||||
|
{'source': source id, 'id': source-local id, 'package': 'org.example.app' or None,
|
||||||
|
'name': str, 'summary': str, 'icon': url or None, 'page': url or None,
|
||||||
|
'version': '1.2', 'version_code': 12, 'min_sdk': 24, 'abis': ['arm64-v8a'],
|
||||||
|
'vr': True/False/None, 'size': bytes, 'free': True, 'license': 'GPL-3.0' or None,
|
||||||
|
'updated': 'YYYY-MM-DD', 'downloadable': bool, # False = open page only
|
||||||
|
'versions': [ {version, version_code, min_sdk, size, updated}, ... ]} # details() only
|
||||||
|
|
||||||
|
Rules
|
||||||
|
-----
|
||||||
|
- Only sources that distribute APKs with the developer's consent: free listings,
|
||||||
|
never paid apps re-hosted, no licence or entitlement workarounds.
|
||||||
|
- Honour each site's terms and robots rules; if automated download isn't allowed,
|
||||||
|
return entries with 'downloadable': False and a 'page' link instead.
|
||||||
|
- Cache indexes under frame_host.cache_dir('apk-sources'); send a clear
|
||||||
|
User-Agent ('FrameControl/<version>'); keep requests modest.
|
||||||
|
- Tests use recorded fixtures, never the network.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class SourceError(Exception):
|
||||||
|
"""User-readable failure from a source (network, format, verification)."""
|
||||||
+90
-7
@@ -6,12 +6,18 @@ apps, the lepton-show-flatscreen marker; plus a non-Steam shortcut, so it shows
|
|||||||
in the Steam library and gets its own SteamVR panel. Nothing goes through
|
in the Steam library and gets its own SteamVR panel. Nothing goes through
|
||||||
Lepton Development, which wipes its apps on exit. See docs/apks.md.
|
Lepton Development, which wipes its apps on exit. See docs/apks.md.
|
||||||
|
|
||||||
Python stdlib only. CLI: python3 ui/frame_android.py {info APK|versions APK-or-PKG|install APK|list|launch PKG|stop PKG|remove PKG|probe PKG}
|
Python stdlib only. CLI: python3 ui/frame_android.py
|
||||||
|
install APK [--vr|--flat] [--no-xr-compat] | info APK | versions APK-or-PKG
|
||||||
|
patch SRC DST [--add NAME=PATH ...] | list | launch PKG | stop PKG | remove PKG | probe PKG
|
||||||
"""
|
"""
|
||||||
import json, os, re, shlex, shutil, subprocess, sys, threading, time, zlib
|
import json, os, re, shlex, shutil, struct, subprocess, sys, threading, time, zlib
|
||||||
|
|
||||||
import frame_apk
|
import frame_apk
|
||||||
import frame_host
|
import frame_host
|
||||||
|
import tempfile
|
||||||
|
import zipfile
|
||||||
|
from frame_apk_vr import add_launcher_category
|
||||||
|
from frame_apk_sign import repack
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
FRAME = os.environ.get('FRAME_ALIAS', 'frame')
|
FRAME = os.environ.get('FRAME_ALIAS', 'frame')
|
||||||
@@ -20,6 +26,13 @@ COMPAT = '.local/share/Steam/steamapps/compatdata'
|
|||||||
SHADERS = '.local/share/Steam/steamapps/shadercache'
|
SHADERS = '.local/share/Steam/steamapps/shadercache'
|
||||||
LAUNCHER = os.path.join(ROOT, 'frame', 'android', 'lepton-app.sh')
|
LAUNCHER = os.path.join(ROOT, 'frame', 'android', 'lepton-app.sh')
|
||||||
SHORTCUTS = os.path.join(ROOT, 'frame', 'android', 'steam_shortcuts.py')
|
SHORTCUTS = os.path.join(ROOT, 'frame', 'android', 'steam_shortcuts.py')
|
||||||
|
# OpenXR API layer that lets OpenXR 1.1 apps run on SteamVR's 1.0-only Android
|
||||||
|
# runtime (frame/openxr-compat, docs/vr-apks.md). Injected into VR APKs.
|
||||||
|
XR_COMPAT = os.path.join(ROOT, 'frame', 'openxr-compat')
|
||||||
|
XR_COMPAT_FILES = {
|
||||||
|
'assets/openxr/1/api_layers/implicit.d/XrApiLayer_FRAME_compat.json': 'XrApiLayer_FRAME_compat.json',
|
||||||
|
'lib/arm64-v8a/libXrApiLayer_FRAME_compat.so': 'prebuilt/arm64-v8a/libXrApiLayer_FRAME_compat.so',
|
||||||
|
}
|
||||||
PKG_RE = re.compile(r'^[A-Za-z][\w]*(\.[A-Za-z_][\w]*)+$')
|
PKG_RE = re.compile(r'^[A-Za-z][\w]*(\.[A-Za-z_][\w]*)+$')
|
||||||
SSH_OPTS = ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=8']
|
SSH_OPTS = ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=8']
|
||||||
|
|
||||||
@@ -65,6 +78,22 @@ def apk_info(path):
|
|||||||
raise FrameError(f'{os.path.basename(path)}: {e}')
|
raise FrameError(f'{os.path.basename(path)}: {e}')
|
||||||
|
|
||||||
|
|
||||||
|
def xr_compat_files(apk_path):
|
||||||
|
"""The layer's files to add, or {} if the APK has no OpenXR loader or already has the layer."""
|
||||||
|
with zipfile.ZipFile(apk_path) as z:
|
||||||
|
names = set(z.namelist())
|
||||||
|
if 'lib/arm64-v8a/libopenxr_loader.so' not in names or names & set(XR_COMPAT_FILES):
|
||||||
|
return {}
|
||||||
|
add = {}
|
||||||
|
for entry, rel in XR_COMPAT_FILES.items():
|
||||||
|
try:
|
||||||
|
with open(os.path.join(XR_COMPAT, rel), 'rb') as f:
|
||||||
|
add[entry] = f.read()
|
||||||
|
except OSError:
|
||||||
|
raise FrameError("the OpenXR compatibility layer isn't built; run frame/openxr-compat/build.sh")
|
||||||
|
return add
|
||||||
|
|
||||||
|
|
||||||
def check_installable(info):
|
def check_installable(info):
|
||||||
if info['min_sdk'] and info['min_sdk'] > 30:
|
if info['min_sdk'] and info['min_sdk'] > 30:
|
||||||
raise FrameError(f"{info['label']} needs Android API {info['min_sdk']}; Lepton is Android 11 (API 30)")
|
raise FrameError(f"{info['label']} needs Android API {info['min_sdk']}; Lepton is Android 11 (API 30)")
|
||||||
@@ -111,7 +140,7 @@ def _write_meta(d, meta):
|
|||||||
install_hooks = []
|
install_hooks = []
|
||||||
|
|
||||||
|
|
||||||
def install(apk_path, flatscreen=True, name=None, source=None, icon_png=None):
|
def install(apk_path, flatscreen=None, name=None, source=None, icon_png=None, xr_compat=None):
|
||||||
start, info = time.time(), None
|
start, info = time.time(), None
|
||||||
try:
|
try:
|
||||||
info = apk_info(apk_path)
|
info = apk_info(apk_path)
|
||||||
@@ -121,8 +150,20 @@ def install(apk_path, flatscreen=True, name=None, source=None, icon_png=None):
|
|||||||
pkg = info['package']
|
pkg = info['package']
|
||||||
if not PKG_RE.match(pkg):
|
if not PKG_RE.match(pkg):
|
||||||
raise FrameError(f'unexpected package name {pkg!r}')
|
raise FrameError(f'unexpected package name {pkg!r}')
|
||||||
|
if flatscreen is None:
|
||||||
|
flatscreen = not info['vr']
|
||||||
|
# VR apps get the OpenXR compatibility layer unless told otherwise; it only
|
||||||
|
# changes calls SteamVR would otherwise reject.
|
||||||
|
add = xr_compat_files(apk_path) if (info['vr'] if xr_compat is None else xr_compat) else {}
|
||||||
with _install_lock:
|
with _install_lock:
|
||||||
meta = _install(apk_path, info, pkg, flatscreen, name, source)
|
if add or info['repairable']:
|
||||||
|
with tempfile.TemporaryDirectory(prefix='frame-vr-') as tmp:
|
||||||
|
patched = os.path.join(tmp, 'app.apk')
|
||||||
|
info['patched'] = patch(apk_path, patched, add)['patched']
|
||||||
|
info['launchable'] = True
|
||||||
|
meta = _install(patched, info, pkg, flatscreen, name, source or os.path.basename(apk_path))
|
||||||
|
else:
|
||||||
|
meta = _install(apk_path, info, pkg, flatscreen, name, source)
|
||||||
except FrameError as e:
|
except FrameError as e:
|
||||||
_after_install(info, None, e, start)
|
_after_install(info, None, e, start)
|
||||||
raise
|
raise
|
||||||
@@ -163,6 +204,8 @@ def _install(apk_path, info, pkg, flatscreen, name, source):
|
|||||||
raise FrameError(f'Steam did not return a shortcut id (got {reply[:80]!r})')
|
raise FrameError(f'Steam did not return a shortcut id (got {reply[:80]!r})')
|
||||||
meta = {'package': pkg, 'label': name or info['label'], 'version': info['version'],
|
meta = {'package': pkg, 'label': name or info['label'], 'version': info['version'],
|
||||||
'instance': iid, 'shortcut': shortcut, 'game_id': game_id(shortcut),
|
'instance': iid, 'shortcut': shortcut, 'game_id': game_id(shortcut),
|
||||||
|
'vr': info.get('vr', False), 'vr_issues': info.get('vr_issues', []),
|
||||||
|
'launchable': info.get('launchable', False), 'patched': info.get('patched', []),
|
||||||
'flatscreen': flatscreen, 'installed': time.strftime('%Y-%m-%dT%H:%M:%S'),
|
'flatscreen': flatscreen, 'installed': time.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||||
'source': source or os.path.basename(apk_path)}
|
'source': source or os.path.basename(apk_path)}
|
||||||
_write_meta(d, meta)
|
_write_meta(d, meta)
|
||||||
@@ -293,19 +336,59 @@ def probe(pkg, wait=20):
|
|||||||
'container_up': ctr in running_instances()}
|
'container_up': ctr in running_instances()}
|
||||||
|
|
||||||
|
|
||||||
|
def patch(src, dst, add=None):
|
||||||
|
try:
|
||||||
|
info = apk_info(src)
|
||||||
|
with zipfile.ZipFile(src) as z:
|
||||||
|
original = frame_apk._read(z, 'AndroidManifest.xml', frame_apk.MAX_MANIFEST)
|
||||||
|
manifest = add_launcher_category(original) if info['repairable'] else original
|
||||||
|
if not info['launchable'] and not info['repairable']:
|
||||||
|
raise FrameError('APK has no MAIN/LAUNCHER activity that Frame Control can patch')
|
||||||
|
repack(src, dst, replace={'AndroidManifest.xml': manifest}, add=add)
|
||||||
|
result = apk_info(dst)
|
||||||
|
result.pop('icon_png', None)
|
||||||
|
result['patched'] = (['launcher'] if manifest != original else []) + \
|
||||||
|
(['openxr-compat'] if add and set(XR_COMPAT_FILES) <= set(add) else [])
|
||||||
|
return result
|
||||||
|
except (OSError, ValueError, IndexError, struct.error, zipfile.BadZipFile, frame_apk.ApkError) as e:
|
||||||
|
raise FrameError(str(e)) from e
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
cmd, *args = sys.argv[1:] or ['help']
|
cmd, *args = sys.argv[1:] or ['help']
|
||||||
try:
|
try:
|
||||||
if cmd in ('info', 'versions'):
|
if cmd in ('info', 'versions'):
|
||||||
import frame_apk_versions
|
import frame_apk_versions
|
||||||
if cmd == 'info':
|
if cmd == 'info':
|
||||||
print(frame_apk_versions.describe(apk_info(args[0])))
|
info = apk_info(args[0])
|
||||||
|
print(frame_apk_versions.describe(info))
|
||||||
|
fix = '; Frame Control adds the LAUNCHER entry Lepton needs' if info.get('repairable') else ''
|
||||||
|
if info.get('vr') or fix:
|
||||||
|
print(('VR app' if info.get('vr') else 'Android app') + fix)
|
||||||
|
for note in info.get('vr_issues', []):
|
||||||
|
print(note)
|
||||||
return
|
return
|
||||||
info = apk_info(args[0]) if os.path.isfile(args[0]) or args[0].lower().endswith('.apk') else None
|
info = apk_info(args[0]) if os.path.isfile(args[0]) or args[0].lower().endswith('.apk') else None
|
||||||
r = frame_apk_versions.alternatives(
|
r = frame_apk_versions.alternatives(
|
||||||
info['package'] if info else args[0], info.get('version_code') if info else None)
|
info['package'] if info else args[0], info.get('version_code') if info else None)
|
||||||
elif cmd == 'install':
|
elif cmd == 'install':
|
||||||
r = install(args[0], flatscreen='--vr' not in args)
|
r = install(args[0], flatscreen=False if '--vr' in args else True if '--flat' in args else None,
|
||||||
|
xr_compat=False if '--no-xr-compat' in args else None)
|
||||||
|
elif cmd == 'patch':
|
||||||
|
import argparse
|
||||||
|
parser = argparse.ArgumentParser(description='Patch and v2-sign an APK locally')
|
||||||
|
parser.add_argument('src')
|
||||||
|
parser.add_argument('dst')
|
||||||
|
parser.add_argument('--add', action='append', default=[], metavar='NAME=PATH')
|
||||||
|
opts = parser.parse_args(args)
|
||||||
|
additions = {}
|
||||||
|
for item in opts.add:
|
||||||
|
if '=' not in item:
|
||||||
|
raise FrameError('--add requires NAME=PATH')
|
||||||
|
entry, path = item.split('=', 1)
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
additions[entry] = f.read()
|
||||||
|
r = patch(opts.src, opts.dst, additions)
|
||||||
elif cmd == 'list':
|
elif cmd == 'list':
|
||||||
r = list_apps()
|
r = list_apps()
|
||||||
elif cmd in ('launch', 'stop', 'probe'):
|
elif cmd in ('launch', 'stop', 'probe'):
|
||||||
@@ -314,7 +397,7 @@ def main():
|
|||||||
r = remove(args[0], keep_data='--keep-data' in args)
|
r = remove(args[0], keep_data='--keep-data' in args)
|
||||||
else:
|
else:
|
||||||
sys.exit(__doc__)
|
sys.exit(__doc__)
|
||||||
except FrameError as e:
|
except (FrameError, OSError) as e:
|
||||||
sys.exit(f'error: {e}')
|
sys.exit(f'error: {e}')
|
||||||
print(json.dumps(r, indent=1))
|
print(json.dumps(r, indent=1))
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -10,7 +10,8 @@ import zipfile
|
|||||||
|
|
||||||
# android: attribute resource ids; names can be stripped by shrinkers, ids can't.
|
# android: attribute resource ids; names can be stripped by shrinkers, ids can't.
|
||||||
ATTR = {0x01010001: 'label', 0x01010002: 'icon', 0x01010003: 'name',
|
ATTR = {0x01010001: 'label', 0x01010002: 'icon', 0x01010003: 'name',
|
||||||
0x0101021b: 'versionCode', 0x0101021c: 'versionName', 0x0101020c: 'minSdkVersion'}
|
0x01010024: 'value', 0x0101021b: 'versionCode', 0x0101021c: 'versionName', 0x0101020c: 'minSdkVersion',
|
||||||
|
0x01010202: 'targetActivity'}
|
||||||
T_REF, T_STRING, T_INT_DEC, T_INT_HEX = 0x01, 0x03, 0x10, 0x11
|
T_REF, T_STRING, T_INT_DEC, T_INT_HEX = 0x01, 0x03, 0x10, 0x11
|
||||||
# APKs can come from websites (install links), so nothing read from one may be
|
# APKs can come from websites (install links), so nothing read from one may be
|
||||||
# unbounded. zipfile stops at a member's declared size, so checking it is enough.
|
# unbounded. zipfile stops at a member's declared size, so checking it is enough.
|
||||||
@@ -215,8 +216,11 @@ def apk_info(path):
|
|||||||
if 'AndroidManifest.xml' not in names:
|
if 'AndroidManifest.xml' not in names:
|
||||||
raise ApkError('not an APK: no AndroidManifest.xml')
|
raise ApkError('not an APK: no AndroidManifest.xml')
|
||||||
try:
|
try:
|
||||||
elements = manifest_elements(_read(z, 'AndroidManifest.xml', MAX_MANIFEST))
|
manifest_data = _read(z, 'AndroidManifest.xml', MAX_MANIFEST)
|
||||||
|
elements = manifest_elements(manifest_data)
|
||||||
res = Resources(_read(z, 'resources.arsc', MAX_ARSC) if 'resources.arsc' in names else b'')
|
res = Resources(_read(z, 'resources.arsc', MAX_ARSC) if 'resources.arsc' in names else b'')
|
||||||
|
from frame_apk_vr import detection
|
||||||
|
vr_info = detection(manifest_data, names)
|
||||||
except (struct.error, IndexError, zipfile.BadZipFile) as e:
|
except (struct.error, IndexError, zipfile.BadZipFile) as e:
|
||||||
raise ApkError(f'could not read the APK manifest: {e}')
|
raise ApkError(f'could not read the APK manifest: {e}')
|
||||||
tags = {}
|
tags = {}
|
||||||
@@ -236,6 +240,7 @@ def apk_info(path):
|
|||||||
'min_sdk': min_sdk[1] if min_sdk and min_sdk[0] in (T_INT_DEC, T_INT_HEX) else None,
|
'min_sdk': min_sdk[1] if min_sdk and min_sdk[0] in (T_INT_DEC, T_INT_HEX) else None,
|
||||||
'icon_png': None,
|
'icon_png': None,
|
||||||
}
|
}
|
||||||
|
info.update(vr_info)
|
||||||
try:
|
try:
|
||||||
info['icon_png'] = _icon_png(z, names, _icons(app.get('icon'), res))
|
info['icon_png'] = _icon_png(z, names, _icons(app.get('icon'), res))
|
||||||
except Exception: # noqa: BLE001 - any unreadable icon just means no icon
|
except Exception: # noqa: BLE001 - any unreadable icon just means no icon
|
||||||
|
|||||||
@@ -0,0 +1,361 @@
|
|||||||
|
"""Lossless ZIP repacking and APK v2 RSA/SHA-256 signing, Python 3.9 stdlib.
|
||||||
|
|
||||||
|
Spec: https://source.android.com/docs/security/features/apksigning/v2
|
||||||
|
Sections: APK Signing Block; APK Signature Scheme v2 Block; Integrity-protected
|
||||||
|
contents; Verification. No verity algorithm is used, so no verity padding.
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import secrets
|
||||||
|
import struct
|
||||||
|
import tempfile
|
||||||
|
import zipfile
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
import frame_host
|
||||||
|
|
||||||
|
MAGIC = b'APK Sig Block 42'
|
||||||
|
V2 = 0x7109871a
|
||||||
|
ALG = 0x0103
|
||||||
|
SHA256_DER = bytes.fromhex('3031300d060960864801650304020105000420')
|
||||||
|
|
||||||
|
|
||||||
|
def u32(n):
|
||||||
|
return struct.pack('<I', n)
|
||||||
|
|
||||||
|
|
||||||
|
def lp(b):
|
||||||
|
return u32(len(b)) + b
|
||||||
|
|
||||||
|
|
||||||
|
def der(tag, b):
|
||||||
|
n = len(b)
|
||||||
|
length = bytes([n]) if n < 128 else bytes([128 + (n.bit_length() + 7) // 8]) + n.to_bytes((n.bit_length() + 7) // 8, 'big')
|
||||||
|
return bytes([tag]) + length + b
|
||||||
|
|
||||||
|
|
||||||
|
def integer(n):
|
||||||
|
b = n.to_bytes((n.bit_length() + 7) // 8 or 1, 'big')
|
||||||
|
return der(2, (b'\0' if b[0] & 128 else b'') + b)
|
||||||
|
|
||||||
|
|
||||||
|
def sequence(*items):
|
||||||
|
return der(0x30, b''.join(items))
|
||||||
|
|
||||||
|
|
||||||
|
RSA_ALG = bytes.fromhex('300d06092a864886f70d0101010500')
|
||||||
|
CERT_ALG = bytes.fromhex('300d06092a864886f70d01010b0500')
|
||||||
|
|
||||||
|
|
||||||
|
def public_key(key):
|
||||||
|
return sequence(RSA_ALG, der(3, b'\0' + sequence(integer(key['n']), integer(key['e']))))
|
||||||
|
|
||||||
|
|
||||||
|
def encoded_hash(data, size):
|
||||||
|
digest = SHA256_DER + hashlib.sha256(data).digest()
|
||||||
|
return b'\0\1' + b'\xff' * (size - len(digest) - 3) + b'\0' + digest
|
||||||
|
|
||||||
|
|
||||||
|
def rsa_sign(data, key):
|
||||||
|
size = (key['n'].bit_length() + 7) // 8
|
||||||
|
return pow(int.from_bytes(encoded_hash(data, size), 'big'), key['d'], key['n']).to_bytes(size, 'big')
|
||||||
|
|
||||||
|
|
||||||
|
def rsa_verify(data, sig, n, e):
|
||||||
|
size = (n.bit_length() + 7) // 8
|
||||||
|
if len(sig) != size or int.from_bytes(sig, 'big') >= n:
|
||||||
|
raise ValueError('invalid RSA signature size/value')
|
||||||
|
actual = pow(int.from_bytes(sig, 'big'), e, n).to_bytes(size, 'big')
|
||||||
|
if actual != encoded_hash(data, size):
|
||||||
|
raise ValueError('RSA signature mismatch')
|
||||||
|
|
||||||
|
|
||||||
|
def certificate(key):
|
||||||
|
name = sequence(der(0x31, sequence(bytes.fromhex('0603550403'), der(12, b'Frame Control APK signer'))))
|
||||||
|
validity = sequence(der(0x17, b'200101000000Z'), der(0x18, b'21200101000000Z'))
|
||||||
|
tbs = sequence(der(0xa0, integer(2)), integer(1), CERT_ALG, name, validity, name, public_key(key))
|
||||||
|
return sequence(tbs, CERT_ALG, der(3, b'\0' + rsa_sign(tbs, key)))
|
||||||
|
|
||||||
|
|
||||||
|
def _prime(bits):
|
||||||
|
small = (3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47)
|
||||||
|
while True:
|
||||||
|
n = secrets.randbits(bits) | (3 << (bits - 2)) | 1
|
||||||
|
if any(n % p == 0 for p in small) or (n - 1) % 65537 == 0:
|
||||||
|
continue
|
||||||
|
d, s = n - 1, 0
|
||||||
|
while d % 2 == 0:
|
||||||
|
d //= 2
|
||||||
|
s += 1
|
||||||
|
for _ in range(40): # Miller-Rabin error bound <= 2^-80
|
||||||
|
x = pow(secrets.randbelow(n - 3) + 2, d, n)
|
||||||
|
if x in (1, n - 1):
|
||||||
|
continue
|
||||||
|
for _ in range(s - 1):
|
||||||
|
x = pow(x, 2, n)
|
||||||
|
if x == n - 1:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def signing_key(path=None):
|
||||||
|
"""Persistent identity in app data, never an evictable cache. Atomic publication.
|
||||||
|
|
||||||
|
Hard-linking a fully written private temp file prevents concurrent first-use
|
||||||
|
callers from selecting different identities or reading a partial key.
|
||||||
|
"""
|
||||||
|
path = Path(path) if path is not None else frame_host.data_dir('apk-signing-key.json')
|
||||||
|
if not path.exists():
|
||||||
|
p, q = _prime(1024), _prime(1024)
|
||||||
|
while q == p:
|
||||||
|
q = _prime(1024)
|
||||||
|
key = {'n': p * q, 'e': 65537, 'd': pow(65537, -1, math.lcm(p - 1, q - 1))}
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
fd, tmp = tempfile.mkstemp(prefix='.apk-key-', dir=str(path.parent))
|
||||||
|
try:
|
||||||
|
with os.fdopen(fd, 'w') as f:
|
||||||
|
json.dump(key, f)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno())
|
||||||
|
try:
|
||||||
|
os.link(tmp, path) # never replaces a key another process wrote first
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
except OSError: # no hard links (FAT/exFAT): plain rename
|
||||||
|
if not path.exists():
|
||||||
|
os.replace(tmp, path)
|
||||||
|
finally:
|
||||||
|
if os.path.exists(tmp):
|
||||||
|
os.unlink(tmp)
|
||||||
|
os.chmod(path, 0o600) # Windows ignores this; the per-user app-data folder is the protection there
|
||||||
|
key = json.loads(path.read_text())
|
||||||
|
if key['n'].bit_length() != 2048 or key['e'] != 65537:
|
||||||
|
raise ValueError(f'invalid cached APK signing key; delete {path} to make a new one '
|
||||||
|
'(re-signed apps then need reinstalling)')
|
||||||
|
rsa_verify(b'key check', rsa_sign(b'key check', key), key['n'], key['e'])
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
def _eocd(data):
|
||||||
|
# ZIP comments can contain the EOCD signature; accept only an exact EOF fit.
|
||||||
|
for at in range(len(data) - 22, max(-1, len(data) - 65558), -1):
|
||||||
|
if data[at:at + 4] == b'PK\5\6' and at + 22 + struct.unpack_from('<H', data, at + 20)[0] == len(data):
|
||||||
|
disk, cd_disk, count_disk, count, size, cd = struct.unpack_from('<HHHHII', data, at + 4)
|
||||||
|
if disk or cd_disk or count_disk != count or count == 65535 or cd + size != at:
|
||||||
|
raise ValueError('multi-disk/ZIP64 or invalid APK directory')
|
||||||
|
return at, cd
|
||||||
|
raise ValueError('missing ZIP end record')
|
||||||
|
|
||||||
|
|
||||||
|
def content_digest(sections):
|
||||||
|
chunks = []
|
||||||
|
for section in sections:
|
||||||
|
for off in range(0, len(section), 1024 * 1024):
|
||||||
|
part = section[off:off + 1024 * 1024]
|
||||||
|
chunks.append(hashlib.sha256(b'\xa5' + u32(len(part)) + part).digest())
|
||||||
|
return hashlib.sha256(b'\x5a' + u32(len(chunks)) + b''.join(chunks)).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def sign_apk(data, key):
|
||||||
|
eo, cd = _eocd(data)
|
||||||
|
digest = content_digest((memoryview(data)[:cd], memoryview(data)[cd:eo], data[eo:]))
|
||||||
|
signed = lp(lp(u32(ALG) + lp(digest))) + lp(lp(certificate(key))) + lp(b'')
|
||||||
|
signer = lp(signed) + lp(lp(u32(ALG) + lp(rsa_sign(signed, key)))) + lp(public_key(key))
|
||||||
|
value = lp(lp(signer))
|
||||||
|
pair = struct.pack('<Q', 4 + len(value)) + u32(V2) + value
|
||||||
|
size = len(pair) + 24
|
||||||
|
block = struct.pack('<Q', size) + pair + struct.pack('<Q', size) + MAGIC
|
||||||
|
end = bytearray(data[eo:])
|
||||||
|
struct.pack_into('<I', end, 16, cd + len(block))
|
||||||
|
return data[:cd] + block + data[cd:eo] + end
|
||||||
|
|
||||||
|
|
||||||
|
def _parts(data):
|
||||||
|
result, off = [], 0
|
||||||
|
while off < len(data):
|
||||||
|
if off + 4 > len(data):
|
||||||
|
raise ValueError('truncated length prefix')
|
||||||
|
size = struct.unpack_from('<I', data, off)[0]
|
||||||
|
off += 4
|
||||||
|
if off + size > len(data):
|
||||||
|
raise ValueError('length prefix outside block')
|
||||||
|
result.append(data[off:off + size])
|
||||||
|
off += size
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _der_parts(data):
|
||||||
|
result, off = [], 0
|
||||||
|
while off < len(data):
|
||||||
|
start = off
|
||||||
|
tag, size = data[off:off + 2]
|
||||||
|
off += 2
|
||||||
|
if size & 128:
|
||||||
|
count = size & 127
|
||||||
|
if not count or count > 4:
|
||||||
|
raise ValueError('invalid DER length')
|
||||||
|
size = int.from_bytes(data[off:off + count], 'big')
|
||||||
|
off += count
|
||||||
|
if off + size > len(data):
|
||||||
|
raise ValueError('truncated DER')
|
||||||
|
result.append((tag, data[off:off + size], data[start:off + size]))
|
||||||
|
off += size
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _cert_key(cert):
|
||||||
|
outer = _der_parts(cert)
|
||||||
|
if len(outer) != 1 or outer[0][0] != 0x30:
|
||||||
|
raise ValueError('invalid certificate')
|
||||||
|
fields = _der_parts(outer[0][1])
|
||||||
|
tbs = _der_parts(fields[0][1])
|
||||||
|
spki = tbs[6 if tbs[0][0] == 0xa0 else 5][2]
|
||||||
|
pub = _der_parts(_der_parts(spki)[0][1])
|
||||||
|
if pub[0][2] != RSA_ALG or pub[1][1][:1] != b'\0':
|
||||||
|
raise ValueError('certificate is not RSA')
|
||||||
|
numbers = _der_parts(_der_parts(pub[1][1][1:])[0][1])
|
||||||
|
n, e = [int.from_bytes(item[1], 'big') for item in numbers]
|
||||||
|
return n, e, spki
|
||||||
|
|
||||||
|
|
||||||
|
def verify(path):
|
||||||
|
"""Verify this v2-only format; return True or raise ValueError on corruption.
|
||||||
|
|
||||||
|
A valid signature establishes integrity, not trust in the APK publisher.
|
||||||
|
"""
|
||||||
|
data = Path(path).read_bytes()
|
||||||
|
try:
|
||||||
|
eo, cd = _eocd(data)
|
||||||
|
if data[cd - 16:cd] != MAGIC:
|
||||||
|
raise ValueError('no APK signing block')
|
||||||
|
size = struct.unpack_from('<Q', data, cd - 24)[0]
|
||||||
|
start = cd - size - 8
|
||||||
|
if start < 0 or struct.unpack_from('<Q', data, start)[0] != size:
|
||||||
|
raise ValueError('invalid signing block size')
|
||||||
|
off, values = start + 8, []
|
||||||
|
while off < cd - 24:
|
||||||
|
length = struct.unpack_from('<Q', data, off)[0]
|
||||||
|
if length < 4 or off + 8 + length > cd - 24:
|
||||||
|
raise ValueError('invalid signing pair')
|
||||||
|
ident = struct.unpack_from('<I', data, off + 8)[0]
|
||||||
|
if ident == V2:
|
||||||
|
values.append(data[off + 12:off + 8 + length])
|
||||||
|
off += 8 + length
|
||||||
|
if off != cd - 24 or len(values) != 1:
|
||||||
|
raise ValueError('missing or duplicate v2 signer block')
|
||||||
|
end = bytearray(data[eo:])
|
||||||
|
struct.pack_into('<I', end, 16, start)
|
||||||
|
digest = content_digest((memoryview(data)[:start], memoryview(data)[cd:eo], end))
|
||||||
|
wrappers = _parts(values[0])
|
||||||
|
if len(wrappers) != 1:
|
||||||
|
raise ValueError('invalid signers sequence')
|
||||||
|
signers = _parts(wrappers[0])
|
||||||
|
if not signers:
|
||||||
|
raise ValueError('no signers')
|
||||||
|
for signer in signers:
|
||||||
|
signed, signatures, pub = _parts(signer)
|
||||||
|
digests, certs, attrs = _parts(signed)
|
||||||
|
cert = _parts(certs)[0]
|
||||||
|
n, e, cert_pub = _cert_key(cert)
|
||||||
|
if cert_pub != pub:
|
||||||
|
raise ValueError('public key differs from certificate')
|
||||||
|
sigs, digs = _parts(signatures), _parts(digests)
|
||||||
|
if len(sigs) != 1 or len(digs) != 1 or sigs[0][:4] != u32(ALG) or digs[0][:4] != u32(ALG):
|
||||||
|
raise ValueError('unsupported signature algorithm')
|
||||||
|
if _parts(digs[0][4:]) != [digest]:
|
||||||
|
raise ValueError('APK content digest mismatch')
|
||||||
|
sig = _parts(sigs[0][4:])
|
||||||
|
if len(sig) != 1:
|
||||||
|
raise ValueError('invalid signature sequence')
|
||||||
|
rsa_verify(signed, sig[0], n, e)
|
||||||
|
return True
|
||||||
|
except (IndexError, struct.error, OverflowError) as exc:
|
||||||
|
raise ValueError('malformed APK signature: ' + str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
|
def repack(src, dst, replace=None, add=None, sign=True):
|
||||||
|
"""Copy raw compressed members; reconstruct ZIP headers without descriptors.
|
||||||
|
|
||||||
|
Reject ZIP64/encrypted archives. Output is atomically replaced after signing.
|
||||||
|
"""
|
||||||
|
replace, add = dict(replace or {}), dict(add or {})
|
||||||
|
central, output = [], io.BytesIO()
|
||||||
|
with open(src, 'rb') as raw, zipfile.ZipFile(raw) as archive:
|
||||||
|
names = archive.namelist()
|
||||||
|
if len(set(names)) != len(names):
|
||||||
|
raise ValueError('duplicate ZIP member names')
|
||||||
|
if set(replace) - set(names) or set(add) & set(names) or set(add) & set(replace):
|
||||||
|
raise ValueError('replace must exist and add must be new')
|
||||||
|
items = archive.infolist() + [zipfile.ZipInfo(name) for name in add]
|
||||||
|
for info in items:
|
||||||
|
name = info.filename
|
||||||
|
if re.match(r'^META-INF/(?:[^/]+\.(?:SF|RSA|EC|DSA)|MANIFEST\.MF)$', name, re.I):
|
||||||
|
continue
|
||||||
|
if info.flag_bits & 1 or info.compress_type not in (0, 8):
|
||||||
|
raise ValueError('unsupported ZIP encryption/compression')
|
||||||
|
method = info.compress_type
|
||||||
|
content = add.get(name) if name in add else replace.get(name)
|
||||||
|
if content is None:
|
||||||
|
raw.seek(info.header_offset)
|
||||||
|
header = raw.read(30)
|
||||||
|
if header[:4] != b'PK\3\4':
|
||||||
|
raise ValueError('invalid local ZIP header')
|
||||||
|
nl, el = struct.unpack_from('<HH', header, 26)
|
||||||
|
raw.seek(nl + el, 1)
|
||||||
|
compressed = raw.read(info.compress_size)
|
||||||
|
crc, usize = info.CRC, info.file_size
|
||||||
|
if len(compressed) != info.compress_size:
|
||||||
|
raise ValueError('truncated ZIP member')
|
||||||
|
else:
|
||||||
|
crc, usize = zlib.crc32(content), len(content)
|
||||||
|
if method == 8:
|
||||||
|
compressor = zlib.compressobj(6, zlib.DEFLATED, -15)
|
||||||
|
compressed = compressor.compress(content) + compressor.flush()
|
||||||
|
else:
|
||||||
|
compressed = content
|
||||||
|
encoded = name.encode('utf-8')
|
||||||
|
offset = output.tell()
|
||||||
|
align = 16384 if name.endswith('.so') else 4
|
||||||
|
needs_alignment = method == 0 or name.endswith('.so')
|
||||||
|
padding = (-(offset + 30 + len(encoded) + 6) % align) if needs_alignment else 0
|
||||||
|
# Android zipalign extra: alignment (uint16), then padding bytes.
|
||||||
|
extra = struct.pack('<HHH', 0xd935, padding + 2, align) + bytes(padding) if needs_alignment else b''
|
||||||
|
dt = info.date_time
|
||||||
|
dos_time = (dt[3] << 11) | (dt[4] << 5) | (dt[5] // 2)
|
||||||
|
dos_date = ((dt[0] - 1980) << 9) | (dt[1] << 5) | dt[2]
|
||||||
|
csize = len(compressed)
|
||||||
|
if max(offset, csize, usize) >= 0xffffffff:
|
||||||
|
raise ValueError('ZIP64 APKs are unsupported')
|
||||||
|
output.write(struct.pack('<IHHHHHIIIHH', 0x04034b50, 20, 0x800, method, dos_time, dos_date,
|
||||||
|
crc, csize, usize, len(encoded), len(extra)) + encoded + extra + compressed)
|
||||||
|
central.append(struct.pack('<IHHHHHHIIIHHHHHII', 0x02014b50, 0x314, 20, 0x800, method,
|
||||||
|
dos_time, dos_date, crc, csize, usize, len(encoded), 0, len(info.comment),
|
||||||
|
0, info.internal_attr, info.external_attr, offset) + encoded + info.comment)
|
||||||
|
cd = output.tell()
|
||||||
|
directory = b''.join(central)
|
||||||
|
if len(central) >= 65535 or cd + len(directory) >= 0xffffffff:
|
||||||
|
raise ValueError('ZIP64 APKs are unsupported')
|
||||||
|
output.write(directory)
|
||||||
|
output.write(struct.pack('<IHHHHIIH', 0x06054b50, 0, 0, len(central), len(central), len(directory), cd, len(archive.comment)) + archive.comment)
|
||||||
|
data = output.getvalue()
|
||||||
|
if sign:
|
||||||
|
data = sign_apk(data, signing_key())
|
||||||
|
dst = Path(dst)
|
||||||
|
fd, tmp = tempfile.mkstemp(prefix='.apk-', dir=str(dst.parent))
|
||||||
|
try:
|
||||||
|
with os.fdopen(fd, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
|
if sign:
|
||||||
|
verify(tmp)
|
||||||
|
os.replace(tmp, dst)
|
||||||
|
finally:
|
||||||
|
if os.path.exists(tmp):
|
||||||
|
os.unlink(tmp)
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
"""Binary-manifest VR inspection and minimal launcher repair (stdlib only)."""
|
||||||
|
import struct
|
||||||
|
import frame_apk
|
||||||
|
|
||||||
|
MAIN = 'android.intent.action.MAIN'
|
||||||
|
LAUNCHER = 'android.intent.category.LAUNCHER'
|
||||||
|
VR = {'com.oculus.intent.category.VR', 'org.khronos.openxr.intent.category.IMMERSIVE_HMD'}
|
||||||
|
|
||||||
|
|
||||||
|
def inspect(data):
|
||||||
|
elements = iter(frame_apk.manifest_elements(data))
|
||||||
|
stack, filters, samsung = [], [], False
|
||||||
|
current, owner, package = None, None, ''
|
||||||
|
for kind, hs, off, size in frame_apk._chunks(data, 8, len(data)):
|
||||||
|
if kind == 0x0102:
|
||||||
|
tag, attrs = next(elements)
|
||||||
|
value = attrs.get('name', (None, None, None))[2]
|
||||||
|
if tag == 'manifest':
|
||||||
|
package = attrs.get('package', (None, None, None))[2] or ''
|
||||||
|
if tag in ('activity', 'activity-alias'):
|
||||||
|
owner = attrs.get('targetActivity', (None, None, None))[2] if tag == 'activity-alias' else value
|
||||||
|
if owner and '.' not in owner: # PackageParser.buildClassName: bare names are relative too
|
||||||
|
owner = '.' + owner
|
||||||
|
owner = package + owner if owner and owner.startswith('.') else owner
|
||||||
|
if tag == 'intent-filter' and stack and stack[-1] in ('activity', 'activity-alias'):
|
||||||
|
current = {'actions': set(), 'categories': set(), 'templates': [], 'alias': stack[-1] == 'activity-alias', 'activity': owner}
|
||||||
|
if current is not None and stack and stack[-1] == 'intent-filter':
|
||||||
|
if tag == 'action':
|
||||||
|
current['actions'].add(value)
|
||||||
|
if tag == 'category':
|
||||||
|
current['categories'].add(value)
|
||||||
|
current['templates'].append((off, size, hs))
|
||||||
|
if tag == 'meta-data' and stack and stack[-1] == 'application':
|
||||||
|
samsung |= value == 'com.samsung.android.vr.application.mode' and attrs.get('value', (0, 0, None))[2] == 'vr_only'
|
||||||
|
stack.append(tag)
|
||||||
|
elif kind == 0x0103 and stack:
|
||||||
|
tag = stack.pop()
|
||||||
|
if tag == 'intent-filter' and current is not None:
|
||||||
|
current['end'] = off
|
||||||
|
filters.append(current)
|
||||||
|
current = None
|
||||||
|
mains = [f for f in filters if MAIN in f['actions']]
|
||||||
|
vr_filters = [f for f in mains if VR & f['categories']]
|
||||||
|
# Lepton's apk-info-extractor ignores <activity-alias>; Godot 4 puts LAUNCHER only there.
|
||||||
|
real = [f for f in mains if not f['alias']]
|
||||||
|
targets = [f for f in real if VR & f['categories']]
|
||||||
|
if not targets and any(LAUNCHER in f['categories'] or VR & f['categories'] for f in mains if f['alias']):
|
||||||
|
aimed = {f['activity'] for f in mains if f['alias'] and (LAUNCHER in f['categories'] or VR & f['categories'])}
|
||||||
|
targets = [f for f in real if f['templates']] # the patch copies an existing <category>
|
||||||
|
targets = [f for f in targets if f['activity'] in aimed] or targets
|
||||||
|
launchable = any(LAUNCHER in f['categories'] for f in real)
|
||||||
|
return {'launchable': launchable, 'repairable': not launchable and bool(targets),
|
||||||
|
'vr_activity': bool(vr_filters), 'vr': bool(vr_filters) or samsung}, targets
|
||||||
|
|
||||||
|
|
||||||
|
def _append_string(chunk, text):
|
||||||
|
_, hs, size, count, styles, flags, start, style_start = struct.unpack_from('<HHIIIIII', chunk)
|
||||||
|
strings_end = style_start or size
|
||||||
|
encoded = text.encode('utf-8' if flags & 0x100 else 'utf-16-le')
|
||||||
|
# LAUNCHER is short enough for both single-unit length encodings.
|
||||||
|
new = (bytes([len(text), len(encoded)]) + encoded + b'\0' if flags & 0x100
|
||||||
|
else struct.pack('<H', len(text)) + encoded + b'\0\0')
|
||||||
|
string_data = chunk[start:strings_end] + new
|
||||||
|
string_data += bytes(-len(string_data) % 4)
|
||||||
|
header = bytearray(chunk[:hs])
|
||||||
|
new_start = start + 4
|
||||||
|
new_styles = new_start + len(string_data) if style_start else 0
|
||||||
|
body = (chunk[hs:hs + count * 4] + struct.pack('<I', strings_end - start)
|
||||||
|
+ chunk[hs + count * 4:start] + string_data + (chunk[style_start:] if style_start else b''))
|
||||||
|
struct.pack_into('<IIIII', header, 8, count + 1, styles, flags & ~1, new_start, new_styles)
|
||||||
|
struct.pack_into('<I', header, 4, len(header) + len(body))
|
||||||
|
return bytes(header) + body, count
|
||||||
|
|
||||||
|
|
||||||
|
def add_launcher_category(axml_bytes):
|
||||||
|
info, filters = inspect(axml_bytes)
|
||||||
|
if info['launchable']:
|
||||||
|
return axml_bytes
|
||||||
|
if not filters:
|
||||||
|
raise frame_apk.ApkError('no activity with a MAIN intent filter that Frame Control can patch')
|
||||||
|
target = filters[0]
|
||||||
|
chunks = list(frame_apk._chunks(axml_bytes, 8, len(axml_bytes)))
|
||||||
|
pool = next(c for c in chunks if c[0] == 1)
|
||||||
|
_, _, po, ps = pool
|
||||||
|
new_pool, index = _append_string(axml_bytes[po:po + ps], LAUNCHER)
|
||||||
|
off, size, hs = target['templates'][0]
|
||||||
|
start = bytearray(axml_bytes[off:off + size])
|
||||||
|
attr_start, attr_size, count = struct.unpack_from('<HHH', start, hs + 8)
|
||||||
|
strings = frame_apk._string_pool(axml_bytes, po)
|
||||||
|
resmap = []
|
||||||
|
for kind, header_size, offset, chunk_size in chunks:
|
||||||
|
if kind == 0x180:
|
||||||
|
resmap = struct.unpack_from('<%dI' % ((chunk_size - header_size) // 4),
|
||||||
|
axml_bytes, offset + header_size)
|
||||||
|
for i in range(count):
|
||||||
|
a = hs + attr_start + i * attr_size
|
||||||
|
name = struct.unpack_from('<I', start, a + 4)[0]
|
||||||
|
if (name < len(resmap) and resmap[name] == 0x01010003) or strings[name] == 'name':
|
||||||
|
struct.pack_into('<I', start, a + 8, index)
|
||||||
|
struct.pack_into('<HBBI', start, a + 12, 8, 0, 3, index)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise frame_apk.ApkError('category has no name attribute')
|
||||||
|
end = struct.pack('<HHI', 0x0103, hs, hs + 8) + start[8:hs] + start[hs:hs + 8]
|
||||||
|
result = bytearray(axml_bytes[:8])
|
||||||
|
for _, _, off, size in chunks:
|
||||||
|
if off == target['end']:
|
||||||
|
result += start + end
|
||||||
|
result += new_pool if off == po else axml_bytes[off:off + size]
|
||||||
|
struct.pack_into('<I', result, 4, len(result))
|
||||||
|
result = bytes(result)
|
||||||
|
if not inspect(result)[0]['launchable']:
|
||||||
|
raise frame_apk.ApkError('launcher repair failed verification')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def detection(data, names):
|
||||||
|
info, _ = inspect(data)
|
||||||
|
issues = []
|
||||||
|
if 'lib/arm64-v8a/libvrapi.so' in names:
|
||||||
|
issues.append("Uses Meta's legacy VrApi, which the Frame doesn't have; it won't run.")
|
||||||
|
if any(n.endswith('/libovrplatformloader.so') for n in names):
|
||||||
|
issues.append("Uses Meta's platform SDK; if it checks your Quest store licence it will quit.")
|
||||||
|
if 'lib/arm64-v8a/libopenxr_loader.so' in names:
|
||||||
|
info['vr'] = True
|
||||||
|
issues.append('Uses OpenXR (good).')
|
||||||
|
info['vr_issues'] = issues
|
||||||
|
return info
|
||||||
@@ -0,0 +1,469 @@
|
|||||||
|
"""Keyboard and pointer for the Steam Frame. Frame Control's server runs this ON the Frame.
|
||||||
|
|
||||||
|
It speaks KDE Connect's LAN protocol (version 7, as in KDE Connect 24.02) to the
|
||||||
|
Frame's own kdeconnectd, as a phone would, and forwards remote-input events read
|
||||||
|
from stdin: one JSON object (or list of them) per line, each a KDE Connect
|
||||||
|
"mousepad" request body such as {"dx": 4, "dy": -2} or {"key": "hello"}.
|
||||||
|
KDE Connect does the typing and clicking.
|
||||||
|
|
||||||
|
KDE Connect isn't installed on the Frame. Frame Control ships Valve's build of it
|
||||||
|
for the Frame and the few libraries the Frame lacks (frame/kdeconnect); the server
|
||||||
|
copies them over the SSH connection and this unpacks them into
|
||||||
|
~/.local/share/frame-control/kdeconnect: no root, no internet, and SteamOS
|
||||||
|
updates leave it alone.
|
||||||
|
|
||||||
|
argv: client id, client name, the folder holding the packages, and a JSON list
|
||||||
|
of [file, sha256] naming them (see frame/kdeconnect/packages.json).
|
||||||
|
|
||||||
|
Status goes to stdout, one JSON object per line:
|
||||||
|
{"state": "installing" | "starting" | "pairing" | "ready" | "error" | "need-packages", ...}.
|
||||||
|
|
||||||
|
Standard library only: this runs on the Frame's own Python.
|
||||||
|
"""
|
||||||
|
import fcntl
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import selectors
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
BASE = Path.home() / ".local/share/frame-control/kdeconnect"
|
||||||
|
ROOT = BASE / "root"
|
||||||
|
BRIDGE = BASE / "bridge"
|
||||||
|
STAMP = ".frame-control-packages" # in ROOT: which packages it was unpacked from
|
||||||
|
PORT = int(os.environ.get("FRAME_INPUT_PORT", "1716"))
|
||||||
|
UID = os.getuid()
|
||||||
|
MOUSEPAD = "kdeconnect.mousepad.request"
|
||||||
|
|
||||||
|
|
||||||
|
def say(state, **more):
|
||||||
|
print(json.dumps({"state": state, **more}), flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def packet(kind, body):
|
||||||
|
return (json.dumps({"id": int(time.time() * 1000), "type": kind, "body": body}) + "\n").encode()
|
||||||
|
|
||||||
|
|
||||||
|
# ---- KDE Connect on the Frame ------------------------------------------------
|
||||||
|
|
||||||
|
SYSTEM_DAEMON = Path("/usr/lib/kdeconnectd")
|
||||||
|
|
||||||
|
|
||||||
|
def stamp(packages):
|
||||||
|
"""What ROOT/STAMP holds once these packages are unpacked (the server checks it too)."""
|
||||||
|
return "".join(f"{sha} {name}\n" for name, sha in packages)
|
||||||
|
|
||||||
|
|
||||||
|
def installed(packages):
|
||||||
|
try:
|
||||||
|
return (ROOT / STAMP).read_text() == stamp(packages)
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def sha256(path):
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
for block in iter(lambda: f.read(1 << 20), b""):
|
||||||
|
digest.update(block)
|
||||||
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def install(folder, packages):
|
||||||
|
"""Unpack the packages the server copied to `folder` into ROOT, checking each one first."""
|
||||||
|
if not packages:
|
||||||
|
raise RuntimeError("This copy of Frame Control doesn't include KDE Connect")
|
||||||
|
say("installing", message="Unpacking KDE Connect on the Frame")
|
||||||
|
stage = BASE / "root.new"
|
||||||
|
shutil.rmtree(stage, ignore_errors=True)
|
||||||
|
stage.mkdir(parents=True)
|
||||||
|
for name, sha in packages:
|
||||||
|
path = Path(folder) / name
|
||||||
|
if not path.is_file():
|
||||||
|
raise RuntimeError(f"{name} didn't reach the Frame")
|
||||||
|
if sha256(path) != sha:
|
||||||
|
raise RuntimeError(f"{name} arrived damaged (its SHA-256 doesn't match)")
|
||||||
|
if subprocess.run(["tar", "--zstd", "-xf", str(path), "-C", str(stage)], capture_output=True).returncode:
|
||||||
|
subprocess.run(["bsdtar", "-xf", str(path), "-C", str(stage)], check=True, capture_output=True)
|
||||||
|
(stage / STAMP).write_text(stamp(packages))
|
||||||
|
stop_daemon() # an older copy may still be running from ROOT
|
||||||
|
shutil.rmtree(ROOT, ignore_errors=True)
|
||||||
|
stage.rename(ROOT)
|
||||||
|
|
||||||
|
|
||||||
|
def app_display():
|
||||||
|
"""The X display that apps (not Steam's own VR menus) are on.
|
||||||
|
|
||||||
|
gamescope runs two Xwayland servers: on 2026-09-28 :0 held Steam's VR bar and
|
||||||
|
menus and ignored XTest pointer motion, while :1 held apps such as Chromium and
|
||||||
|
took it. Inferred to hold in general.
|
||||||
|
"""
|
||||||
|
return ":1" if Path("/tmp/.X11-unix/X1").exists() else ":0"
|
||||||
|
|
||||||
|
|
||||||
|
def daemon_env(daemon):
|
||||||
|
env = dict(os.environ, DBUS_SESSION_BUS_ADDRESS=f"unix:path=/run/user/{UID}/bus",
|
||||||
|
XDG_RUNTIME_DIR=f"/run/user/{UID}", DISPLAY=app_display(), QT_QPA_PLATFORM="xcb")
|
||||||
|
if str(daemon).startswith(str(ROOT)):
|
||||||
|
env.update(LD_LIBRARY_PATH=str(ROOT / "usr/lib"), QT_PLUGIN_PATH=str(ROOT / "usr/lib/qt6/plugins"),
|
||||||
|
QML_IMPORT_PATH=str(ROOT / "usr/lib/qt6/qml"),
|
||||||
|
XDG_DATA_DIRS=f"{ROOT / 'usr/share'}:/usr/share")
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def listening():
|
||||||
|
try:
|
||||||
|
socket.create_connection(("127.0.0.1", PORT), 1).close()
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def our_daemons():
|
||||||
|
"""Process ids of the kdeconnectd that Frame Control installed (never a system one)."""
|
||||||
|
pids = []
|
||||||
|
for proc in Path("/proc").iterdir():
|
||||||
|
if proc.name.isdigit():
|
||||||
|
try:
|
||||||
|
if os.readlink(proc / "exe").startswith(str(ROOT) + "/"):
|
||||||
|
pids.append(int(proc.name))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return pids
|
||||||
|
|
||||||
|
|
||||||
|
def stop_daemon():
|
||||||
|
"""Stop our kdeconnectd and wait until it's gone (so its port is closed too)."""
|
||||||
|
for sig, wait in ((signal.SIGTERM, 30), (signal.SIGKILL, 30)): # tenths of a second
|
||||||
|
for pid in our_daemons():
|
||||||
|
try:
|
||||||
|
os.kill(pid, sig)
|
||||||
|
except ProcessLookupError:
|
||||||
|
pass
|
||||||
|
for _ in range(wait):
|
||||||
|
if not our_daemons() and not listening():
|
||||||
|
return
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
|
class NeedPackages(Exception):
|
||||||
|
"""This build of KDE Connect isn't unpacked and the server didn't send it (it thought it was there)."""
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_daemon(folder, packages):
|
||||||
|
"""Start KDE Connect: the Frame's own if it ever has one, else ours, unpacked first if needed.
|
||||||
|
|
||||||
|
A copy from another Frame Control version that another device is using right
|
||||||
|
now is left running and used as it is (they speak the same protocol); it's
|
||||||
|
replaced the next time nobody is using it.
|
||||||
|
"""
|
||||||
|
system = SYSTEM_DAEMON.exists()
|
||||||
|
if not system and not installed(packages) and not (listening() and our_daemons()):
|
||||||
|
if not folder or not Path(folder).is_dir():
|
||||||
|
raise NeedPackages()
|
||||||
|
install(folder, packages)
|
||||||
|
if listening():
|
||||||
|
return
|
||||||
|
BASE.mkdir(parents=True, exist_ok=True)
|
||||||
|
daemon = SYSTEM_DAEMON if system else ROOT / "usr/lib/kdeconnectd"
|
||||||
|
say("starting", message="Starting KDE Connect on the Frame")
|
||||||
|
log = open(BASE / "kdeconnectd.log", "ab")
|
||||||
|
# Its own session, so it outlives this connection and serves the next one.
|
||||||
|
subprocess.Popen([str(daemon)], env=daemon_env(daemon), cwd=str(Path.home()), stdin=subprocess.DEVNULL,
|
||||||
|
stdout=log, stderr=log, start_new_session=True)
|
||||||
|
for _ in range(40):
|
||||||
|
if listening():
|
||||||
|
return
|
||||||
|
time.sleep(0.25)
|
||||||
|
raise RuntimeError(f"KDE Connect didn't start; see {BASE / 'kdeconnectd.log'} on the Frame")
|
||||||
|
|
||||||
|
|
||||||
|
def qdbus(device, method):
|
||||||
|
"""Call a method on KDE Connect's D-Bus object for our device; its output, or None."""
|
||||||
|
env = dict(os.environ, DBUS_SESSION_BUS_ADDRESS=f"unix:path=/run/user/{UID}/bus")
|
||||||
|
try:
|
||||||
|
r = subprocess.run(["qdbus6", "org.kde.kdeconnect", f"/modules/kdeconnect/devices/{device}",
|
||||||
|
f"org.kde.kdeconnect.device.{method}"], capture_output=True, text=True, env=env, timeout=5)
|
||||||
|
except (OSError, subprocess.TimeoutExpired):
|
||||||
|
return None
|
||||||
|
return r.stdout.strip() if r.returncode == 0 else None
|
||||||
|
|
||||||
|
|
||||||
|
# ---- our identity --------------------------------------------------------------
|
||||||
|
|
||||||
|
def identity(client):
|
||||||
|
"""A device id and certificate for this client, made once and kept (pairing is tied to them).
|
||||||
|
|
||||||
|
Each computer or phone gets its own: KDE Connect keeps one connection per device,
|
||||||
|
so a shared identity would make them knock each other off.
|
||||||
|
"""
|
||||||
|
folder = BRIDGE / client
|
||||||
|
folder.mkdir(parents=True, exist_ok=True)
|
||||||
|
id_file, cert, key = folder / "id", folder / "cert.pem", folder / "key.pem"
|
||||||
|
if not (id_file.exists() and cert.exists() and key.exists()):
|
||||||
|
device = "framecontrol_" + os.urandom(12).hex() # KDE Connect wants 32-38 of [A-Za-z0-9_]
|
||||||
|
subprocess.run(["openssl", "req", "-x509", "-newkey", "ec", "-pkeyopt", "ec_paramgen_curve:prime256v1",
|
||||||
|
"-nodes", "-days", "3650", "-subj", f"/O=KDE/OU=Kde connect/CN={device}",
|
||||||
|
"-keyout", str(key), "-out", str(cert)], check=True, capture_output=True)
|
||||||
|
os.chmod(key, 0o600)
|
||||||
|
id_file.write_text(device)
|
||||||
|
return id_file.read_text().strip(), cert, key
|
||||||
|
|
||||||
|
|
||||||
|
# ---- the link ------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Link:
|
||||||
|
"""One TLS connection to kdeconnectd, as a paired device that sends remote input."""
|
||||||
|
|
||||||
|
def __init__(self, device, cert, key, port=PORT, name="Frame Control"):
|
||||||
|
self.device, self.buf, self.keyboard = device, b"", None
|
||||||
|
raw = socket.create_connection(("127.0.0.1", port), 5)
|
||||||
|
raw.sendall(packet("kdeconnect.identity", {
|
||||||
|
"deviceId": device, "deviceName": name, "deviceType": "phone", "protocolVersion": 7,
|
||||||
|
"incomingCapabilities": [], "outgoingCapabilities": [MOUSEPAD], "tcpPort": port}))
|
||||||
|
# KDE Connect's rule: whoever opened the TCP connection is the TLS server.
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
|
ctx.load_cert_chain(str(cert), str(key))
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE # both sides are on this machine
|
||||||
|
self.sock = ctx.wrap_socket(raw, server_side=True)
|
||||||
|
self.sock.setblocking(False)
|
||||||
|
|
||||||
|
def send(self, body):
|
||||||
|
# Bounded: if KDE Connect stops reading, fail (and be restarted) rather than hang.
|
||||||
|
self.sock.settimeout(5)
|
||||||
|
try:
|
||||||
|
self.sock.sendall(packet(MOUSEPAD, body))
|
||||||
|
finally:
|
||||||
|
self.sock.setblocking(False)
|
||||||
|
|
||||||
|
def pair(self, paired, accept, timeout=15):
|
||||||
|
"""Ask to pair and accept it on KDE Connect's side (we control both ends).
|
||||||
|
|
||||||
|
Only asks when not already paired: a pair request to a device that is
|
||||||
|
already paired makes KDE Connect unpair it.
|
||||||
|
"""
|
||||||
|
if paired():
|
||||||
|
return
|
||||||
|
self.sock.settimeout(5)
|
||||||
|
self.sock.sendall(packet("kdeconnect.pair", {"pair": True}))
|
||||||
|
self.sock.setblocking(False)
|
||||||
|
end = time.time() + timeout
|
||||||
|
while time.time() < end:
|
||||||
|
accept()
|
||||||
|
self.read(0.5)
|
||||||
|
if paired():
|
||||||
|
return
|
||||||
|
raise RuntimeError("KDE Connect didn't accept the pairing")
|
||||||
|
|
||||||
|
def read(self, wait=0.0):
|
||||||
|
"""Packets waiting from kdeconnectd; None once it has closed the connection."""
|
||||||
|
if wait:
|
||||||
|
sel = selectors.DefaultSelector()
|
||||||
|
sel.register(self.sock, selectors.EVENT_READ)
|
||||||
|
sel.select(wait)
|
||||||
|
sel.close()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
chunk = self.sock.recv(65536)
|
||||||
|
if not chunk:
|
||||||
|
return None
|
||||||
|
self.buf += chunk
|
||||||
|
except (ssl.SSLWantReadError, BlockingIOError):
|
||||||
|
pass
|
||||||
|
out = []
|
||||||
|
while b"\n" in self.buf:
|
||||||
|
line, self.buf = self.buf.split(b"\n", 1)
|
||||||
|
if line.strip():
|
||||||
|
p = json.loads(line)
|
||||||
|
if p.get("type") == "kdeconnect.mousepad.keyboardstate":
|
||||||
|
self.keyboard = bool(p.get("body", {}).get("state"))
|
||||||
|
out.append(p)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def events(line):
|
||||||
|
"""The event bodies in one stdin line (an object or a list of objects)."""
|
||||||
|
try:
|
||||||
|
value = json.loads(line)
|
||||||
|
except ValueError:
|
||||||
|
return []
|
||||||
|
return [e for e in (value if isinstance(value, list) else [value]) if isinstance(e, dict) and e]
|
||||||
|
|
||||||
|
|
||||||
|
def connect(device, cert, key, name):
|
||||||
|
link = Link(device, cert, key, name=name)
|
||||||
|
link.pair(lambda: qdbus(device, "isPaired") == "true", lambda: qdbus(device, "acceptPairing"))
|
||||||
|
link.read(0.5) # its hello, including whether it can type
|
||||||
|
return link
|
||||||
|
|
||||||
|
|
||||||
|
def client_args():
|
||||||
|
"""argv: a folder-safe id for the computer or phone, the name KDE Connect shows for it,
|
||||||
|
the folder holding the packages, and their [file, sha256] list."""
|
||||||
|
client = sys.argv[1] if len(sys.argv) > 1 else "default"
|
||||||
|
client = "".join(c for c in client if c.isalnum() or c in "-_")[:64] or "default"
|
||||||
|
name = (sys.argv[2] if len(sys.argv) > 2 else "")[:60].strip()
|
||||||
|
folder = os.path.expanduser(sys.argv[3]) if len(sys.argv) > 3 else ""
|
||||||
|
try:
|
||||||
|
packages = [(str(f), str(h)) for f, h in json.loads(sys.argv[4])] if len(sys.argv) > 4 else []
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
packages = []
|
||||||
|
return client, f"Frame Control ({name})" if name else "Frame Control", folder, packages
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
client, name, folder, packages = client_args()
|
||||||
|
# A dropped ssh (the Frame slept, the app quit) hangs up on us: exit through the
|
||||||
|
# clean-up below rather than dying on the spot.
|
||||||
|
for sig in (signal.SIGHUP, signal.SIGTERM):
|
||||||
|
signal.signal(sig, lambda *_: sys.exit(0))
|
||||||
|
BASE.mkdir(parents=True, exist_ok=True)
|
||||||
|
# Every agent holds this lock shared while it runs. The last one out gets it
|
||||||
|
# exclusively and stops KDE Connect, so it runs, and shows up on the network,
|
||||||
|
# only while something is using the keyboard and trackpad.
|
||||||
|
clients = open(BASE / "clients.lock", "w")
|
||||||
|
fcntl.flock(clients, fcntl.LOCK_SH)
|
||||||
|
try:
|
||||||
|
return run(client, name, folder, packages)
|
||||||
|
finally:
|
||||||
|
# Finish the clean-up even if a second hang-up or TERM arrives meanwhile.
|
||||||
|
for sig in (signal.SIGHUP, signal.SIGTERM):
|
||||||
|
signal.signal(sig, signal.SIG_IGN)
|
||||||
|
fcntl.flock(clients, fcntl.LOCK_UN)
|
||||||
|
try:
|
||||||
|
fcntl.flock(clients, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||||
|
except OSError:
|
||||||
|
pass # another device is still using it
|
||||||
|
else:
|
||||||
|
with daemon_lock():
|
||||||
|
stop_daemon()
|
||||||
|
|
||||||
|
|
||||||
|
def tidy_incoming(folder):
|
||||||
|
"""Remove this start's copy of the packages, and others nobody is using.
|
||||||
|
|
||||||
|
Another copy goes only if no agent holds its .in-use lock and it's over an
|
||||||
|
hour old (so not one a server is still copying, before its agent starts).
|
||||||
|
"""
|
||||||
|
incoming = BASE / "incoming"
|
||||||
|
if folder.startswith(str(incoming) + "/"):
|
||||||
|
shutil.rmtree(folder, ignore_errors=True)
|
||||||
|
try:
|
||||||
|
others = list(incoming.iterdir())
|
||||||
|
except OSError:
|
||||||
|
return
|
||||||
|
for other in others:
|
||||||
|
try:
|
||||||
|
if time.time() - other.stat().st_mtime < 3600:
|
||||||
|
continue
|
||||||
|
with open(other / ".in-use", "a") as lock:
|
||||||
|
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||||
|
shutil.rmtree(other, ignore_errors=True)
|
||||||
|
except OSError:
|
||||||
|
pass # in use, or already gone
|
||||||
|
try:
|
||||||
|
incoming.rmdir()
|
||||||
|
except OSError:
|
||||||
|
pass # another start's copy is still there
|
||||||
|
|
||||||
|
|
||||||
|
def hold_incoming(folder):
|
||||||
|
"""Mark this start's copy as in use (tidy_incoming leaves it alone); the lock lasts as long as the file."""
|
||||||
|
if not folder.startswith(str(BASE / "incoming") + "/"):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
lock = open(Path(folder) / ".in-use", "a")
|
||||||
|
fcntl.flock(lock, fcntl.LOCK_SH)
|
||||||
|
return lock
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class daemon_lock:
|
||||||
|
"""Installing, starting and restarting KDE Connect happen one agent at a time."""
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.file = open(BASE / "daemon.lock", "w")
|
||||||
|
fcntl.flock(self.file, fcntl.LOCK_EX)
|
||||||
|
|
||||||
|
def __exit__(self, *_):
|
||||||
|
self.file.close()
|
||||||
|
|
||||||
|
|
||||||
|
def run(client, name, folder, packages):
|
||||||
|
"""Set up, pair and forward events. This start's copy of the packages stays
|
||||||
|
until it ends, however it ends: restarting KDE Connect may need to unpack it."""
|
||||||
|
held = hold_incoming(folder)
|
||||||
|
try:
|
||||||
|
return serve(client, name, folder, packages)
|
||||||
|
finally:
|
||||||
|
if held:
|
||||||
|
held.close()
|
||||||
|
tidy_incoming(folder)
|
||||||
|
|
||||||
|
|
||||||
|
def serve(client, name, folder, packages):
|
||||||
|
try:
|
||||||
|
with daemon_lock():
|
||||||
|
ensure_daemon(folder, packages)
|
||||||
|
device, cert, key = identity(client)
|
||||||
|
say("pairing")
|
||||||
|
seen = our_daemons()
|
||||||
|
try:
|
||||||
|
link = connect(device, cert, key, name)
|
||||||
|
except (OSError, RuntimeError):
|
||||||
|
if not seen:
|
||||||
|
raise
|
||||||
|
# Ours, but not answering (KDE Connect 24.02 can hang, for one after
|
||||||
|
# unpairing a device that's offline): start it afresh, once. If another
|
||||||
|
# agent already replaced it, just use the new one.
|
||||||
|
say("starting", message="Restarting KDE Connect on the Frame")
|
||||||
|
with daemon_lock():
|
||||||
|
if set(our_daemons()) & set(seen):
|
||||||
|
stop_daemon()
|
||||||
|
ensure_daemon(folder, packages)
|
||||||
|
link = connect(device, cert, key, name)
|
||||||
|
except NeedPackages:
|
||||||
|
say("need-packages") # the server copies them and starts again
|
||||||
|
return 1
|
||||||
|
except (OSError, RuntimeError, subprocess.SubprocessError) as e:
|
||||||
|
say("error", message=str(e))
|
||||||
|
return 1
|
||||||
|
say("ready", keyboard=link.keyboard is not False)
|
||||||
|
stdin, pending = sys.stdin.fileno(), b""
|
||||||
|
sel = selectors.DefaultSelector()
|
||||||
|
sel.register(stdin, selectors.EVENT_READ)
|
||||||
|
sel.register(link.sock, selectors.EVENT_READ)
|
||||||
|
while True:
|
||||||
|
for key_, _ in sel.select(30):
|
||||||
|
if key_.fileobj == stdin:
|
||||||
|
chunk = os.read(stdin, 65536) # raw reads: a buffered readline could strand lines select can't see
|
||||||
|
if not chunk: # the server went away
|
||||||
|
return 0
|
||||||
|
*lines, pending = (pending + chunk).split(b"\n")
|
||||||
|
try:
|
||||||
|
for line in lines:
|
||||||
|
for body in events(line):
|
||||||
|
link.send(body)
|
||||||
|
except OSError as e:
|
||||||
|
say("error", message=f"Lost KDE Connect: {e}")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
packets = link.read()
|
||||||
|
if packets is None:
|
||||||
|
say("error", message="KDE Connect closed the connection")
|
||||||
|
return 1
|
||||||
|
if any(p.get("type") == "kdeconnect.pair" and not p.get("body", {}).get("pair") for p in packets):
|
||||||
|
say("error", message="KDE Connect unpaired Frame Control")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
+298
-3
@@ -265,9 +265,9 @@
|
|||||||
.and-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 22px; align-items: start; }
|
.and-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 22px; align-items: start; }
|
||||||
.and-col { display: grid; gap: 22px; align-content: start; }
|
.and-col { display: grid; gap: 22px; align-content: start; }
|
||||||
.rep-item .s { white-space: normal; }
|
.rep-item .s { white-space: normal; }
|
||||||
#bugDlg, #repDlg, #titleDlg, #wiDlg, #pwDlg, #apkAltDlg { background: #1e2329; color: var(--text); border: 1px solid rgba(255,255,255,.1); border-radius: 4px;
|
#bugDlg, #repDlg, #titleDlg, #wiDlg, #pwDlg, #apkAltDlg, #aboutDlg { background: #1e2329; color: var(--text); border: 1px solid rgba(255,255,255,.1); border-radius: 4px;
|
||||||
padding: 22px; width: min(560px, 92vw); box-shadow: 0 20px 60px rgba(0,0,0,.6); }
|
padding: 22px; width: min(560px, 92vw); box-shadow: 0 20px 60px rgba(0,0,0,.6); }
|
||||||
#bugDlg::backdrop, #repDlg::backdrop, #titleDlg::backdrop, #wiDlg::backdrop, #pwDlg::backdrop, #apkAltDlg::backdrop { background: rgba(0,0,0,.55); }
|
#bugDlg::backdrop, #repDlg::backdrop, #titleDlg::backdrop, #wiDlg::backdrop, #pwDlg::backdrop, #apkAltDlg::backdrop, #aboutDlg::backdrop { background: rgba(0,0,0,.55); }
|
||||||
#bugDlg { width: min(640px, calc(100vw - 40px)); }
|
#bugDlg { width: min(640px, calc(100vw - 40px)); }
|
||||||
#bugForm label.field { display: block; font-size: 12.5px; color: var(--muted); margin-top: 10px; }
|
#bugForm label.field { display: block; font-size: 12.5px; color: var(--muted); margin-top: 10px; }
|
||||||
#bugForm label.field input, #bugForm label.field textarea, #bugForm select { margin-top: 5px; }
|
#bugForm label.field input, #bugForm label.field textarea, #bugForm select { margin-top: 5px; }
|
||||||
@@ -276,7 +276,10 @@
|
|||||||
#bugForm .popt { margin: 14px 0 0; }
|
#bugForm .popt { margin: 14px 0 0; }
|
||||||
#bugForm .sentlog { max-height: 200px; }
|
#bugForm .sentlog { max-height: 200px; }
|
||||||
#bugWarn { color: var(--muted); font-size: 12.5px; line-height: 1.45; margin: 12px 0 0; }
|
#bugWarn { color: var(--muted); font-size: 12.5px; line-height: 1.45; margin: 12px 0 0; }
|
||||||
#bugDlg h2, #repDlg h2, #titleDlg h2, #wiDlg h2, #pwDlg h2, #apkAltDlg h2 { margin: 0 0 14px; font-size: 15px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--bright); }
|
#bugDlg h2, #repDlg h2, #titleDlg h2, #wiDlg h2, #pwDlg h2, #apkAltDlg h2, #aboutDlg h2 { margin: 0 0 14px; font-size: 15px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--bright); }
|
||||||
|
.about-text { max-height: 55vh; overflow: auto; }
|
||||||
|
.about-text pre { white-space: pre-wrap; font-size: 12px; color: var(--muted); }
|
||||||
|
.about-text summary { cursor: pointer; margin: 8px 0; }
|
||||||
#repForm label, #titleForm label { display: block; font-size: 12.5px; color: var(--muted); margin-top: 10px; }
|
#repForm label, #titleForm label { display: block; font-size: 12.5px; color: var(--muted); margin-top: 10px; }
|
||||||
#repForm label input[type=text], #repForm textarea, #titleForm label input, #titleForm label select { margin-top: 5px; }
|
#repForm label input[type=text], #repForm textarea, #titleForm label input, #titleForm label select { margin-top: 5px; }
|
||||||
#titleForm select { width: 100%; background: rgba(0,0,0,.28); color: var(--text); border: 1px solid transparent;
|
#titleForm select { width: 100%; background: rgba(0,0,0,.28); color: var(--text); border: 1px solid transparent;
|
||||||
@@ -348,6 +351,19 @@
|
|||||||
border-radius: 3px; height: 32px; padding: 0 8px; font: inherit; font-size: 13px; }
|
border-radius: 3px; height: 32px; padding: 0 8px; font: inherit; font-size: 13px; }
|
||||||
/* Touch screens can't hover: keep the library's name and Play button showing. */
|
/* Touch screens can't hover: keep the library's name and Play button showing. */
|
||||||
@media (hover: none) { .capsule .over { opacity: 1; } .capsule:hover { transform: none; } }
|
@media (hover: none) { .capsule .over { opacity: 1; } .capsule:hover { transform: none; } }
|
||||||
|
/* ---- keyboard and trackpad ---- */
|
||||||
|
.pad-area { position: relative; height: 190px; margin: 12px 0; border-radius: 4px; background: rgba(0,0,0,.28);
|
||||||
|
border: 1px dashed rgba(255,255,255,.14); display: grid; place-items: center; cursor: pointer;
|
||||||
|
touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; outline: none; }
|
||||||
|
.pad-area:focus-visible, .pad-area.captured { border: 1px solid var(--blue); box-shadow: 0 0 0 1px var(--blue) inset; }
|
||||||
|
.pad-area.off { cursor: default; opacity: .55; }
|
||||||
|
.pad-hint { max-width: 420px; padding: 0 16px; text-align: center; color: var(--muted); font-size: 13px; pointer-events: none; }
|
||||||
|
.pad-keys { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
||||||
|
.pad-keys input { flex: 1 1 220px; min-width: 0; height: 32px; padding: 0 10px; border-radius: 3px; border: 1px solid transparent;
|
||||||
|
background: rgba(0,0,0,.28); color: var(--text); font: inherit; }
|
||||||
|
.pad-keys input:focus { outline: none; border-color: var(--blue); }
|
||||||
|
#pad .hint { margin-top: 10px; }
|
||||||
|
|
||||||
/* ---- phones, upright or on their side: tabs move to a bottom bar, as in iOS apps. Every edge keeps clear of
|
/* ---- phones, upright or on their side: tabs move to a bottom bar, as in iOS apps. Every edge keeps clear of
|
||||||
the safe area (notch or Dynamic Island, rounded corners, home indicator); env() is zero on desktops. ---- */
|
the safe area (notch or Dynamic Island, rounded corners, home indicator); env() is zero on desktops. ---- */
|
||||||
@media (max-width: 640px), (max-height: 500px) and (hover: none) {
|
@media (max-width: 640px), (max-height: 500px) and (hover: none) {
|
||||||
@@ -384,6 +400,10 @@
|
|||||||
button.small { height: 32px; }
|
button.small { height: 32px; }
|
||||||
.actions button { height: 46px; }
|
.actions button { height: 46px; }
|
||||||
.cat-tools select { max-width: none; flex: 1 1 100%; }
|
.cat-tools select { max-width: none; flex: 1 1 100%; }
|
||||||
|
.pad-area { height: 240px; }
|
||||||
|
.pad-keys .seg { flex: 1 1 100%; display: grid; grid-template-columns: repeat(4, 1fr); }
|
||||||
|
.pad-keys .pad-clicks { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.pad-keys .seg button { padding: 0 4px; justify-content: center; text-align: center; }
|
||||||
/* The on-screen keyboard covers the bottom of the screen; the tab bar would ride on top of it. */
|
/* The on-screen keyboard covers the bottom of the screen; the tab bar would ride on top of it. */
|
||||||
body.typing nav { display: none; }
|
body.typing nav { display: none; }
|
||||||
}
|
}
|
||||||
@@ -516,6 +536,37 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section class="panel" id="pad">
|
||||||
|
<div class="shelf-head"><h2>Keyboard and trackpad</h2><span class="sub" id="padState"></span><span class="spacer"></span>
|
||||||
|
<button class="small" id="padOn">Turn on</button>
|
||||||
|
</div>
|
||||||
|
<div class="pad-area" id="padArea" tabindex="0" role="application" aria-label="Trackpad for the Frame">
|
||||||
|
<div class="pad-hint" id="padHint"></div>
|
||||||
|
</div>
|
||||||
|
<div class="pad-keys" aria-label="Keys and clicks">
|
||||||
|
<input id="padType" type="text" placeholder="Type here to type on the Frame" autocomplete="off" autocapitalize="off"
|
||||||
|
autocorrect="off" spellcheck="false" enterkeyhint="send">
|
||||||
|
<span class="seg pad-clicks">
|
||||||
|
<button data-pad-click="singleclick" title="Left click">Click</button>
|
||||||
|
<button data-pad-click="rightclick" title="Right click">Right-click</button>
|
||||||
|
</span>
|
||||||
|
<span class="seg pad-special">
|
||||||
|
<button data-pad-key="14" title="Escape">Esc</button>
|
||||||
|
<button data-pad-key="2" title="Tab">Tab</button>
|
||||||
|
<button data-pad-key="4" title="Left arrow" aria-label="Left arrow">←</button>
|
||||||
|
<button data-pad-key="5" title="Up arrow" aria-label="Up arrow">↑</button>
|
||||||
|
<button data-pad-key="7" title="Down arrow" aria-label="Down arrow">↓</button>
|
||||||
|
<button data-pad-key="6" title="Right arrow" aria-label="Right arrow">→</button>
|
||||||
|
<button data-pad-key="1" title="Backspace" aria-label="Backspace">⌫</button>
|
||||||
|
<button data-pad-key="12" title="Enter">Enter</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="hint">Types and points in apps on the Frame, such as Chromium or the desktop's Linux apps; Steam's own VR menus
|
||||||
|
don't take it. It works through KDE Connect, which comes with Frame Control: the first time, it copies Valve's build
|
||||||
|
for the Frame (3.6 MB, 18 MB unpacked) into your home folder there and pairs with it. No internet needed, and nothing
|
||||||
|
else to install. <a href="#" data-about>Licences</a></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="shots">
|
<section id="shots">
|
||||||
<div class="shelf-head"><h2>Screenshots</h2><span class="count" id="shotCount"></span><span class="spacer"></span>
|
<div class="shelf-head"><h2>Screenshots</h2><span class="count" id="shotCount"></span><span class="spacer"></span>
|
||||||
<button class="small" id="shotsRefresh">Refresh</button>
|
<button class="small" id="shotsRefresh">Refresh</button>
|
||||||
@@ -647,6 +698,12 @@
|
|||||||
<span class="desk-only">You can also drop files anywhere in this window.</span>
|
<span class="desk-only">You can also drop files anywhere in this window.</span>
|
||||||
<input type="file" id="fileInput" multiple hidden>
|
<input type="file" id="fileInput" multiple hidden>
|
||||||
</div>
|
</div>
|
||||||
|
<label class="sub" for="apkDisplay">Android display</label>
|
||||||
|
<select id="apkDisplay" aria-label="Android app display mode">
|
||||||
|
<option value="auto">Automatic (detect VR)</option>
|
||||||
|
<option value="flat">Flat screen</option>
|
||||||
|
<option value="vr">VR app</option>
|
||||||
|
</select>
|
||||||
<div class="progress" id="prog"><i></i></div>
|
<div class="progress" id="prog"><i></i></div>
|
||||||
<div class="shelf-head" style="margin-top:18px"><h2>Clipboard</h2></div>
|
<div class="shelf-head" style="margin-top:18px"><h2>Clipboard</h2></div>
|
||||||
<textarea id="clipText" placeholder="Text to put on the Frame's clipboard…"></textarea>
|
<textarea id="clipText" placeholder="Text to put on the Frame's clipboard…"></textarea>
|
||||||
@@ -682,6 +739,7 @@
|
|||||||
<div class="hint desk-only">Sleep, Restart and Shut down open a terminal window for the Developer Mode password.</div>
|
<div class="hint desk-only">Sleep, Restart and Shut down open a terminal window for the Developer Mode password.</div>
|
||||||
<div class="hint mobile-only">Sleep, Restart and Shut down ask for the Developer Mode password. SSH, SFTP, Steam Link and remote desktop open in the app that handles them.</div>
|
<div class="hint mobile-only">Sleep, Restart and Shut down ask for the Developer Mode password. SSH, SFTP, Steam Link and remote desktop open in the app that handles them.</div>
|
||||||
<div class="row mobile-only" style="margin-top:14px"><button class="small" id="changeHeadset">Change headset…</button></div>
|
<div class="row mobile-only" style="margin-top:14px"><button class="small" id="changeHeadset">Change headset…</button></div>
|
||||||
|
<div class="row" style="margin-top:14px"><button class="small" data-about>About and licences</button></div>
|
||||||
<div class="links desk-only">
|
<div class="links desk-only">
|
||||||
<div><a href="https://store.steampowered.com/remoteplay" target="_blank">Steam Link</a>: Valve's remote view of the headset</div>
|
<div><a href="https://store.steampowered.com/remoteplay" target="_blank">Steam Link</a>: Valve's remote view of the headset</div>
|
||||||
<div><a href="https://streamframe.app/" target="_blank">Stream Frame</a>: third-party recorder (macOS 14+)</div>
|
<div><a href="https://streamframe.app/" target="_blank">Stream Frame</a>: third-party recorder (macOS 14+)</div>
|
||||||
@@ -751,6 +809,14 @@
|
|||||||
<div class="actions"><button id="apkAltClose">Close</button></div>
|
<div class="actions"><button id="apkAltClose">Close</button></div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="aboutDlg" aria-labelledby="aboutTitle">
|
||||||
|
<h2 id="aboutTitle">About and licences</h2>
|
||||||
|
<p>Frame Control is MIT-licensed. It ships other people's software, each under its own licence: the notices and
|
||||||
|
licence texts follow, with where to get the source.</p>
|
||||||
|
<div id="aboutText" class="about-text">Loading…</div>
|
||||||
|
<div class="actions"><button id="aboutClose">Close</button></div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<dialog id="repDlg" aria-labelledby="repTitle">
|
<dialog id="repDlg" aria-labelledby="repTitle">
|
||||||
<form method="dialog" id="repForm">
|
<form method="dialog" id="repForm">
|
||||||
<h2 id="repTitle">Report an APK</h2>
|
<h2 id="repTitle">Report an APK</h2>
|
||||||
@@ -1522,6 +1588,7 @@ function upload(file, mode) {
|
|||||||
xhr.setRequestHeader("X-Frame-UI", UI_KEY);
|
xhr.setRequestHeader("X-Frame-UI", UI_KEY);
|
||||||
xhr.setRequestHeader("X-Filename", encodeURIComponent(file.name));
|
xhr.setRequestHeader("X-Filename", encodeURIComponent(file.name));
|
||||||
xhr.setRequestHeader("X-Mode", mode);
|
xhr.setRequestHeader("X-Mode", mode);
|
||||||
|
if (mode === "apk") xhr.setRequestHeader("X-APK-Display", $("apkDisplay").value);
|
||||||
const bar = $("prog").firstElementChild;
|
const bar = $("prog").firstElementChild;
|
||||||
$("prog").style.display = "block"; bar.style.width = "0";
|
$("prog").style.display = "block"; bar.style.width = "0";
|
||||||
xhr.upload.onprogress = e => { if (e.lengthComputable) bar.style.width = (100 * e.loaded / e.total) + "%"; };
|
xhr.upload.onprogress = e => { if (e.lengthComputable) bar.style.width = (100 * e.loaded / e.total) + "%"; };
|
||||||
@@ -1579,6 +1646,21 @@ function showApkAlternatives(reason, result) {
|
|||||||
}
|
}
|
||||||
$("apkAltClose").onclick = () => $("apkAltDlg").close();
|
$("apkAltClose").onclick = () => $("apkAltDlg").close();
|
||||||
|
|
||||||
|
// ---- about and licences ----
|
||||||
|
document.addEventListener("click", async e => {
|
||||||
|
if (!e.target.closest("[data-about]")) return;
|
||||||
|
e.preventDefault();
|
||||||
|
$("aboutDlg").showModal();
|
||||||
|
try {
|
||||||
|
const { notices } = await api("/api/licenses");
|
||||||
|
$("aboutText").innerHTML = notices.map((n, i) =>
|
||||||
|
`<details${i ? "" : " open"}><summary>${esc(n.title)}</summary><pre>${esc(n.text)}</pre></details>`).join("");
|
||||||
|
} catch (err) {
|
||||||
|
$("aboutText").textContent = `Couldn't load the licences: ${err.message}. They're also in THIRD_PARTY_NOTICES.md and frame/kdeconnect in Frame Control's source.`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("aboutClose").onclick = () => $("aboutDlg").close();
|
||||||
|
|
||||||
const TITLE_EXT = /\.(zip|exe)$/i;
|
const TITLE_EXT = /\.(zip|exe)$/i;
|
||||||
async function sendFiles(files, dirs = new Set()) {
|
async function sendFiles(files, dirs = new Set()) {
|
||||||
for (const [i, f] of files.entries()) {
|
for (const [i, f] of files.entries()) {
|
||||||
@@ -2376,6 +2458,219 @@ window.addEventListener("hashchange", showPage);
|
|||||||
const typesText = el => el?.matches?.('textarea, input:not([type=range], [type=checkbox], [type=radio], [type=file], [type=button])');
|
const typesText = el => el?.matches?.('textarea, input:not([type=range], [type=checkbox], [type=radio], [type=file], [type=button])');
|
||||||
document.addEventListener("focusin", e => document.body.classList.toggle("typing", !!typesText(e.target)));
|
document.addEventListener("focusin", e => document.body.classList.toggle("typing", !!typesText(e.target)));
|
||||||
document.addEventListener("focusout", () => document.body.classList.remove("typing"));
|
document.addEventListener("focusout", () => document.body.classList.remove("typing"));
|
||||||
|
|
||||||
|
// ---- keyboard and trackpad: events go to KDE Connect on the Frame (see ui/frame_input_agent.py) ----
|
||||||
|
const pad = { state: "off", queue: [], sending: false, poll: null, captured: false };
|
||||||
|
const PAD_STATES = {
|
||||||
|
off: "", starting: "Connecting…", installing: "Setting up KDE Connect on the Frame (first time only)…",
|
||||||
|
pairing: "Pairing with KDE Connect…", ready: "On",
|
||||||
|
};
|
||||||
|
// KDE Connect's specialKey numbers (plugins/mousepad in its source).
|
||||||
|
const PAD_SPECIAL = { Backspace: 1, Tab: 2, ArrowLeft: 4, ArrowUp: 5, ArrowRight: 6, ArrowDown: 7, PageUp: 8,
|
||||||
|
PageDown: 9, Home: 10, End: 11, Enter: 12, Delete: 13, Escape: 14,
|
||||||
|
...Object.fromEntries(Array.from({ length: 12 }, (_, i) => [`F${i + 1}`, 21 + i])) };
|
||||||
|
const IS_MAC = /^Mac/.test(navigator.platform);
|
||||||
|
const TOUCH = matchMedia("(hover: none)").matches;
|
||||||
|
|
||||||
|
function padShow(status) {
|
||||||
|
pad.state = status.state || "off";
|
||||||
|
const ready = pad.state === "ready";
|
||||||
|
const busy = !ready && pad.state !== "off" && pad.state !== "error";
|
||||||
|
$("padState").textContent = pad.state === "error" ? "" : PAD_STATES[pad.state] || "";
|
||||||
|
$("padOn").hidden = ready || busy;
|
||||||
|
$("padOn").textContent = pad.state === "error" ? "Try again" : "Turn on";
|
||||||
|
$("padArea").classList.toggle("off", !ready);
|
||||||
|
$("padHint").textContent = pad.state === "error" ? status.message || "Couldn't reach KDE Connect on the Frame."
|
||||||
|
: !ready ? (busy ? PAD_STATES[pad.state] : "Turn on to use this as a trackpad for the Frame.")
|
||||||
|
: TOUCH ? "Drag to move the pointer · tap to click · two fingers to scroll · two-finger tap to right-click"
|
||||||
|
: pad.captured ? "Your mouse and keyboard now control the Frame. Press Esc to stop."
|
||||||
|
: "Click here to use your mouse and keyboard on the Frame (Esc to stop), or drag like a trackpad.";
|
||||||
|
clearTimeout(pad.poll);
|
||||||
|
if (ready && pad.queue.length) padFlush();
|
||||||
|
if (busy) pad.poll = setTimeout(async () => padShow(await api("/api/input").catch(e => ({ state: "error", message: e.message }))), 1000);
|
||||||
|
}
|
||||||
|
async function padStart() {
|
||||||
|
localStorage.padUsed = "1"; // from now on it turns on with the page
|
||||||
|
padShow({ state: "starting" });
|
||||||
|
padShow(await api("/api/input?start=1").catch(e => ({ state: "error", message: e.message })));
|
||||||
|
}
|
||||||
|
$("padOn").onclick = padStart;
|
||||||
|
|
||||||
|
// Queue events and send them one request at a time, merging pointer moves made meanwhile.
|
||||||
|
const isMove = e => Object.keys(e).every(k => k === "dx" || k === "dy");
|
||||||
|
function padSend(event) {
|
||||||
|
// While it reconnects, keys and clicks wait in the queue; pointer moves would be stale.
|
||||||
|
// A button release is always kept, so a drag never leaves the button held on the Frame.
|
||||||
|
const room = pad.queue.length < 200 || event.singlerelease;
|
||||||
|
const reconnecting = ["starting", "pairing", "installing"].includes(pad.state) && room;
|
||||||
|
// Even after an error: a button pressed on the Frame must be let go once it's back.
|
||||||
|
const release = event.singlerelease && pad.state !== "off";
|
||||||
|
if (pad.state !== "ready" && !(reconnecting && !isMove(event)) && !release) return;
|
||||||
|
const last = pad.queue[pad.queue.length - 1];
|
||||||
|
if (last && isMove(last) && isMove(event)) { last.dx = (last.dx || 0) + (event.dx || 0); last.dy = (last.dy || 0) + (event.dy || 0); }
|
||||||
|
else pad.queue.push(event);
|
||||||
|
padFlush();
|
||||||
|
}
|
||||||
|
// Events that didn't reach the Frame (the link had dropped, and is reconnecting): keep
|
||||||
|
// the keys and clicks for when it's back, but not pointer moves, which would be stale.
|
||||||
|
// Button releases are kept whatever the queue's length.
|
||||||
|
function padKeep(events) {
|
||||||
|
pad.queue = [...events, ...pad.queue].filter(e => !isMove(e)).filter((e, i) => i < 200 || e.singlerelease);
|
||||||
|
}
|
||||||
|
async function padFlush() {
|
||||||
|
if (pad.sending || !pad.queue.length || pad.state !== "ready") return;
|
||||||
|
pad.sending = true;
|
||||||
|
const events = pad.queue.splice(0, 200);
|
||||||
|
try {
|
||||||
|
const status = await api("/api/input", { events });
|
||||||
|
if (!status.sent) padKeep(events);
|
||||||
|
if (status.state !== "ready") padShow(status);
|
||||||
|
} catch (e) {
|
||||||
|
padKeep(events);
|
||||||
|
padShow({ state: "error", message: e.message });
|
||||||
|
} finally {
|
||||||
|
pad.sending = false;
|
||||||
|
if (pad.queue.length) padFlush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function padKey(e) {
|
||||||
|
const mods = { ...(e.ctrlKey || (IS_MAC && e.metaKey) ? { ctrl: true } : {}), ...(e.altKey ? { alt: true } : {}),
|
||||||
|
...(!IS_MAC && e.metaKey ? { super: true } : {}) };
|
||||||
|
if (PAD_SPECIAL[e.key]) return { specialKey: PAD_SPECIAL[e.key], ...mods, ...(e.shiftKey ? { shift: true } : {}) };
|
||||||
|
if (e.key.length === 1) return { key: e.key, ...mods }; // already shifted, so no shift flag
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touch: one finger moves, a quick tap clicks; two fingers scroll, or right-click when tapped.
|
||||||
|
const PAD_SPEED = 1.6, PAD_SCROLL_STEP = 18;
|
||||||
|
const touches = new Map();
|
||||||
|
let gesture = null;
|
||||||
|
$("padArea").addEventListener("pointerdown", e => {
|
||||||
|
if (pad.state === "off" || pad.state === "error") { padStart(); return; }
|
||||||
|
if (pad.state !== "ready") return;
|
||||||
|
if (e.pointerType === "mouse") {
|
||||||
|
if (pad.captured) return; // mousedown and mouseup below send the clicks
|
||||||
|
// Lock the pointer to the pad and pass the whole mouse and keyboard through; where
|
||||||
|
// that isn't allowed, the mouse drags like a finger on a trackpad instead.
|
||||||
|
if (e.button === 0 && !pad.noLock && $("padArea").requestPointerLock) { $("padArea").requestPointerLock(); return; }
|
||||||
|
// Only the left button drags and taps like a finger; the others are clicks of their own.
|
||||||
|
if (e.button === 1 || e.button === 2) { padSend(e.button === 2 ? { rightclick: true } : { middleclick: true }); return; }
|
||||||
|
if (e.button !== 0) return;
|
||||||
|
}
|
||||||
|
try { $("padArea").setPointerCapture(e.pointerId); } catch {} // keeps the drag if the finger leaves the pad
|
||||||
|
touches.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
||||||
|
if (touches.size === 1) gesture = { start: performance.now(), moved: 0, fingers: 1, scroll: 0 };
|
||||||
|
else if (gesture) gesture.fingers = Math.max(gesture.fingers, touches.size);
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
$("padArea").addEventListener("pointermove", e => {
|
||||||
|
if (pad.captured) { padSend({ dx: e.movementX, dy: e.movementY }); return; }
|
||||||
|
const t = touches.get(e.pointerId);
|
||||||
|
if (!t || !gesture) return;
|
||||||
|
const dx = e.clientX - t.x, dy = e.clientY - t.y;
|
||||||
|
t.x = e.clientX; t.y = e.clientY;
|
||||||
|
gesture.moved += Math.abs(dx) + Math.abs(dy);
|
||||||
|
if (touches.size >= 2) {
|
||||||
|
// Scroll with the lead finger only, a notch per step, like a laptop trackpad (content follows the fingers).
|
||||||
|
if (e.pointerId !== touches.keys().next().value) return;
|
||||||
|
gesture.scroll += dy;
|
||||||
|
while (Math.abs(gesture.scroll) >= PAD_SCROLL_STEP) {
|
||||||
|
const up = gesture.scroll > 0;
|
||||||
|
padSend({ scroll: true, dy: up ? 1 : -1 });
|
||||||
|
gesture.scroll -= up ? PAD_SCROLL_STEP : -PAD_SCROLL_STEP;
|
||||||
|
}
|
||||||
|
} else if (gesture.fingers === 1) {
|
||||||
|
padSend({ dx: dx * PAD_SPEED, dy: dy * PAD_SPEED });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function padLift(e) {
|
||||||
|
if (!touches.delete(e.pointerId) || touches.size || !gesture) return;
|
||||||
|
const quick = performance.now() - gesture.start < 250 && gesture.moved < 10;
|
||||||
|
if (quick) padSend(gesture.fingers >= 2 ? { rightclick: true } : { singleclick: true });
|
||||||
|
gesture = null;
|
||||||
|
}
|
||||||
|
$("padArea").addEventListener("pointerup", padLift);
|
||||||
|
$("padArea").addEventListener("pointercancel", e => { touches.delete(e.pointerId); if (!touches.size) gesture = null; });
|
||||||
|
|
||||||
|
// A computer's mouse and keyboard: captured while the pointer is locked to the pad.
|
||||||
|
document.addEventListener("pointerlockerror", () => { pad.noLock = true; });
|
||||||
|
document.addEventListener("pointerlockchange", () => {
|
||||||
|
pad.captured = document.pointerLockElement === $("padArea");
|
||||||
|
window.frameApp?.captureKeys?.(pad.captured); // the desktop app's menu shortcuts go to the Frame too
|
||||||
|
// Esc or switching windows mid-drag: let go of the button on the Frame as well.
|
||||||
|
if (!pad.captured && pad.held) { padSend({ singlerelease: true }); pad.held = false; }
|
||||||
|
$("padArea").classList.toggle("captured", pad.captured);
|
||||||
|
padShow({ state: pad.state });
|
||||||
|
});
|
||||||
|
$("padArea").addEventListener("mousedown", e => {
|
||||||
|
if (!pad.captured) return;
|
||||||
|
if (e.button === 0) { padSend({ singlehold: true }); pad.held = true; }
|
||||||
|
else if (e.button === 1) padSend({ middleclick: true });
|
||||||
|
else if (e.button === 2) padSend({ rightclick: true });
|
||||||
|
});
|
||||||
|
$("padArea").addEventListener("mouseup", e => { if (pad.held && e.button === 0) { padSend({ singlerelease: true }); pad.held = false; } });
|
||||||
|
$("padArea").addEventListener("contextmenu", e => e.preventDefault());
|
||||||
|
$("padArea").addEventListener("wheel", e => {
|
||||||
|
if (pad.state !== "ready") return;
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.deltaY) padSend({ scroll: true, dy: e.deltaY > 0 ? -1 : 1 });
|
||||||
|
}, { passive: false });
|
||||||
|
// Capture phase, so the page's own shortcuts (tabs 1-4, R, zoom) don't also fire.
|
||||||
|
window.addEventListener("keydown", e => {
|
||||||
|
if (!pad.captured) return;
|
||||||
|
e.preventDefault(); e.stopImmediatePropagation();
|
||||||
|
const event = padKey(e);
|
||||||
|
if (event) padSend(event);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
// The text field: each keystroke goes to the Frame. While focused it holds one space,
|
||||||
|
// so Backspace always has something to delete; empty otherwise, to show its prompt.
|
||||||
|
const padType = $("padType");
|
||||||
|
padType.addEventListener("beforeinput", e => {
|
||||||
|
if (e.inputType === "insertCompositionText") return; // IME composing; sent on compositionend
|
||||||
|
e.preventDefault();
|
||||||
|
if (pad.state === "off" || pad.state === "error") padStart(); // what's typed meanwhile waits in the queue
|
||||||
|
const text = e.data ?? e.dataTransfer?.getData("text/plain") ?? "";
|
||||||
|
if (/^insert(Text|ReplacementText|FromPaste|FromDrop)$/.test(e.inputType) && text) padText(text);
|
||||||
|
else if (e.inputType === "deleteContentBackward") padSend({ specialKey: 1 });
|
||||||
|
else if (e.inputType === "deleteWordBackward") padSend({ specialKey: 1, ctrl: true }); // Ctrl+Backspace: a word, on Linux
|
||||||
|
else if (e.inputType === "deleteContentForward") padSend({ specialKey: 13 });
|
||||||
|
else if (e.inputType === "deleteWordForward") padSend({ specialKey: 13, ctrl: true });
|
||||||
|
else if (e.inputType === "insertLineBreak" || e.inputType === "insertParagraph") padSend({ specialKey: 12 });
|
||||||
|
});
|
||||||
|
padType.addEventListener("compositionend", e => { if (e.data) padText(e.data); padType.value = " "; padCaret(); });
|
||||||
|
// Text in pieces the server takes (500 characters), with Enter for each line break.
|
||||||
|
function padText(text) {
|
||||||
|
text.replace(/\r\n?/g, "\n").split("\n").forEach((line, i) => {
|
||||||
|
if (i) padSend({ specialKey: 12 });
|
||||||
|
const chars = Array.from(line); // whole characters, so an emoji isn't cut in two
|
||||||
|
for (let at = 0; at < chars.length; at += 500) padSend({ key: chars.slice(at, at + 500).join("") });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
padType.addEventListener("keydown", e => {
|
||||||
|
const special = PAD_SPECIAL[e.key];
|
||||||
|
// Text and deletions arrive as beforeinput; this takes the keys that don't edit the field.
|
||||||
|
if (!special || e.key === "Backspace" || e.key === "Delete" || e.isComposing) return;
|
||||||
|
e.preventDefault();
|
||||||
|
padSend(padKey(e));
|
||||||
|
});
|
||||||
|
// The caret stays after the space, so Backspace always has something to delete.
|
||||||
|
const padCaret = () => requestAnimationFrame(() => padType.setSelectionRange(1, 1));
|
||||||
|
padType.addEventListener("focus", () => { padType.value = " "; padCaret(); if (pad.state === "off") padStart(); });
|
||||||
|
padType.addEventListener("blur", () => { padType.value = ""; });
|
||||||
|
padType.addEventListener("input", e => {
|
||||||
|
if (e.isComposing) return; // an input method is still composing; compositionend sends it
|
||||||
|
if (padType.value !== " ") padType.value = " ";
|
||||||
|
padCaret();
|
||||||
|
});
|
||||||
|
padType.addEventListener("click", padCaret);
|
||||||
|
document.querySelector("#pad .pad-keys").addEventListener("click", e => {
|
||||||
|
const b = e.target.closest("button");
|
||||||
|
if (!b) return;
|
||||||
|
if (b.dataset.padClick) padSend({ [b.dataset.padClick]: true });
|
||||||
|
else if (b.dataset.padKey) padSend({ specialKey: +b.dataset.padKey });
|
||||||
|
});
|
||||||
|
api("/api/input").then(s => s.state === "off" && localStorage.padUsed ? padStart() : padShow(s), () => padShow({ state: "off" }));
|
||||||
showPage();
|
showPage();
|
||||||
document.addEventListener("keydown", e => {
|
document.addEventListener("keydown", e => {
|
||||||
if (e.metaKey || e.ctrlKey || e.altKey || /INPUT|TEXTAREA|SELECT/.test(document.activeElement.tagName) || document.querySelector("dialog[open]")) return;
|
if (e.metaKey || e.ctrlKey || e.altKey || /INPUT|TEXTAREA|SELECT/.test(document.activeElement.tagName) || document.querySelector("dialog[open]")) return;
|
||||||
|
|||||||
+335
-2
@@ -10,9 +10,11 @@ Env: FRAME_ALIAS (default frame)
|
|||||||
FRAME_LOCAL=1 run on the Frame itself (the iPhone app starts it there over SSH)
|
FRAME_LOCAL=1 run on the Frame itself (the iPhone app starts it there over SSH)
|
||||||
FRAME_UI_KEY required X-Frame-UI value (the iPhone app passes a fresh one)
|
FRAME_UI_KEY required X-Frame-UI value (the iPhone app passes a fresh one)
|
||||||
FRAME_DEVICE what to call the device the page runs on (e.g. iPhone)
|
FRAME_DEVICE what to call the device the page runs on (e.g. iPhone)
|
||||||
|
FRAME_CLIENT a stable id for that device (keyboard-and-trackpad pairing is kept per id)
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import base64
|
import base64
|
||||||
|
import hashlib
|
||||||
import http.client
|
import http.client
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@@ -61,6 +63,8 @@ if LOCAL:
|
|||||||
os.environ["PATH"] = f"{HERE / 'local-bin'}{os.pathsep}{os.environ.get('PATH', '')}"
|
os.environ["PATH"] = f"{HERE / 'local-bin'}{os.pathsep}{os.environ.get('PATH', '')}"
|
||||||
UI_KEY = os.environ.get("FRAME_UI_KEY") or "1"
|
UI_KEY = os.environ.get("FRAME_UI_KEY") or "1"
|
||||||
DEVICE = os.environ.get("FRAME_DEVICE") or "phone"
|
DEVICE = os.environ.get("FRAME_DEVICE") or "phone"
|
||||||
|
# What the Frame's KDE Connect calls this device (keyboard and trackpad).
|
||||||
|
INPUT_NAME = DEVICE if LOCAL else socket.gethostname().split(".")[0]
|
||||||
FRAME = os.environ.get("FRAME_ALIAS", "frame")
|
FRAME = os.environ.get("FRAME_ALIAS", "frame")
|
||||||
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", FRAME):
|
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", FRAME):
|
||||||
sys.exit(f"FRAME_ALIAS must be a plain host alias, not {FRAME!r}")
|
sys.exit(f"FRAME_ALIAS must be a plain host alias, not {FRAME!r}")
|
||||||
@@ -521,6 +525,322 @@ def clipboard(body):
|
|||||||
return {"message": ssh(PASTE_CMD, stdin=text, timeout=30).strip()}
|
return {"message": ssh(PASTE_CMD, stdin=text, timeout=30).strip()}
|
||||||
|
|
||||||
|
|
||||||
|
# ---- keyboard and pointer (KDE Connect on the Frame, see frame_input_agent.py) ----
|
||||||
|
|
||||||
|
INPUT_FLAGS = ("singleclick", "doubleclick", "middleclick", "rightclick", "singlehold", "singlerelease",
|
||||||
|
"scroll", "ctrl", "alt", "shift", "super")
|
||||||
|
INPUT_MOVE_LIMIT = 2000 # pixels per event
|
||||||
|
INPUT_TEXT_LIMIT = 500 # characters per event
|
||||||
|
INPUT_BATCH_LIMIT = 200 # events per request
|
||||||
|
|
||||||
|
|
||||||
|
def input_event(event):
|
||||||
|
"""A KDE Connect remote-input body with only the fields it knows, in range."""
|
||||||
|
if not isinstance(event, dict):
|
||||||
|
raise Failure("each input event must be an object", 400)
|
||||||
|
out = {}
|
||||||
|
for name in ("dx", "dy"):
|
||||||
|
value = event.get(name)
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
if isinstance(value, bool) or not isinstance(value, (int, float)) or value != value:
|
||||||
|
raise Failure(f"{name} must be a number", 400)
|
||||||
|
out[name] = max(-INPUT_MOVE_LIMIT, min(INPUT_MOVE_LIMIT, round(float(value), 2)))
|
||||||
|
for name in INPUT_FLAGS:
|
||||||
|
if event.get(name) is True:
|
||||||
|
out[name] = True
|
||||||
|
key = event.get("key")
|
||||||
|
if key is not None:
|
||||||
|
if not isinstance(key, str) or not 0 < len(key) <= INPUT_TEXT_LIMIT:
|
||||||
|
raise Failure(f"key must be text of 1 to {INPUT_TEXT_LIMIT} characters", 400)
|
||||||
|
out["key"] = key
|
||||||
|
special = event.get("specialKey")
|
||||||
|
if special is not None:
|
||||||
|
# KDE Connect's numbering: 1 Backspace … 14 Escape, 21-32 F1-F12.
|
||||||
|
if isinstance(special, bool) or not isinstance(special, int) or not 1 <= special <= 32:
|
||||||
|
raise Failure("specialKey must be a whole number from 1 to 32", 400)
|
||||||
|
out["specialKey"] = special
|
||||||
|
if not set(out) - {"ctrl", "alt", "shift", "super"}:
|
||||||
|
raise Failure("input event has nothing to do", 400)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def input_client():
|
||||||
|
"""A stable id for this device, so KDE Connect on the Frame keeps its pairing apart.
|
||||||
|
|
||||||
|
The iPhone app passes one (FRAME_CLIENT). A computer makes one the first time
|
||||||
|
and keeps it: host names alone can clash (desk.home and desk.office).
|
||||||
|
"""
|
||||||
|
if os.environ.get("FRAME_CLIENT"):
|
||||||
|
return os.environ["FRAME_CLIENT"]
|
||||||
|
if LOCAL:
|
||||||
|
return DEVICE
|
||||||
|
path = frame_host.data_dir("input-client-id")
|
||||||
|
try:
|
||||||
|
saved = path.read_text().strip()
|
||||||
|
if re.fullmatch(r"[A-Za-z0-9_-]{4,64}", saved):
|
||||||
|
return saved
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
made = (re.sub(r"[^A-Za-z0-9-]", "", INPUT_NAME)[:24] or "computer") + "-" + secrets.token_hex(4)
|
||||||
|
try:
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(made)
|
||||||
|
except OSError:
|
||||||
|
pass # still works this time; it pairs again next time
|
||||||
|
return made
|
||||||
|
|
||||||
|
|
||||||
|
# KDE Connect for the Frame, as Frame Control ships it (frame/kdeconnect/NOTICE.md).
|
||||||
|
KDECONNECT = HERE.parent / "frame" / "kdeconnect"
|
||||||
|
KDECONNECT_HOME = ".local/share/frame-control/kdeconnect"
|
||||||
|
|
||||||
|
|
||||||
|
def kdeconnect_packages():
|
||||||
|
"""[(file, sha256), ...] from frame/kdeconnect/packages.json; [] if it's missing."""
|
||||||
|
try:
|
||||||
|
manifest = json.loads((KDECONNECT / "packages.json").read_text())
|
||||||
|
return [(p["file"], p["sha256"]) for p in manifest["packages"]]
|
||||||
|
except (OSError, ValueError, KeyError, TypeError):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def kdeconnect_stamp(packages):
|
||||||
|
"""What the agent writes once these are unpacked (frame_input_agent.stamp)."""
|
||||||
|
return "".join(f"{sha} {name}\n" for name, sha in packages)
|
||||||
|
|
||||||
|
|
||||||
|
def file_sha256(path):
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
for block in iter(lambda: f.read(1 << 20), b""):
|
||||||
|
digest.update(block)
|
||||||
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
class InputAgent:
|
||||||
|
"""frame_input_agent.py running on the Frame, fed events over one long-lived ssh.
|
||||||
|
|
||||||
|
Before starting it, copies KDE Connect to the Frame if it isn't there yet. The
|
||||||
|
agent unpacks it, pairs, and reports its state ({"state": "off" | "installing" |
|
||||||
|
"starting" | "pairing" | "ready" | "error"}).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, source=HERE / "frame_input_agent.py", packages=None):
|
||||||
|
self.source, self.proc, self.lock = source, None, threading.Lock()
|
||||||
|
self.packages = kdeconnect_packages() if packages is None else packages
|
||||||
|
# generation counts stop()s; launching is the generation a launch is under way for.
|
||||||
|
self.status, self.launching, self.generation = {"state": "off"}, None, 0
|
||||||
|
|
||||||
|
def command(self, folder=""):
|
||||||
|
code = base64.b64encode(self.source.read_bytes()).decode()
|
||||||
|
client = input_client()
|
||||||
|
return ("python3 -u -c " + shlex.quote(
|
||||||
|
f"import base64;exec(compile(base64.b64decode('{code}'),'frame_input_agent','exec'))")
|
||||||
|
+ f" {shlex.quote(client)} {shlex.quote(INPUT_NAME)} {shlex.quote(folder)}"
|
||||||
|
+ f" {shlex.quote(json.dumps(self.packages, separators=(',', ':')))}")
|
||||||
|
|
||||||
|
def deliver(self, report, force=False):
|
||||||
|
"""Where the agent finds the packages on the Frame, copying them there first if needed.
|
||||||
|
|
||||||
|
On the Frame itself (the iPhone app) they came with the bundle. Otherwise they
|
||||||
|
go over the SSH connection, unless the Frame already has them unpacked (`force`:
|
||||||
|
the agent found it didn't after all).
|
||||||
|
"""
|
||||||
|
if LOCAL:
|
||||||
|
return str(KDECONNECT / "packages")
|
||||||
|
if not self.packages:
|
||||||
|
return "" # nothing to copy (the agent says so if it needed them)
|
||||||
|
if not force:
|
||||||
|
# Bytes, so Windows doesn't turn the stamp's line ends into CRLF.
|
||||||
|
have = ssh(f"{{ test -x /usr/lib/kdeconnectd || cmp -s - {KDECONNECT_HOME}/root/.frame-control-packages; }}"
|
||||||
|
" && echo yes || true", stdin=kdeconnect_stamp(self.packages).encode(), text=False, timeout=20)
|
||||||
|
if have.strip() == b"yes":
|
||||||
|
return ""
|
||||||
|
# A folder of its own: a cancelled start's agent may still be cleaning up another.
|
||||||
|
folder = f"{KDECONNECT_HOME}/incoming/{secrets.token_hex(8)}"
|
||||||
|
ssh(f"mkdir -p {folder}", timeout=20)
|
||||||
|
try:
|
||||||
|
for name, sha in self.packages:
|
||||||
|
path = KDECONNECT / "packages" / name
|
||||||
|
if not path.is_file() or file_sha256(path) != sha:
|
||||||
|
raise Failure(f"{name} is missing or damaged in this copy of Frame Control"
|
||||||
|
" (a build runs app/build/fetch-deps.js to add it)", 500)
|
||||||
|
report(f"Copying KDE Connect to the Frame ({name.rsplit('-', 3)[0]})")
|
||||||
|
quoted = shlex.quote(name)
|
||||||
|
ssh(f"cd {folder} && cat > {quoted}.part && mv {quoted}.part {quoted}",
|
||||||
|
stdin=path.read_bytes(), text=False, timeout=600)
|
||||||
|
except (Failure, OSError):
|
||||||
|
self.discard(folder) # a partial copy is no use to anyone
|
||||||
|
raise
|
||||||
|
return f"~/{folder}"
|
||||||
|
|
||||||
|
def discard(self, folder):
|
||||||
|
"""Remove a copy no agent will take over (best effort; agents tidy up old ones too)."""
|
||||||
|
folder = folder.removeprefix("~/")
|
||||||
|
if folder.startswith(f"{KDECONNECT_HOME}/incoming/") and not LOCAL:
|
||||||
|
try:
|
||||||
|
ssh(f"rm -rf {folder}", timeout=20)
|
||||||
|
except (Failure, OSError):
|
||||||
|
pass # never let tidying up get in the way of reporting and retrying
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
with self.lock:
|
||||||
|
if self.launching == self.generation or (self.proc and self.proc.poll() is None):
|
||||||
|
return
|
||||||
|
# (A launch from before a stop() may still be finishing; it ends itself.)
|
||||||
|
self.launching, self.status = self.generation, {"state": "starting"}
|
||||||
|
generation = self.generation
|
||||||
|
threading.Thread(target=self._launch, args=(generation,), daemon=True).start()
|
||||||
|
|
||||||
|
def _launch(self, generation, force=False):
|
||||||
|
try:
|
||||||
|
self._launch_once(generation, force)
|
||||||
|
except Exception as e: # whatever went wrong, never leave it stuck "starting"
|
||||||
|
with self.lock:
|
||||||
|
if self.launching == generation:
|
||||||
|
self.launching = None
|
||||||
|
if self.generation == generation and self.status.get("state") in ("starting", "installing"):
|
||||||
|
self.status = {"state": "error", "message": f"Couldn't start the keyboard and trackpad: {e}"}
|
||||||
|
|
||||||
|
def _launch_once(self, generation, force):
|
||||||
|
def report(message):
|
||||||
|
with self.lock:
|
||||||
|
if self.generation == generation:
|
||||||
|
self.status = {"state": "installing", "message": message}
|
||||||
|
folder = ""
|
||||||
|
try:
|
||||||
|
errors = tempfile.TemporaryFile()
|
||||||
|
ensure_master()
|
||||||
|
folder = self.deliver(report, force)
|
||||||
|
with self.lock:
|
||||||
|
stopped = self.generation != generation
|
||||||
|
if stopped: # turned off while copying
|
||||||
|
raise Failure("stopped")
|
||||||
|
proc = subprocess.Popen([*SSH, FRAME, self.command(folder)], stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE, stderr=errors)
|
||||||
|
except (Failure, OSError) as e:
|
||||||
|
self.discard(folder) # no agent will take the copy over
|
||||||
|
message = str(e)
|
||||||
|
friendly = unreachable(message)
|
||||||
|
with self.lock:
|
||||||
|
if self.launching == generation:
|
||||||
|
self.launching = None
|
||||||
|
if self.generation == generation:
|
||||||
|
self.status = {"state": "error", "message": friendly or message,
|
||||||
|
**({"offline": True} if friendly else {})}
|
||||||
|
return
|
||||||
|
_live_tunnels.add(proc)
|
||||||
|
with self.lock:
|
||||||
|
if self.launching == generation:
|
||||||
|
self.launching = None
|
||||||
|
stale = self.generation != generation
|
||||||
|
if not stale:
|
||||||
|
self.proc = proc
|
||||||
|
if stale: # turned off meanwhile
|
||||||
|
proc.terminate()
|
||||||
|
wanted, heard = self._watch(proc, errors, retry=not force)
|
||||||
|
if not heard:
|
||||||
|
# The agent never started (or was stopped first), so it can't tidy the copy up.
|
||||||
|
self.discard(folder)
|
||||||
|
if wanted and not force:
|
||||||
|
# It needed the packages after all (another device changed what's
|
||||||
|
# installed after we looked): copy them and start once more.
|
||||||
|
with self.lock:
|
||||||
|
# Unless a start() already took over (it launches, and copies if still needed).
|
||||||
|
if self.proc is not proc or self.generation != generation or self.launching is not None:
|
||||||
|
return
|
||||||
|
self.proc, self.launching, self.status = None, generation, {"state": "starting"}
|
||||||
|
self._launch(generation, force=True)
|
||||||
|
|
||||||
|
def _watch(self, proc, errors, retry=False):
|
||||||
|
"""Follow the agent's status until it exits. Returns whether it asked for the
|
||||||
|
packages (`retry`: the caller will send them, so that isn't an error yet), and
|
||||||
|
whether it said anything at all (then it holds its copy and tidies it up)."""
|
||||||
|
wanted = heard = False
|
||||||
|
for line in proc.stdout:
|
||||||
|
try:
|
||||||
|
status = json.loads(line)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
if isinstance(status, dict) and isinstance(status.get("state"), str):
|
||||||
|
heard = True
|
||||||
|
if status["state"] == "need-packages":
|
||||||
|
wanted = True
|
||||||
|
continue
|
||||||
|
with self.lock:
|
||||||
|
if self.proc is proc:
|
||||||
|
self.status = status
|
||||||
|
proc.wait()
|
||||||
|
_live_tunnels.discard(proc)
|
||||||
|
try:
|
||||||
|
errors.seek(0)
|
||||||
|
detail = strip_ansi(errors.read().decode(errors="replace")).strip()
|
||||||
|
except OSError:
|
||||||
|
detail = ""
|
||||||
|
with self.lock:
|
||||||
|
if self.proc is proc and self.status.get("state") != "error" and not (wanted and retry):
|
||||||
|
message = detail.splitlines()[-1] if detail else "The connection to the Frame ended"
|
||||||
|
if wanted:
|
||||||
|
message = "KDE Connect didn't reach the Frame"
|
||||||
|
friendly = unreachable(message)
|
||||||
|
self.status = {"state": "error", "message": friendly or message, **({"offline": True} if friendly else {})}
|
||||||
|
return wanted, heard
|
||||||
|
|
||||||
|
def send(self, events):
|
||||||
|
"""Forward events if the agent is ready; start it if it isn't running.
|
||||||
|
|
||||||
|
Returns the state, with "sent" saying whether the events went; if not,
|
||||||
|
the page keeps them and sends them again once the state is "ready".
|
||||||
|
"""
|
||||||
|
with self.lock:
|
||||||
|
proc, ready = self.proc, self.status.get("state") == "ready"
|
||||||
|
sent = False
|
||||||
|
if not (proc and proc.poll() is None):
|
||||||
|
self.start()
|
||||||
|
elif ready and events:
|
||||||
|
try:
|
||||||
|
proc.stdin.write((json.dumps(events) + "\n").encode())
|
||||||
|
proc.stdin.flush()
|
||||||
|
sent = True
|
||||||
|
except (BrokenPipeError, OSError, ValueError):
|
||||||
|
pass # _watch reports how it ended
|
||||||
|
with self.lock:
|
||||||
|
return {**self.status, "sent": sent}
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
with self.lock:
|
||||||
|
proc, self.proc, self.status = self.proc, None, {"state": "off"}
|
||||||
|
self.generation += 1
|
||||||
|
if proc and proc.poll() is None:
|
||||||
|
proc.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
_input = InputAgent()
|
||||||
|
|
||||||
|
|
||||||
|
def licenses():
|
||||||
|
"""The notices and licence texts for what Frame Control ships (the About dialog)."""
|
||||||
|
found = [("Third-party notices", HERE.parent / "THIRD_PARTY_NOTICES.md"), ("KDE Connect for the Frame", KDECONNECT / "NOTICE.md"),
|
||||||
|
("Frame Control (MIT)", HERE.parent / "LICENSE")]
|
||||||
|
found += [(f"{p.parent.name}: {p.stem}", p) for p in sorted((KDECONNECT / "LICENSES").glob("*/*.txt"))]
|
||||||
|
notices = []
|
||||||
|
for title, path in found:
|
||||||
|
try:
|
||||||
|
notices.append({"title": title, "text": path.read_text(errors="replace")})
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
return notices
|
||||||
|
|
||||||
|
|
||||||
|
def remote_input(body):
|
||||||
|
"""{"events": [...]} sends keyboard and pointer events; {} (or none yet) just starts the agent."""
|
||||||
|
events = body.get("events", [])
|
||||||
|
if not isinstance(events, list) or len(events) > INPUT_BATCH_LIMIT:
|
||||||
|
raise Failure(f"events must be a list of at most {INPUT_BATCH_LIMIT}", 400)
|
||||||
|
return _input.send([input_event(e) for e in events])
|
||||||
|
|
||||||
|
|
||||||
def flatpak(body):
|
def flatpak(body):
|
||||||
app, action = str(body.get("id", "")), body.get("action")
|
app, action = str(body.get("id", "")), body.get("action")
|
||||||
if not FLATPAK_ID.match(app):
|
if not FLATPAK_ID.match(app):
|
||||||
@@ -1327,6 +1647,8 @@ def macview_action(body):
|
|||||||
except frame_macview.MacViewError as e:
|
except frame_macview.MacViewError as e:
|
||||||
raise Failure(str(e), 502)
|
raise Failure(str(e), 502)
|
||||||
raise Failure("unknown action", 400)
|
raise Failure("unknown action", 400)
|
||||||
|
|
||||||
|
|
||||||
# ---- Report a problem (frame_report.py) --------------------------------------
|
# ---- Report a problem (frame_report.py) --------------------------------------
|
||||||
|
|
||||||
def report_preview(body):
|
def report_preview(body):
|
||||||
@@ -1355,6 +1677,7 @@ def agent_approval(body):
|
|||||||
|
|
||||||
POST = {"/api/agent/call": agent_call, "/api/agent/approval": agent_approval,
|
POST = {"/api/agent/call": agent_call, "/api/agent/approval": agent_approval,
|
||||||
"/api/assistant/chat": assistant_chat, "/api/android/display": android_display, "/api/android": android, "/api/titles": titles, "/api/launch": launch, "/api/steam": steam, "/api/volume": set_volume, "/api/clipboard": clipboard,
|
"/api/assistant/chat": assistant_chat, "/api/android/display": android_display, "/api/android": android, "/api/titles": titles, "/api/launch": launch, "/api/steam": steam, "/api/volume": set_volume, "/api/clipboard": clipboard,
|
||||||
|
"/api/input": remote_input,
|
||||||
"/api/flatpak": flatpak, "/api/open": open_thing, "/api/shots/save": save_shots,
|
"/api/flatpak": flatpak, "/api/open": open_thing, "/api/shots/save": save_shots,
|
||||||
"/api/webinstall/check": webinstall_check, "/api/webinstall/start": webinstall_start,
|
"/api/webinstall/check": webinstall_check, "/api/webinstall/start": webinstall_start,
|
||||||
"/api/webinstall/cancel": webinstall_cancel,
|
"/api/webinstall/cancel": webinstall_cancel,
|
||||||
@@ -1486,6 +1809,10 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self.send_json({"titles": frame_titles.list_titles()})
|
self.send_json({"titles": frame_titles.list_titles()})
|
||||||
elif path == "/api/titles/job":
|
elif path == "/api/titles/job":
|
||||||
self.send_json(title_job(url.query))
|
self.send_json(title_job(url.query))
|
||||||
|
elif path == "/api/licenses":
|
||||||
|
self.send_json({"notices": licenses()})
|
||||||
|
elif path == "/api/input":
|
||||||
|
self.send_json(_input.send([]) if parse_qs(url.query).get("start") == ["1"] else dict(_input.status))
|
||||||
elif path == "/api/job":
|
elif path == "/api/job":
|
||||||
self.send_json(job_status(url.query))
|
self.send_json(job_status(url.query))
|
||||||
elif path == "/api/android/displays":
|
elif path == "/api/android/displays":
|
||||||
@@ -1677,10 +2004,16 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
raise Failure(str(e), 400, {"package": info["package"], "version_code": info.get("version_code"), "blocker": str(e)})
|
raise Failure(str(e), 400, {"package": info["package"], "version_code": info.get("version_code"), "blocker": str(e)})
|
||||||
ensure_master()
|
ensure_master()
|
||||||
try:
|
try:
|
||||||
m = frame_android.install(str(dest), source=name)
|
display = self.headers.get("X-APK-Display", "auto")
|
||||||
|
if display not in ("auto", "flat", "vr"):
|
||||||
|
raise frame_android.FrameError("invalid APK display mode")
|
||||||
|
m = frame_android.install(str(dest), source=name,
|
||||||
|
flatscreen=None if display == "auto" else display == "flat")
|
||||||
except frame_android.FrameError as e:
|
except frame_android.FrameError as e:
|
||||||
raise Failure(str(e), 400)
|
raise Failure(str(e), 400)
|
||||||
return {"message": f"Installed {m['label']} as its own app in the Steam library", "app": m}
|
kind = "VR app" if not m['flatscreen'] else "app"
|
||||||
|
notes = " ".join(m.get("vr_issues", []))
|
||||||
|
return {"message": f"Installed {m['label']} as its own {kind} in the Steam library. {notes}".strip(), "app": m}
|
||||||
return {"message": push_file(dest)}
|
return {"message": push_file(dest)}
|
||||||
finally:
|
finally:
|
||||||
if not keep:
|
if not keep:
|
||||||
|
|||||||
Reference in new issue
Block a user