Skip to content

Commit 337a183

Browse files
committed
New example - C Starter Kit
1 parent 0adf811 commit 337a183

12 files changed

Lines changed: 959 additions & 0 deletions

File tree

‎examples/starter_kit_c/.gitignore‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Generated at build time
2+
bin/
3+
lib/
4+
src/vfs.h
5+
tools/vfs
6+
tools/vfs.exe
7+
icon.o
8+
icon.res
9+
*.obj
10+
*.pdb
11+
*.ilk

‎examples/starter_kit_c/GNUmakefile‎

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# WebUI C Starter Kit
2+
# Linux / macOS / Windows (MinGW)
3+
#
4+
# make Build a single portable executable into bin/
5+
# make debug Same, with a console window and debug symbols
6+
# make clean Remove generated build files (keeps the lib/ snapshot)
7+
#
8+
# The first build must run inside the WebUI repository (examples/starter_kit_c)
9+
# so the WebUI static library can be copied into lib/. After that, this folder
10+
# is fully self-contained and can be moved anywhere.
11+
12+
# == 1. VARIABLES =============================================================
13+
14+
# Your application name (the executable name in bin/)
15+
APP_NAME = my_app
16+
17+
# Compiler: gcc or clang (macOS always uses clang)
18+
CC = gcc
19+
20+
# WebUI repository paths (only used when building inside the repository)
21+
WEBUI_DIST = ../../dist
22+
WEBUI_INCLUDE = ../../include
23+
24+
# WebUI static library file name
25+
LIB_FILE = libwebui-2-static.a
26+
27+
# Platform conditions
28+
ifeq ($(OS),Windows_NT)
29+
# Windows (MinGW)
30+
PLATFORM := windows
31+
SHELL := CMD
32+
OUT := bin/$(APP_NAME).exe
33+
ICON_OBJ := icon.o
34+
LIBS := -L"lib" -lwebui-2-static -lws2_32 -lole32 -luser32 -lstdc++ -luuid -static
35+
GUI_APP := -Wl,-subsystem=windows
36+
CONSOLE_APP := -Wl,-subsystem=console
37+
STRIP_OPT := --strip-all
38+
else
39+
OUT := bin/$(APP_NAME)
40+
ICON_OBJ :=
41+
ifeq ($(shell uname),Darwin)
42+
# macOS
43+
PLATFORM := macos
44+
CC = clang
45+
LIBS := -L"lib" -lwebui-2-static -lpthread -lm -ldl -framework Cocoa -framework WebKit
46+
else
47+
# Linux
48+
PLATFORM := linux
49+
LIBS := -L"lib" -lwebui-2-static -lpthread -lm -ldl
50+
STRIP_OPT := --strip-all
51+
ifeq ($(CC),clang)
52+
LLVM_OPT := llvm-
53+
endif
54+
endif
55+
endif
56+
57+
# == 2. TARGETS ===============================================================
58+
59+
all: release
60+
61+
release: --prepare
62+
@echo "Build $(APP_NAME) (release)..."
63+
@$(CC) -Os -Wall $(GUI_APP) src/main.c $(ICON_OBJ) -I"lib" $(LIBS) -o $(OUT)
64+
@$(LLVM_OPT)strip $(STRIP_OPT) $(OUT)
65+
ifeq ($(PLATFORM),windows)
66+
@- del icon.o >nul 2>&1
67+
endif
68+
ifeq ($(PLATFORM),linux)
69+
@echo "Generate bin/$(APP_NAME).desktop..."
70+
@cp ui/icon.png bin/icon.png
71+
@printf '%s\n' \
72+
'[Desktop Entry]' \
73+
'Type=Application' \
74+
'Name=$(APP_NAME)' \
75+
'Exec="$(CURDIR)/bin/$(APP_NAME)"' \
76+
'Icon=$(CURDIR)/bin/icon.png' \
77+
'Terminal=false' \
78+
'Categories=Utility;' \
79+
'StartupWMClass=$(APP_NAME)' \
80+
> bin/$(APP_NAME).desktop
81+
@chmod +x bin/$(APP_NAME).desktop
82+
endif
83+
ifeq ($(PLATFORM),macos)
84+
@echo "Bundle bin/$(APP_NAME).app..."
85+
@rm -rf "bin/$(APP_NAME).app"
86+
@mkdir -p "bin/$(APP_NAME).app/Contents/MacOS" "bin/$(APP_NAME).app/Contents/Resources"
87+
@mv $(OUT) "bin/$(APP_NAME).app/Contents/MacOS/$(APP_NAME)"
88+
@sips -s format icns ui/icon.png --out "bin/$(APP_NAME).app/Contents/Resources/icon.icns" >/dev/null
89+
@printf '%s\n' \
90+
'<?xml version="1.0" encoding="UTF-8"?>' \
91+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' \
92+
'<plist version="1.0">' \
93+
'<dict>' \
94+
' <key>CFBundleExecutable</key>' \
95+
' <string>$(APP_NAME)</string>' \
96+
' <key>CFBundleIdentifier</key>' \
97+
' <string>com.webui.$(APP_NAME)</string>' \
98+
' <key>CFBundleName</key>' \
99+
' <string>$(APP_NAME)</string>' \
100+
' <key>CFBundleIconFile</key>' \
101+
' <string>icon</string>' \
102+
' <key>CFBundlePackageType</key>' \
103+
' <string>APPL</string>' \
104+
' <key>CFBundleInfoDictionaryVersion</key>' \
105+
' <string>6.0</string>' \
106+
' <key>NSHighResolutionCapable</key>' \
107+
' <true/>' \
108+
'</dict>' \
109+
'</plist>' \
110+
> "bin/$(APP_NAME).app/Contents/Info.plist"
111+
@echo "Done: bin/$(APP_NAME).app"
112+
else
113+
@echo "Done: $(OUT)"
114+
endif
115+
116+
debug: --prepare
117+
@echo "Build $(APP_NAME) (debug)..."
118+
@$(CC) -g -Wall $(CONSOLE_APP) src/main.c $(ICON_OBJ) -I"lib" $(LIBS) -o $(OUT)
119+
ifeq ($(PLATFORM),windows)
120+
@- del icon.o >nul 2>&1
121+
endif
122+
@echo "Done: $(OUT)"
123+
124+
clean:
125+
ifeq ($(PLATFORM),windows)
126+
@- rmdir /S /Q "bin" >nul 2>&1
127+
@- del src\vfs.h >nul 2>&1
128+
@- del icon.o >nul 2>&1
129+
@- del tools\vfs.exe >nul 2>&1
130+
else
131+
@- rm -rf bin
132+
@- rm -f src/vfs.h
133+
@- rm -f tools/vfs
134+
endif
135+
@echo "Clean done (lib/ snapshot kept)."
136+
137+
# == 3. INTERNAL ==============================================================
138+
139+
# Prepare: snapshot the WebUI library into lib/, pack ui/ into src/vfs.h,
140+
# and compile the Windows icon resource.
141+
--prepare:
142+
ifeq ($(PLATFORM),windows)
143+
@if not exist "bin" mkdir "bin"
144+
@if not exist "lib" mkdir "lib"
145+
@if exist "..\..\dist\$(LIB_FILE)" copy /Y "..\..\dist\$(LIB_FILE)" "lib" >nul
146+
@if exist "..\..\include\webui.h" copy /Y "..\..\include\webui.h" "lib" >nul
147+
@if not exist "lib\$(LIB_FILE)" (echo [Error] WebUI static library not found. Run 'mingw32-make' in the WebUI repository root first. & exit /b 1)
148+
@echo Pack ui/ into src/vfs.h...
149+
@$(CC) tools/vfs.c -o tools/vfs.exe
150+
@tools\vfs.exe ui src\vfs.h
151+
@windres icon.rc -o icon.o
152+
else
153+
@mkdir -p bin lib
154+
@if [ -f "$(WEBUI_DIST)/$(LIB_FILE)" ]; then cp -f "$(WEBUI_DIST)/$(LIB_FILE)" lib/; fi
155+
@if [ -f "$(WEBUI_INCLUDE)/webui.h" ]; then cp -f "$(WEBUI_INCLUDE)/webui.h" lib/; fi
156+
@if [ ! -f "lib/$(LIB_FILE)" ]; then echo "[Error] WebUI static library not found. Run 'make' in the WebUI repository root first."; exit 1; fi
157+
@echo "Pack ui/ into src/vfs.h..."
158+
@$(CC) tools/vfs.c -o tools/vfs
159+
@./tools/vfs ui src/vfs.h
160+
endif

