mirror of
https://github.com/fathyb/carbonyl.git
synced 2025-01-07 03:07:10 +08:00
feat(browser): introduce quadrant rendering (#120)
Fixes #28 Fixes #55 Fixes #78
This commit is contained in:
parent
6b906b0165
commit
67474982b2
@ -1,216 +1,265 @@
|
||||
import { commit } from "refloat";
|
||||
import docker from "github.com/refloat-plugins/docker";
|
||||
|
||||
import pkg from "../package.json";
|
||||
|
||||
const { version } = JSON.parse(pkg);
|
||||
const triple = (platform, arch) => `${archs[arch]}-${platforms[platform]}`;
|
||||
const sharedLib = {
|
||||
macos: "dylib",
|
||||
linux: "so",
|
||||
macos: "dylib",
|
||||
linux: "so",
|
||||
};
|
||||
const platforms = {
|
||||
macos: "apple-darwin",
|
||||
linux: "unknown-linux-gnu",
|
||||
macos: "apple-darwin",
|
||||
linux: "unknown-linux-gnu",
|
||||
};
|
||||
const archs = {
|
||||
arm64: "aarch64",
|
||||
amd64: "x86_64",
|
||||
arm64: "aarch64",
|
||||
amd64: "x86_64",
|
||||
};
|
||||
|
||||
export const jobs = ["macos", "linux"].flatMap((platform) => {
|
||||
return [
|
||||
return [
|
||||
{
|
||||
name: `Build runtime (${platform})`,
|
||||
agent: { tags: ["chromium-src", platform] },
|
||||
steps: [
|
||||
...["arm64", "amd64"].map((arch) => ({
|
||||
import: { workspace: `core-${triple(platform, arch)}` },
|
||||
})),
|
||||
{
|
||||
name: `Build runtime (${platform})`,
|
||||
agent: { tags: ["chromium-src", platform] },
|
||||
steps: [
|
||||
...["arm64", "amd64"].map((arch) => ({
|
||||
import: { workspace: `core-${triple(platform, arch)}` },
|
||||
})),
|
||||
{
|
||||
name: "Fetch Chromium",
|
||||
command: `
|
||||
if [ -z "$CHROMIUM_ROOT" ]; then
|
||||
echo "Chromium build environment not setup"
|
||||
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if scripts/runtime-pull.sh arm64; then
|
||||
touch skip-build-arm64
|
||||
fi
|
||||
if scripts/runtime-pull.sh amd64; then
|
||||
touch skip-build-amd64
|
||||
fi
|
||||
|
||||
if [ ! -f skip-build-amd64 ] || [ ! -f skip-build-amd64 ]; then
|
||||
cp chromium/.gclient "$CHROMIUM_ROOT"
|
||||
|
||||
scripts/gclient.sh sync
|
||||
scripts/patches.sh apply
|
||||
|
||||
rm -rf "$CHROMIUM_ROOT/src/carbonyl"
|
||||
mkdir "$CHROMIUM_ROOT/src/carbonyl"
|
||||
ln -s "$(pwd)/src" "$CHROMIUM_ROOT/src/carbonyl/src"
|
||||
ln -s "$(pwd)/build" "$CHROMIUM_ROOT/src/carbonyl/build"
|
||||
fi
|
||||
`,
|
||||
},
|
||||
{
|
||||
parallel: ["arm64", "amd64"].map((arch) => {
|
||||
const target =
|
||||
platform === "linux" && arch === "amd64" ? "Default" : arch;
|
||||
|
||||
return {
|
||||
serial: [
|
||||
{
|
||||
name: `Build Chromium (${arch})`,
|
||||
command: `
|
||||
if [ ! -f skip-build-${arch} ]; then
|
||||
(
|
||||
export PATH="$PATH:$CHROMIUM_ROOT/depot_tools"
|
||||
|
||||
cd "$CHROMIUM_ROOT/src/out/${target}"
|
||||
ninja headless:headless_shell -j4
|
||||
)
|
||||
|
||||
scripts/copy-binaries.sh ${target} ${arch}
|
||||
fi
|
||||
`,
|
||||
env: {
|
||||
AR_AARCH64_UNKNOWN_LINUX_GNU: "aarch64-linux-gnu-ar",
|
||||
CC_AARCH64_UNKNOWN_LINUX_GNU: "aarch64-linux-gnu-gcc",
|
||||
CXX_AARCH64_UNKNOWN_LINUX_GNU: "aarch64-linux-gnu-g++",
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER:
|
||||
"aarch64-linux-gnu-gcc",
|
||||
AR_X86_64_UNKNOWN_LINUX_GNU: "x86_64-linux-gnu-ar",
|
||||
CC_X86_64_UNKNOWN_LINUX_GNU: "x86_64-linux-gnu-gcc",
|
||||
CXX_X86_64_UNKNOWN_LINUX_GNU: "x86_64-linux-gnu-g++",
|
||||
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER:
|
||||
"x86_64-linux-gnu-gcc",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
parallel: [
|
||||
{
|
||||
name: `Push binaries to CDN (${arch})`,
|
||||
command: `
|
||||
if [ ! -f skip-build-${arch} ]; then
|
||||
scripts/runtime-push.sh ${arch}
|
||||
fi
|
||||
`,
|
||||
env: {
|
||||
CDN_ACCESS_KEY_ID: { secret: true },
|
||||
CDN_SECRET_ACCESS_KEY: { secret: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
export: {
|
||||
workspace: `runtime-${triple(platform, arch)}`,
|
||||
path: `build/pre-built/${triple(platform, arch)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
},
|
||||
],
|
||||
parallel: ["arm64", "amd64"].map((arch) => ({
|
||||
name: `Fetch pre-built runtime for ${arch}`,
|
||||
command: `
|
||||
if scripts/runtime-pull.sh ${arch}; then
|
||||
touch skip-build-${arch}
|
||||
fi
|
||||
`,
|
||||
})),
|
||||
},
|
||||
...["arm64", "amd64"].flatMap((arch) => {
|
||||
const triple = `${archs[arch]}-${platforms[platform]}`;
|
||||
const lib = `build/${triple}/release/libcarbonyl.${sharedLib[platform]}`;
|
||||
{
|
||||
name: "Fetch Chromium",
|
||||
command: `
|
||||
if [ -z "$CHROMIUM_ROOT" ]; then
|
||||
echo "Chromium build environment not setup"
|
||||
|
||||
return [
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -f skip-build-arm64 ] || [ ! -f skip-build-amd64 ]; then
|
||||
cp chromium/.gclient "$CHROMIUM_ROOT"
|
||||
|
||||
scripts/gclient.sh sync
|
||||
scripts/patches.sh apply
|
||||
|
||||
rm -rf "$CHROMIUM_ROOT/src/carbonyl"
|
||||
mkdir "$CHROMIUM_ROOT/src/carbonyl"
|
||||
ln -s "$(pwd)/src" "$CHROMIUM_ROOT/src/carbonyl/src"
|
||||
ln -s "$(pwd)/build" "$CHROMIUM_ROOT/src/carbonyl/build"
|
||||
fi
|
||||
`,
|
||||
},
|
||||
{
|
||||
parallel: ["arm64", "amd64"].map((arch) => {
|
||||
const target =
|
||||
platform === "linux" && arch === "amd64" ? "Default" : arch;
|
||||
|
||||
return {
|
||||
serial: [
|
||||
{
|
||||
name: `Build core (${platform}/${arch})`,
|
||||
docker:
|
||||
platform === "linux"
|
||||
? {
|
||||
image: "fathyb/rust-cross",
|
||||
cache: ["/usr/local/cargo/registry"],
|
||||
}
|
||||
: undefined,
|
||||
agent: { tags: platform === "linux" ? ["docker"] : ["macos"] },
|
||||
steps: [
|
||||
{
|
||||
name: "Install Rust toolchain",
|
||||
command: `rustup target add ${triple}`,
|
||||
},
|
||||
{
|
||||
name: "Build core library",
|
||||
command: `cargo build --target ${triple} --release`,
|
||||
env: { MACOSX_DEPLOYMENT_TARGET: "10.13" },
|
||||
},
|
||||
{
|
||||
name: "Set core library install name",
|
||||
command:
|
||||
platform === "macos"
|
||||
? `install_name_tool -id @executable_path/libcarbonyl.dylib ${lib}`
|
||||
: "echo not necessary",
|
||||
},
|
||||
{
|
||||
export: {
|
||||
workspace: `core-${triple}`,
|
||||
path: "build/*/release/*.{dylib,so,dll}",
|
||||
},
|
||||
},
|
||||
],
|
||||
name: `Build Chromium (${arch})`,
|
||||
command: `
|
||||
if [ ! -f skip-build-${arch} ]; then
|
||||
scripts/build.sh ${target} ${arch}
|
||||
scripts/copy-binaries.sh ${target} ${arch}
|
||||
fi
|
||||
`,
|
||||
env: {
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.13",
|
||||
CARBONYL_SKIP_CARGO_BUILD: "true",
|
||||
AR_AARCH64_UNKNOWN_LINUX_GNU: "aarch64-linux-gnu-ar",
|
||||
CC_AARCH64_UNKNOWN_LINUX_GNU: "aarch64-linux-gnu-gcc",
|
||||
CXX_AARCH64_UNKNOWN_LINUX_GNU: "aarch64-linux-gnu-g++",
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER:
|
||||
"aarch64-linux-gnu-gcc",
|
||||
AR_X86_64_UNKNOWN_LINUX_GNU: "x86_64-linux-gnu-ar",
|
||||
CC_X86_64_UNKNOWN_LINUX_GNU: "x86_64-linux-gnu-gcc",
|
||||
CXX_X86_64_UNKNOWN_LINUX_GNU: "x86_64-linux-gnu-g++",
|
||||
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER:
|
||||
"x86_64-linux-gnu-gcc",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: `Package (${platform}/${arch})`,
|
||||
docker: "fathyb/rust-cross",
|
||||
agent: { tags: ["docker"] },
|
||||
steps: [
|
||||
{
|
||||
import: { workspace: `runtime-${triple}` },
|
||||
},
|
||||
{
|
||||
name: "Zip binaries",
|
||||
command: `
|
||||
mkdir build/zip
|
||||
cp -r build/pre-built/${triple} build/zip/carbonyl-${version}
|
||||
|
||||
cd build/zip
|
||||
zip -r package.zip carbonyl-${version}
|
||||
`,
|
||||
},
|
||||
{
|
||||
export: {
|
||||
artifact: {
|
||||
name: `carbonyl.${platform}-${arch}.zip`,
|
||||
path: "build/zip/package.zip",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
parallel: [
|
||||
{
|
||||
name: `Push binaries to CDN (${arch})`,
|
||||
command: `
|
||||
if [ ! -f skip-build-${arch} ]; then
|
||||
scripts/runtime-push.sh ${arch}
|
||||
fi
|
||||
`,
|
||||
env: {
|
||||
CDN_ACCESS_KEY_ID: { secret: true },
|
||||
CDN_SECRET_ACCESS_KEY: { secret: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
export: {
|
||||
workspace: `runtime-${triple(platform, arch)}`,
|
||||
path: `build/pre-built/${triple(platform, arch)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}),
|
||||
];
|
||||
],
|
||||
};
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
...["arm64", "amd64"].flatMap((arch) => {
|
||||
const triple = `${archs[arch]}-${platforms[platform]}`;
|
||||
const lib = `build/${triple}/release/libcarbonyl.${sharedLib[platform]}`;
|
||||
|
||||
return [
|
||||
{
|
||||
name: `Build core (${platform}/${arch})`,
|
||||
docker:
|
||||
platform === "linux"
|
||||
? {
|
||||
image: "fathyb/rust-cross",
|
||||
cache: ["/usr/local/cargo/registry"],
|
||||
}
|
||||
: undefined,
|
||||
agent: { tags: platform === "linux" ? ["docker"] : ["macos"] },
|
||||
steps: [
|
||||
{
|
||||
name: "Install Rust toolchain",
|
||||
command: `rustup target add ${triple}`,
|
||||
},
|
||||
{
|
||||
name: "Build core library",
|
||||
command: `cargo build --target ${triple} --release`,
|
||||
env: { MACOSX_DEPLOYMENT_TARGET: "10.13" },
|
||||
},
|
||||
{
|
||||
name: "Set core library install name",
|
||||
command:
|
||||
platform === "macos"
|
||||
? `install_name_tool -id @executable_path/libcarbonyl.dylib ${lib}`
|
||||
: "echo not necessary",
|
||||
},
|
||||
{
|
||||
export: {
|
||||
workspace: `core-${triple}`,
|
||||
path: "build/*/release/*.{dylib,so,dll}",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: `Package (${platform}/${arch})`,
|
||||
docker: "fathyb/rust-cross",
|
||||
agent: { tags: ["docker"] },
|
||||
steps: [
|
||||
{
|
||||
import: { workspace: `runtime-${triple}` },
|
||||
},
|
||||
{
|
||||
name: "Zip binaries",
|
||||
command: `
|
||||
mkdir build/zip
|
||||
cp -r build/pre-built/${triple} build/zip/carbonyl-${version}
|
||||
|
||||
cd build/zip
|
||||
zip -r package.zip carbonyl-${version}
|
||||
`,
|
||||
},
|
||||
{
|
||||
export: {
|
||||
artifact: {
|
||||
name: `carbonyl.${platform}-${arch}.zip`,
|
||||
path: "build/zip/package.zip",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}),
|
||||
];
|
||||
});
|
||||
|
||||
if (commit.defaultBranch) {
|
||||
jobs.push({
|
||||
name: "Publish to npm",
|
||||
agent: { tags: ["carbonyl-publish"] },
|
||||
docker: "node:18",
|
||||
steps: [
|
||||
...["macos", "linux"].flatMap((platform) =>
|
||||
["arm64", "amd64"].map((arch) => ({
|
||||
import: { workspace: `runtime-${triple(platform, arch)}` },
|
||||
}))
|
||||
),
|
||||
{
|
||||
name: "Publish",
|
||||
env: { CARBONYL_NPM_PUBLISH_TOKEN: { secret: true } },
|
||||
command: `
|
||||
echo "//registry.npmjs.org/:_authToken=\${CARBONYL_NPM_PUBLISH_TOKEN}" > ~/.npmrc
|
||||
|
||||
scripts/npm-publish.sh --tag next
|
||||
`,
|
||||
},
|
||||
],
|
||||
});
|
||||
jobs.push(
|
||||
{
|
||||
name: "Publish to Docker",
|
||||
agent: { tags: ["carbonyl-publish"] },
|
||||
docker: "fathyb/rust-cross",
|
||||
steps: [
|
||||
{
|
||||
serial: ["arm64", "amd64"].map((arch) => ({
|
||||
import: { workspace: `runtime-${triple("linux", arch)}` },
|
||||
})),
|
||||
},
|
||||
{
|
||||
parallel: ["arm64", "amd64"].map((arch) => ({
|
||||
serial: [
|
||||
{
|
||||
name: `Build ${arch} image`,
|
||||
command: `scripts/docker-build.sh ${arch}`,
|
||||
},
|
||||
],
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Publish images to DockerHub",
|
||||
command: "scripts/docker-push.sh next",
|
||||
using: docker.login({
|
||||
username: { secret: "DOCKER_PUBLISH_USERNAME" },
|
||||
password: { secret: "DOCKER_PUBLISH_TOKEN" },
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Publish to npm",
|
||||
agent: { tags: ["carbonyl-publish"] },
|
||||
docker: "node:18",
|
||||
steps: [
|
||||
...["macos", "linux"].flatMap((platform) =>
|
||||
["arm64", "amd64"].map((arch) => ({
|
||||
import: { workspace: `runtime-${triple(platform, arch)}` },
|
||||
}))
|
||||
),
|
||||
{
|
||||
name: "Package",
|
||||
command: "scripts/npm-package.sh",
|
||||
},
|
||||
{
|
||||
name: "Write npm token",
|
||||
env: { CARBONYL_NPM_PUBLISH_TOKEN: { secret: true } },
|
||||
command:
|
||||
'echo "//registry.npmjs.org/:_authToken=${CARBONYL_NPM_PUBLISH_TOKEN}" > ~/.npmrc',
|
||||
},
|
||||
{
|
||||
parallel: ["amd64", "arm64"].flatMap((arch) =>
|
||||
["linux", "macos"].map((platform) => ({
|
||||
name: `Publish ${platform}/${arch} package`,
|
||||
command: "scripts/npm-publish.sh --tag next",
|
||||
env: {
|
||||
CARBONYL_PUBLISH_ARCH: arch,
|
||||
CARBONYL_PUBLISH_PLATFORM: platform,
|
||||
},
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Publish main package",
|
||||
command: "scripts/npm-publish.sh --tag next",
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
14
Dockerfile
14
Dockerfile
@ -24,21 +24,15 @@ RUN groupadd -r carbonyl && \
|
||||
mkdir -p /carbonyl/data && \
|
||||
chown -R carbonyl:carbonyl /carbonyl && \
|
||||
apt-get update && \
|
||||
apt-get install -y \
|
||||
libasound2 libatk-bridge2.0-0 libatk1.0-0 libatomic1 libatspi2.0-0 \
|
||||
libbrotli1 libc6 libcairo2 libcups2 libdbus-1-3 libdouble-conversion3 \
|
||||
libdrm2 libevent-2.1-7 libexpat1 libflac8 libfontconfig1 libfreetype6 \
|
||||
libgbm1 libgcc-s1 libglib2.0-0 libjpeg62-turbo libjsoncpp24 liblcms2-2 \
|
||||
libminizip1 libnspr4 libnss3 libopenjp2-7 libopus0 libpango-1.0-0 \
|
||||
libpng16-16 libpulse0 libre2-9 libsnappy1v5 libstdc++6 libwebp6 \
|
||||
libwebpdemux2 libwebpmux3 libwoff1 libx11-6 libxcb1 libxcomposite1 \
|
||||
libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxml2 libxnvctrl0 \
|
||||
libxrandr2 libxslt1.1 zlib1g libgtk-3-0 && \
|
||||
apt-get install -y libasound2 libexpat1 libfontconfig1 libnss3 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
USER carbonyl
|
||||
VOLUME /carbonyl/data
|
||||
ENV HOME=/carbonyl/data
|
||||
|
||||
COPY . /carbonyl
|
||||
|
||||
RUN /carbonyl/carbonyl --version
|
||||
|
||||
ENTRYPOINT ["/carbonyl/carbonyl", "--no-sandbox", "--disable-dev-shm-usage", "--user-data-dir=/carbonyl/data"]
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 0ed9a390f25d73492ce1170ce229b95772fd458d Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:20:50 +0100
|
||||
Subject: [PATCH 1/9] Add Carbonyl library
|
||||
Subject: [PATCH 01/14] Add Carbonyl library
|
||||
|
||||
---
|
||||
carbonyl/build | 1 +
|
||||
@ -66,6 +66,3 @@ index bfae1e3290de0..8018111ed9898 100644
|
||||
|
||||
if (!headless_use_embedded_resources) {
|
||||
data = [
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 795b29828fd7ac95548c4dcab483cbc3b6c1d361 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:21:59 +0100
|
||||
Subject: [PATCH 2/9] Add Carbonyl service
|
||||
Subject: [PATCH 02/14] Add Carbonyl service
|
||||
|
||||
---
|
||||
cc/trees/layer_tree_host.cc | 21 ++
|
||||
@ -637,6 +637,3 @@ index 089a11b156ade..dfdc79eacce3b 100644
|
||||
ShapeResultBloberizer::FillGlyphsNG bloberizer(
|
||||
GetFontDescription(), device_scale_factor > 1.0f, text_info.text,
|
||||
text_info.from, text_info.to, text_info.shape_result,
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
From eea9662f4ba08a655057143d320e4e692fd92469 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:24:29 +0100
|
||||
Subject: [PATCH 3/9] Setup shared software rendering surface
|
||||
Subject: [PATCH 03/14] Setup shared software rendering surface
|
||||
|
||||
---
|
||||
components/viz/host/host_display_client.cc | 4 +++-
|
||||
@ -224,6 +224,3 @@ index 50cea82c6b477..f024e6013bfb9 100644
|
||||
// The root of the Layer tree drawn by this compositor.
|
||||
raw_ptr<Layer> root_layer_ = nullptr;
|
||||
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
From c960c9b1f7ef3f16b27e4eaa4896e3563c88ea91 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:27:27 +0100
|
||||
Subject: [PATCH 4/9] Setup browser default settings
|
||||
Subject: [PATCH 04/14] Setup browser default settings
|
||||
|
||||
---
|
||||
headless/public/headless_browser.cc | 4 ++--
|
||||
@ -46,6 +46,3 @@ index 48efaa7d57ca2..afc0236147519 100644
|
||||
|
||||
// Whether or not BeginFrames will be issued over DevTools protocol
|
||||
// (experimental).
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 481ff19118891fe65e80b8be0e1f4498874d3b56 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:28:35 +0100
|
||||
Subject: [PATCH 5/9] Remove some debug assertions
|
||||
Subject: [PATCH 05/14] Remove some debug assertions
|
||||
|
||||
---
|
||||
.../browser/web_contents/web_contents_impl.cc | 1 -
|
||||
@ -124,6 +124,3 @@ index 2518b71275670..3a1b8e6646c43 100644
|
||||
}
|
||||
|
||||
void GraphicsContext::CopyConfigFrom(GraphicsContext& other) {
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,19 +1,28 @@
|
||||
From 5693445bf736c51d3255c10115767ba8886d68da Mon Sep 17 00:00:00 2001
|
||||
From cc9c37adb3ad2613a114bd37e1fde43f83951d88 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:29:34 +0100
|
||||
Subject: [PATCH 6/9] Setup display DPI
|
||||
Date: Sun, 12 Feb 2023 01:00:43 +0100
|
||||
Subject: [PATCH 06/14] Setup display DPI
|
||||
|
||||
---
|
||||
.../lib/browser/headless_browser_impl_aura.cc | 9 +---
|
||||
headless/lib/browser/headless_screen.cc | 2 +-
|
||||
ui/display/display.cc | 50 +++++++++----------
|
||||
3 files changed, 28 insertions(+), 33 deletions(-)
|
||||
.../lib/browser/headless_browser_impl_aura.cc | 11 ++--
|
||||
headless/lib/browser/headless_screen.cc | 5 +-
|
||||
ui/display/display.cc | 52 ++++++++++---------
|
||||
3 files changed, 35 insertions(+), 33 deletions(-)
|
||||
|
||||
diff --git a/headless/lib/browser/headless_browser_impl_aura.cc b/headless/lib/browser/headless_browser_impl_aura.cc
|
||||
index 81261215c702f..b8eb5fc429a2a 100644
|
||||
index 81261215c702f..508660db32151 100644
|
||||
--- a/headless/lib/browser/headless_browser_impl_aura.cc
|
||||
+++ b/headless/lib/browser/headless_browser_impl_aura.cc
|
||||
@@ -57,13 +57,8 @@ void HeadlessBrowserImpl::PlatformSetWebContentsBounds(
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "ui/events/devices/device_data_manager.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
|
||||
+#include "carbonyl/src/browser/bridge.h"
|
||||
+
|
||||
namespace headless {
|
||||
|
||||
void HeadlessBrowserImpl::PlatformInitialize() {
|
||||
@@ -57,13 +59,8 @@ void HeadlessBrowserImpl::PlatformSetWebContentsBounds(
|
||||
const gfx::Rect& bounds) {
|
||||
// Browser's window bounds should contain all web contents, so that we're sure
|
||||
// that we will actually produce visible damage when taking a screenshot.
|
||||
@ -24,29 +33,48 @@ index 81261215c702f..b8eb5fc429a2a 100644
|
||||
- std::max(old_host_bounds.height(), bounds.y() + bounds.height()));
|
||||
- web_contents->window_tree_host()->SetBoundsInPixels(new_host_bounds);
|
||||
- web_contents->window_tree_host()->window()->SetBounds(new_host_bounds);
|
||||
+ web_contents->window_tree_host()->SetBoundsInPixels(ScaleToEnclosedRect(bounds, 1.0 / 7.0));
|
||||
+ web_contents->window_tree_host()->SetBoundsInPixels(ScaleToEnclosedRect(bounds, carbonyl::Renderer::GetDPI()));
|
||||
+ web_contents->window_tree_host()->window()->SetBounds(bounds);
|
||||
|
||||
gfx::NativeView native_view = web_contents->web_contents()->GetNativeView();
|
||||
native_view->SetBounds(bounds);
|
||||
diff --git a/headless/lib/browser/headless_screen.cc b/headless/lib/browser/headless_screen.cc
|
||||
index 28f1a65f6dce5..e595735be187b 100644
|
||||
index 28f1a65f6dce5..8bf00ef5e036a 100644
|
||||
--- a/headless/lib/browser/headless_screen.cc
|
||||
+++ b/headless/lib/browser/headless_screen.cc
|
||||
@@ -49,7 +49,7 @@ display::Display HeadlessScreen::GetDisplayNearestWindow(
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "ui/gfx/geometry/size_conversions.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
+#include "carbonyl/src/browser/bridge.h"
|
||||
+
|
||||
namespace headless {
|
||||
|
||||
// static
|
||||
@@ -49,7 +51,8 @@ display::Display HeadlessScreen::GetDisplayNearestWindow(
|
||||
HeadlessScreen::HeadlessScreen(const gfx::Rect& screen_bounds) {
|
||||
static int64_t synthesized_display_id = 2000;
|
||||
display::Display display(synthesized_display_id++);
|
||||
- display.SetScaleAndBounds(1.0f, screen_bounds);
|
||||
+ display.SetScaleAndBounds(1.0f / 7.0, ScaleToEnclosedRect(screen_bounds, 1.0 / 7.0));
|
||||
+ float dpi = carbonyl::Renderer::GetDPI();
|
||||
+ display.SetScaleAndBounds(dpi, ScaleToEnclosedRect(screen_bounds, dpi));
|
||||
ProcessDisplayChanged(display, true /* is_primary */);
|
||||
}
|
||||
|
||||
diff --git a/ui/display/display.cc b/ui/display/display.cc
|
||||
index 466ef1fd1fe6e..cb503dc5cda0f 100644
|
||||
index 466ef1fd1fe6e..1d71f3b4c9857 100644
|
||||
--- a/ui/display/display.cc
|
||||
+++ b/ui/display/display.cc
|
||||
@@ -39,22 +39,22 @@ float g_forced_device_scale_factor = -1.0;
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "ui/gfx/geometry/transform.h"
|
||||
#include "ui/gfx/icc_profile.h"
|
||||
|
||||
+#include "carbonyl/src/browser/bridge.h"
|
||||
+
|
||||
namespace display {
|
||||
namespace {
|
||||
|
||||
@@ -39,22 +41,22 @@ float g_forced_device_scale_factor = -1.0;
|
||||
constexpr float kDisplaySizeAllowanceEpsilon = 0.01f;
|
||||
|
||||
bool HasForceDeviceScaleFactorImpl() {
|
||||
@ -78,11 +106,11 @@ index 466ef1fd1fe6e..cb503dc5cda0f 100644
|
||||
+ // scale_in_double = 1.0;
|
||||
+ // }
|
||||
+ // }
|
||||
+ return 1.0 / 7.0;
|
||||
+ return carbonyl::Bridge::GetCurrent()->GetDPI();
|
||||
}
|
||||
|
||||
const char* ToRotationString(display::Display::Rotation rotation) {
|
||||
@@ -97,11 +97,11 @@ void Display::ResetForceDeviceScaleFactorForTesting() {
|
||||
@@ -97,11 +99,11 @@ void Display::ResetForceDeviceScaleFactorForTesting() {
|
||||
// static
|
||||
void Display::SetForceDeviceScaleFactor(double dsf) {
|
||||
// Reset any previously set values and unset the flag.
|
||||
@ -98,7 +126,7 @@ index 466ef1fd1fe6e..cb503dc5cda0f 100644
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -273,15 +273,15 @@ void Display::SetScaleAndBounds(float device_scale_factor,
|
||||
@@ -273,15 +275,15 @@ void Display::SetScaleAndBounds(float device_scale_factor,
|
||||
}
|
||||
|
||||
void Display::SetScale(float device_scale_factor) {
|
||||
@ -122,6 +150,3 @@ index 466ef1fd1fe6e..cb503dc5cda0f 100644
|
||||
}
|
||||
|
||||
void Display::SetSize(const gfx::Size& size_in_pixel) {
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 5557ea65c9769fd0f99bd1f8eb58b05634f9faa2 Mon Sep 17 00:00:00 2001
|
||||
From 022ed4d808369659eab4e83cd677eb974215c58c Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:31:17 +0100
|
||||
Subject: [PATCH 7/9] Disable text effects
|
||||
Subject: [PATCH 07/14] Disable text effects
|
||||
|
||||
---
|
||||
.../core/paint/ng/ng_text_painter_base.cc | 30 ++++++++--------
|
||||
@ -108,6 +108,3 @@ index 67fbf128ea158..a645ba61c8597 100644
|
||||
}
|
||||
|
||||
StyleIterator::StyleIterator(const BreakList<SkColor>* colors,
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 1820cd34967c756d2459f4a2894492662edbf58b Mon Sep 17 00:00:00 2001
|
||||
From 7b1f72900f704ffecc48c66da7ccd6de205b88f7 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:32:14 +0100
|
||||
Subject: [PATCH 8/9] Fix text layout
|
||||
Subject: [PATCH 08/14] Fix text layout
|
||||
|
||||
---
|
||||
.../core/css/resolver/style_resolver.cc | 17 ++++++++++++++++-
|
||||
@ -42,6 +42,3 @@ index 6207b72d17cb9..79cb8c85b697f 100644
|
||||
state.LoadPendingResources();
|
||||
|
||||
// Now return the style.
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
From 0697c3ab8f52592b0a0d4241e2938b8e622a01f6 Mon Sep 17 00:00:00 2001
|
||||
From 792e123bb57b1b379b0367b2568302e2cb0dc3c9 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:32:30 +0100
|
||||
Subject: [PATCH 9/9] Bridge browser into Carbonyl library
|
||||
Subject: [PATCH 09/14] Bridge browser into Carbonyl library
|
||||
|
||||
---
|
||||
headless/app/headless_shell.cc | 33 +-
|
||||
@ -645,6 +645,3 @@ index b80147fd06be8..09773596aa5ce 100644
|
||||
|
||||
content::WebContents* web_contents() const;
|
||||
bool OpenURL(const GURL& url);
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -0,0 +1,137 @@
|
||||
From bdc80f35a7113b7523c4d992edc9170db082deb0 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Sun, 12 Feb 2023 00:55:33 +0100
|
||||
Subject: [PATCH 10/14] Conditionally enable text rendering
|
||||
|
||||
---
|
||||
content/renderer/render_frame_impl.cc | 3 ++-
|
||||
.../core/css/resolver/style_resolver.cc | 26 +++++++++++--------
|
||||
third_party/blink/renderer/platform/BUILD.gn | 1 +
|
||||
.../blink/renderer/platform/fonts/font.cc | 10 +++----
|
||||
4 files changed, 23 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
|
||||
index 97b61ffb954be..891efd6a9d796 100644
|
||||
--- a/content/renderer/render_frame_impl.cc
|
||||
+++ b/content/renderer/render_frame_impl.cc
|
||||
@@ -259,6 +259,7 @@
|
||||
// Carbonyl
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
+#include "carbonyl/src/browser/bridge.h"
|
||||
#include "cc/paint/paint_recorder.h"
|
||||
#include "cc/paint/skia_paint_canvas.h"
|
||||
#include "cc/raster/playback_image_provider.h"
|
||||
@@ -2221,7 +2222,7 @@ void RenderFrameImpl::Initialize(blink::WebFrame* parent) {
|
||||
|
||||
render_callback_ = std::make_shared<std::function<bool()>>(
|
||||
[=]() -> bool {
|
||||
- if (!IsMainFrame() || IsHidden()) {
|
||||
+ if (!IsMainFrame() || IsHidden() || carbonyl::Bridge::BitmapMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
|
||||
index 79cb8c85b697f..7129982acf4a6 100644
|
||||
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
|
||||
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
|
||||
@@ -116,6 +116,8 @@
|
||||
#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
|
||||
|
||||
+#include "carbonyl/src/browser/bridge.h"
|
||||
+
|
||||
namespace blink {
|
||||
|
||||
namespace {
|
||||
@@ -1041,18 +1043,20 @@ scoped_refptr<ComputedStyle> StyleResolver::ResolveStyle(
|
||||
UseCounter::Count(GetDocument(), WebFeature::kHasGlyphRelativeUnits);
|
||||
}
|
||||
|
||||
- auto font = state.StyleBuilder().GetFontDescription();
|
||||
- FontFamily family;
|
||||
+ if (!carbonyl::Bridge::BitmapMode()) {
|
||||
+ auto font = state.StyleBuilder().GetFontDescription();
|
||||
+ FontFamily family;
|
||||
|
||||
- family.SetFamily("monospace", FontFamily::Type::kGenericFamily);
|
||||
- font.SetFamily(family);
|
||||
- font.SetStretch(ExtraExpandedWidthValue());
|
||||
- font.SetKerning(FontDescription::kNoneKerning);
|
||||
- font.SetComputedSize(11.75 / 7.0);
|
||||
- font.SetGenericFamily(FontDescription::kMonospaceFamily);
|
||||
- font.SetIsAbsoluteSize(true);
|
||||
- state.StyleBuilder().SetFontDescription(font);
|
||||
- state.StyleBuilder().SetLineHeight(Length::Fixed(14.0 / 7.0));
|
||||
+ family.SetFamily("monospace", FontFamily::Type::kGenericFamily);
|
||||
+ font.SetFamily(family);
|
||||
+ font.SetStretch(ExtraExpandedWidthValue());
|
||||
+ font.SetKerning(FontDescription::kNoneKerning);
|
||||
+ font.SetComputedSize(13.25 / 4.0);
|
||||
+ font.SetGenericFamily(FontDescription::kMonospaceFamily);
|
||||
+ font.SetIsAbsoluteSize(true);
|
||||
+ state.StyleBuilder().SetFontDescription(font);
|
||||
+ state.StyleBuilder().SetLineHeight(Length::Fixed(16.0 / 4.0));
|
||||
+ }
|
||||
|
||||
state.LoadPendingResources();
|
||||
|
||||
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
|
||||
index e7b1c1a52e4c9..63fc13e44b5ae 100644
|
||||
--- a/third_party/blink/renderer/platform/BUILD.gn
|
||||
+++ b/third_party/blink/renderer/platform/BUILD.gn
|
||||
@@ -1678,6 +1678,7 @@ component("platform") {
|
||||
"//base/allocator:buildflags",
|
||||
"//build:chromecast_buildflags",
|
||||
"//build:chromeos_buildflags",
|
||||
+ "//carbonyl/src/browser:carbonyl",
|
||||
"//cc/ipc",
|
||||
"//cc/mojo_embedder",
|
||||
"//components/paint_preview/common",
|
||||
diff --git a/third_party/blink/renderer/platform/fonts/font.cc b/third_party/blink/renderer/platform/fonts/font.cc
|
||||
index dfdc79eacce3b..4625300729523 100644
|
||||
--- a/third_party/blink/renderer/platform/fonts/font.cc
|
||||
+++ b/third_party/blink/renderer/platform/fonts/font.cc
|
||||
@@ -49,6 +49,8 @@
|
||||
#include "third_party/skia/include/core/SkTextBlob.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
|
||||
+#include "carbonyl/src/browser/bridge.h"
|
||||
+
|
||||
namespace blink {
|
||||
|
||||
namespace {
|
||||
@@ -151,14 +153,12 @@ bool Font::operator==(const Font& other) const {
|
||||
|
||||
namespace {
|
||||
|
||||
-static const bool carbonyl_b64_text = true;
|
||||
-
|
||||
void DrawBlobs(cc::PaintCanvas* canvas,
|
||||
const cc::PaintFlags& flags,
|
||||
const ShapeResultBloberizer::BlobBuffer& blobs,
|
||||
const gfx::PointF& point,
|
||||
cc::NodeId node_id = cc::kInvalidNodeId) {
|
||||
- if (carbonyl_b64_text) {
|
||||
+ if (!carbonyl::Bridge::BitmapMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ void Font::DrawText(cc::PaintCanvas* canvas,
|
||||
if (ShouldSkipDrawing())
|
||||
return;
|
||||
|
||||
- if (carbonyl_b64_text) {
|
||||
+ if (!carbonyl::Bridge::BitmapMode()) {
|
||||
auto string = StringView(
|
||||
run_info.run.ToStringView(),
|
||||
run_info.from,
|
||||
@@ -285,7 +285,7 @@ void Font::DrawText(cc::PaintCanvas* canvas,
|
||||
if (ShouldSkipDrawing())
|
||||
return;
|
||||
|
||||
- if (carbonyl_b64_text) {
|
||||
+ if (!carbonyl::Bridge::BitmapMode()) {
|
||||
auto string = StringView(
|
||||
text_info.text,
|
||||
text_info.from,
|
@ -0,0 +1,138 @@
|
||||
From fa52dbb68b7822ee4c01a697197e68ef1ab4a19c Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Sun, 12 Feb 2023 01:29:05 +0100
|
||||
Subject: [PATCH 11/14] Rename carbonyl::Renderer to carbonyl::Bridge
|
||||
|
||||
---
|
||||
headless/app/headless_shell.cc | 5 ++++-
|
||||
headless/app/headless_shell_main.cc | 2 +-
|
||||
headless/lib/browser/headless_browser_impl.cc | 8 ++++----
|
||||
headless/lib/browser/headless_browser_impl_aura.cc | 2 +-
|
||||
headless/lib/browser/headless_screen.cc | 2 +-
|
||||
headless/lib/browser/headless_web_contents_impl.cc | 4 ++--
|
||||
6 files changed, 13 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
|
||||
index 5b51c22ae1da3..b6a52857e8f90 100644
|
||||
--- a/headless/app/headless_shell.cc
|
||||
+++ b/headless/app/headless_shell.cc
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "base/bind.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_util.h"
|
||||
+#include "base/functional/callback.h"
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "build/branding_buildflags.h"
|
||||
@@ -92,7 +93,9 @@ void HeadlessShell::OnBrowserStart(HeadlessBrowser* browser) {
|
||||
HeadlessBrowserContext::Builder context_builder =
|
||||
browser_->CreateBrowserContextBuilder();
|
||||
|
||||
- context_builder.SetWindowSize(carbonyl::Renderer::Main()->GetSize());
|
||||
+ carbonyl::Bridge::GetCurrent()->StartRenderer();
|
||||
+
|
||||
+ context_builder.SetWindowSize(carbonyl::Bridge::GetCurrent()->GetSize());
|
||||
|
||||
// Retrieve the locale set by InitApplicationLocale() in
|
||||
// headless_content_main_delegate.cc in a way that is free of side-effects.
|
||||
diff --git a/headless/app/headless_shell_main.cc b/headless/app/headless_shell_main.cc
|
||||
index f9b8bac5c18a5..739df1ae1bd58 100644
|
||||
--- a/headless/app/headless_shell_main.cc
|
||||
+++ b/headless/app/headless_shell_main.cc
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "carbonyl/src/browser/bridge.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
- carbonyl_shell_main();
|
||||
+ carbonyl::Bridge::Main();
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
sandbox::SandboxInterfaceInfo sandbox_info = {nullptr};
|
||||
diff --git a/headless/lib/browser/headless_browser_impl.cc b/headless/lib/browser/headless_browser_impl.cc
|
||||
index fd45d215479ab..1df3ffe72c93d 100644
|
||||
--- a/headless/lib/browser/headless_browser_impl.cc
|
||||
+++ b/headless/lib/browser/headless_browser_impl.cc
|
||||
@@ -119,7 +119,7 @@ void HeadlessBrowserImpl::set_browser_main_parts(
|
||||
}
|
||||
|
||||
void HeadlessBrowserImpl::Resize() {
|
||||
- auto size = carbonyl::Renderer::GetSize();
|
||||
+ auto size = carbonyl::Bridge::GetCurrent()->Resize();
|
||||
auto rect = gfx::Rect(0, 0, size.width(), size.height());
|
||||
|
||||
for (auto* ctx: GetAllBrowserContexts()) {
|
||||
@@ -134,7 +134,7 @@ void HeadlessBrowserImpl::Resize() {
|
||||
}
|
||||
}
|
||||
|
||||
- carbonyl::Renderer::Main()->Resize();
|
||||
+ carbonyl::Bridge::GetCurrent()->Resize();
|
||||
}
|
||||
|
||||
void HeadlessBrowserImpl::OnShutdownInput() {
|
||||
@@ -279,7 +279,7 @@ void HeadlessBrowserImpl::OnKeyPressInput(char key) {
|
||||
blink::WebKeyboardEvent::Type::kRawKeyDown,
|
||||
blink::WebInputEvent::kNoModifiers,
|
||||
base::TimeTicks::Now());
|
||||
-
|
||||
+
|
||||
// TODO(fathy): support IME
|
||||
switch (key) {
|
||||
case 0x11:
|
||||
@@ -500,7 +500,7 @@ void HeadlessBrowserImpl::RunOnStartCallback() {
|
||||
}
|
||||
};
|
||||
|
||||
- carbonyl::Renderer::Main()->Listen(&delegate);
|
||||
+ carbonyl::Bridge::GetCurrent()->Listen(&delegate);
|
||||
});
|
||||
}
|
||||
|
||||
diff --git a/headless/lib/browser/headless_browser_impl_aura.cc b/headless/lib/browser/headless_browser_impl_aura.cc
|
||||
index 508660db32151..80340d9f1b3b3 100644
|
||||
--- a/headless/lib/browser/headless_browser_impl_aura.cc
|
||||
+++ b/headless/lib/browser/headless_browser_impl_aura.cc
|
||||
@@ -59,7 +59,7 @@ void HeadlessBrowserImpl::PlatformSetWebContentsBounds(
|
||||
const gfx::Rect& bounds) {
|
||||
// Browser's window bounds should contain all web contents, so that we're sure
|
||||
// that we will actually produce visible damage when taking a screenshot.
|
||||
- web_contents->window_tree_host()->SetBoundsInPixels(ScaleToEnclosedRect(bounds, carbonyl::Renderer::GetDPI()));
|
||||
+ web_contents->window_tree_host()->SetBoundsInPixels(ScaleToEnclosedRect(bounds, carbonyl::Bridge::GetCurrent()->GetDPI()));
|
||||
web_contents->window_tree_host()->window()->SetBounds(bounds);
|
||||
|
||||
gfx::NativeView native_view = web_contents->web_contents()->GetNativeView();
|
||||
diff --git a/headless/lib/browser/headless_screen.cc b/headless/lib/browser/headless_screen.cc
|
||||
index 8bf00ef5e036a..89c5ccc8d7759 100644
|
||||
--- a/headless/lib/browser/headless_screen.cc
|
||||
+++ b/headless/lib/browser/headless_screen.cc
|
||||
@@ -51,7 +51,7 @@ display::Display HeadlessScreen::GetDisplayNearestWindow(
|
||||
HeadlessScreen::HeadlessScreen(const gfx::Rect& screen_bounds) {
|
||||
static int64_t synthesized_display_id = 2000;
|
||||
display::Display display(synthesized_display_id++);
|
||||
- float dpi = carbonyl::Renderer::GetDPI();
|
||||
+ float dpi = carbonyl::Bridge::GetCurrent()->GetDPI();
|
||||
display.SetScaleAndBounds(dpi, ScaleToEnclosedRect(screen_bounds, dpi));
|
||||
ProcessDisplayChanged(display, true /* is_primary */);
|
||||
}
|
||||
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
|
||||
index fad8c3fdd2bfe..a166a08f6ea15 100644
|
||||
--- a/headless/lib/browser/headless_web_contents_impl.cc
|
||||
+++ b/headless/lib/browser/headless_web_contents_impl.cc
|
||||
@@ -400,7 +400,7 @@ void HeadlessWebContentsImpl::TitleWasSet(content::NavigationEntry* entry) {
|
||||
if (!web_contents() || !web_contents()->GetPrimaryMainFrame()->IsActive())
|
||||
return;
|
||||
|
||||
- carbonyl::Renderer::Main()->SetTitle(base::UTF16ToUTF8(entry->GetTitleForDisplay()));
|
||||
+ carbonyl::Bridge::GetCurrent()->SetTitle(base::UTF16ToUTF8(entry->GetTitleForDisplay()));
|
||||
}
|
||||
|
||||
void HeadlessWebContentsImpl::DidFinishNavigation(content::NavigationHandle* handle) {
|
||||
@@ -411,7 +411,7 @@ void HeadlessWebContentsImpl::DidFinishNavigation(content::NavigationHandle* han
|
||||
|
||||
auto& nav = web_contents()->GetController();
|
||||
|
||||
- carbonyl::Renderer::Main()->PushNav(
|
||||
+ carbonyl::Bridge::GetCurrent()->PushNav(
|
||||
handle->GetURL().spec(),
|
||||
nav.CanGoBack(),
|
||||
nav.CanGoForward()
|
@ -0,0 +1,86 @@
|
||||
From 6862e372717eff278470453e800dc693f33b873c Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Sun, 12 Feb 2023 02:20:32 +0100
|
||||
Subject: [PATCH 12/14] Create separate bridge for Blink
|
||||
|
||||
---
|
||||
.../blink/renderer/core/css/resolver/style_resolver.cc | 4 ++--
|
||||
third_party/blink/renderer/platform/BUILD.gn | 2 +-
|
||||
third_party/blink/renderer/platform/fonts/font.cc | 8 ++++----
|
||||
3 files changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
|
||||
index 7129982acf4a6..cb116ee07c8f6 100644
|
||||
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
|
||||
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
|
||||
@@ -116,7 +116,7 @@
|
||||
#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
|
||||
|
||||
-#include "carbonyl/src/browser/bridge.h"
|
||||
+#include "carbonyl/src/browser/blink.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -1043,7 +1043,7 @@ scoped_refptr<ComputedStyle> StyleResolver::ResolveStyle(
|
||||
UseCounter::Count(GetDocument(), WebFeature::kHasGlyphRelativeUnits);
|
||||
}
|
||||
|
||||
- if (!carbonyl::Bridge::BitmapMode()) {
|
||||
+ if (!carbonyl::blink::BitmapMode()) {
|
||||
auto font = state.StyleBuilder().GetFontDescription();
|
||||
FontFamily family;
|
||||
|
||||
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
|
||||
index 63fc13e44b5ae..ceb41d781acf6 100644
|
||||
--- a/third_party/blink/renderer/platform/BUILD.gn
|
||||
+++ b/third_party/blink/renderer/platform/BUILD.gn
|
||||
@@ -1678,7 +1678,7 @@ component("platform") {
|
||||
"//base/allocator:buildflags",
|
||||
"//build:chromecast_buildflags",
|
||||
"//build:chromeos_buildflags",
|
||||
- "//carbonyl/src/browser:carbonyl",
|
||||
+ "//carbonyl/src/browser:blink",
|
||||
"//cc/ipc",
|
||||
"//cc/mojo_embedder",
|
||||
"//components/paint_preview/common",
|
||||
diff --git a/third_party/blink/renderer/platform/fonts/font.cc b/third_party/blink/renderer/platform/fonts/font.cc
|
||||
index 4625300729523..3d1b463e9651c 100644
|
||||
--- a/third_party/blink/renderer/platform/fonts/font.cc
|
||||
+++ b/third_party/blink/renderer/platform/fonts/font.cc
|
||||
@@ -49,7 +49,7 @@
|
||||
#include "third_party/skia/include/core/SkTextBlob.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
|
||||
-#include "carbonyl/src/browser/bridge.h"
|
||||
+#include "carbonyl/src/browser/blink.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -158,7 +158,7 @@ void DrawBlobs(cc::PaintCanvas* canvas,
|
||||
const ShapeResultBloberizer::BlobBuffer& blobs,
|
||||
const gfx::PointF& point,
|
||||
cc::NodeId node_id = cc::kInvalidNodeId) {
|
||||
- if (!carbonyl::Bridge::BitmapMode()) {
|
||||
+ if (!carbonyl::blink::BitmapMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ void Font::DrawText(cc::PaintCanvas* canvas,
|
||||
if (ShouldSkipDrawing())
|
||||
return;
|
||||
|
||||
- if (!carbonyl::Bridge::BitmapMode()) {
|
||||
+ if (!carbonyl::blink::BitmapMode()) {
|
||||
auto string = StringView(
|
||||
run_info.run.ToStringView(),
|
||||
run_info.from,
|
||||
@@ -285,7 +285,7 @@ void Font::DrawText(cc::PaintCanvas* canvas,
|
||||
if (ShouldSkipDrawing())
|
||||
return;
|
||||
|
||||
- if (!carbonyl::Bridge::BitmapMode()) {
|
||||
+ if (!carbonyl::blink::BitmapMode()) {
|
||||
auto string = StringView(
|
||||
text_info.text,
|
||||
text_info.from,
|
1008
chromium/patches/chromium/0013-Refactor-rendering-bridge.patch
Normal file
1008
chromium/patches/chromium/0013-Refactor-rendering-bridge.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,46 @@
|
||||
From 2275364ee7e16ba6b46f0f339e34326d4a8c7584 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Mon, 13 Feb 2023 17:13:38 +0100
|
||||
Subject: [PATCH 14/14] Move Skia text rendering control to bridge
|
||||
|
||||
---
|
||||
content/renderer/render_frame_impl.cc | 5 -----
|
||||
skia/BUILD.gn | 2 +-
|
||||
2 files changed, 1 insertion(+), 6 deletions(-)
|
||||
|
||||
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
|
||||
index 379cf6c58b2b0..891efd6a9d796 100644
|
||||
--- a/content/renderer/render_frame_impl.cc
|
||||
+++ b/content/renderer/render_frame_impl.cc
|
||||
@@ -285,7 +285,6 @@
|
||||
#include "third_party/skia/include/svg/SkSVGCanvas.h"
|
||||
#include "third_party/skia/include/utils/SkBase64.h"
|
||||
#include "third_party/skia/src/text/GlyphRun.h"
|
||||
-#include "third_party/skia/src/core/SkBitmapDevice.h"
|
||||
#include "third_party/skia/src/core/SkClipStackDevice.h"
|
||||
#include "third_party/skia/src/core/SkDevice.h"
|
||||
#include "third_party/skia/src/core/SkFontPriv.h"
|
||||
@@ -2244,10 +2243,6 @@ void RenderFrameImpl::Initialize(blink::WebFrame* parent) {
|
||||
);
|
||||
|
||||
host->ObserveTerminalRender(render_callback_);
|
||||
-
|
||||
- if (!carbonyl::Bridge::BitmapMode()) {
|
||||
- SkBitmapDevice::DisableTextRendering();
|
||||
- }
|
||||
}
|
||||
|
||||
void RenderFrameImpl::GetInterface(
|
||||
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
|
||||
index b330273c16db3..297ffacf073fa 100644
|
||||
--- a/skia/BUILD.gn
|
||||
+++ b/skia/BUILD.gn
|
||||
@@ -203,7 +203,7 @@ source_set("skcms") {
|
||||
}
|
||||
|
||||
component("skia") {
|
||||
- deps = []
|
||||
+ deps = [ "//carbonyl/src/browser:bridge" ]
|
||||
sources = [
|
||||
# Chrome sources.
|
||||
"config/SkUserConfig.h",
|
@ -1,27 +1,35 @@
|
||||
From 5d889651fd377f5fe51feafc6efbba31a72c6b03 Mon Sep 17 00:00:00 2001
|
||||
From 218fbf4bba772c465712c4ea442adb57968e9c22 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:37:53 +0100
|
||||
Date: Mon, 13 Feb 2023 17:18:18 +0100
|
||||
Subject: [PATCH 1/2] Disable text rendering
|
||||
|
||||
---
|
||||
src/core/SkBitmapDevice.cpp | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
src/core/SkBitmapDevice.cpp | 8 ++++++--
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
|
||||
index b497d690f7..5e4ee2b1ce 100644
|
||||
index b497d690f7..9631f47967 100644
|
||||
--- a/src/core/SkBitmapDevice.cpp
|
||||
+++ b/src/core/SkBitmapDevice.cpp
|
||||
@@ -522,8 +522,8 @@ void SkBitmapDevice::onDrawGlyphRunList(SkCanvas* canvas,
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "src/image/SkImage_Base.h"
|
||||
#include "src/text/GlyphRun.h"
|
||||
|
||||
+#include "carbonyl/src/browser/bridge.h"
|
||||
+
|
||||
struct Bounder {
|
||||
SkRect fBounds;
|
||||
bool fHasBounds;
|
||||
@@ -522,8 +524,10 @@ void SkBitmapDevice::onDrawGlyphRunList(SkCanvas* canvas,
|
||||
const sktext::GlyphRunList& glyphRunList,
|
||||
const SkPaint& initialPaint,
|
||||
const SkPaint& drawingPaint) {
|
||||
- SkASSERT(!glyphRunList.hasRSXForm());
|
||||
- LOOP_TILER( drawGlyphRunList(canvas, &fGlyphPainter, glyphRunList, drawingPaint), nullptr )
|
||||
+ // SkASSERT(!glyphRunList.hasRSXForm());
|
||||
+ // LOOP_TILER( drawGlyphRunList(canvas, &fGlyphPainter, glyphRunList, drawingPaint), nullptr )
|
||||
+ if (carbonyl::Bridge::BitmapMode()) {
|
||||
+ SkASSERT(!glyphRunList.hasRSXForm());
|
||||
+ LOOP_TILER( drawGlyphRunList(canvas, &fGlyphPainter, glyphRunList, drawingPaint), nullptr )
|
||||
+ }
|
||||
}
|
||||
|
||||
void SkBitmapDevice::drawVertices(const SkVertices* vertices,
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
From a2a7ed0af31ef13bd8e30be86bcc58fa2c34b41c Mon Sep 17 00:00:00 2001
|
||||
From a271b203a2b60f0cd450bda0fa2cc14885f1d9a8 Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Thu, 9 Feb 2023 03:38:05 +0100
|
||||
Subject: [PATCH 2/2] Export some private APIs
|
||||
@ -77,6 +77,3 @@ index 95ca905bf1..a31aba8e2b 100644
|
||||
public:
|
||||
/* This is the size we use when we ask for a glyph's path. We then
|
||||
* post-transform it as we draw to match the request.
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
22
chromium/patches/webrtc/0001-Disable-GIO-on-Linux.patch
Normal file
22
chromium/patches/webrtc/0001-Disable-GIO-on-Linux.patch
Normal file
@ -0,0 +1,22 @@
|
||||
From 5ad57c96f23739717bcea018baf2bc8f4157b01d Mon Sep 17 00:00:00 2001
|
||||
From: Fathy Boundjadj <hey@fathy.fr>
|
||||
Date: Mon, 13 Feb 2023 16:37:40 +0100
|
||||
Subject: [PATCH] Disable GIO on Linux
|
||||
|
||||
---
|
||||
modules/portal/BUILD.gn | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/modules/portal/BUILD.gn b/modules/portal/BUILD.gn
|
||||
index 36bcb53e8e..822688b1dc 100644
|
||||
--- a/modules/portal/BUILD.gn
|
||||
+++ b/modules/portal/BUILD.gn
|
||||
@@ -85,7 +85,7 @@ if ((is_linux || is_chromeos) && rtc_use_pipewire) {
|
||||
# `rtc_use_pipewire` is not set, which causes pipewire_config to not be
|
||||
# included in targets. More details in: webrtc:13898
|
||||
if (is_linux && !is_castos) {
|
||||
- defines += [ "WEBRTC_USE_GIO" ]
|
||||
+ $ defines += [ "WEBRTC_USE_GIO" ]
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- "$(pwd)")
|
||||
export INSTALL_DEPOT_TOOLS="true"
|
||||
|
||||
cd $CARBONYL_ROOT
|
||||
cd "$CARBONYL_ROOT"
|
||||
source scripts/env.sh
|
||||
|
||||
triple=$(scripts/platform-triple.sh "$2")
|
||||
target="$1"
|
||||
cpu="$2"
|
||||
|
||||
if [ -z "$MACOSX_DEPLOYMENT_TARGET" ]; then
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
if [ ! -z "$target" ]; then
|
||||
shift
|
||||
fi
|
||||
if [ ! -z "$cpu" ]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
cargo build --target "$triple" --release
|
||||
triple=$(scripts/platform-triple.sh "$cpu")
|
||||
|
||||
if [ -z "$CARBONYL_SKIP_CARGO_BUILD" ]; then
|
||||
if [ -z "$MACOSX_DEPLOYMENT_TARGET" ]; then
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
fi
|
||||
|
||||
cargo build --target "$triple" --release
|
||||
fi
|
||||
|
||||
if [ -f "build/$triple/release/libcarbonyl.dylib" ]; then
|
||||
cp "build/$triple/release/libcarbonyl.dylib" "$CHROMIUM_SRC/out/$target"
|
||||
install_name_tool \
|
||||
-id @executable_path/libcarbonyl.dylib \
|
||||
"build/$triple/release/libcarbonyl.dylib"
|
||||
else
|
||||
cp "build/$triple/release/libcarbonyl.so" "$CHROMIUM_SRC/out/$target"
|
||||
fi
|
||||
|
||||
cd "$CHROMIUM_SRC/out/$1"
|
||||
cd "$CHROMIUM_SRC/out/$target"
|
||||
|
||||
ninja headless:headless_shell
|
||||
ninja headless:headless_shell "$@"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- $(pwd))
|
||||
export SKIP_DEPOT_TOOLS="true"
|
||||
|
||||
cd "$CARBONYL_ROOT"
|
||||
source "scripts/env.sh"
|
||||
|
@ -2,15 +2,16 @@
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- "$(pwd)")
|
||||
|
||||
source "$CARBONYL_ROOT/scripts/env.sh"
|
||||
cd "$CARBONYL_ROOT"
|
||||
source "scripts/env.sh"
|
||||
|
||||
target="$1"
|
||||
cpu="$2"
|
||||
cpu="$1"
|
||||
|
||||
build_dir="$CARBONYL_ROOT/build/browser/$cpu"
|
||||
triple=$(scripts/platform-triple.sh "$cpu")
|
||||
triple=$(scripts/platform-triple.sh "$cpu" linux)
|
||||
build_dir="build/docker/$triple"
|
||||
|
||||
rm -rf "$build_dir"
|
||||
mkdir -p "build/docker"
|
||||
cp -r "$CARBONYL_ROOT/build/pre-built/$triple" "$build_dir"
|
||||
cp "$CARBONYL_ROOT/Dockerfile" "$build_dir"
|
||||
|
||||
|
@ -20,7 +20,7 @@ fi
|
||||
|
||||
export PATH="$PATH:$DEPOT_TOOLS_ROOT"
|
||||
|
||||
if [ ! -f "$DEPOT_TOOLS_ROOT/README.md" ] && [ -z "$SKIP_DEPOT_TOOLS" ]; then
|
||||
if [ "$INSTALL_DEPOT_TOOLS" = "true" ] && [ ! -f "$DEPOT_TOOLS_ROOT/README.md" ]; then
|
||||
echo "depot_tools not found, fetching submodule.."
|
||||
|
||||
git -C "$CARBONYL_ROOT" submodule update --init --recursive
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- "$(pwd)")
|
||||
export INSTALL_DEPOT_TOOLS="true"
|
||||
|
||||
source "$CARBONYL_ROOT/scripts/env.sh"
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- "$(pwd)")
|
||||
export INSTALL_DEPOT_TOOLS="true"
|
||||
|
||||
source "$CARBONYL_ROOT/scripts/env.sh"
|
||||
|
||||
|
153
scripts/npm-package.mjs
Normal file
153
scripts/npm-package.mjs
Normal file
@ -0,0 +1,153 @@
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const pkg = JSON.parse(
|
||||
await fs.readFile(path.resolve(dirname, "../package.json"), "utf-8")
|
||||
);
|
||||
const version = process.env.RELEASE_MODE
|
||||
? pkg.version
|
||||
: `${pkg.version}-next.${process.env.VERSION_ID}`;
|
||||
const manifest = {
|
||||
version,
|
||||
license: "BSD-3-Clause",
|
||||
description: "Chromium running in your terminal",
|
||||
homepage: "https://github.com/fathyb/carbonyl",
|
||||
repository: "fathyb/carbonyl",
|
||||
bugs: "https://github.com/fathyb/carbonyl/issues",
|
||||
author: {
|
||||
name: "Fathy Boundjadj",
|
||||
email: "hey@fathy.fr",
|
||||
url: "https://fathy.fr",
|
||||
},
|
||||
};
|
||||
|
||||
async function buildMain() {
|
||||
const root = path.resolve(dirname, "../build/packages/carbonyl");
|
||||
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
await fs.mkdir(root, { recursive: true });
|
||||
await Promise.all([
|
||||
Promise.all(
|
||||
["readme.md", "license.md"].map((file) =>
|
||||
fs.cp(path.join(dirname, "..", file), path.join(root, file))
|
||||
)
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "carbonyl",
|
||||
...manifest,
|
||||
files: ["index.sh", "index.sh.js", "index.js"],
|
||||
bin: { carbonyl: "index.sh" },
|
||||
optionalDependencies: {
|
||||
"@fathyb/carbonyl-linux-amd64": version,
|
||||
"@fathyb/carbonyl-linux-arm64": version,
|
||||
"@fathyb/carbonyl-macos-amd64": version,
|
||||
"@fathyb/carbonyl-macos-arm64": version,
|
||||
},
|
||||
},
|
||||
null,
|
||||
4
|
||||
)
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, "index.sh"),
|
||||
["#!/usr/bin/env bash", `"$(node "$(realpath "$0")".js)" "$@"`].join(
|
||||
"\n"
|
||||
),
|
||||
{ mode: "755" }
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, "index.sh.js"),
|
||||
`process.stdout.write(require('.'))`
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, "index.js"),
|
||||
`
|
||||
const archs = {
|
||||
x64: 'amd64',
|
||||
arm64: 'arm64',
|
||||
}
|
||||
const platforms = {
|
||||
linux: 'linux',
|
||||
darwin: 'macos',
|
||||
}
|
||||
|
||||
const arch = archs[process.arch]
|
||||
const platform = platforms[process.platform]
|
||||
|
||||
if (!arch) {
|
||||
throw new Error('Processor architecture not supported: ' + process.arch)
|
||||
}
|
||||
if (!platform) {
|
||||
throw new Error('Platform not supported: ' + process.platform)
|
||||
}
|
||||
|
||||
module.exports = require('@fathyb/carbonyl-' + platform + '-' + arch)
|
||||
`
|
||||
),
|
||||
]);
|
||||
|
||||
return root;
|
||||
}
|
||||
async function buildPlatform([os, npmOs, llvmOs], [cpu, npmCpu, llvmCpu]) {
|
||||
const pkg = `carbonyl-${os}-${cpu}`;
|
||||
const root = path.resolve(dirname, `../build/packages/${pkg}`);
|
||||
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
await fs.mkdir(root, { recursive: true });
|
||||
await Promise.all([
|
||||
Promise.all(
|
||||
["readme.md", "license.md"].map((file) =>
|
||||
fs.cp(path.join(dirname, "..", file), path.join(root, file))
|
||||
)
|
||||
),
|
||||
fs.cp(
|
||||
path.join(dirname, `../build/pre-built/${llvmCpu}-${llvmOs}`),
|
||||
path.join(root, "build"),
|
||||
{ recursive: true }
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: `@fathyb/${pkg}`,
|
||||
...manifest,
|
||||
files: ["build", "index.js"],
|
||||
os: [npmOs],
|
||||
cpu: [npmCpu],
|
||||
},
|
||||
null,
|
||||
4
|
||||
)
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, "index.js"),
|
||||
`module.exports = __dirname + '/build/carbonyl'`
|
||||
),
|
||||
]);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
const [root, platforms] = await Promise.all([
|
||||
buildMain(),
|
||||
|
||||
Promise.all(
|
||||
[
|
||||
["macos", "darwin", "apple-darwin"],
|
||||
["linux", "linux", "unknown-linux-gnu"],
|
||||
].map(
|
||||
async (os) =>
|
||||
await Promise.all(
|
||||
[
|
||||
["arm64", "arm64", "aarch64"],
|
||||
["amd64", "x64", "x86_64"],
|
||||
].map(async (cpu) => await buildPlatform(os, cpu))
|
||||
)
|
||||
)
|
||||
),
|
||||
]);
|
9
scripts/npm-package.sh
Executable file
9
scripts/npm-package.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- $(pwd))
|
||||
|
||||
cd "$CARBONYL_ROOT"
|
||||
source "scripts/env.sh"
|
||||
|
||||
VERSION_ID="$(git rev-parse --short HEAD)" \
|
||||
node "$CARBONYL_ROOT/scripts/npm-package.mjs"
|
@ -1,149 +0,0 @@
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const pkg = JSON.parse(await fs.readFile(path.resolve(dirname, '../package.json'), 'utf-8'))
|
||||
const version = process.env.RELEASE_MODE ? pkg.version : `${pkg.version}-next.${process.env.VERSION_ID}`
|
||||
const manifest = {
|
||||
version,
|
||||
license: 'BSD-3-Clause',
|
||||
description: 'Chromium running in your terminal',
|
||||
homepage: 'https://github.com/fathyb/carbonyl',
|
||||
repository: 'fathyb/carbonyl',
|
||||
bugs: 'https://github.com/fathyb/carbonyl/issues',
|
||||
author: {
|
||||
name: 'Fathy Boundjadj',
|
||||
email: 'hey@fathy.fr',
|
||||
url: 'https://fathy.fr'
|
||||
}
|
||||
}
|
||||
|
||||
async function buildMain() {
|
||||
const root = path.resolve(dirname, '../build/packages/carbonyl')
|
||||
|
||||
await fs.rm(root, { recursive: true, force: true })
|
||||
await fs.mkdir(root, { recursive: true })
|
||||
await Promise.all([
|
||||
Promise.all(
|
||||
['readme.md', 'license.md'].map(file =>
|
||||
fs.cp(path.join(dirname, '..', file), path.join(root, file)),
|
||||
)
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, 'package.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: 'carbonyl',
|
||||
...manifest,
|
||||
files: ['index.sh', 'index.sh.js', 'index.js'],
|
||||
bin: { carbonyl: 'index.sh' },
|
||||
optionalDependencies: {
|
||||
'@fathyb/carbonyl-linux-amd64': version,
|
||||
'@fathyb/carbonyl-linux-arm64': version,
|
||||
'@fathyb/carbonyl-macos-amd64': version,
|
||||
'@fathyb/carbonyl-macos-arm64': version
|
||||
}
|
||||
},
|
||||
null,
|
||||
4
|
||||
)
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, 'index.sh'),
|
||||
[
|
||||
'#!/usr/bin/env bash',
|
||||
`"$(node "$(realpath "$0")".js)" "$@"`
|
||||
].join('\n'),
|
||||
{ mode: '755' }
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, 'index.sh.js'),
|
||||
`process.stdout.write(require('.'))`
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, 'index.js'),
|
||||
`
|
||||
function tryModule(name) {
|
||||
try {
|
||||
return require(name)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const path = (
|
||||
tryModule('@fathyb/carbonyl-linux-amd64') ||
|
||||
tryModule('@fathyb/carbonyl-linux-arm64') ||
|
||||
tryModule('@fathyb/carbonyl-macos-amd64') ||
|
||||
tryModule('@fathyb/carbonyl-macos-arm64')
|
||||
)
|
||||
|
||||
if (path) {
|
||||
module.exports = path
|
||||
} else {
|
||||
throw new Error('Could not find a Carbonyl runtime installed')
|
||||
}
|
||||
`
|
||||
),
|
||||
])
|
||||
|
||||
return root
|
||||
}
|
||||
async function buildPlatform([os, npmOs, llvmOs], [cpu, npmCpu, llvmCpu]) {
|
||||
const pkg = `carbonyl-${os}-${cpu}`
|
||||
const root = path.resolve(dirname, `../build/packages/${pkg}`)
|
||||
|
||||
await fs.rm(root, { recursive: true, force: true })
|
||||
await fs.mkdir(root, { recursive: true })
|
||||
await Promise.all([
|
||||
Promise.all(
|
||||
['readme.md', 'license.md'].map(file =>
|
||||
fs.cp(path.join(dirname, '..', file), path.join(root, file)),
|
||||
)
|
||||
),
|
||||
fs.cp(
|
||||
path.join(dirname, `../build/pre-built/${llvmCpu}-${llvmOs}`),
|
||||
path.join(root, 'build'),
|
||||
{ recursive: true }
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, 'package.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: `@fathyb/${pkg}`,
|
||||
...manifest,
|
||||
files: ['build', 'index.js'],
|
||||
os: [npmOs],
|
||||
cpu: [npmCpu],
|
||||
},
|
||||
null,
|
||||
4
|
||||
)
|
||||
),
|
||||
fs.writeFile(
|
||||
path.join(root, 'index.js'),
|
||||
`module.exports = __dirname + '/build/carbonyl'`
|
||||
)
|
||||
])
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
const [root, platforms] = await Promise.all([
|
||||
buildMain(),
|
||||
|
||||
Promise.all([
|
||||
['macos', 'darwin', 'apple-darwin'],
|
||||
['linux', 'linux', 'unknown-linux-gnu']
|
||||
].map(async (os) =>
|
||||
await Promise.all(
|
||||
[
|
||||
['arm64', 'arm64', 'aarch64'],
|
||||
['amd64', 'x64', 'x86_64']
|
||||
].map(async (cpu) => await buildPlatform(os, cpu))
|
||||
)
|
||||
)),
|
||||
])
|
||||
|
||||
|
@ -1,27 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- $(pwd))
|
||||
export SKIP_DEPOT_TOOLS="true"
|
||||
|
||||
cd "$CARBONYL_ROOT"
|
||||
source "scripts/env.sh"
|
||||
|
||||
VERSION_ID="$(git rev-parse --short HEAD)" \
|
||||
node "$CARBONYL_ROOT/scripts/npm-publish.mjs"
|
||||
cd "build/packages"
|
||||
|
||||
cd "$CARBONYL_ROOT/build/packages"
|
||||
if [ -z "$CARBONYL_PUBLISH_PLATFORM" ] && [ -z "$CARBONYL_PUBLISH_ARCH" ]; then
|
||||
cd "carbonyl"
|
||||
else
|
||||
cd "carbonyl-$CARBONYL_PUBLISH_PLATFORM-$CARBONYL_PUBLISH_ARCH"
|
||||
fi
|
||||
|
||||
cd carbonyl-linux-amd64
|
||||
yarn publish --non-interactive --access public "$@"
|
||||
|
||||
cd ../carbonyl-linux-arm64
|
||||
yarn publish --non-interactive --access public "$@"
|
||||
|
||||
cd ../carbonyl-macos-amd64
|
||||
yarn publish --non-interactive --access public "$@"
|
||||
|
||||
cd ../carbonyl-macos-arm64
|
||||
yarn publish --non-interactive --access public "$@"
|
||||
|
||||
cd ../carbonyl
|
||||
yarn publish --non-interactive --access public "$@"
|
||||
|
@ -8,6 +8,7 @@ cd "$CHROMIUM_SRC"
|
||||
|
||||
chromium_upstream="92da8189788b1b373cbd3348f73d695dfdc521b6"
|
||||
skia_upstream="486deb23bc2a4d3d09c66fef52c2ad64d8b4f761"
|
||||
webrtc_upstream="727080cbacd58a2f303ed8a03f0264fe1493e47a"
|
||||
|
||||
if [[ "$1" == "apply" ]]; then
|
||||
echo "Stashing Chromium changes.."
|
||||
@ -20,7 +21,7 @@ if [[ "$1" == "apply" ]]; then
|
||||
"$CARBONYL_ROOT/scripts/restore-mtime.sh" "$chromium_upstream"
|
||||
|
||||
echo "Stashing Skia changes.."
|
||||
cd third_party/skia
|
||||
cd "$CHROMIUM_SRC/third_party/skia"
|
||||
git add -A .
|
||||
git stash
|
||||
|
||||
@ -29,6 +30,16 @@ if [[ "$1" == "apply" ]]; then
|
||||
git am --committer-date-is-author-date "$CARBONYL_ROOT/chromium/patches/skia"/*
|
||||
"$CARBONYL_ROOT/scripts/restore-mtime.sh" "$skia_upstream"
|
||||
|
||||
echo "Stashing WebRTC changes.."
|
||||
cd "$CHROMIUM_SRC/third_party/webrtc"
|
||||
git add -A .
|
||||
git stash
|
||||
|
||||
echo "Applying WebRTC patches.."
|
||||
git checkout "$webrtc_upstream"
|
||||
git am --committer-date-is-author-date "$CARBONYL_ROOT/chromium/patches/webrtc"/*
|
||||
"$CARBONYL_ROOT/scripts/restore-mtime.sh" "$webrtc_upstream"
|
||||
|
||||
echo "Patches successfully applied"
|
||||
elif [[ "$1" == "save" ]]; then
|
||||
if [[ -d carbonyl ]]; then
|
||||
@ -36,11 +47,18 @@ elif [[ "$1" == "save" ]]; then
|
||||
fi
|
||||
|
||||
echo "Updating Chromium patches.."
|
||||
git format-patch --output-directory "$CARBONYL_ROOT/chromium/patches/chromium" "$chromium_upstream"
|
||||
rm -rf "$CARBONYL_ROOT/chromium/patches/chromium"
|
||||
git format-patch --no-signature --output-directory "$CARBONYL_ROOT/chromium/patches/chromium" "$chromium_upstream"
|
||||
|
||||
echo "Updating Skia patches.."
|
||||
cd third_party/skia
|
||||
git format-patch --output-directory "$CARBONYL_ROOT/chromium/patches/skia" "$skia_upstream"
|
||||
cd "$CHROMIUM_SRC/third_party/skia"
|
||||
rm -rf "$CARBONYL_ROOT/chromium/patches/skia"
|
||||
git format-patch --no-signature --output-directory "$CARBONYL_ROOT/chromium/patches/skia" "$skia_upstream"
|
||||
|
||||
echo "Updating WebRTC patches.."
|
||||
cd "$CHROMIUM_SRC/third_party/webrtc"
|
||||
rm -rf "$CARBONYL_ROOT/chromium/patches/webrtc"
|
||||
git format-patch --no-signature --output-directory "$CARBONYL_ROOT/chromium/patches/webrtc" "$webrtc_upstream"
|
||||
|
||||
echo "Patches successfully updated"
|
||||
else
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- $(pwd))
|
||||
export SKIP_DEPOT_TOOLS="true"
|
||||
|
||||
cd "$CARBONYL_ROOT"
|
||||
source "scripts/env.sh"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CARBONYL_ROOT=$(cd $(dirname -- "$0") && dirname -- $(pwd))
|
||||
export SKIP_DEPOT_TOOLS="true"
|
||||
|
||||
cd "$CARBONYL_ROOT"
|
||||
source "scripts/env.sh"
|
||||
|
@ -10,32 +10,37 @@ mojom("mojom") {
|
||||
]
|
||||
}
|
||||
|
||||
component("carbonyl") {
|
||||
component("bridge") {
|
||||
output_name = "carbonyl_bridge"
|
||||
defines = [ "CARBONYL_BRIDGE_IMPLEMENTATION" ]
|
||||
sources = [
|
||||
"bridge.cc",
|
||||
"bridge.h",
|
||||
]
|
||||
}
|
||||
|
||||
component("viz") {
|
||||
output_name = "carbonyl_viz"
|
||||
defines = [ "CARBONYL_VIZ_IMPLEMENTATION" ]
|
||||
sources = [
|
||||
"host_display_client.cc",
|
||||
"host_display_client.h",
|
||||
"render_service_impl.cc",
|
||||
"render_service_impl.h",
|
||||
"software_output_device_proxy.cc",
|
||||
"software_output_device_proxy.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":mojom",
|
||||
":renderer",
|
||||
"//base",
|
||||
"//components/viz/host",
|
||||
"//services/viz/privileged/mojom/compositing",
|
||||
"//ui/compositor",
|
||||
"//ui/display",
|
||||
"//services/viz/privileged/mojom",
|
||||
]
|
||||
}
|
||||
|
||||
config("lib") {
|
||||
target = ""
|
||||
|
||||
if (target_cpu == "x64") {
|
||||
if (current_cpu == "x64") {
|
||||
target += "x86_64-"
|
||||
} else if (target_cpu == "arm64") {
|
||||
} else if (current_cpu == "arm64") {
|
||||
target += "aarch64-"
|
||||
}
|
||||
|
||||
@ -48,3 +53,22 @@ component("carbonyl") {
|
||||
libs = ["carbonyl"]
|
||||
lib_dirs = ["//carbonyl/build/$target/release"]
|
||||
}
|
||||
|
||||
component("renderer") {
|
||||
output_name = "carbonyl_renderer"
|
||||
defines = [ "CARBONYL_RENDERER_IMPLEMENTATION" ]
|
||||
sources = [
|
||||
"render_service_impl.cc",
|
||||
"render_service_impl.h",
|
||||
"renderer.cc",
|
||||
"renderer.h",
|
||||
]
|
||||
|
||||
configs += [ ":lib" ]
|
||||
deps = [
|
||||
":mojom",
|
||||
":bridge",
|
||||
"//base",
|
||||
"//skia"
|
||||
]
|
||||
}
|
||||
|
@ -5,10 +5,41 @@ headless_use_embedded_resources = true
|
||||
ffmpeg_branding = "Chrome"
|
||||
proprietary_codecs = true
|
||||
|
||||
# Disable unused dependencies
|
||||
ozone_platform = "headless"
|
||||
ozone_platform_x11 = false
|
||||
|
||||
use_static_angle = true
|
||||
|
||||
use_qt = false
|
||||
use_gio = false
|
||||
use_gtk = false
|
||||
use_cups = false
|
||||
use_dbus = false
|
||||
use_glib = false
|
||||
use_libpci = false
|
||||
use_kerberos = false
|
||||
use_vaapi_x11 = false
|
||||
use_xkbcommon = false
|
||||
|
||||
angle_use_x11 = false
|
||||
angle_use_wayland = false
|
||||
|
||||
rtc_use_x11 = false
|
||||
rtc_use_pipewire = false
|
||||
rtc_use_x11_extensions = false
|
||||
|
||||
# Linux only
|
||||
# use_wayland_gbm = false
|
||||
# use_system_libdrm = false
|
||||
# use_system_minigbm = false
|
||||
|
||||
# Disable unused features
|
||||
enable_pdf = false
|
||||
enable_nacl = false
|
||||
enable_ppapi = false
|
||||
enable_printing = false
|
||||
enable_print_content_analysis = false
|
||||
enable_plugins = false
|
||||
enable_rust_json = false
|
||||
enable_tagged_pdf = false
|
||||
|
@ -1,114 +1,27 @@
|
||||
#include "carbonyl/src/browser/bridge.h"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
namespace {
|
||||
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct carbonyl_renderer* carbonyl_renderer_create();
|
||||
void carbonyl_renderer_resize(struct carbonyl_renderer* renderer);
|
||||
void carbonyl_output_get_size(struct carbonyl_bridge_size* size);
|
||||
void carbonyl_renderer_push_nav(struct carbonyl_renderer* renderer, const char* url, bool can_go_back, bool can_go_forward);
|
||||
void carbonyl_renderer_set_title(struct carbonyl_renderer* renderer, const char* title);
|
||||
void carbonyl_renderer_clear_text(struct carbonyl_renderer* renderer);
|
||||
void carbonyl_input_listen(struct carbonyl_renderer* renderer, const struct carbonyl_bridge_browser_delegate* delegate);
|
||||
void carbonyl_renderer_draw_text(
|
||||
struct carbonyl_renderer* renderer,
|
||||
const char* title,
|
||||
const struct carbonyl_bridge_rect* rect,
|
||||
const struct carbonyl_bridge_color* color
|
||||
);
|
||||
void carbonyl_renderer_draw_background(
|
||||
struct carbonyl_renderer* renderer,
|
||||
const unsigned char* pixels,
|
||||
size_t pixels_size,
|
||||
const struct carbonyl_bridge_rect* rect
|
||||
);
|
||||
float dpi_ = 0.0;
|
||||
bool bitmap_mode_ = false;
|
||||
|
||||
}
|
||||
|
||||
namespace carbonyl {
|
||||
|
||||
namespace {
|
||||
static std::unique_ptr<Renderer> globalInstance;
|
||||
void Bridge::Resize() {}
|
||||
|
||||
float Bridge::GetDPI() {
|
||||
return dpi_;
|
||||
}
|
||||
|
||||
Renderer::Renderer(struct carbonyl_renderer* ptr): ptr_(ptr) {}
|
||||
|
||||
Renderer* Renderer::Main() {
|
||||
if (!globalInstance) {
|
||||
globalInstance = std::unique_ptr<Renderer>(
|
||||
new Renderer(carbonyl_renderer_create())
|
||||
);
|
||||
}
|
||||
|
||||
return globalInstance.get();
|
||||
bool Bridge::BitmapMode() {
|
||||
return bitmap_mode_;
|
||||
}
|
||||
|
||||
gfx::Size Renderer::GetSize() {
|
||||
struct carbonyl_bridge_size size;
|
||||
|
||||
carbonyl_output_get_size(&size);
|
||||
|
||||
return gfx::Size(size.width, size.height);
|
||||
}
|
||||
|
||||
void Renderer::Resize() {
|
||||
carbonyl_renderer_resize(ptr_);
|
||||
}
|
||||
|
||||
void Renderer::Listen(const struct carbonyl_bridge_browser_delegate* delegate) {
|
||||
carbonyl_input_listen(ptr_, delegate);
|
||||
}
|
||||
|
||||
void Renderer::PushNav(const std::string& url, bool can_go_back, bool can_go_forward) {
|
||||
if (!url.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
carbonyl_renderer_push_nav(ptr_, url.c_str(), can_go_back, can_go_forward);
|
||||
}
|
||||
|
||||
void Renderer::SetTitle(const std::string& title) {
|
||||
if (!title.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
carbonyl_renderer_set_title(ptr_, title.c_str());
|
||||
}
|
||||
|
||||
void Renderer::ClearText() {
|
||||
carbonyl_renderer_clear_text(ptr_);
|
||||
}
|
||||
|
||||
void Renderer::DrawText(const std::string& text, const gfx::RectF& bounds, uint32_t sk_color) {
|
||||
struct carbonyl_bridge_rect rect;
|
||||
struct carbonyl_bridge_color color;
|
||||
|
||||
rect.origin.x = bounds.x();
|
||||
rect.origin.y = bounds.y();
|
||||
rect.size.width = bounds.width();
|
||||
rect.size.height = bounds.height();
|
||||
|
||||
color.r = SkColorGetR(sk_color);
|
||||
color.g = SkColorGetG(sk_color);
|
||||
color.b = SkColorGetB(sk_color);
|
||||
|
||||
carbonyl_renderer_draw_text(ptr_, text.c_str(), &rect, &color);
|
||||
}
|
||||
|
||||
void Renderer::DrawBackground(const unsigned char* pixels, size_t pixels_size, const gfx::Rect& bounds) {
|
||||
struct carbonyl_bridge_rect rect;
|
||||
|
||||
rect.origin.x = bounds.x();
|
||||
rect.origin.y = bounds.y();
|
||||
rect.size.width = bounds.width();
|
||||
rect.size.height = bounds.height();
|
||||
|
||||
carbonyl_renderer_draw_background(ptr_, pixels, pixels_size, &rect);
|
||||
void Bridge::Configure(float dpi, bool bitmap_mode) {
|
||||
dpi_ = dpi;
|
||||
bitmap_mode_ = bitmap_mode;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,68 +1,22 @@
|
||||
#ifndef CARBONYL_SRC_BROWSER_BRIDGE_H_
|
||||
#define CARBONYL_SRC_BROWSER_BRIDGE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct carbonyl_renderer;
|
||||
|
||||
struct carbonyl_bridge_size {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
};
|
||||
struct carbonyl_bridge_point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
};
|
||||
struct carbonyl_bridge_rect {
|
||||
struct carbonyl_bridge_point origin;
|
||||
struct carbonyl_bridge_size size;
|
||||
};
|
||||
struct carbonyl_bridge_color {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
struct carbonyl_bridge_browser_delegate {
|
||||
void (*shutdown) ();
|
||||
void (*refresh) ();
|
||||
void (*go_to) (const char* url);
|
||||
void (*go_back) ();
|
||||
void (*go_forward) ();
|
||||
void (*scroll) (int);
|
||||
void (*key_press) (char);
|
||||
void (*mouse_up) (unsigned int, unsigned int);
|
||||
void (*mouse_down) (unsigned int, unsigned int);
|
||||
void (*mouse_move) (unsigned int, unsigned int);
|
||||
void (*post_task) (void (*)(void*), void*);
|
||||
};
|
||||
|
||||
void carbonyl_shell_main();
|
||||
|
||||
} /* end extern "C" */
|
||||
#include "carbonyl/src/browser/export.h"
|
||||
|
||||
namespace carbonyl {
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
static Renderer* Main();
|
||||
static gfx::Size GetSize();
|
||||
class Renderer;
|
||||
|
||||
void Resize();
|
||||
void Listen(const struct carbonyl_bridge_browser_delegate* delegate);
|
||||
void PushNav(const std::string& url, bool can_go_back, bool can_go_forward);
|
||||
void SetTitle(const std::string& title);
|
||||
void ClearText();
|
||||
void DrawText(const std::string& text, const gfx::RectF& bounds, uint32_t color);
|
||||
void DrawBackground(const unsigned char* pixels, size_t pixels_size, const gfx::Rect& bounds);
|
||||
class CARBONYL_BRIDGE_EXPORT Bridge {
|
||||
public:
|
||||
static float GetDPI();
|
||||
static bool BitmapMode();
|
||||
|
||||
private:
|
||||
Renderer(struct carbonyl_renderer* ptr);
|
||||
friend class Renderer;
|
||||
|
||||
struct carbonyl_renderer* ptr_;
|
||||
static void Resize();
|
||||
static void Configure(float dpi, bool bitmap_mode);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,57 +1,94 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::mpsc;
|
||||
use std::{env, io};
|
||||
use std::sync::{mpsc, Mutex};
|
||||
use std::{env, io, thread};
|
||||
|
||||
use libc::{c_char, c_int, c_uchar, c_uint, c_void, size_t};
|
||||
use libc::{c_char, c_float, c_int, c_uchar, c_uint, c_void, size_t};
|
||||
|
||||
use crate::cli;
|
||||
use crate::cli::{CommandLine, CommandLineProgram, EnvVar};
|
||||
use crate::gfx::{Cast, Color, Point, Rect, Size};
|
||||
use crate::output::Renderer;
|
||||
use crate::output::{RenderThread, Window};
|
||||
use crate::ui::navigation::NavigationAction;
|
||||
use crate::{input, output, utils::log};
|
||||
use crate::{input, utils::log};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CSize {
|
||||
width: c_uint,
|
||||
height: c_uint,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CPoint {
|
||||
x: c_uint,
|
||||
y: c_uint,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CRect {
|
||||
origin: CPoint,
|
||||
size: CSize,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CColor {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CText {
|
||||
text: *const c_char,
|
||||
rect: CRect,
|
||||
color: CColor,
|
||||
}
|
||||
|
||||
impl<T: Copy> From<&CPoint> for Point<T>
|
||||
#[repr(C)]
|
||||
pub struct RendererBridge {
|
||||
cmd: CommandLine,
|
||||
window: Window,
|
||||
renderer: RenderThread,
|
||||
}
|
||||
|
||||
unsafe impl Send for RendererBridge {}
|
||||
unsafe impl Sync for RendererBridge {}
|
||||
|
||||
pub type RendererPtr = *const Mutex<RendererBridge>;
|
||||
|
||||
impl<T: Copy> From<CPoint> for Point<T>
|
||||
where
|
||||
c_uint: Cast<T>,
|
||||
{
|
||||
fn from(value: &CPoint) -> Self {
|
||||
fn from(value: CPoint) -> Self {
|
||||
Point::new(value.x, value.y).cast()
|
||||
}
|
||||
}
|
||||
impl<T: Copy> From<&CSize> for Size<T>
|
||||
impl From<Size<c_uint>> for CSize {
|
||||
fn from(value: Size<c_uint>) -> Self {
|
||||
Self {
|
||||
width: value.width,
|
||||
height: value.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: Copy> From<CSize> for Size<T>
|
||||
where
|
||||
c_uint: Cast<T>,
|
||||
{
|
||||
fn from(value: &CSize) -> Self {
|
||||
fn from(value: CSize) -> Self {
|
||||
Size::new(value.width, value.height).cast()
|
||||
}
|
||||
}
|
||||
impl From<CColor> for Color {
|
||||
fn from(value: CColor) -> Self {
|
||||
Color::new(value.r, value.g, value.b)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BrowserDelegate {
|
||||
shutdown: extern "C" fn(),
|
||||
refresh: extern "C" fn(),
|
||||
@ -67,23 +104,21 @@ pub struct BrowserDelegate {
|
||||
}
|
||||
|
||||
fn main() -> io::Result<Option<i32>> {
|
||||
const CARBONYL_INSIDE_SHELL: &str = "CARBONYL_INSIDE_SHELL";
|
||||
|
||||
if env::vars().find(|(key, value)| key == CARBONYL_INSIDE_SHELL && value == "1") != None {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let cmd = match cli::main() {
|
||||
let cmd = match CommandLineProgram::parse_or_run() {
|
||||
None => return Ok(Some(0)),
|
||||
Some(cmd) => cmd,
|
||||
};
|
||||
|
||||
if cmd.shell_mode {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut terminal = input::Terminal::setup();
|
||||
let output = Command::new(env::current_exe()?)
|
||||
.args(cmd.args)
|
||||
.arg("--disable-threaded-scrolling")
|
||||
.arg("--disable-threaded-animation")
|
||||
.env(CARBONYL_INSIDE_SHELL, "1")
|
||||
.env(EnvVar::ShellMode, "1")
|
||||
.stdin(Stdio::inherit())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::piped())
|
||||
@ -101,126 +136,176 @@ fn main() -> io::Result<Option<i32>> {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_shell_main() {
|
||||
pub extern "C" fn carbonyl_bridge_main() {
|
||||
if let Some(code) = main().unwrap() {
|
||||
std::process::exit(code)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_create() -> *mut Renderer {
|
||||
let mut renderer = Box::new(Renderer::new());
|
||||
let src = output::size().unwrap();
|
||||
|
||||
log::debug!("creating renderer, terminal size: {:?}", src);
|
||||
|
||||
renderer.set_size(Size::new(7, 14), src);
|
||||
|
||||
Box::into_raw(renderer)
|
||||
pub extern "C" fn carbonyl_bridge_bitmap_mode() -> bool {
|
||||
CommandLine::parse().bitmap
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_resize(renderer: *mut Renderer) {
|
||||
let renderer = unsafe { &mut *renderer };
|
||||
let src = output::size().unwrap();
|
||||
|
||||
log::debug!("resizing renderer, terminal size: {:?}", src);
|
||||
|
||||
renderer.set_size(Size::new(7, 14), src);
|
||||
pub extern "C" fn carbonyl_bridge_get_dpi() -> c_float {
|
||||
Window::read().dpi
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_clear_text(renderer: *mut Renderer) {
|
||||
let renderer = unsafe { &mut *renderer };
|
||||
pub extern "C" fn carbonyl_renderer_create() -> RendererPtr {
|
||||
let bridge = RendererBridge {
|
||||
cmd: CommandLine::parse(),
|
||||
window: Window::read(),
|
||||
renderer: RenderThread::new(),
|
||||
};
|
||||
|
||||
renderer.clear_text()
|
||||
Box::into_raw(Box::new(Mutex::new(bridge)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_start(bridge: RendererPtr) {
|
||||
{
|
||||
let bridge = unsafe { bridge.as_ref() };
|
||||
let mut bridge = bridge.unwrap().lock().unwrap();
|
||||
|
||||
bridge.renderer.enable()
|
||||
}
|
||||
|
||||
carbonyl_renderer_resize(bridge);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_resize(bridge: RendererPtr) {
|
||||
let bridge = unsafe { bridge.as_ref() };
|
||||
let mut bridge = bridge.unwrap().lock().unwrap();
|
||||
let window = bridge.window.update();
|
||||
let cells = window.cells.clone();
|
||||
|
||||
log::debug!("resizing renderer, terminal window: {:?}", window);
|
||||
|
||||
bridge
|
||||
.renderer
|
||||
.render(move |renderer| renderer.set_size(cells));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_push_nav(
|
||||
renderer: *mut Renderer,
|
||||
bridge: RendererPtr,
|
||||
url: *const c_char,
|
||||
can_go_back: bool,
|
||||
can_go_forward: bool,
|
||||
) {
|
||||
let (renderer, url) = unsafe { (&mut *renderer, CStr::from_ptr(url)) };
|
||||
let (bridge, url) = unsafe { (bridge.as_ref(), CStr::from_ptr(url)) };
|
||||
let (mut bridge, url) = (bridge.unwrap().lock().unwrap(), url.to_owned());
|
||||
|
||||
renderer.push_nav(url.to_str().unwrap(), can_go_back, can_go_forward)
|
||||
bridge.renderer.render(move |renderer| {
|
||||
renderer.push_nav(url.to_str().unwrap(), can_go_back, can_go_forward)
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_set_title(renderer: *mut Renderer, title: *const c_char) {
|
||||
let (renderer, title) = unsafe { (&mut *renderer, CStr::from_ptr(title)) };
|
||||
pub extern "C" fn carbonyl_renderer_set_title(bridge: RendererPtr, title: *const c_char) {
|
||||
let (bridge, title) = unsafe { (bridge.as_ref(), CStr::from_ptr(title)) };
|
||||
let (mut bridge, title) = (bridge.unwrap().lock().unwrap(), title.to_owned());
|
||||
|
||||
renderer.set_title(title.to_str().unwrap()).unwrap()
|
||||
bridge
|
||||
.renderer
|
||||
.render(move |renderer| renderer.set_title(title.to_str().unwrap()).unwrap());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_draw_text(
|
||||
renderer: *mut Renderer,
|
||||
text: *const c_char,
|
||||
rect: *const CRect,
|
||||
color: *const CColor,
|
||||
bridge: RendererPtr,
|
||||
text: *const CText,
|
||||
text_size: size_t,
|
||||
) {
|
||||
let (renderer, text, rect, color) =
|
||||
unsafe { (&mut *renderer, CStr::from_ptr(text), &*rect, &*color) };
|
||||
let (bridge, text) = unsafe { (bridge.as_ref(), std::slice::from_raw_parts(text, text_size)) };
|
||||
let mut bridge = bridge.unwrap().lock().unwrap();
|
||||
let mut vec = text
|
||||
.iter()
|
||||
.map(|text| {
|
||||
let str = unsafe { CStr::from_ptr(text.text) };
|
||||
|
||||
renderer.draw_text(
|
||||
text.to_str().unwrap(),
|
||||
Point::from(&rect.origin),
|
||||
Size::from(&rect.size),
|
||||
Color::new(color.r, color.g, color.b),
|
||||
)
|
||||
(
|
||||
str.to_str().unwrap().to_owned(),
|
||||
text.rect.origin.into(),
|
||||
text.rect.size.into(),
|
||||
text.color.into(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(String, Point, Size, Color)>>();
|
||||
|
||||
bridge.renderer.render(move |renderer| {
|
||||
renderer.clear_text();
|
||||
|
||||
for (text, origin, size, color) in std::mem::take(&mut vec) {
|
||||
renderer.draw_text(&text, origin, size, color)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_draw_background(
|
||||
renderer: *mut Renderer,
|
||||
pixels: *mut c_uchar,
|
||||
pixels_size: size_t,
|
||||
rect: *const CRect,
|
||||
) {
|
||||
let (renderer, pixels, rect) = unsafe {
|
||||
(
|
||||
&mut *renderer,
|
||||
std::slice::from_raw_parts_mut(pixels, pixels_size),
|
||||
&*rect,
|
||||
)
|
||||
};
|
||||
#[derive(Clone, Copy)]
|
||||
struct CallbackData(*const c_void);
|
||||
|
||||
renderer
|
||||
.draw_background(
|
||||
impl CallbackData {
|
||||
pub fn as_ptr(&self) -> *const c_void {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for CallbackData {}
|
||||
unsafe impl Sync for CallbackData {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_renderer_draw_bitmap(
|
||||
bridge: RendererPtr,
|
||||
pixels: *const c_uchar,
|
||||
pixels_size: CSize,
|
||||
rect: CRect,
|
||||
callback: extern "C" fn(*const c_void),
|
||||
callback_data: *const c_void,
|
||||
) {
|
||||
let length = (pixels_size.width * pixels_size.height * 4) as usize;
|
||||
let (bridge, pixels) = unsafe { (bridge.as_ref(), std::slice::from_raw_parts(pixels, length)) };
|
||||
let callback_data = CallbackData(callback_data);
|
||||
let mut bridge = bridge.unwrap().lock().unwrap();
|
||||
|
||||
bridge.renderer.render(move |renderer| {
|
||||
renderer.draw_background(
|
||||
pixels,
|
||||
pixels_size.into(),
|
||||
Rect {
|
||||
origin: Point::from(&rect.origin),
|
||||
size: Size::from(&rect.size),
|
||||
size: rect.size.into(),
|
||||
origin: rect.origin.into(),
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
callback(callback_data.as_ptr());
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_output_get_size(size: *mut CSize) {
|
||||
let dst = unsafe { &mut *size };
|
||||
let src = output::size().unwrap().cast::<c_uint>();
|
||||
pub extern "C" fn carbonyl_renderer_get_size(bridge: RendererPtr) -> CSize {
|
||||
let bridge = unsafe { bridge.as_ref() };
|
||||
let bridge = bridge.unwrap().lock().unwrap();
|
||||
|
||||
log::debug!("terminal size: {:?}", src);
|
||||
log::debug!("terminal size: {:?}", bridge.window.browser);
|
||||
|
||||
dst.width = src.width * 7;
|
||||
dst.height = src.height * 14;
|
||||
bridge.window.browser.into()
|
||||
}
|
||||
|
||||
extern "C" fn post_task_handler(callback: *mut c_void) {
|
||||
let mut closure = unsafe { Box::from_raw(callback as *mut Box<dyn FnMut() -> io::Result<()>>) };
|
||||
let mut closure = unsafe { Box::from_raw(callback as *mut Box<dyn FnMut()>) };
|
||||
|
||||
closure().unwrap();
|
||||
closure()
|
||||
}
|
||||
|
||||
fn post_task<F>(handle: &extern "C" fn(extern "C" fn(*mut c_void), *mut c_void), run: F)
|
||||
unsafe fn post_task<F>(handle: extern "C" fn(extern "C" fn(*mut c_void), *mut c_void), run: F)
|
||||
where
|
||||
F: FnMut() -> io::Result<()>,
|
||||
F: FnMut() + Send + 'static,
|
||||
{
|
||||
let closure: *mut Box<dyn FnMut() -> io::Result<()>> = Box::into_raw(Box::new(Box::new(run)));
|
||||
let closure: *mut Box<dyn FnMut()> = Box::into_raw(Box::new(Box::new(run)));
|
||||
|
||||
handle(post_task_handler, closure as *mut c_void);
|
||||
}
|
||||
@ -230,100 +315,113 @@ where
|
||||
/// This will block so the calling code should start and own a dedicated thread.
|
||||
/// It will panic if there is any error.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn carbonyl_input_listen(renderer: *mut Renderer, delegate: *mut BrowserDelegate) {
|
||||
let char_width = 7;
|
||||
let char_height = 14;
|
||||
let BrowserDelegate {
|
||||
shutdown,
|
||||
refresh,
|
||||
go_to,
|
||||
go_back,
|
||||
go_forward,
|
||||
scroll,
|
||||
key_press,
|
||||
mouse_up,
|
||||
mouse_down,
|
||||
mouse_move,
|
||||
post_task: handle,
|
||||
} = unsafe { &*delegate };
|
||||
let dispatch = |action: NavigationAction| {
|
||||
use NavigationAction::*;
|
||||
|
||||
match action {
|
||||
Ignore => (),
|
||||
Forward => return true,
|
||||
GoBack() => go_back(),
|
||||
GoForward() => go_forward(),
|
||||
Refresh() => refresh(),
|
||||
GoTo(url) => {
|
||||
let c_str = CString::new(url).unwrap();
|
||||
|
||||
go_to(c_str.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
};
|
||||
pub extern "C" fn carbonyl_renderer_listen(bridge: RendererPtr, delegate: *mut BrowserDelegate) {
|
||||
let bridge = unsafe { &*bridge };
|
||||
let delegate = unsafe { *delegate };
|
||||
|
||||
use input::*;
|
||||
|
||||
listen(|event| {
|
||||
post_task(handle, move || {
|
||||
let renderer = unsafe { &mut *renderer };
|
||||
thread::spawn(move || {
|
||||
macro_rules! emit {
|
||||
($event:ident($($args:expr),*) => $closure:expr) => {{
|
||||
let run = move || {
|
||||
(delegate.$event)($($args),*);
|
||||
|
||||
use Event::*;
|
||||
$closure
|
||||
};
|
||||
|
||||
match event.clone() {
|
||||
Exit => (),
|
||||
Scroll { delta } => scroll(delta as c_int * char_height as c_int),
|
||||
KeyPress { ref key } => {
|
||||
if dispatch(renderer.keypress(key)?) {
|
||||
key_press(key.char as c_char)
|
||||
}
|
||||
}
|
||||
MouseUp { col, row } => {
|
||||
if dispatch(renderer.mouse_up((col as _, row as _).into())?) {
|
||||
mouse_up(
|
||||
(col as c_uint) * char_width,
|
||||
(row as c_uint - 1) * char_height,
|
||||
)
|
||||
}
|
||||
}
|
||||
MouseDown { col, row } => {
|
||||
if dispatch(renderer.mouse_down((col as _, row as _).into())?) {
|
||||
mouse_down(
|
||||
(col as c_uint) * char_width,
|
||||
(row as c_uint - 1) * char_height,
|
||||
)
|
||||
}
|
||||
}
|
||||
MouseMove { col, row } => {
|
||||
if dispatch(renderer.mouse_move((col as _, row as _).into())?) {
|
||||
mouse_move(
|
||||
(col as c_uint) * char_width,
|
||||
(row as c_uint - 1) * char_height,
|
||||
)
|
||||
}
|
||||
}
|
||||
Terminal(terminal) => match terminal {
|
||||
TerminalEvent::Name(name) => log::debug!("terminal name: {name}"),
|
||||
TerminalEvent::TrueColorSupported => renderer.enable_true_color(),
|
||||
},
|
||||
};
|
||||
unsafe { post_task(delegate.post_task, run) }
|
||||
}};
|
||||
($event:ident($($args:expr),*)) => {{
|
||||
emit!($event($($args),*) => {})
|
||||
}};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
listen(|mut events| {
|
||||
bridge.lock().unwrap().renderer.render(move |renderer| {
|
||||
let get_scale = || bridge.lock().unwrap().window.scale;
|
||||
let scale = |col, row| {
|
||||
let scale = get_scale();
|
||||
|
||||
scale
|
||||
.mul(((col as f32 + 0.5), (row as f32 - 0.5)))
|
||||
.floor()
|
||||
.cast()
|
||||
.into()
|
||||
};
|
||||
let dispatch = |action| {
|
||||
match action {
|
||||
NavigationAction::Ignore => (),
|
||||
NavigationAction::Forward => return true,
|
||||
NavigationAction::GoBack() => emit!(go_back()),
|
||||
NavigationAction::GoForward() => emit!(go_forward()),
|
||||
NavigationAction::Refresh() => emit!(refresh()),
|
||||
NavigationAction::GoTo(url) => {
|
||||
let c_str = CString::new(url).unwrap();
|
||||
|
||||
emit!(go_to(c_str.as_ptr()))
|
||||
}
|
||||
};
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
for event in std::mem::take(&mut events) {
|
||||
use Event::*;
|
||||
|
||||
match event {
|
||||
Exit => (),
|
||||
Scroll { delta } => {
|
||||
let scale = get_scale();
|
||||
|
||||
emit!(scroll((delta as f32 * scale.height) as c_int))
|
||||
}
|
||||
KeyPress { key } => {
|
||||
if dispatch(renderer.keypress(&key).unwrap()) {
|
||||
emit!(key_press(key.char as c_char))
|
||||
}
|
||||
}
|
||||
MouseUp { col, row } => {
|
||||
if dispatch(renderer.mouse_up((col as _, row as _).into()).unwrap()) {
|
||||
let (width, height) = scale(col, row);
|
||||
|
||||
emit!(mouse_up(width, height))
|
||||
}
|
||||
}
|
||||
MouseDown { col, row } => {
|
||||
if dispatch(renderer.mouse_down((col as _, row as _).into()).unwrap()) {
|
||||
let (width, height) = scale(col, row);
|
||||
|
||||
emit!(mouse_down(width, height))
|
||||
}
|
||||
}
|
||||
MouseMove { col, row } => {
|
||||
if dispatch(renderer.mouse_move((col as _, row as _).into()).unwrap()) {
|
||||
let (width, height) = scale(col, row);
|
||||
|
||||
emit!(mouse_move(width, height))
|
||||
}
|
||||
}
|
||||
Terminal(terminal) => match terminal {
|
||||
TerminalEvent::Name(name) => log::debug!("terminal name: {name}"),
|
||||
TerminalEvent::TrueColorSupported => renderer.enable_true_color(),
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
// Setup single-use channel
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
post_task(handle, move || {
|
||||
shutdown();
|
||||
tx.send(()).unwrap();
|
||||
// Signal the browser to shutdown and notify our thread
|
||||
emit!(shutdown() => tx.send(()).unwrap());
|
||||
rx.recv().unwrap();
|
||||
|
||||
Ok(())
|
||||
// Shutdown rendering thread
|
||||
// if let Some(handle) = { bridge.lock().unwrap().renderer().stop() } {
|
||||
// handle.join().unwrap()
|
||||
// }
|
||||
});
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
85
src/browser/export.h
Normal file
85
src/browser/export.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef CARBONYL_SRC_BROWSER_BRIDGE_EXPORT_H_
|
||||
#define CARBONYL_SRC_BROWSER_BRIDGE_EXPORT_H_
|
||||
|
||||
// CARBONYL_BRIDGE_EXPORT
|
||||
#if defined(COMPONENT_BUILD)
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#if defined(CARBONYL_BRIDGE_IMPLEMENTATION)
|
||||
#define CARBONYL_BRIDGE_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define CARBONYL_BRIDGE_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#else // !defined(WIN32)
|
||||
|
||||
#if defined(CARBONYL_BRIDGE_IMPLEMENTATION)
|
||||
#define CARBONYL_BRIDGE_EXPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define CARBONYL_BRIDGE_EXPORT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#else // !defined(COMPONENT_BUILD)
|
||||
|
||||
#define CARBONYL_BRIDGE_EXPORT
|
||||
|
||||
#endif
|
||||
|
||||
// CARBONYL_RENDERER_EXPORT
|
||||
#if defined(COMPONENT_BUILD)
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#if defined(CARBONYL_RENDERER_IMPLEMENTATION)
|
||||
#define CARBONYL_RENDERER_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define CARBONYL_RENDERER_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#else // !defined(WIN32)
|
||||
|
||||
#if defined(CARBONYL_RENDERER_IMPLEMENTATION)
|
||||
#define CARBONYL_RENDERER_EXPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define CARBONYL_RENDERER_EXPORT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#else // !defined(COMPONENT_BUILD)
|
||||
|
||||
#define CARBONYL_RENDERER_EXPORT
|
||||
|
||||
#endif
|
||||
|
||||
// CARBONYL_VIZ_EXPORT
|
||||
#if defined(COMPONENT_BUILD)
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#if defined(CARBONYL_VIZ_IMPLEMENTATION)
|
||||
#define CARBONYL_VIZ_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define CARBONYL_VIZ_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#else // !defined(WIN32)
|
||||
|
||||
#if defined(CARBONYL_VIZ_IMPLEMENTATION)
|
||||
#define CARBONYL_VIZ_EXPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define CARBONYL_VIZ_EXPORT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#else // !defined(COMPONENT_BUILD)
|
||||
|
||||
#define CARBONYL_VIZ_EXPORT
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CARBONYL_SRC_BROWSER_BRIDGE_EXPORT_H_
|
@ -15,13 +15,17 @@
|
||||
#include "skia/ext/skia_utils_win.h"
|
||||
#endif
|
||||
|
||||
#include "carbonyl/src/browser/bridge.h"
|
||||
#include "carbonyl/src/browser/renderer.h"
|
||||
|
||||
namespace carbonyl {
|
||||
|
||||
LayeredWindowUpdater::LayeredWindowUpdater(
|
||||
mojo::PendingReceiver<viz::mojom::LayeredWindowUpdater> receiver)
|
||||
: receiver_(this, std::move(receiver)) {}
|
||||
mojo::PendingReceiver<viz::mojom::LayeredWindowUpdater> receiver
|
||||
)
|
||||
:
|
||||
receiver_(this, std::move(receiver)),
|
||||
task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault())
|
||||
{}
|
||||
|
||||
LayeredWindowUpdater::~LayeredWindowUpdater() = default;
|
||||
|
||||
@ -30,17 +34,27 @@ void LayeredWindowUpdater::OnAllocatedSharedMemory(
|
||||
base::UnsafeSharedMemoryRegion region) {
|
||||
if (region.IsValid())
|
||||
shm_mapping_ = region.Map();
|
||||
|
||||
pixel_size_ = pixel_size;
|
||||
}
|
||||
|
||||
void LayeredWindowUpdater::Draw(const gfx::Rect& damage_rect,
|
||||
DrawCallback draw_callback) {
|
||||
Renderer::Main()->DrawBackground(
|
||||
DrawCallback callback) {
|
||||
Renderer::GetCurrent()->DrawBitmap(
|
||||
shm_mapping_.GetMemoryAs<uint8_t>(),
|
||||
shm_mapping_.size(),
|
||||
damage_rect
|
||||
pixel_size_,
|
||||
damage_rect,
|
||||
base::BindOnce(
|
||||
[](
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
||||
DrawCallback callback
|
||||
) {
|
||||
task_runner->PostTask(FROM_HERE, std::move(callback));
|
||||
},
|
||||
task_runner_,
|
||||
std::move(callback)
|
||||
)
|
||||
);
|
||||
|
||||
std::move(draw_callback).Run();
|
||||
}
|
||||
|
||||
HostDisplayClient::HostDisplayClient()
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/shared_memory_mapping.h"
|
||||
#include "carbonyl/src/browser/export.h"
|
||||
#include "components/viz/host/host_display_client.h"
|
||||
#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
@ -14,7 +15,7 @@ namespace carbonyl {
|
||||
typedef base::RepeatingCallback<void(const gfx::Rect&, const SkBitmap&)>
|
||||
OnPaintCallback;
|
||||
|
||||
class LayeredWindowUpdater : public viz::mojom::LayeredWindowUpdater {
|
||||
class CARBONYL_VIZ_EXPORT LayeredWindowUpdater : public viz::mojom::LayeredWindowUpdater {
|
||||
public:
|
||||
explicit LayeredWindowUpdater(
|
||||
mojo::PendingReceiver<viz::mojom::LayeredWindowUpdater> receiver);
|
||||
@ -32,9 +33,13 @@ class LayeredWindowUpdater : public viz::mojom::LayeredWindowUpdater {
|
||||
private:
|
||||
mojo::Receiver<viz::mojom::LayeredWindowUpdater> receiver_;
|
||||
base::WritableSharedMemoryMapping shm_mapping_;
|
||||
gfx::Size pixel_size_;
|
||||
DrawCallback callback_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
||||
base::WeakPtrFactory<LayeredWindowUpdater> weak_ptr_factory_ { this };
|
||||
};
|
||||
|
||||
class HostDisplayClient : public viz::HostDisplayClient {
|
||||
class CARBONYL_VIZ_EXPORT HostDisplayClient : public viz::HostDisplayClient {
|
||||
public:
|
||||
explicit HostDisplayClient();
|
||||
~HostDisplayClient() override;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "carbonyl/src/browser/bridge.h"
|
||||
#include "carbonyl/src/browser/renderer.h"
|
||||
|
||||
namespace carbonyl {
|
||||
|
||||
@ -14,13 +14,13 @@ CarbonylRenderServiceImpl::CarbonylRenderServiceImpl(
|
||||
CarbonylRenderServiceImpl::~CarbonylRenderServiceImpl() = default;
|
||||
|
||||
void CarbonylRenderServiceImpl::DrawText(std::vector<mojom::TextDataPtr> data) {
|
||||
auto* renderer = Renderer::Main();
|
||||
|
||||
renderer->ClearText();
|
||||
std::vector<Text> mapped;
|
||||
|
||||
for (auto& text: data) {
|
||||
renderer->DrawText(text->contents, text->bounds, text->color);
|
||||
mapped.emplace_back(text->contents, text->bounds, text->color);
|
||||
}
|
||||
|
||||
Renderer::GetCurrent()->DrawText(mapped);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
#ifndef CARBONYL_SRC_BROWSER_RENDER_SERVICE_IMPL_H_
|
||||
#define CARBONYL_SRC_BROWSER_RENDER_SERVICE_IMPL_H_
|
||||
|
||||
#include "carbonyl/src/browser/export.h"
|
||||
#include "carbonyl/src/browser/carbonyl.mojom.h"
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
|
||||
namespace carbonyl {
|
||||
|
||||
class CarbonylRenderServiceImpl: public mojom::CarbonylRenderService {
|
||||
class CARBONYL_RENDERER_EXPORT CarbonylRenderServiceImpl: public mojom::CarbonylRenderService {
|
||||
public:
|
||||
explicit CarbonylRenderServiceImpl(mojo::PendingReceiver<mojom::CarbonylRenderService> receiver);
|
||||
CarbonylRenderServiceImpl(const CarbonylRenderServiceImpl&) = delete;
|
||||
|
181
src/browser/renderer.cc
Normal file
181
src/browser/renderer.cc
Normal file
@ -0,0 +1,181 @@
|
||||
#include "carbonyl/src/browser/renderer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "base/functional/callback.h"
|
||||
#include "carbonyl/src/browser/bridge.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct carbonyl_renderer_size {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
};
|
||||
struct carbonyl_renderer_point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
};
|
||||
struct carbonyl_renderer_rect {
|
||||
struct carbonyl_renderer_point origin;
|
||||
struct carbonyl_renderer_size size;
|
||||
};
|
||||
struct carbonyl_renderer_color {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
struct carbonyl_renderer_text {
|
||||
const char* text;
|
||||
carbonyl_renderer_rect rect;
|
||||
carbonyl_renderer_color color;
|
||||
};
|
||||
|
||||
void carbonyl_bridge_main();
|
||||
bool carbonyl_bridge_bitmap_mode();
|
||||
float carbonyl_bridge_get_dpi();
|
||||
|
||||
struct carbonyl_renderer* carbonyl_renderer_create();
|
||||
void carbonyl_renderer_start(struct carbonyl_renderer* renderer);
|
||||
void carbonyl_renderer_resize(struct carbonyl_renderer* renderer);
|
||||
struct carbonyl_renderer_size carbonyl_renderer_get_size(struct carbonyl_renderer* renderer);
|
||||
void carbonyl_renderer_push_nav(struct carbonyl_renderer* renderer, const char* url, bool can_go_back, bool can_go_forward);
|
||||
void carbonyl_renderer_set_title(struct carbonyl_renderer* renderer, const char* title);
|
||||
void carbonyl_renderer_clear_text(struct carbonyl_renderer* renderer);
|
||||
void carbonyl_renderer_listen(struct carbonyl_renderer* renderer, const struct carbonyl_renderer_browser_delegate* delegate);
|
||||
void carbonyl_renderer_draw_text(
|
||||
struct carbonyl_renderer* renderer,
|
||||
const struct carbonyl_renderer_text* text,
|
||||
size_t text_size
|
||||
);
|
||||
void carbonyl_renderer_draw_bitmap(
|
||||
struct carbonyl_renderer* renderer,
|
||||
const unsigned char* pixels,
|
||||
const struct carbonyl_renderer_size size,
|
||||
const struct carbonyl_renderer_rect rect,
|
||||
void (*callback) (void*),
|
||||
void* callback_data
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
namespace carbonyl {
|
||||
|
||||
namespace {
|
||||
static std::unique_ptr<Renderer> globalInstance;
|
||||
}
|
||||
|
||||
Renderer::Renderer(struct carbonyl_renderer* ptr): ptr_(ptr) {}
|
||||
|
||||
void Renderer::Main() {
|
||||
carbonyl_bridge_main();
|
||||
|
||||
Bridge::Configure(
|
||||
carbonyl_bridge_get_dpi(),
|
||||
carbonyl_bridge_bitmap_mode()
|
||||
);
|
||||
}
|
||||
|
||||
Renderer* Renderer::GetCurrent() {
|
||||
if (!globalInstance) {
|
||||
globalInstance = std::unique_ptr<Renderer>(
|
||||
new Renderer(carbonyl_renderer_create())
|
||||
);
|
||||
}
|
||||
|
||||
return globalInstance.get();
|
||||
}
|
||||
|
||||
void Renderer::StartRenderer() {
|
||||
carbonyl_renderer_start(ptr_);
|
||||
}
|
||||
|
||||
gfx::Size Renderer::GetSize() {
|
||||
auto size = carbonyl_renderer_get_size(ptr_);
|
||||
|
||||
return gfx::Size(size.width, size.height);
|
||||
}
|
||||
|
||||
gfx::Size Renderer::Resize() {
|
||||
carbonyl_renderer_resize(ptr_);
|
||||
Bridge::Resize();
|
||||
|
||||
return GetSize();
|
||||
}
|
||||
|
||||
void Renderer::Listen(const struct carbonyl_renderer_browser_delegate* delegate) {
|
||||
carbonyl_renderer_listen(ptr_, delegate);
|
||||
}
|
||||
|
||||
void Renderer::PushNav(const std::string& url, bool can_go_back, bool can_go_forward) {
|
||||
if (!url.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
carbonyl_renderer_push_nav(ptr_, url.c_str(), can_go_back, can_go_forward);
|
||||
}
|
||||
|
||||
void Renderer::SetTitle(const std::string& title) {
|
||||
if (!title.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
carbonyl_renderer_set_title(ptr_, title.c_str());
|
||||
}
|
||||
|
||||
void Renderer::DrawText(const std::vector<Text>& text) {
|
||||
struct carbonyl_renderer_text data[text.size()];
|
||||
|
||||
for (size_t i = 0; i < text.size(); i++) {
|
||||
data[i].text = text[i].text.c_str();
|
||||
data[i].color.r = SkColorGetR(text[i].color);
|
||||
data[i].color.g = SkColorGetG(text[i].color);
|
||||
data[i].color.b = SkColorGetB(text[i].color);
|
||||
data[i].rect.origin.x = text[i].rect.x();
|
||||
data[i].rect.origin.y = text[i].rect.y();
|
||||
data[i].rect.size.width = std::ceil(text[i].rect.width());
|
||||
data[i].rect.size.height = std::ceil(text[i].rect.height());
|
||||
}
|
||||
|
||||
carbonyl_renderer_draw_text(ptr_, data, text.size());
|
||||
}
|
||||
|
||||
void Renderer::DrawBitmap(
|
||||
const unsigned char* pixels,
|
||||
const gfx::Size& pixels_size,
|
||||
const gfx::Rect& damage,
|
||||
base::OnceCallback<void()> callback
|
||||
) {
|
||||
auto* box = new base::OnceCallback<void()>(std::move(callback));
|
||||
|
||||
carbonyl_renderer_draw_bitmap(
|
||||
ptr_,
|
||||
pixels,
|
||||
{
|
||||
.width = (unsigned int)pixels_size.width(),
|
||||
.height = (unsigned int)pixels_size.height(),
|
||||
},
|
||||
{
|
||||
.origin = {
|
||||
.x = (unsigned int)damage.x(),
|
||||
.y = (unsigned int)damage.y(),
|
||||
},
|
||||
.size = {
|
||||
.width = (unsigned int)damage.width(),
|
||||
.height = (unsigned int)damage.height(),
|
||||
},
|
||||
},
|
||||
[](void* box) {
|
||||
auto* ptr = static_cast<base::OnceCallback<void()>*>(box);
|
||||
|
||||
std::move(*ptr).Run();
|
||||
delete ptr;
|
||||
},
|
||||
box
|
||||
);
|
||||
}
|
||||
|
||||
}
|
76
src/browser/renderer.h
Normal file
76
src/browser/renderer.h
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef CARBONYL_SRC_BROWSER_RENDERER_H_
|
||||
#define CARBONYL_SRC_BROWSER_RENDERER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include "base/functional/callback.h"
|
||||
#include "carbonyl/src/browser/export.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct carbonyl_renderer;
|
||||
struct carbonyl_renderer_browser_delegate {
|
||||
void (*shutdown) ();
|
||||
void (*refresh) ();
|
||||
void (*go_to) (const char* url);
|
||||
void (*go_back) ();
|
||||
void (*go_forward) ();
|
||||
void (*scroll) (int);
|
||||
void (*key_press) (char);
|
||||
void (*mouse_up) (unsigned int, unsigned int);
|
||||
void (*mouse_down) (unsigned int, unsigned int);
|
||||
void (*mouse_move) (unsigned int, unsigned int);
|
||||
void (*post_task) (void (*)(void*), void*);
|
||||
};
|
||||
|
||||
} /* end extern "C" */
|
||||
|
||||
namespace carbonyl {
|
||||
|
||||
struct CARBONYL_RENDERER_EXPORT Text {
|
||||
Text(
|
||||
std::string text,
|
||||
gfx::RectF rect,
|
||||
uint32_t color
|
||||
):
|
||||
text(text),
|
||||
rect(rect),
|
||||
color(color)
|
||||
{}
|
||||
|
||||
std::string text;
|
||||
gfx::RectF rect;
|
||||
uint32_t color;
|
||||
};
|
||||
|
||||
class CARBONYL_RENDERER_EXPORT Renderer {
|
||||
public:
|
||||
static void Main();
|
||||
static Renderer* GetCurrent();
|
||||
|
||||
gfx::Size GetSize();
|
||||
|
||||
gfx::Size Resize();
|
||||
void StartRenderer();
|
||||
void Listen(const struct carbonyl_renderer_browser_delegate* delegate);
|
||||
void PushNav(const std::string& url, bool can_go_back, bool can_go_forward);
|
||||
void SetTitle(const std::string& title);
|
||||
void DrawText(const std::vector<Text>& text);
|
||||
void DrawBitmap(
|
||||
const unsigned char* pixels,
|
||||
const gfx::Size& size,
|
||||
const gfx::Rect& damage,
|
||||
base::OnceCallback<void()> callback
|
||||
);
|
||||
|
||||
private:
|
||||
Renderer(struct carbonyl_renderer* ptr);
|
||||
|
||||
struct carbonyl_renderer* ptr_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CARBONYL_SRC_BROWSER_RENDERER_H_
|
@ -1,155 +0,0 @@
|
||||
#include "carbonyl/src/browser/software_output_device_proxy.h"
|
||||
|
||||
#include "base/memory/unsafe_shared_memory_region.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/viz/common/resources/resource_sizes.h"
|
||||
#include "components/viz/service/display_embedder/output_device_backing.h"
|
||||
#include "mojo/public/cpp/system/platform_handle.h"
|
||||
#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h"
|
||||
#include "skia/ext/platform_canvas.h"
|
||||
#include "third_party/skia/include/core/SkCanvas.h"
|
||||
#include "ui/gfx/skia_util.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "skia/ext/skia_utils_win.h"
|
||||
#include "ui/gfx/gdi_util.h"
|
||||
#include "ui/gfx/win/hwnd_util.h"
|
||||
#else
|
||||
#include "mojo/public/cpp/base/shared_memory_utils.h"
|
||||
#endif
|
||||
|
||||
namespace viz {
|
||||
|
||||
SoftwareOutputDeviceBase::~SoftwareOutputDeviceBase() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
||||
DCHECK(!in_paint_);
|
||||
}
|
||||
|
||||
void SoftwareOutputDeviceBase::Resize(const gfx::Size& viewport_pixel_size,
|
||||
float scale_factor) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
||||
DCHECK(!in_paint_);
|
||||
|
||||
if (viewport_pixel_size_ == viewport_pixel_size)
|
||||
return;
|
||||
|
||||
viewport_pixel_size_ = viewport_pixel_size;
|
||||
ResizeDelegated();
|
||||
}
|
||||
|
||||
SkCanvas* SoftwareOutputDeviceBase::BeginPaint(
|
||||
const gfx::Rect& damage_rect) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
||||
DCHECK(!in_paint_);
|
||||
|
||||
damage_rect_ = damage_rect;
|
||||
in_paint_ = true;
|
||||
return BeginPaintDelegated();
|
||||
}
|
||||
|
||||
void SoftwareOutputDeviceBase::EndPaint() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
||||
DCHECK(in_paint_);
|
||||
|
||||
in_paint_ = false;
|
||||
|
||||
gfx::Rect intersected_damage_rect = damage_rect_;
|
||||
intersected_damage_rect.Intersect(gfx::Rect(viewport_pixel_size_));
|
||||
if (intersected_damage_rect.IsEmpty())
|
||||
return;
|
||||
|
||||
EndPaintDelegated(intersected_damage_rect);
|
||||
}
|
||||
|
||||
SoftwareOutputDeviceProxy::~SoftwareOutputDeviceProxy() = default;
|
||||
|
||||
SoftwareOutputDeviceProxy::SoftwareOutputDeviceProxy(
|
||||
mojo::PendingRemote<mojom::LayeredWindowUpdater> layered_window_updater)
|
||||
: layered_window_updater_(std::move(layered_window_updater)) {
|
||||
DCHECK(layered_window_updater_.is_bound());
|
||||
}
|
||||
|
||||
void SoftwareOutputDeviceProxy::OnSwapBuffers(
|
||||
SoftwareOutputDevice::SwapBuffersCallback swap_ack_callback,
|
||||
gfx::FrameData data) {
|
||||
DCHECK(swap_ack_callback_.is_null());
|
||||
|
||||
// We aren't waiting on DrawAck() and can immediately run the callback.
|
||||
if (!waiting_on_draw_ack_) {
|
||||
task_runner_->PostTask(FROM_HERE,
|
||||
base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_));
|
||||
return;
|
||||
}
|
||||
|
||||
swap_ack_callback_ = std::move(swap_ack_callback);
|
||||
}
|
||||
|
||||
void SoftwareOutputDeviceProxy::ResizeDelegated() {
|
||||
canvas_.reset();
|
||||
|
||||
size_t required_bytes;
|
||||
if (!ResourceSizes::MaybeSizeInBytes(
|
||||
viewport_pixel_size_, ResourceFormat::RGBA_8888, &required_bytes)) {
|
||||
DLOG(ERROR) << "Invalid viewport size " << viewport_pixel_size_.ToString();
|
||||
return;
|
||||
}
|
||||
|
||||
base::UnsafeSharedMemoryRegion region =
|
||||
base::UnsafeSharedMemoryRegion::Create(required_bytes);
|
||||
if (!region.IsValid()) {
|
||||
DLOG(ERROR) << "Failed to allocate " << required_bytes << " bytes";
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
canvas_ = skia::CreatePlatformCanvasWithSharedSection(
|
||||
viewport_pixel_size_.width(), viewport_pixel_size_.height(), false,
|
||||
region.GetPlatformHandle(), skia::CRASH_ON_FAILURE);
|
||||
#else
|
||||
shm_mapping_ = region.Map();
|
||||
if (!shm_mapping_.IsValid()) {
|
||||
DLOG(ERROR) << "Failed to map " << required_bytes << " bytes";
|
||||
return;
|
||||
}
|
||||
|
||||
canvas_ = skia::CreatePlatformCanvasWithPixels(
|
||||
viewport_pixel_size_.width(), viewport_pixel_size_.height(), false,
|
||||
static_cast<uint8_t*>(shm_mapping_.memory()), skia::CRASH_ON_FAILURE);
|
||||
#endif
|
||||
|
||||
// Transfer region ownership to the browser process.
|
||||
layered_window_updater_->OnAllocatedSharedMemory(viewport_pixel_size_,
|
||||
std::move(region));
|
||||
}
|
||||
|
||||
SkCanvas* SoftwareOutputDeviceProxy::BeginPaintDelegated() {
|
||||
return canvas_.get();
|
||||
}
|
||||
|
||||
void SoftwareOutputDeviceProxy::EndPaintDelegated(
|
||||
const gfx::Rect& damage_rect) {
|
||||
DCHECK(!waiting_on_draw_ack_);
|
||||
|
||||
if (!canvas_)
|
||||
return;
|
||||
|
||||
layered_window_updater_->Draw(damage_rect, base::BindOnce(
|
||||
&SoftwareOutputDeviceProxy::DrawAck, base::Unretained(this)));
|
||||
waiting_on_draw_ack_ = true;
|
||||
|
||||
TRACE_EVENT_ASYNC_BEGIN0("viz", "SoftwareOutputDeviceProxy::Draw", this);
|
||||
}
|
||||
|
||||
void SoftwareOutputDeviceProxy::DrawAck() {
|
||||
DCHECK(waiting_on_draw_ack_);
|
||||
DCHECK(!swap_ack_callback_.is_null());
|
||||
|
||||
TRACE_EVENT_ASYNC_END0("viz", "SoftwareOutputDeviceProxy::Draw", this);
|
||||
|
||||
waiting_on_draw_ack_ = false;
|
||||
std::move(swap_ack_callback_).Run(viewport_pixel_size_);
|
||||
}
|
||||
|
||||
} // namespace viz
|
@ -1,91 +0,0 @@
|
||||
#ifndef CARBONYL_SRC_BROWSER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_
|
||||
#define CARBONYL_SRC_BROWSER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/memory/shared_memory_mapping.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/viz/host/host_display_client.h"
|
||||
#include "components/viz/service/display/software_output_device.h"
|
||||
#include "components/viz/service/viz_service_export.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/viz/privileged/mojom/compositing/display_private.mojom.h"
|
||||
#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace viz {
|
||||
|
||||
// Shared base class for SoftwareOutputDevice implementations.
|
||||
class SoftwareOutputDeviceBase : public SoftwareOutputDevice {
|
||||
public:
|
||||
SoftwareOutputDeviceBase() = default;
|
||||
~SoftwareOutputDeviceBase() override;
|
||||
|
||||
SoftwareOutputDeviceBase(const SoftwareOutputDeviceBase&) = delete;
|
||||
SoftwareOutputDeviceBase& operator=(const SoftwareOutputDeviceBase&) = delete;
|
||||
|
||||
// SoftwareOutputDevice implementation.
|
||||
void Resize(const gfx::Size& viewport_pixel_size,
|
||||
float scale_factor) override;
|
||||
SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override;
|
||||
void EndPaint() override;
|
||||
|
||||
// Called from Resize() if |viewport_pixel_size_| has changed.
|
||||
virtual void ResizeDelegated() = 0;
|
||||
|
||||
// Called from BeginPaint() and should return an SkCanvas.
|
||||
virtual SkCanvas* BeginPaintDelegated() = 0;
|
||||
|
||||
// Called from EndPaint() if there is damage.
|
||||
virtual void EndPaintDelegated(const gfx::Rect& damage_rect) = 0;
|
||||
|
||||
private:
|
||||
bool in_paint_ = false;
|
||||
|
||||
THREAD_CHECKER(thread_checker_);
|
||||
};
|
||||
|
||||
// SoftwareOutputDevice implementation that draws indirectly. An implementation
|
||||
// of mojom::LayeredWindowUpdater in the browser process handles the actual
|
||||
// drawing. Pixel backing is in SharedMemory so no copying between processes
|
||||
// is required.
|
||||
class SoftwareOutputDeviceProxy : public SoftwareOutputDeviceBase {
|
||||
public:
|
||||
explicit SoftwareOutputDeviceProxy(
|
||||
mojo::PendingRemote<mojom::LayeredWindowUpdater> layered_window_updater);
|
||||
~SoftwareOutputDeviceProxy() override;
|
||||
|
||||
SoftwareOutputDeviceProxy(const SoftwareOutputDeviceProxy&) = delete;
|
||||
SoftwareOutputDeviceProxy& operator=(const SoftwareOutputDeviceProxy&) = delete;
|
||||
|
||||
// SoftwareOutputDevice implementation.
|
||||
void OnSwapBuffers(SoftwareOutputDevice::SwapBuffersCallback swap_ack_callback, gfx::FrameData data) override;
|
||||
|
||||
// SoftwareOutputDeviceBase implementation.
|
||||
void ResizeDelegated() override;
|
||||
SkCanvas* BeginPaintDelegated() override;
|
||||
void EndPaintDelegated(const gfx::Rect& rect) override;
|
||||
|
||||
private:
|
||||
// Runs |swap_ack_callback_| after draw has happened.
|
||||
void DrawAck();
|
||||
|
||||
mojo::Remote<mojom::LayeredWindowUpdater> layered_window_updater_;
|
||||
|
||||
std::unique_ptr<SkCanvas> canvas_;
|
||||
bool waiting_on_draw_ack_ = false;
|
||||
SoftwareOutputDevice::SwapBuffersCallback swap_ack_callback_;
|
||||
|
||||
#if !defined(WIN32)
|
||||
base::WritableSharedMemoryMapping shm_mapping_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace viz
|
||||
|
||||
#endif // CARBONYL_SRC_BROWSER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_
|
@ -1,5 +1,5 @@
|
||||
mod main;
|
||||
mod parser;
|
||||
mod cli;
|
||||
mod program;
|
||||
|
||||
pub use main::*;
|
||||
pub use parser::*;
|
||||
pub use cli::*;
|
||||
pub use program::*;
|
||||
|
109
src/cli/cli.rs
Normal file
109
src/cli/cli.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use std::{env, ffi::OsStr};
|
||||
|
||||
use super::CommandLineProgram;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CommandLine {
|
||||
pub args: Vec<String>,
|
||||
pub fps: f32,
|
||||
pub zoom: f32,
|
||||
pub debug: bool,
|
||||
pub bitmap: bool,
|
||||
pub program: CommandLineProgram,
|
||||
pub shell_mode: bool,
|
||||
}
|
||||
|
||||
pub enum EnvVar {
|
||||
Debug,
|
||||
Bitmap,
|
||||
ShellMode,
|
||||
}
|
||||
|
||||
impl EnvVar {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
EnvVar::Debug => "CARBONYL_ENV_DEBUG",
|
||||
EnvVar::Bitmap => "CARBONYL_ENV_BITMAP",
|
||||
EnvVar::ShellMode => "CARBONYL_ENV_SHELL_MODE",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> for EnvVar {
|
||||
fn as_ref(&self) -> &OsStr {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandLine {
|
||||
pub fn parse() -> CommandLine {
|
||||
let mut fps = 60.0;
|
||||
let mut zoom = 1.0;
|
||||
let mut debug = false;
|
||||
let mut bitmap = false;
|
||||
let mut shell_mode = false;
|
||||
let mut program = CommandLineProgram::Main;
|
||||
let args = env::args().skip(1).collect::<Vec<String>>();
|
||||
|
||||
for arg in &args {
|
||||
let split: Vec<&str> = arg.split("=").collect();
|
||||
let default = arg.as_str();
|
||||
let (key, value) = (split.get(0).unwrap_or(&default), split.get(1));
|
||||
|
||||
macro_rules! set {
|
||||
($var:ident, $enum:ident) => {{
|
||||
$var = true;
|
||||
|
||||
env::set_var(EnvVar::$enum, "1");
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! set_f32 {
|
||||
($var:ident = $expr:expr) => {{
|
||||
if let Some(value) = value {
|
||||
if let Some(value) = value.parse::<f32>().ok() {
|
||||
$var = {
|
||||
let $var = value;
|
||||
|
||||
$expr
|
||||
};
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
match *key {
|
||||
"-f" | "--fps" => set_f32!(fps = fps),
|
||||
"-z" | "--zoom" => set_f32!(zoom = zoom / 100.0),
|
||||
"-d" | "--debug" => set!(debug, Debug),
|
||||
"-b" | "--bitmap" => set!(bitmap, Bitmap),
|
||||
|
||||
"-h" | "--help" => program = CommandLineProgram::Help,
|
||||
"-v" | "--version" => program = CommandLineProgram::Version,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if env::var(EnvVar::Debug).is_ok() {
|
||||
debug = true;
|
||||
}
|
||||
|
||||
if env::var(EnvVar::Bitmap).is_ok() {
|
||||
bitmap = true;
|
||||
}
|
||||
|
||||
if env::var(EnvVar::ShellMode).is_ok() {
|
||||
shell_mode = true;
|
||||
}
|
||||
|
||||
CommandLine {
|
||||
args,
|
||||
fps,
|
||||
zoom,
|
||||
debug,
|
||||
bitmap,
|
||||
program,
|
||||
shell_mode,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
use super::{CommandLine, CommandLineProgram};
|
||||
|
||||
pub fn main() -> Option<CommandLine> {
|
||||
match CommandLineProgram::parse() {
|
||||
CommandLineProgram::Main(cmd) => return Some(cmd),
|
||||
CommandLineProgram::Help => {
|
||||
println!("{}", include_str!("usage.txt"))
|
||||
}
|
||||
CommandLineProgram::Version => {
|
||||
println!("Carbonyl {}", env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
use std::env;
|
||||
|
||||
pub struct CommandLine {
|
||||
pub args: Vec<String>,
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
pub enum CommandLineProgram {
|
||||
Help,
|
||||
Version,
|
||||
Main(CommandLine),
|
||||
}
|
||||
|
||||
impl CommandLineProgram {
|
||||
pub fn parse() -> CommandLineProgram {
|
||||
let mut debug = false;
|
||||
let mut args = Vec::new();
|
||||
|
||||
for arg in env::args().skip(1) {
|
||||
match arg.as_str() {
|
||||
"-d" | "--debug" => debug = true,
|
||||
"-h" | "--help" => return CommandLineProgram::Help,
|
||||
"-v" | "--version" => return CommandLineProgram::Version,
|
||||
_ => args.push(arg),
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineProgram::Main(CommandLine { args, debug })
|
||||
}
|
||||
}
|
26
src/cli/program.rs
Normal file
26
src/cli/program.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use super::CommandLine;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CommandLineProgram {
|
||||
Main,
|
||||
Help,
|
||||
Version,
|
||||
}
|
||||
|
||||
impl CommandLineProgram {
|
||||
pub fn parse_or_run() -> Option<CommandLine> {
|
||||
let cmd = CommandLine::parse();
|
||||
|
||||
match cmd.program {
|
||||
CommandLineProgram::Main => return Some(cmd),
|
||||
CommandLineProgram::Help => {
|
||||
println!("{}", include_str!("usage.txt"))
|
||||
}
|
||||
CommandLineProgram::Version => {
|
||||
println!("Carbonyl {}", env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
@ -7,6 +7,9 @@ O —— Cr —— O In addition to the following options,
|
||||
Usage: carbonyl [options] [url]
|
||||
|
||||
Options:
|
||||
-h, --help display this help message
|
||||
-f, --fps=<fps> set the maximum number of frames per second (default: 60)
|
||||
-z, --zoom=<zoom> set the zoom level in percent (default: 100)
|
||||
-b, --bitmap render text as bitmaps
|
||||
-d, --debug enable debug logs
|
||||
-h, --help display this help message
|
||||
-v, --version output the version number
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::Vector2;
|
||||
use crate::impl_vector_overload;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||
pub struct Size<T: Copy = u32> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
|
@ -137,6 +137,12 @@ macro_rules! impl_vector_overload {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<$struct<T>> for (T, T) {
|
||||
fn from(vector: $struct<T>) -> Self {
|
||||
(vector.$x, vector.$y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<(T, T)> for $struct<T> {
|
||||
fn from((x, y): (T, T)) -> Self {
|
||||
Self::new(x, y)
|
||||
@ -219,6 +225,12 @@ macro_rules! impl_vector_overload {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<$struct<T>> for (T, T, T) {
|
||||
fn from(vector: $struct<T>) -> Self {
|
||||
(vector.$x, vector.$y, vector.$z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> From<[T; 3]> for $struct<T> {
|
||||
fn from(array: [T; 3]) -> Self {
|
||||
Self::new(array[0], array[1], array[2])
|
||||
@ -338,6 +350,14 @@ macro_rules! impl_vector_traits {
|
||||
self.map(|v| v.round())
|
||||
}
|
||||
|
||||
pub fn floor(&self) -> Self {
|
||||
self.map(|v| v.floor())
|
||||
}
|
||||
|
||||
pub fn ceil(&self) -> Self {
|
||||
self.map(|v| v.ceil())
|
||||
}
|
||||
|
||||
pub fn min<U>(&self, min: U) -> Self
|
||||
where
|
||||
U: Into<Self>
|
||||
|
@ -6,7 +6,7 @@ use crate::input::*;
|
||||
/// This will block, so it should run from a dedicated thread.
|
||||
pub fn listen<F>(mut callback: F) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(Event),
|
||||
F: FnMut(Vec<Event>),
|
||||
{
|
||||
let mut buf = [0u8; 1024];
|
||||
let mut stdin = io::stdin();
|
||||
@ -15,20 +15,22 @@ where
|
||||
loop {
|
||||
// Wait for some input
|
||||
let size = stdin.read(&mut buf)?;
|
||||
let read = parser.parse(&buf[0..size]);
|
||||
let mut scroll = 0;
|
||||
let mut events = Vec::with_capacity(read.len());
|
||||
|
||||
// Parse the input for xterm commands
|
||||
for event in parser.parse(&buf[0..size]) {
|
||||
// Allow the callback to return early (ie. handle ctrl+c)
|
||||
for event in read {
|
||||
match event {
|
||||
Event::Exit => return Ok(()),
|
||||
Event::Scroll { delta } => scroll += delta,
|
||||
event => callback(event),
|
||||
event => events.push(event),
|
||||
}
|
||||
}
|
||||
|
||||
if scroll != 0 {
|
||||
callback(Event::Scroll { delta: scroll })
|
||||
events.push(Event::Scroll { delta: scroll })
|
||||
}
|
||||
|
||||
callback(events)
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,18 @@
|
||||
// mod kd_tree;
|
||||
// mod quantizer;
|
||||
mod cell;
|
||||
mod frame_sync;
|
||||
mod painter;
|
||||
mod quad;
|
||||
mod render_thread;
|
||||
mod renderer;
|
||||
mod size;
|
||||
mod window;
|
||||
mod xterm;
|
||||
|
||||
pub use cell::*;
|
||||
pub use frame_sync::*;
|
||||
pub use painter::*;
|
||||
pub use quad::*;
|
||||
pub use render_thread::*;
|
||||
pub use renderer::*;
|
||||
pub use size::*;
|
||||
pub use window::*;
|
||||
|
@ -14,22 +14,23 @@ pub struct Grapheme {
|
||||
/// Terminal cell with `height = width * 2`
|
||||
#[derive(PartialEq)]
|
||||
pub struct Cell {
|
||||
/// Top pixel color value
|
||||
pub top: Color,
|
||||
/// Bottom pixel color value
|
||||
pub bottom: Color,
|
||||
pub cursor: Point<u32>,
|
||||
/// Text grapheme if any
|
||||
pub grapheme: Option<Rc<Grapheme>>,
|
||||
pub quadrant: (Color, Color, Color, Color),
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn new(x: u32, y: u32) -> Cell {
|
||||
Cell {
|
||||
top: Color::black(),
|
||||
bottom: Color::black(),
|
||||
cursor: Point::new(x, y),
|
||||
grapheme: None,
|
||||
quadrant: (
|
||||
Color::black(),
|
||||
Color::black(),
|
||||
Color::black(),
|
||||
Color::black(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
src/output/frame_sync.rs
Normal file
34
src/output/frame_sync.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// A utility to synchronize rendering with a given FPS
|
||||
pub struct FrameSync {
|
||||
render_start: Option<Instant>,
|
||||
frame_duration: Duration,
|
||||
}
|
||||
|
||||
impl FrameSync {
|
||||
pub fn new(fps: f32) -> Self {
|
||||
Self {
|
||||
render_start: None,
|
||||
frame_duration: Duration::from_micros((1_000_000.0 / fps) as u64),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark the beginning of the render
|
||||
pub fn start(&mut self) {
|
||||
self.render_start = Some(Instant::now());
|
||||
}
|
||||
|
||||
/// Get a deadline until the next frame
|
||||
pub fn deadline(&self) -> Instant {
|
||||
match self.render_start {
|
||||
// We never rendered yet, render now!
|
||||
None => Instant::now(),
|
||||
// Else we should render `frame_duration` after the last render start.
|
||||
// If we render at 60 FPS, this should be 16ms after the render start.
|
||||
// If the render takes more than the frame duration, this will always
|
||||
// return a deadline in a the past, making render happen immediately.
|
||||
Some(render_start) => render_start + self.frame_duration,
|
||||
}
|
||||
}
|
||||
}
|
@ -2,16 +2,9 @@ use std::io::{self, Stdout, Write};
|
||||
|
||||
use crate::gfx::{Color, Point};
|
||||
|
||||
use super::Cell;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum PaintMode {
|
||||
Text,
|
||||
Bitmap,
|
||||
}
|
||||
use super::{binarize_quandrant, Cell};
|
||||
|
||||
pub struct Painter {
|
||||
mode: PaintMode,
|
||||
output: Stdout,
|
||||
buffer: Vec<u8>,
|
||||
cursor: Option<Point<u32>>,
|
||||
@ -25,7 +18,6 @@ pub struct Painter {
|
||||
impl Painter {
|
||||
pub fn new() -> Painter {
|
||||
Painter {
|
||||
mode: PaintMode::Text,
|
||||
buffer: Vec::new(),
|
||||
cursor: None,
|
||||
output: io::stdout(),
|
||||
@ -73,42 +65,29 @@ impl Painter {
|
||||
pub fn paint(&mut self, cell: &Cell) -> io::Result<()> {
|
||||
let &Cell {
|
||||
cursor,
|
||||
quadrant,
|
||||
ref grapheme,
|
||||
top: mut background,
|
||||
bottom: mut foreground,
|
||||
} = cell;
|
||||
|
||||
let (char, width, escape) = if let Some(grapheme) = grapheme {
|
||||
let (char, background, foreground, width) = if let Some(grapheme) = grapheme {
|
||||
if grapheme.index > 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
background = background.avg_with(foreground);
|
||||
foreground = grapheme.color;
|
||||
|
||||
(
|
||||
grapheme.char.as_str(),
|
||||
quadrant
|
||||
.0
|
||||
.avg_with(quadrant.1)
|
||||
.avg_with(quadrant.2)
|
||||
.avg_with(quadrant.3),
|
||||
grapheme.color,
|
||||
grapheme.width as u32,
|
||||
if self.mode == PaintMode::Bitmap {
|
||||
self.mode = PaintMode::Text;
|
||||
|
||||
Some("\x1b[22m\x1b[24m")
|
||||
} else {
|
||||
None
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"▄",
|
||||
1,
|
||||
if self.mode == PaintMode::Text {
|
||||
self.mode = PaintMode::Bitmap;
|
||||
let (char, background, foreground) = binarize_quandrant(quadrant);
|
||||
|
||||
Some("\x1b[1m\x1b[4m")
|
||||
} else {
|
||||
None
|
||||
},
|
||||
)
|
||||
(char, background, foreground, 1)
|
||||
};
|
||||
|
||||
if self.cursor != Some(cursor) {
|
||||
@ -157,10 +136,6 @@ impl Painter {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(escape) = escape {
|
||||
self.buffer.write_all(escape.as_bytes())?
|
||||
}
|
||||
|
||||
self.buffer.write_all(char.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
|
40
src/output/quad.rs
Normal file
40
src/output/quad.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::gfx::Color;
|
||||
use crate::utils::FourBits::{self, *};
|
||||
|
||||
/// Turn a quadrant of four colors into two colors and a quadrant unicode character.
|
||||
pub fn binarize_quandrant(
|
||||
(x, y, z, w): (Color, Color, Color, Color),
|
||||
) -> (&'static str, Color, Color) {
|
||||
// Step 1: grayscale
|
||||
const LUMA: Color<f32> = Color::new(0.299, 0.587, 0.114);
|
||||
let (a, b, c, d) = (
|
||||
LUMA.dot(x.cast()),
|
||||
LUMA.dot(y.cast()),
|
||||
LUMA.dot(z.cast()),
|
||||
LUMA.dot(w.cast()),
|
||||
);
|
||||
// Step 2: luminance middlepoint
|
||||
let min = a.min(b).min(c).min(d);
|
||||
let max = a.max(b).max(c).max(d);
|
||||
let mid = min + (max - min) / 2.0;
|
||||
|
||||
// Step 3: average colors based on binary mask
|
||||
match FourBits::new(a > mid, b > mid, c > mid, d > mid) {
|
||||
B0000 => ("▄", x.avg_with(y), z.avg_with(w)),
|
||||
B0001 => ("▖", x.avg_with(y).avg_with(z), w),
|
||||
B0010 => ("▗", x.avg_with(y).avg_with(w), z),
|
||||
B0011 => ("▄", x.avg_with(y), z.avg_with(w)),
|
||||
B0100 => ("▝", x.avg_with(z).avg_with(w), y),
|
||||
B0101 => ("▞", x.avg_with(z), y.avg_with(w)),
|
||||
B0110 => ("▐", x.avg_with(w), y.avg_with(z)),
|
||||
B0111 => ("▘", y.avg_with(z).avg_with(w), x),
|
||||
B1000 => ("▘", y.avg_with(z).avg_with(w), x),
|
||||
B1001 => ("▌", y.avg_with(z), x.avg_with(w)),
|
||||
B1010 => ("▚", y.avg_with(w), x.avg_with(z)),
|
||||
B1011 => ("▝", x.avg_with(z).avg_with(w), y),
|
||||
B1100 => ("▄", x.avg_with(y), z.avg_with(w)),
|
||||
B1101 => ("▗", x.avg_with(y).avg_with(w), z),
|
||||
B1110 => ("▖", x.avg_with(y).avg_with(z), w),
|
||||
B1111 => ("▄", x.avg_with(y), z.avg_with(w)),
|
||||
}
|
||||
}
|
109
src/output/render_thread.rs
Normal file
109
src/output/render_thread.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use std::{
|
||||
sync::mpsc::{self, Receiver, Sender},
|
||||
thread::{self, JoinHandle},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use crate::cli::CommandLine;
|
||||
|
||||
use super::{FrameSync, Renderer};
|
||||
|
||||
/// Control a rendering thread that lazily starts.
|
||||
/// This allows the `Bridge` struct to be used in places
|
||||
/// where we do not expected the rendering thread to start.
|
||||
pub struct RenderThread {
|
||||
thread: Option<(Sender<Message>, JoinHandle<()>)>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
type RenderClosure = Box<dyn FnMut(&mut Renderer) + Send>;
|
||||
enum Message {
|
||||
Run(RenderClosure),
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
impl RenderThread {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
thread: None,
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable the rendering thread.
|
||||
/// Allows the thread to be lazily initiated.
|
||||
pub fn enable(&mut self) {
|
||||
self.enabled = true
|
||||
}
|
||||
|
||||
/// Stop the rendering thread.
|
||||
/// Returns a `JoinHandle` if a thread was started.
|
||||
pub fn stop(&mut self) -> Option<JoinHandle<()>> {
|
||||
self.enabled = false;
|
||||
self.send(Message::Shutdown);
|
||||
|
||||
let (_, handle) = self.thread.take()?;
|
||||
|
||||
Some(handle)
|
||||
}
|
||||
|
||||
/// Run a closure on the rendering thread.
|
||||
pub fn render<F>(&mut self, run: F)
|
||||
where
|
||||
F: FnMut(&mut Renderer) + Send + 'static,
|
||||
{
|
||||
self.send(Message::Run(Box::new(run)))
|
||||
}
|
||||
|
||||
/// Boot the rendering thread, contains a simple event loop.
|
||||
fn boot(rx: Receiver<Message>) {
|
||||
let cmd = CommandLine::parse();
|
||||
let mut sync = FrameSync::new(cmd.fps);
|
||||
let mut renderer = Renderer::new();
|
||||
let mut needs_render = false;
|
||||
|
||||
loop {
|
||||
// Get the deadline for the next frame
|
||||
let deadline = sync.deadline();
|
||||
|
||||
loop {
|
||||
// Wait for some events before the deadline
|
||||
match rx.recv_timeout(deadline - Instant::now()).ok() {
|
||||
// Timeout and no message, render if needed
|
||||
None => break,
|
||||
// Shutdown the thread
|
||||
Some(Message::Shutdown) => return,
|
||||
// Run a closure and schedule a render
|
||||
Some(Message::Run(mut closure)) => {
|
||||
closure(&mut renderer);
|
||||
|
||||
needs_render = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render if needed
|
||||
if needs_render {
|
||||
needs_render = false;
|
||||
|
||||
// Update the frame sync timings
|
||||
sync.start();
|
||||
renderer.render().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a message to the rendering thread.
|
||||
/// Creates a new thread if enabled and needed.
|
||||
fn send(&mut self, message: Message) {
|
||||
if let Some((tx, _)) = &self.thread {
|
||||
tx.send(message).unwrap()
|
||||
} else if self.enabled {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tx.send(message).unwrap();
|
||||
|
||||
self.thread = Some((tx.clone(), thread::spawn(move || Self::boot(rx))));
|
||||
}
|
||||
}
|
||||
}
|
@ -10,24 +10,16 @@ use crate::{
|
||||
gfx::{Color, Point, Rect, Size},
|
||||
input::Key,
|
||||
ui::navigation::{Navigation, NavigationAction},
|
||||
utils::log,
|
||||
};
|
||||
|
||||
use super::{Cell, Grapheme, Painter};
|
||||
|
||||
struct Dimensions {
|
||||
/// Size of a terminal cell in pixels
|
||||
cell: Size,
|
||||
/// Size of the browser window in pixels
|
||||
browser: Size,
|
||||
/// Size of the terminal window in cells
|
||||
terminal: Size,
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
nav: Navigation,
|
||||
cells: Vec<(Cell, Cell)>,
|
||||
dimensions: Dimensions,
|
||||
painter: Painter,
|
||||
size: Size,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
@ -36,11 +28,7 @@ impl Renderer {
|
||||
nav: Navigation::new(),
|
||||
cells: Vec::with_capacity(0),
|
||||
painter: Painter::new(),
|
||||
dimensions: Dimensions {
|
||||
cell: Size::new(7, 14),
|
||||
browser: Size::new(0, 0),
|
||||
terminal: Size::new(0, 0),
|
||||
},
|
||||
size: Size::new(0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,29 +39,21 @@ impl Renderer {
|
||||
pub fn keypress(&mut self, key: &Key) -> io::Result<NavigationAction> {
|
||||
let action = self.nav.keypress(key);
|
||||
|
||||
self.render()?;
|
||||
|
||||
Ok(action)
|
||||
}
|
||||
pub fn mouse_up(&mut self, origin: Point) -> io::Result<NavigationAction> {
|
||||
let action = self.nav.mouse_up(origin);
|
||||
|
||||
self.render()?;
|
||||
|
||||
Ok(action)
|
||||
}
|
||||
pub fn mouse_down(&mut self, origin: Point) -> io::Result<NavigationAction> {
|
||||
let action = self.nav.mouse_down(origin);
|
||||
|
||||
self.render()?;
|
||||
|
||||
Ok(action)
|
||||
}
|
||||
pub fn mouse_move(&mut self, origin: Point) -> io::Result<NavigationAction> {
|
||||
let action = self.nav.mouse_move(origin);
|
||||
|
||||
self.render()?;
|
||||
|
||||
Ok(action)
|
||||
}
|
||||
|
||||
@ -82,23 +62,20 @@ impl Renderer {
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> Size {
|
||||
self.dimensions.terminal
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn set_size(&mut self, cell: Size, terminal: Size) {
|
||||
let size = (terminal.width + terminal.width * terminal.height) as usize;
|
||||
|
||||
self.nav.set_size(terminal);
|
||||
self.dimensions.cell = cell;
|
||||
self.dimensions.terminal = terminal;
|
||||
self.dimensions.browser = cell * terminal;
|
||||
pub fn set_size(&mut self, size: Size) {
|
||||
self.nav.set_size(size);
|
||||
self.size = size;
|
||||
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
let bound = terminal.width - 1;
|
||||
let bound = size.width - 1;
|
||||
let cells = (size.width + size.width * size.height) as usize;
|
||||
|
||||
self.cells.clear();
|
||||
self.cells.resize_with(size, || {
|
||||
self.cells.resize_with(cells, || {
|
||||
let cell = (Cell::new(x, y), Cell::new(x, y));
|
||||
|
||||
if x < bound {
|
||||
@ -113,14 +90,19 @@ impl Renderer {
|
||||
}
|
||||
|
||||
pub fn render(&mut self) -> io::Result<()> {
|
||||
let size = self.dimensions.terminal;
|
||||
let size = self.size;
|
||||
|
||||
for (origin, element) in self.nav.render(size) {
|
||||
self.fill_rect(
|
||||
Rect::new(origin.x, origin.y, element.text.width() as u32, 1),
|
||||
element.background,
|
||||
);
|
||||
self.draw_text(&element.text, origin, Size::splat(0), element.foreground);
|
||||
self.draw_text(
|
||||
&element.text,
|
||||
origin * (2, 1),
|
||||
Size::splat(0),
|
||||
element.foreground,
|
||||
);
|
||||
}
|
||||
|
||||
self.painter.begin()?;
|
||||
@ -130,8 +112,7 @@ impl Renderer {
|
||||
continue;
|
||||
}
|
||||
|
||||
previous.top = current.top;
|
||||
previous.bottom = current.bottom;
|
||||
previous.quadrant = current.quadrant;
|
||||
previous.grapheme = current.grapheme.clone();
|
||||
|
||||
self.painter.paint(current)?;
|
||||
@ -143,53 +124,55 @@ impl Renderer {
|
||||
}
|
||||
|
||||
/// Draw the background from a pixel array encoded in RGBA8888
|
||||
pub fn draw_background(&mut self, pixels: &mut [u8], rect: Rect) -> io::Result<()> {
|
||||
let viewport = self.dimensions.terminal.cast::<usize>();
|
||||
let pixels_row = viewport.width * 4;
|
||||
pub fn draw_background(&mut self, pixels: &[u8], pixels_size: Size, rect: Rect) {
|
||||
let viewport = self.size.cast::<usize>();
|
||||
|
||||
if pixels.len() != pixels_row * viewport.height * 2 {
|
||||
return Ok(());
|
||||
if pixels.len() < viewport.width * viewport.height * 8 * 4 {
|
||||
log::debug!(
|
||||
"unexpected size, actual: {}, expected: {}",
|
||||
pixels.len(),
|
||||
viewport.width * viewport.height * 8 * 4
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let pos = rect.origin.cast::<usize>() / (1, 2);
|
||||
let size = rect.size.cast::<usize>() / (1, 2);
|
||||
let pixels_left = pos.x * 4;
|
||||
let pixels_width = size.width * 4;
|
||||
let origin = rect.origin.cast::<f32>().max(0.0) / (2.0, 4.0);
|
||||
let size = rect.size.cast::<f32>().max(0.0) / (2.0, 4.0);
|
||||
let top = (origin.y.floor() as usize).min(viewport.height);
|
||||
let left = (origin.x.floor() as usize).min(viewport.width);
|
||||
let right = ((origin.x + size.width).ceil() as usize)
|
||||
.min(viewport.width)
|
||||
.max(left);
|
||||
let bottom = ((origin.y + size.height).ceil() as usize)
|
||||
.min(viewport.height)
|
||||
.max(top);
|
||||
let row_length = pixels_size.width as usize;
|
||||
let pixel = |x, y| {
|
||||
Color::new(
|
||||
pixels[((x + y * row_length) * 4 + 2) as usize],
|
||||
pixels[((x + y * row_length) * 4 + 1) as usize],
|
||||
pixels[((x + y * row_length) * 4 + 0) as usize],
|
||||
)
|
||||
};
|
||||
let pair = |x, y| pixel(x, y).avg_with(pixel(x, y + 1));
|
||||
|
||||
// Iterate over each row
|
||||
for y in pos.y..pos.y + size.height {
|
||||
// Terminal chars have an aspect ratio of 2:1.
|
||||
// In order to display perfectly squared pixels, we
|
||||
// render a unicode glyph taking the bottom half of the cell
|
||||
// using a foreground representing the bottom pixel,
|
||||
// and a background representing the top pixel.
|
||||
// This means that the pixel input buffer should be twice the size
|
||||
// of the terminal cell buffer (two pixels take one terminal cell).
|
||||
let left = pixels_left + y * 2 * pixels_row;
|
||||
let right = left + pixels_width;
|
||||
// Get a slice pointing to the top pixel row
|
||||
let mut top_row = pixels[left..right].iter();
|
||||
// Get a slice pointing to the bottom pixel row
|
||||
let mut bottom_row = pixels[left + pixels_row..right + pixels_row].iter();
|
||||
let cells_left = y * viewport.width + pos.x + viewport.width;
|
||||
let cells = self.cells[cells_left..].iter_mut();
|
||||
for y in top..bottom {
|
||||
let index = (y + 1) * viewport.width;
|
||||
let start = index + left;
|
||||
let end = index + right;
|
||||
let (mut x, y) = (left * 2, y * 4);
|
||||
|
||||
// Iterate over each column
|
||||
for (_, cell) in cells {
|
||||
match (
|
||||
Color::from_iter(&mut top_row),
|
||||
Color::from_iter(&mut bottom_row),
|
||||
) {
|
||||
(Some(top), Some(bottom)) => {
|
||||
cell.top = top;
|
||||
cell.bottom = bottom;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
for (_, cell) in &mut self.cells[start..end] {
|
||||
cell.quadrant = (
|
||||
pair(x + 0, y + 0),
|
||||
pair(x + 1, y + 0),
|
||||
pair(x + 1, y + 2),
|
||||
pair(x + 0, y + 2),
|
||||
);
|
||||
|
||||
x += 2;
|
||||
}
|
||||
}
|
||||
|
||||
self.render()
|
||||
}
|
||||
|
||||
pub fn clear_text(&mut self) {
|
||||
@ -210,9 +193,8 @@ impl Renderer {
|
||||
|
||||
pub fn fill_rect(&mut self, rect: Rect, color: Color) {
|
||||
self.draw(rect, |cell| {
|
||||
cell.top = color;
|
||||
cell.bottom = color;
|
||||
cell.grapheme = None;
|
||||
cell.quadrant = (color, color, color, color);
|
||||
})
|
||||
}
|
||||
|
||||
@ -222,7 +204,7 @@ impl Renderer {
|
||||
{
|
||||
let origin = bounds.origin.cast::<usize>();
|
||||
let size = bounds.size.cast::<usize>();
|
||||
let viewport_width = self.dimensions.terminal.width as usize;
|
||||
let viewport_width = self.size.width as usize;
|
||||
let top = origin.y;
|
||||
let bottom = top + size.height;
|
||||
|
||||
@ -241,25 +223,28 @@ impl Renderer {
|
||||
pub fn draw_text(&mut self, string: &str, origin: Point, size: Size, color: Color) {
|
||||
// Get an iterator starting at the text origin
|
||||
let len = self.cells.len();
|
||||
let viewport = &self.dimensions.terminal;
|
||||
let viewport = &self.size.cast::<usize>();
|
||||
|
||||
if size.width > 2 && size.height > 2 {
|
||||
let x = origin.x.max(0).min(viewport.width as i32);
|
||||
let top = (origin.y + 1).max(0);
|
||||
let bottom = top + size.height as i32;
|
||||
let origin = (origin.cast::<f32>() / (2.0, 4.0) + (0.0, 1.0)).round();
|
||||
let size = (size.cast::<f32>() / (2.0, 4.0)).round();
|
||||
let left = (origin.x.max(0.0) as usize).min(viewport.width);
|
||||
let right = ((origin.x + size.width).max(0.0) as usize).min(viewport.width);
|
||||
let top = (origin.y.max(0.0) as usize).min(viewport.height);
|
||||
let bottom = ((origin.y + size.height).max(0.0) as usize).min(viewport.height);
|
||||
|
||||
for y in top..bottom {
|
||||
let index = x + y / 2 * (viewport.width as i32);
|
||||
let left = len.min(index as usize);
|
||||
let right = len.min(left + size.width as usize);
|
||||
let index = y * viewport.width;
|
||||
let start = index + left;
|
||||
let end = index + right;
|
||||
|
||||
for (_, cell) in self.cells[left..right].iter_mut() {
|
||||
for (_, cell) in self.cells[start..end].iter_mut() {
|
||||
cell.grapheme = None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Compute the buffer index based on the position
|
||||
let index = origin.x + (origin.y + 1) / 2 * (viewport.width as i32);
|
||||
let index = origin.x / 2 + (origin.y + 1) / 4 * (viewport.width as i32);
|
||||
let mut iter = self.cells[len.min(index as usize)..].iter_mut();
|
||||
|
||||
// Get every Unicode grapheme in the input string
|
||||
|
@ -1,43 +0,0 @@
|
||||
use core::mem::MaybeUninit;
|
||||
use std::{io, str::FromStr};
|
||||
|
||||
use crate::{gfx::Size, utils::log};
|
||||
|
||||
pub fn size() -> io::Result<Size> {
|
||||
let mut ptr = MaybeUninit::<libc::winsize>::uninit();
|
||||
let mut size = unsafe {
|
||||
if libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, ptr.as_mut_ptr()) == 0 {
|
||||
Some(ptr.assume_init())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
.ok_or_else(io::Error::last_os_error)?;
|
||||
|
||||
if size.ws_col == 0 || size.ws_row == 0 {
|
||||
let cols = parse_var("COLUMNS").unwrap_or(80);
|
||||
let rows = parse_var("LINES").unwrap_or(24);
|
||||
|
||||
log::warning!(
|
||||
"TIOCGWINSZ returned an empty size ({}x{}), defaulting to {}x{}",
|
||||
size.ws_col,
|
||||
size.ws_row,
|
||||
cols,
|
||||
rows
|
||||
);
|
||||
|
||||
size.ws_col = cols;
|
||||
size.ws_row = rows;
|
||||
}
|
||||
|
||||
Ok(Size::new(
|
||||
if size.ws_col > 2 { size.ws_col } else { 1 },
|
||||
// Keep some space for the UI
|
||||
if size.ws_row > 2 { size.ws_row - 1 } else { 1 },
|
||||
)
|
||||
.cast())
|
||||
}
|
||||
|
||||
fn parse_var<T: FromStr>(var: &str) -> Option<T> {
|
||||
std::env::var(var).ok()?.parse().ok()
|
||||
}
|
105
src/output/window.rs
Normal file
105
src/output/window.rs
Normal file
@ -0,0 +1,105 @@
|
||||
use core::mem::MaybeUninit;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::{cli::CommandLine, gfx::Size, utils::log};
|
||||
|
||||
/// A terminal window.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Window {
|
||||
/// Device pixel ratio
|
||||
pub dpi: f32,
|
||||
/// Size of a terminal cell in pixels
|
||||
pub scale: Size<f32>,
|
||||
/// Size of the termina window in cells
|
||||
pub cells: Size,
|
||||
/// Size of the browser window in pixels
|
||||
pub browser: Size,
|
||||
/// Command line arguments
|
||||
pub cmd: CommandLine,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
/// Read the window
|
||||
pub fn read() -> Window {
|
||||
let mut window = Self {
|
||||
dpi: 1.0,
|
||||
scale: (0.0, 0.0).into(),
|
||||
cells: (0, 0).into(),
|
||||
browser: (0, 0).into(),
|
||||
cmd: CommandLine::parse(),
|
||||
};
|
||||
|
||||
window.update();
|
||||
|
||||
window
|
||||
}
|
||||
|
||||
pub fn update(&mut self) -> &Self {
|
||||
let (mut term, mut cell) = unsafe {
|
||||
let mut ptr = MaybeUninit::<libc::winsize>::uninit();
|
||||
|
||||
if libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, ptr.as_mut_ptr()) == 0 {
|
||||
let size = ptr.assume_init();
|
||||
|
||||
(
|
||||
Size::new(size.ws_col, size.ws_row),
|
||||
Size::new(size.ws_xpixel, size.ws_ypixel),
|
||||
)
|
||||
} else {
|
||||
(Size::splat(0), Size::splat(0))
|
||||
}
|
||||
};
|
||||
|
||||
if cell.width == 0 || cell.height == 0 {
|
||||
cell.width = 8;
|
||||
cell.height = 16;
|
||||
}
|
||||
|
||||
if term.width == 0 || term.height == 0 {
|
||||
let cols = match parse_var("COLUMNS").unwrap_or(0) {
|
||||
0 => 80,
|
||||
x => x,
|
||||
};
|
||||
let rows = match parse_var("LINES").unwrap_or(0) {
|
||||
0 => 24,
|
||||
x => x,
|
||||
};
|
||||
|
||||
log::warning!(
|
||||
"TIOCGWINSZ returned an empty size ({}x{}), defaulting to {}x{}",
|
||||
term.width,
|
||||
term.height,
|
||||
cols,
|
||||
rows
|
||||
);
|
||||
|
||||
term.width = cols;
|
||||
term.height = rows;
|
||||
}
|
||||
|
||||
let zoom = 1.5 * self.cmd.zoom;
|
||||
let cells = Size::new(term.width.max(1), term.height.max(2) - 1);
|
||||
let auto_scale = false;
|
||||
let cell_pixels = if auto_scale {
|
||||
Size::new(cell.width as f32, cell.height as f32) / cells.cast()
|
||||
} else {
|
||||
Size::new(8.0, 16.0)
|
||||
};
|
||||
// Normalize the cells dimensions for an aspect ratio of 1:2
|
||||
let cell_width = (cell_pixels.width + cell_pixels.height / 2.0) / 2.0;
|
||||
|
||||
// Round DPI to 2 decimals for proper viewport computations
|
||||
self.dpi = (2.0 / cell_width * zoom * 100.0).ceil() / 100.0;
|
||||
// A virtual cell should contain a 2x4 pixel quadrant
|
||||
self.scale = Size::new(2.0, 4.0) / self.dpi;
|
||||
// Keep some space for the UI
|
||||
self.cells = Size::new(term.width.max(1), term.height.max(2) - 1).cast();
|
||||
self.browser = self.cells.cast::<f32>().mul(self.scale).ceil().cast();
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_var<T: FromStr>(var: &str) -> Option<T> {
|
||||
std::env::var(var).ok()?.parse().ok()
|
||||
}
|
@ -189,8 +189,9 @@ impl Navigation {
|
||||
}
|
||||
|
||||
pub fn render(&self, size: Size) -> Vec<(Point, NavigationElement)> {
|
||||
let space = if size.width >= 13 {
|
||||
size.width as usize - 13
|
||||
let ui_elements = 13;
|
||||
let space = if size.width >= ui_elements {
|
||||
(size.width - ui_elements) as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
@ -1,4 +1,8 @@
|
||||
pub mod log;
|
||||
mod four_bits;
|
||||
mod try_block;
|
||||
|
||||
pub mod log;
|
||||
|
||||
use try_block::*;
|
||||
|
||||
pub use four_bits::*;
|
||||
|
44
src/utils/four_bits.rs
Normal file
44
src/utils/four_bits.rs
Normal file
@ -0,0 +1,44 @@
|
||||
pub enum FourBits {
|
||||
B0000 = 0b0000,
|
||||
B0001 = 0b0001,
|
||||
B0010 = 0b0010,
|
||||
B0011 = 0b0011,
|
||||
B0100 = 0b0100,
|
||||
B0101 = 0b0101,
|
||||
B0110 = 0b0110,
|
||||
B0111 = 0b0111,
|
||||
B1000 = 0b1000,
|
||||
B1001 = 0b1001,
|
||||
B1010 = 0b1010,
|
||||
B1011 = 0b1011,
|
||||
B1100 = 0b1100,
|
||||
B1101 = 0b1101,
|
||||
B1110 = 0b1110,
|
||||
B1111 = 0b1111,
|
||||
}
|
||||
|
||||
impl FourBits {
|
||||
pub fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
|
||||
use FourBits::*;
|
||||
|
||||
match (x as u8) << 3 | (y as u8) << 2 | (z as u8) << 1 | (w as u8) << 0 {
|
||||
0b0000 => B0000,
|
||||
0b0001 => B0001,
|
||||
0b0010 => B0010,
|
||||
0b0011 => B0011,
|
||||
0b0100 => B0100,
|
||||
0b0101 => B0101,
|
||||
0b0110 => B0110,
|
||||
0b0111 => B0111,
|
||||
0b1000 => B1000,
|
||||
0b1001 => B1001,
|
||||
0b1010 => B1010,
|
||||
0b1011 => B1011,
|
||||
0b1100 => B1100,
|
||||
0b1101 => B1101,
|
||||
0b1110 => B1110,
|
||||
0b1111 => B1111,
|
||||
_ => panic!("Unexpected mask value"),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user