Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,39 @@ jobs:
- run: mix test
env:
DESKTOP_WEBVIEW_BINARY: ${{ github.workspace }}/priv/native/macos/DesktopWebView

linux-e2e:
name: Linux host + E2E
runs-on: ubuntu-latest
env:
NO_WX: "1"
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "26"
elixir-version: "1.18"
- name: Checkout desktop (path dep)
uses: actions/checkout@v4
with:
repository: elixir-desktop/desktop
path: desktop
- name: Install GTK / WebKitGTK
run: |
sudo apt-get update -qq
sudo apt-get install -y \
build-essential cmake pkg-config \
libgtk-4-dev libwebkitgtk-6.0-dev \
libnotify-dev libjson-glib-dev \
xvfb dbus-x11
- name: Build host
run: ./scripts/build_linux.sh
- run: mix deps.get
- name: E2E under Xvfb
run: dbus-run-session -- xvfb-run -a mix test
env:
DESKTOP_WEBVIEW_BINARY: ${{ github.workspace }}/priv/native/linux/DesktopWebView
WEBKIT_DISABLE_COMPOSITING_MODE: "1"
WEBKIT_DISABLE_DMABUF_RENDERER: "1"
WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS: "1"
LIBGL_ALWAYS_SOFTWARE: "1"
29 changes: 28 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,32 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

linux:
name: Linux x86_64 artifact
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install GTK / WebKitGTK
run: |
sudo apt-get update -qq
sudo apt-get install -y \
build-essential cmake pkg-config \
libgtk-4-dev libwebkitgtk-6.0-dev \
libnotify-dev libjson-glib-dev
- name: Build host
run: ./scripts/build_linux.sh
- name: Pack
run: |
mkdir -p dist
cp priv/native/linux/DesktopWebView dist/DesktopWebView-linux-x86_64
sha256sum dist/DesktopWebView-linux-x86_64 > dist/DesktopWebView-linux-x86_64.sha256
- uses: softprops/action-gh-release@v2
with:
draft: true
files: |
dist/DesktopWebView-linux-x86_64
dist/DesktopWebView-linux-x86_64.sha256
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# windows: add when native/windows host exists — attach DesktopWebView-windows-* to the same draft release
# linux: add when native/linux host exists — attach DesktopWebView-linux-* to the same draft release
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ desktop_webview-*.tar
*.xcworkspace/xcuserdata/
native/macos/.build/
native/macos/Package.resolved
native/linux/build/
priv/native/macos/DesktopWebView
priv/native/linux/DesktopWebView
.DS_Store
*.swp
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ spawns the BEAM release; in development an existing BEAM can launch the host wit
|----------|--------|--------|
| macOS | WKWebView | Primary — see [docs/status/macos.md](docs/status/macos.md) |
| Windows | WebView2 | Planned — [docs/status/windows.md](docs/status/windows.md) |
| Linux | WebKitGTK | Planned — [docs/status/linux.md](docs/status/linux.md) |
| Linux | WebKitGTK | Usable — [docs/status/linux.md](docs/status/linux.md) |

## Quick start (development)