‎examples/starter_kit_c/Makefile‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# WebUI C Starter Kit
2+
# Windows - Microsoft Visual C (nmake, from a Developer Command Prompt)
3+
#
4+
# nmake Build a single portable executable into bin\
5+
# nmake debug Same, with a console window and debug symbols
6+
# nmake clean Remove generated build files (keeps the lib\ snapshot)
7+
#
8+
# The first build must run inside the WebUI repository (examples\starter_kit_c)
9+
# so the WebUI static library can be copied into lib\. After that, this folder
10+
# is fully self-contained and can be moved anywhere.
11+
12+
SHELL = CMD
13+
14+
# Your application name (the executable name in bin\)
15+
APP_NAME = my_app
16+
17+
# WebUI static library file name
18+
LIB_FILE = webui-2-static.lib
19+
20+
# WebUI repository paths (only used when building inside the repository)
21+
WEBUI_DIST = ..\..\dist
22+
WEBUI_INCLUDE = ..\..\include
23+
24+
all: release
25+
26+
release: prepare
27+
@echo Build $(APP_NAME) (Release)...
28+
@cl /nologo /O2 src\main.c icon.res /I"lib" /link /LIBPATH:"lib" /SUBSYSTEM:WINDOWS $(LIB_FILE) user32.lib Advapi32.lib Shell32.lib Ole32.lib /OUT:bin\$(APP_NAME).exe
29+
@- del main.obj >nul 2>&1
30+
@- del icon.res >nul 2>&1
31+
@echo Done: bin\$(APP_NAME).exe
32+
33+
debug: prepare
34+
@echo Build $(APP_NAME) (Debug)...
35+
@cl /nologo /Zi src\main.c icon.res /I"lib" /link /LIBPATH:"lib" /SUBSYSTEM:CONSOLE $(LIB_FILE) user32.lib Advapi32.lib Shell32.lib Ole32.lib /OUT:bin\$(APP_NAME).exe
36+
@- del main.obj >nul 2>&1
37+
@- del icon.res >nul 2>&1
38+
@echo Done: bin\$(APP_NAME).exe
39+
40+
# Prepare: snapshot the WebUI library into lib\, pack ui\ into src\vfs.h,
41+
# and compile the icon resource.
42+
prepare:
43+
@if not exist "bin" mkdir "bin"
44+
@if not exist "lib" mkdir "lib"
45+
@if exist "$(WEBUI_DIST)\$(LIB_FILE)" copy /Y "$(WEBUI_DIST)\$(LIB_FILE)" "lib" >nul
46+
@if exist "$(WEBUI_INCLUDE)\webui.h" copy /Y "$(WEBUI_INCLUDE)\webui.h" "lib" >nul
47+
@if not exist "lib\$(LIB_FILE)" (echo [Error] WebUI static library not found. Run 'nmake' in the WebUI repository root first. & exit /b 1)
48+
@echo Pack ui\ into src\vfs.h...
49+
@cl /nologo tools\vfs.c /Fetools\vfs.exe /Fotools\vfs.obj
50+
@tools\vfs.exe ui src\vfs.h
51+
@- del tools\vfs.obj >nul 2>&1
52+
@echo Build icon resource...
53+
@rc /nologo /fo icon.res icon.rc
54+
55+
clean:
56+
- rmdir /S /Q bin >nul 2>&1
57+
- del src\vfs.h >nul 2>&1
58+
- del icon.res >nul 2>&1
59+
- del tools\vfs.exe >nul 2>&1
60+
- del tools\vfs.obj >nul 2>&1
61+
- del *.obj >nul 2>&1
62+
- del *.pdb >nul 2>&1
63+
- del *.ilk >nul 2>&1
64+
@echo Clean done (lib\ snapshot kept).

