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
3 changes: 3 additions & 0 deletions .github/workflows/additional-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ jobs:
repository: antono2/glfw
ref: v2.0.1
path: build/modules/antono2/glfw
- name: Verify the declared dependency graph
shell: bash
run: v run scripts/check_installed_deps.vsh --tag-suffix antono2.imgui=-standard
- name: Install graphics dependencies
run: sudo apt-get update && sudo apt-get install -y build-essential cmake libglfw3-dev libvulkan-dev libvulkan-volk-dev pkg-config mesa-vulkan-drivers vulkan-validationlayers xvfb
- name: Build native library
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ jobs:
run: |
make local=1
echo "$GITHUB_WORKSPACE/toolchain/v" >> "$GITHUB_PATH"
- name: Verify the declared dependency graph
shell: bash
run: v run source/demo/scripts/check_installed_deps.vsh
- name: Install native dependencies
run: sudo apt-get update && sudo apt-get install -y build-essential cmake libglfw3-dev libvulkan-dev libvulkan-volk-dev pkg-config mesa-vulkan-drivers vulkan-validationlayers xvfb openbox xdotool x11-utils x11-xserver-utils wmctrl gdb
- name: Build native ImGui library
Expand Down Expand Up @@ -135,6 +138,9 @@ jobs:
shell: pwsh
run: |
"$env:GITHUB_WORKSPACE\toolchain\v" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Verify the declared dependency graph
shell: bash
run: v run source/demo/scripts/check_installed_deps.vsh
- name: Install Vulkan SDK and SwiftShader test artifacts
shell: bash
run: v run source/modules/antono2/imgui/scripts/setup_vulkan_ci.vsh 1.4.309.0 v1.2
Expand Down Expand Up @@ -249,6 +255,9 @@ jobs:
run: |
make -C toolchain/v local=1
echo "$GITHUB_WORKSPACE/toolchain/v" >> "$GITHUB_PATH"
- name: Verify the declared dependency graph
shell: bash
run: v run source/demo/scripts/check_installed_deps.vsh
- name: Check demo with V3
run: |
for source in source/demo source/demo/examples/widget_gallery source/demo/examples/implot_dashboard; do
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ jobs:
repository: antono2/glfw
ref: v2.0.1
path: build/modules/antono2/glfw
- name: Verify the declared dependency graph
shell: bash
run: v run scripts/check_installed_deps.vsh ${{ matrix.variant == 'standard' && '--tag-suffix antono2.imgui=-standard' || '' }}
- name: Install Linux build and rendering tools
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y build-essential cmake libglfw3-dev libvulkan-dev libvulkan-volk-dev libdbus-1-dev libatk1.0-dev libatk-bridge2.0-dev libatspi2.0-dev libglib2.0-dev at-spi2-core dbus-x11 pkg-config mesa-vulkan-drivers vulkan-validationlayers ripgrep gdb xvfb patchelf zip xdotool openbox x11-utils x11-xserver-utils wmctrl
Expand Down
21 changes: 20 additions & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,30 @@ embeds license notices, aligns the APKs, signs them and verifies each signature.
Keep the key and password private and backed up outside the repository. Never
upload them as release assets. Use the same signing identity for updates.
The private key uses the same password as the keystore.
Increment `android:versionCode` and update `android:versionName` for each release.
Increment `android:versionCode` and update `android:versionName` whenever publishing newly built Android APKs.

Test the final universal APK on a device, including the installation/update
path. Review [the testing checklist](testing.md) and package instructions.
Generate `SHA256SUMS.txt` from the final ZIP and APK files. Tag the verified
commits only after checks pass, then publish the assets and installation link.
The upstream imgui release has a docking tag and a companion `-standard` source
tag; the same example packages can be attached to both repositories' releases.

## Desktop-only patch releases

A desktop fix can be released without rebuilding Android. Run the full portable
workflow for the new source commit and promote the four checked desktop ZIPs.
For unchanged Android code, retain the four signed APKs from the previous
release byte-for-byte. Verify their SHA-256 hashes against that release before
uploading them, and include them in the new `SHA256SUMS.txt`.

State explicitly in the release notes and `BUILD-PROVENANCE.txt` that the APKs
retain their previous source revision, Android version and signing identity.
Do not label retained APKs as builds of the desktop patch. New CI debug APKs
remain test artifacts. An Android change requires new version metadata, signing
and device validation as described above.