Expand All @@ -25,10 +25,11 @@ config :desktop, :backend, DesktopWebview.Backend
config :desktop, :menu_adapter, DesktopWebview.Menu.Adapter
```

Build the macOS host (or use the vendored binary under `priv/native/macos/`):
Build the native host for your OS (or use a vendored/CI binary):

```bash
./scripts/build_macos.sh
./scripts/build_macos.sh # macOS → priv/native/macos/DesktopWebView
./scripts/build_linux.sh # Linux → priv/native/linux/DesktopWebView
mix test # unit
mix test.e2e # needs host binary; starts with --edw-test-rpc
```
Expand Down
5 changes: 4 additions & 1 deletion docs/packaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ MyApp/
- Release asset name: `DesktopWebView-linux-x86_64`.
- Prefer shipping against a documented WebKitGTK/GTK baseline (note distro
packages in `native/linux/README.md`).
- Tray: StatusNotifierItem when available.
Ubuntu 24.04 baseline: GTK 4.14 + WebKitGTK 2.52 (`libgtk-4-dev`,
`libwebkitgtk-6.0-dev`).
- Tray: StatusNotifierItem when available; current host keeps an in-memory tray
for RPC conformance (AppIndicator is GTK 3 and not linked).
- Microphone / camera: PipeWire/Pulse + portal prompts may appear; hybrid
permission policy still applies. Flatpak/snap portals need extra packaging
notes when those formats are supported.
Expand Down
57 changes: 29 additions & 28 deletions docs/status/linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,36 @@

States: `done` | `partial` | `todo` | `n/a`

Host not implemented yet. Follow [docs/porting.md](../porting.md).
Binary delivery via GitHub Releases (not Hex `priv/`).
Host: GTK 4 + WebKitGTK 6 (`native/linux/`). Binary delivery via GitHub Releases
(not Hex `priv/`); local/CI uses `./scripts/build_linux.sh` →
`priv/native/linux/DesktopWebView` or `DESKTOP_WEBVIEW_BINARY`.

| Feature | Status | Notes |
|---------|--------|-------|
| TCP listen + `listening <port>` | todo | |
| `initialize` + capabilities | todo | |
| `--edw-no-beam` / argv strip | todo | |
| Ini + defaults | todo | |
| Lifetime reconnect | todo | |
| Lifetime coupled | todo | |
| Window open/show/hide/title/min size | todo | |
| Multi-window (ids) | todo | |
| Close veto → Elixir | todo | |
| Raise / iconize / shown / active | todo | |
| Webview loadURL / reload / current URL | todo | |
| Webview rebuild | todo | |
| New window → external open | todo | |
| Context menu disable | todo | |
| Menubar from DOM | todo | |
| Tray / status item | todo | |
| Apple menu | n/a | |
| Notifications | todo | |
| Icons from path / PNG | todo | |
| OS events (reopen, open url/file) | todo | |
| Locale / os_description | todo | |
| Permission policy hybrid | todo | |
| Microphone in webview | todo | |
| Camera in webview | todo | |
| Test RPC channel | todo | |
| TCP listen + `listening <port>` | done | |
| `initialize` + capabilities | done | `platform: "linux"` |
| `--edw-no-beam` / argv strip | done | |
| Ini + defaults | done | beside executable |
| Lifetime reconnect | done | default |
| Lifetime coupled | done | |
| Window open/show/hide/title/min size | done | |
| Multi-window (ids) | done | |
| Close veto → Elixir | done | |
| Raise / iconize / shown / active | done | |
| Webview loadURL / reload / current URL | done | |
| Webview rebuild | done | |
| New window → external open | done | |
| Context menu disable | done | |
| Menubar from DOM | done | GtkPopoverMenuBar + GMenu |
| Tray / status item | done | In-memory tray for RPC; visual SNI/AppIndicator still TBD |
| Apple menu | n/a | successful no-op |
| Notifications | done | libnotify (falls back to stderr log) |
| Icons from path / PNG | done | |
| OS events (reopen, open url/file) | partial | `system.open_url` works; desktop-file reopen/open-file TBD |
| Locale / os_description | done | |
| Permission policy hybrid | done | |
| Microphone in webview | done | E2E via test RPC + fixture |
| Camera in webview | done | E2E via test RPC + fixture |
| Test RPC channel | done | `--edw-test-rpc` |
| Release artifact download | todo | |
| CI build | todo | |
| CI build | done | ubuntu-latest + xvfb |
19 changes: 17 additions & 2 deletions lib/desktop_webview/launcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ defmodule DesktopWebview.Launcher do

case await_listening(port, Keyword.get(opts, :timeout, 10_000)) do
{:ok, listen_port} ->
{:ok, %{port: port, listen_port: listen_port, binary: binary}}
# Keep draining host stdout/stderr so WebKit logs cannot fill the pipe.
drain_pid = spawn_link(fn -> drain_port(port) end)
true = Port.connect(port, drain_pid)
{:ok, %{port: port, listen_port: listen_port, binary: binary, drain_pid: drain_pid}}

{:error, reason} ->
Port.close(port)
Expand All @@ -49,7 +52,12 @@ defmodule DesktopWebview.Launcher do
end
end

def stop(%{port: port}) when is_port(port) do
def stop(%{port: port} = launcher) when is_port(port) do
if pid = Map.get(launcher, :drain_pid) do
Process.unlink(pid)
Process.exit(pid, :kill)
end

try do
case Port.info(port) do
nil -> :ok
Expand Down Expand Up @@ -101,4 +109,11 @@ defmodule DesktopWebview.Launcher do
end
end
end

defp drain_port(port) do
receive do
{^port, {:data, _}} -> drain_port(port)
{^port, {:exit_status, _}} -> :ok
end
end
end
3 changes: 3 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tools]
erlang = "26"
elixir = "1.18.4-otp-26"
28 changes: 28 additions & 0 deletions native/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.16)
project(DesktopWebView LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(PkgConfig REQUIRED)
pkg_check_modules(DEPS REQUIRED IMPORTED_TARGET
gtk4
webkitgtk-6.0
libnotify
json-glib-1.0
)

add_executable(DesktopWebView
src/main.cpp
src/config.cpp
src/json_util.cpp
src/rpc_server.cpp
src/web_window.cpp
src/host_controller.cpp
)

target_include_directories(DesktopWebView PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(DesktopWebView PRIVATE PkgConfig::DEPS)

install(TARGETS DesktopWebView RUNTIME DESTINATION bin)
70 changes: 57 additions & 13 deletions native/linux/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,63 @@
# Linux host (WebKitGTK)

Not implemented yet. This directory will hold the WebKitGTK host.
C++ + **GTK 4** + **WebKitGTK 6** executable `DesktopWebView`.

## Specs
## Distro packages (Ubuntu 24.04 / noble)

Build:

```text
build-essential cmake pkg-config
libgtk-4-dev
libwebkitgtk-6.0-dev
libnotify-dev
libjson-glib-dev
```

Runtime (same SONAMEs): `libgtk-4-1`, `libwebkitgtk-6.0-4`, `libnotify4`,
`libjson-glib-1.0-0`.

Baseline verified in CI: GTK **4.14**, WebKitGTK **2.52** (`webkitgtk-6.0`).

Tray: AppIndicator libraries are GTK 3 and are not linked into this GTK 4 process.
`tray.*` RPC succeeds with an in-memory tray; StatusNotifierItem integration is
tracked as partial in [status/linux.md](../../docs/status/linux.md).

## Build

```bash
# from repo root
./scripts/build_linux.sh