‎examples/starter_kit_c/README.md‎

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# WebUI C Starter Kit
2+
3+
A ready-to-use project template for building a professional desktop app in C
4+
with a web UI — compiled into a **single portable executable** with the
5+
application icon and every UI file (HTML, CSS, JS, images) embedded inside it.
6+
7+
No runtime files, no installer, no dependencies beyond a C compiler.
8+
9+
## Quick start
10+
11+
1. Clone WebUI: `git clone https://github.com/webui-dev/webui.git`
12+
2. `cd webui`
13+
3. Build the WebUI library:
14+
- Linux / macOS: `make`
15+
- Windows MinGW: `mingw32-make`
16+
- Windows MSVC: `nmake` (from a *Developer Command Prompt*)
17+
4. `cd examples/starter_kit_c`
18+
5. Build the starter kit: `make` / `mingw32-make` / `nmake`
19+
6. Done — your app is `bin/my_app` (`bin\my_app.exe` on Windows), and the
20+
`starter_kit_c` folder is now **self-contained**: copy it anywhere (outside
21+
the WebUI repository) and continue developing your own project from it.
22+
23+
The first build must happen inside the WebUI repository: it copies the WebUI
24+
static library and header into `lib/`. Every later build works from the local
25+
`lib/` snapshot, wherever the folder lives.
26+
27+
## Folder structure
28+
29+
```
30+
starter_kit_c/
31+
├── Makefile # Windows MSVC (nmake)
32+
├── GNUmakefile # Linux, macOS, Windows MinGW (make / mingw32-make)
33+
├── icon.rc # Windows resource: embeds ui/favicon.ico as the EXE icon
34+
├── src/
35+
│ ├── main.c # Your C backend (HTTP handler, bindings, window)
36+
│ └── vfs.h # GENERATED — the ui/ folder packed as C arrays
37+
├── tools/
38+
│ └── vfs.c # Build-time packer: ui/ -> src/vfs.h (auto-compiled)
39+
├── ui/ # Your frontend — every file here gets embedded
40+
│ ├── index.html
41+
│ ├── style.css
42+
│ ├── app.js
43+
│ ├── favicon.ico # App icon: Windows executable icon (via icon.rc)
44+
│ └── icon.png # App icon: HTML favicon + Linux window/launcher icon
45+
├── lib/ # GENERATED — snapshot of webui.h + WebUI static library
46+
└── bin/ # GENERATED — my_app(.exe); Linux: + .desktop + icon.png; macOS: my_app.app
47+
```
48+
49+
## Customize
50+
51+
- **App name** — edit `APP_NAME` at the top of `Makefile` and `GNUmakefile`.
52+
- **Frontend** — edit or add any files under `ui/` (subfolders included).
53+
They are re-packed into the executable automatically on every build.
54+
- **Backend** — add C functions in `src/main.c` and expose them with
55+
`webui_bind()`. Every bound function becomes a global `async` JavaScript
56+
function: `const reply = await greet('World');`
57+
- **Icon** — two files in `ui/` (keep the names, swap the artwork):
58+
`ui/favicon.ico` — the Windows executable icon (resource ID 32512 /
59+
`IDI_APPLICATION`, used by Explorer, the taskbar, and the WebView window;
60+
use a real multi-size `.ico`); `ui/icon.png` — the HTML favicon, on
61+
Linux the window/taskbar icon and the `.desktop` launcher icon (the
62+
build copies it into `bin/`), and on macOS the app icon (converted to
63+
`icon.icns` inside the `.app` bundle).
64+
- **HTTP** — every UI request goes through `http_handler()` in `src/main.c`
65+
(embedded files via `vfs()`, everything else 404) — add custom routes,
66+
headers, or dynamic responses there.
67+
- **Window** — `src/main.c` opens a frameless, transparent WebView window, and
68+
the UI draws its own title bar (`#titlebar` in `ui/index.html`, with drag
69+
regions and minimize/maximize/close buttons). For a normal OS window, remove
70+
the `webui_set_frameless()` / `webui_set_transparent()` lines; for browser
71+
mode, call `webui_show()` instead of `webui_show_wv()`.
72+
73+
## How it works
74+
75+
- `tools/vfs.c` is compiled with your same C compiler and run at build
76+
time. It packs `ui/` into `src/vfs.h` as byte arrays plus a `vfs()` lookup
77+
that serves an embedded file as a full HTTP response (MIME type via
78+
`webui_get_mime_type()`), or returns `NULL` for unknown paths.
79+
- Every HTTP request reaches `http_handler()` in `main.c`: embedded files
80+
are served through `vfs()`, anything else gets a 404 — tweak requests or
81+
add your own routes there (`webui.js` is always served by WebUI itself).
82+
Note: using a custom file handler disables WebUI's cookie-based client
83+
authentication (security becomes your responsibility).
84+
- `icon.rc` embeds the icon at resource ID 32512 (`IDI_APPLICATION`), which
85+
WebUI's WebView window class picks up automatically on Windows.
86+
- `main.c` calls `webui_set_icon_file(win, "icon.png")` before showing the
87+
window; WebUI resolves the file (working directory first, then next to
88+
the executable) and, on Linux, applies it to the WebView window, which
89+
gives the taskbar icon on X11 sessions.
90+
- The app links against `lib/webui-2-static.lib` (MSVC) or
91+
`lib/libwebui-2-static.a` (MinGW / Linux / macOS) — fully static, one file.
92+
- On Linux, the release build copies `ui/icon.png` into `bin/` and generates
93+
`bin/<app>.desktop` — a launcher pointing at the executable and the icon
94+
with absolute paths. Nothing is installed or written outside this folder;
95+
rebuild after moving the folder to refresh the paths.
96+
- On macOS, the release build wraps the executable into `bin/<app>.app` —
97+
`Info.plist` plus `icon.icns` (converted from `ui/icon.png` with the
98+
built-in `sips` tool) — so Finder and the Dock show the app icon.
99+
100+
## Debugging
101+
102+
`make debug` / `nmake debug` builds with debug symbols and a console window,
103+
so `printf()` from your C code is visible. For WebUI's own internal logs,
104+
build the library in debug mode at the repository root (`make debug`), then
105+
copy the static library from `dist/debug/` over the one in `lib/`.
106+
107+
## Updating WebUI
108+
109+
Rebuild the library in the WebUI repository, then run one build of this kit
110+
while it is inside the repository — `lib/` is refreshed automatically. (If you
111+
moved the folder out, copy the new static library and `webui.h` into `lib/`
112+
manually, or rebuild from a fresh clone.)
113+
114+
## Platform notes
115+
116+
- **Windows** — the executable icon works everywhere (Explorer, taskbar, and
117+
the frameless WebView window). WebUI links the WebView2 loader statically and
118+
the WebView2 Runtime ships with Windows 10/11, so the single executable is
119+
all you need. If WebView2 is unavailable, the app falls back to a browser
120+
app-mode window (the embedded favicon shows as the tab icon).
121+
- **Linux** — the frameless WebView window needs GTK + WebKitGTK installed
122+
(the app falls back to a browser app-mode window otherwise). The
123+
window/taskbar icon comes from `webui_set_icon_file()` using `bin/icon.png`
124+
(copied next to the executable by the build) and works on X11 sessions
125+
with nothing installed; Wayland sessions only show icons for apps with an
126+
installed `.desktop` entry. ELF executables cannot embed icons, so the
127+
build also generates `bin/my_app.desktop`, a launcher pointing at the
128+
executable and `bin/icon.png` (absolute paths, regenerated on every
129+
build — rebuild after moving the folder). Everything stays inside this
130+
folder; the build never writes to the system. In GNOME Files, right-click
131+
the `.desktop` and choose "Allow Launching" once — it then shows your app
132+
icon and launches on double-click. Optional, your choice: copy
133+
`bin/my_app.desktop` into `~/.local/share/applications/` to add the app
134+
to the system menu (and the dock icon on Wayland) — the build itself
135+
never does this.
136+
- **macOS** — the release build produces `bin/my_app.app` with the
137+
executable and icon embedded inside the bundle (`icon.icns` is generated
138+
from `ui/icon.png` using `sips`, which ships with macOS). Launch it from
139+
Finder or with `open bin/my_app.app`; the Dock and Finder show the app
140+
icon. The bundle is unsigned — fine for local use; distributing to other
141+
Macs needs `codesign`/notarization. `make debug` still produces a plain
142+
`bin/my_app` binary for terminal debugging.

‎examples/starter_kit_c/icon.rc‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// WebUI C Starter Kit - Windows application icon
2+
//
3+
// 32512 is IDI_APPLICATION: picked up by Explorer, the taskbar, and the
4+
// WebUI WebView window class. Replace ui/favicon.ico to change your icon
5+
// (the same file is also embedded as the browser tab favicon).
6+
32512 ICON "ui/favicon.ico"

0 commit comments

Comments
 (0)