Required desktop jobs run `scripts/check_installed_deps.vsh` before building.
It compares every direct and transitive `v.mod` pin with the installed Git tag
commit. The standard lane explicitly selects `antono2.imgui=-standard`; other
lanes must match the declared tags. Keep advisory master jobs separate.
4 changes: 4 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
Build and rendering results are recorded in the PR checks. See
[the 2026-10-03 interaction report](validation-2026-10-03.md) for completed checks,
the viewport fix and the remaining device checks.
[The 2026-10-09 follow-up](validation-2026-10-09.md) records the Linux startup
fix, dependency checks and desktop patch release scope. Checked interaction
items below describe the recorded earlier sessions; they do not imply a new
manual session for each release.

## Automated checks

Expand Down
42 changes: 42 additions & 0 deletions docs/validation-2026-10-09.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Validation follow-up — 2026-10-09

## Linux startup fix

The raw GLFW/Vulkan example now maps its initially hidden window immediately
after creation and waits for visibility before renderer setup. In the affected Xvfb/Openbox/Lavapipe session,
the previous startup could wait inside `vkQueuePresentKHR` before the window
manager mapped it. The wait processes GLFW events and has a five-second limit. Mapping after
renderer setup still timed out once in the standard package keyboard check,
although that package had passed rendering and AT-SPI. Mapping is therefore
completed before Vulkan/ImGui setup adds further X11 requests and events.
The revised host passed twelve local startup/render/exit runs (six without a
window manager, six with Openbox) under Lavapipe with Vulkan validation. The
updated raw demo also passed F11/held-key/restore/Escape checks. Existing gallery
and dashboard binaries passed the remainder of the keyboard suite.