- [Porting guide](../../docs/porting.md) — checklist, toolchain, E2E gate
- [Protocol](../../docs/protocol.md) — JSON-RPC + behavioral semantics
- [Packaging](../../docs/packaging.md) — Linux layout and release artifact names
- [Status](../../docs/status/linux.md) — feature matrix
# or
cmake -S native/linux -B native/linux/build -DCMAKE_BUILD_TYPE=Release
cmake --build native/linux/build
```

## Contract (summary)
Output is copied to `priv/native/linux/DesktopWebView` for local/CI use.
Release artifact name: `DesktopWebView-linux-x86_64` (see [packaging.md](../../docs/packaging.md)).

## Run (dev / E2E)

```bash
./priv/native/linux/DesktopWebView --edw-no-beam --edw-test-rpc --edw-port=0
# prints: listening <port>
```

Headless CI needs a display (`xvfb-run` or a pre-set `DISPLAY`).

## Structure

| Path | Role |
|------|------|
| `CMakeLists.txt` | CMake project |
| `src/` | Host sources (config, RPC, windows, dispatch) |

Do not add a native unit-test suite; exercise the binary from Elixir E2E (`mix test.e2e`).

## Specs

- Native process listens on TCP; Elixir connects
- JSON-RPC 2.0 over 4-byte length-prefixed frames
- `--edw-*` host flags; remaining argv forwarded to BEAM
- Binary: `DesktopWebView-linux-x86_64` via GitHub Releases (not Hex `priv/`)
- `menu.set_apple` → successful no-op
- Document GTK / WebKitGTK package versions here when the host lands
- [Porting guide](../../docs/porting.md)
- [Protocol](../../docs/protocol.md)
- [Packaging](../../docs/packaging.md)
- [Status](../../docs/status/linux.md)
Loading
Loading