[PR #24](https://github.com/antono2/v_imgui_examples/pull/24) passed all 17 checks:
Linux and Windows builds, the pinned V3 lane, standard and docking portable
packages, keyboard shortcuts, Linux AT-SPI, Windows UI Automation and Android
builds for three ABIs. The earlier interaction results remain in
[the October 3 report](validation-2026-10-03.md).

## Dependency graph verification

Required desktop jobs verify installed tag commits against `v.mod`, recursively
including each dependency's own pins. Standard ImGui requires an explicit
companion-tag selection. Current dependency masters remain advisory.

Local fixtures passed with stable V 0.5.2 and pinned V3
`c0449e860641`: recursive annotated tags and paths containing spaces, rejection
of a mismatched transitive checkout, conflicting pins, implicit companion tags,
unused companion selections and unpinned dependencies; explicit companion
selection passed. The complete release dependency graph also passed locally.

## Release scope

Version 1.0.1 is a desktop patch. Publish the four desktop ZIPs from the passing
portable workflow for this revision and record that run in release provenance.
Retain the signed Android 1.0.0 APKs unchanged, verify their original hashes and
record their earlier provenance. This patch does not claim another Android
device session or new TalkBack/Narrator speech validation. Automated provider
checks establish actions and state, not spoken announcement quality.
34 changes: 18 additions & 16 deletions examples/glfw_vulkan/main.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub fn run(options Options, frame fn (mut App, voidptr), state voidptr) {

// Create window with Vulkan context
glfw.window_hint(glfw.client_api, glfw.no_api)
// Map the window after its renderer and UI are ready.
// Create hidden so startup events can be drained before mapping.
glfw.window_hint(glfw.visible, 0)
// Borderless fullscreen should remain available when switching applications.
glfw.window_hint(glfw.auto_iconify, 0)
Expand All @@ -59,6 +59,23 @@ pub fn run(options Options, frame fn (mut App, voidptr), state voidptr) {
panic(err)
}

// Map before renderer setup adds more X11 requests and events. GLFW 3.3
// can otherwise return from its visibility wait with the window unmapped.
// Drain startup events before asking the window manager to map it.
glfw.poll_events()
glfw.show_window(window)
// Showing a window can return before its window manager maps it. Mesa's
// X11 FIFO presentation can then block before another event poll occurs.
// Observe visibility before submitting the first frame, without sleeping
// indefinitely if the display/window manager cannot show this window.
visibility_deadline := glfw.get_time() + 5.0
for glfw.get_window_attrib(window, glfw.visible) == 0 {
if glfw.get_time() >= visibility_deadline {
panic('Window did not become visible within 5 seconds')
}
glfw.wait_events_timeout(0.01)
}

if !glfw.vulkan_supported() {
panic('GLFW: Vulkan Not Supported')
}
Expand Down Expand Up @@ -124,21 +141,6 @@ pub fn run(options Options, frame fn (mut App, voidptr), state voidptr) {
impl_vulkan.vkinit(&init_info)

options.initialize(state)
// Drain startup events before GLFW's X11 visibility wait. Queued unrelated
// events can prevent that wait from reaching its timeout during creation.
glfw.poll_events()
glfw.show_window(window)
// Showing a window can return before its window manager maps it. Mesa's
// X11 FIFO presentation can then block before another event poll occurs.
// Observe visibility before submitting the first frame, without sleeping
// indefinitely if the display/window manager cannot show this window.
visibility_deadline := glfw.get_time() + 5.0
for glfw.get_window_attrib(window, glfw.visible) == 0 {
if glfw.get_time() >= visibility_deadline {
panic('Window did not become visible within 5 seconds')
}
glfw.wait_events_timeout(0.01)
}

// No custom fonts are loaded, so Dear ImGui uses its default font.

Expand Down
69 changes: 69 additions & 0 deletions scripts/check_installed_deps.vsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env -S v run

// Verify direct and transitive release pins against the installed Git checkouts.
// Companion tags require an explicit --tag-suffix module=suffix argument.
import os
import v.vmod

fn check_module(root string, modules string, suffixes map[string]string, mut checked map[string]string) ! {
manifest := vmod.from_file(os.join_path(root, 'v.mod'))!
for dependency in manifest.dependencies {
name, declared_tag := dependency.rsplit_once('@') or {
return error('Dependency is not pinned in ${root}: ${dependency}')
}
tag := declared_tag + suffixes[name]
if previous := checked[name] {
if previous != tag {
return error('Conflicting dependency pins for ${name}: ${previous} and ${tag}')
}
continue
}
module_dir := os.join_path(modules, ...name.split('.'))
actual := os.execute('git -C ${os.quoted_path(module_dir)} rev-parse HEAD')
expected := os.execute('git -C ${os.quoted_path(module_dir)} rev-parse ${os.quoted_path('refs/tags/' + tag + '^{commit}')}')
if actual.exit_code != 0 || expected.exit_code != 0 {
return error('Cannot verify installed ${name}@${tag}: ${actual.output}${expected.output}')
}
if actual.output.trim_space() != expected.output.trim_space() {
return error('Installed ${name} does not match ${tag}: ${actual.output.trim_space()}')
}
checked[name] = tag
println('Verified ${name}@${tag}')
check_module(module_dir, modules, suffixes, mut checked)!
}
}

fn check_dependencies() ! {
mut suffixes := map[string]string{}
mut index := 1
for index < os.args.len {
if os.args[index] != '--tag-suffix' || index + 1 >= os.args.len {
return error('Usage: v run scripts/check_installed_deps.vsh [--tag-suffix module=suffix]')
}
name, suffix := os.args[index + 1].split_once('=') or {
return error('--tag-suffix requires module=suffix')
}
if name == '' || suffix == '' || name in suffixes {
return error('Invalid or repeated tag suffix: ${os.args[index + 1]}')
}
suffixes[name] = suffix
index += 2
}
root := os.dir(os.dir(os.real_path(@FILE)))
configured := os.getenv('VMODULES')
modules := if configured != '' { configured } else { os.join_path(os.home_dir(), '.vmodules') }
mut checked := map[string]string{}
check_module(root, modules, suffixes, mut checked)!
for name, _ in suffixes {
if name !in checked {
return error('Tag suffix did not match a dependency: ${name}')
}
}
}

fn main() {
check_dependencies() or {
eprintln(err)
exit(1)
}
}
2 changes: 1 addition & 1 deletion v.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Module {
name: 'v_imgui_examples'
description: 'Shows how to use V Dear ImGui'
author: 'Anton Oreskin'
version: '1.0.0'
version: '1.0.1'
license: 'MIT'
dependencies: ['antono2.imgui@v0.3.1']
}
Loading