Which GTK does a bare #include <gtk/gtk.h> mean?
Filed 2026-08-29 by frankC (Track C) out of
[[feature-c-gtk3-header-final-wiring]], which is parked in unfinished/ on this
answer. The capability that ticket was about is proven and gated — stock
GTK3 headers import, link to libgtk-3.so.0 and run a real window
(test/test_c_gtk3_stock.pas). What is not settled is the default.
The fork
Both header sets exist on a normal box and both are reached by the same spelling:
/usr/include/gtk-2.0/gtk/gtk.h
/usr/include/gtk-3.0/gtk/gtk.h
BuildCSysIncludeDirs (compiler/cpreproc.inc) makes /usr/include/gtk-2.0/
a default system include root. /usr/include/gtk-3.0/ is not one — which is
the entire reason GTK3 needs an explicit -I/usr/include/gtk-3.0/ today, and
the reason the ticket's recorded 2026-06-29 probe failure looked like a deep
import bug when it was a wrong include root (it passed
-I/usr/include/gtk-3.0/gtk, so #include <gtk/gtkactionable.h> could not
resolve).
Everything else GTK3 needs — glib, pango, cairo, gdk-pixbuf, atk, harfbuzz, freetype2, pixman, fontconfig — is already in the default roots and is shared with GTK2. The gtk root is the only divergence.
The list is flat and global, so whichever gtk root comes first wins for every C consumer in the process. There is no per-unit include path today. That is what makes this a decision rather than a patch.
Options
- Leave the defaults alone; GTK3 requires an explicit
-I. Status quo, and what the new test does. Costs nothing, breaks nothing, and keeps the GTK2 macro-soup regression guard authoritative. Butuses gtk3_calone does not work, so the PCL binding can never be a plain#include <gtk/gtk.h>without every consumer passing a flag. - Put
/usr/include/gtk-3.0/in the defaults, before gtk-2.0. GTK3 becomes what a bare<gtk/gtk.h>means. This silently re-points the existing GTK2 tests at GTK3 headers while they still linklibgtk-x11-2.0.so.0— a header set and a shared library from different major versions. Would need the GTK2 tests migrated or pinned with their own explicit-Iin the same change. - Put it in the defaults, after gtk-2.0. Achieves nothing: gtk-2.0 still
wins for
<gtk/gtk.h>, so GTK3 consumers still need the-I. Listed only to record that it was considered and is a no-op. - Give the include root a scope — the stem already knows.
CHeaderStemalready maps the unitgtk3_cto the stemgtk-3andgtktogtk-x11-2.0, and that stem is what picks the soname. Let it pick the include root too, so a GTK3 binding searches gtk-3.0 first and a GTK2 binding searches gtk-2.0 first, with no global default disturbed. Principled, and it is the "normalise, don't special-case" answer — one fact (which GTK am I binding) driving both the library and the headers instead of one driving the library and a flag driving the headers.
Recommendation
Option 4, with option 1 as the standing behaviour until it is built. It is the only one where both GTK versions keep working without a flag and without either silently shadowing the other, and it needs no new concept — the stem→root association is the same fact as the existing stem→soname one, which is already computed at exactly the right moment.
Its cost is honest and worth stating: the stem is computed in CHeaderStem
(compiler/pasparser_proc.inc, Track P's file) and the include roots are
built in BuildCSysIncludeDirs (compiler/cpreproc.inc, Track C's), so the
plumbing crosses a lane boundary and wants an owner assigned rather than a lane
guessed. That is the second thing this ticket is asking.
Related
- [[feature-c-gtk3-header-final-wiring]] — parked on this; its capability half is done.
- [[feature-b-migrate-pcl-off-the-curated-gtk3-header]] — wants option 1 or 4 settled first.
RULED 2026-08-31 — GTK 3 is the default
Owner: "i think gtk3 is a sane default in 2026." Everything in the tree already
targets it — lib/pcl/gtk3.pas, gtk3widgets.pas, gtk3gl.pas all bind
libgtk-3.so.0. GTK 2 is the anomaly, not the baseline.
The change is four literals, not a system
compiler/cpreproc.inc:2219and:2220— the two default C include roots, hardcoded to/usr/include/gtk-2.0/and the arch-specificgtk-2.0/include/.compiler/pasparser_proc.inc:3105— header paths built from/usr/include/gtk-2.0/gtk/.compiler/pasparser_proc.inc:2834-2836— the alias map:gtk3_c-> stemgtk-3->libgtk-3.so.0;gtk-> stemgtk-x11-2.0->libgtk-x11-2.0.so.0.
THE NAMING IS THE REAL DEFECT — owner, and it is sharper than "rename it"
"you said 'uses gtk'.. but, logically, that ought to be 'uses gtk3'."
gtk and gtk3 are not parallel names — they live in different namespaces,
which is why this reads wrong and keeps reading wrong:
uses gtk3_cis a C header import, resolved through the alias map.uses gtkis also a C header import, resolved through the same map — to GTK 2.lib/pcl/gtk3.pasis a Pascal unit — a real file holdingSignalConnect.uses gtk3finds that file, not an alias.
So the two spellings a reader would take as "version 2 vs version 3 of the same thing" are actually "a C library alias" and "a Pascal source file". Renaming without fixing that just moves the confusion.
Blast radius, and it is small and visible: three files use uses gtk and
flip from GTK 2 to GTK 3 — test/test_c_gtk.pas, test_c_gtk_call.pas,
test_c_gtk_types.pas. Nothing else in the tree does.
NilPy's tk is NOT affected — checked, not assumed
lib/pcl/tk.pas is a thin Tcl/Tk 8.6 embed: it links the system Tcl/Tk
sonames directly via external, "needs no -dev headers and no change to the
compiler's C-import registry", and the whole GUI is command strings through
TkEval. It never touches GTK at any version. The tkinter mimicry question is
orthogonal to this ticket.
What is installed here, for whoever implements it
plexus has headers for gtk-2.0 and gtk-3.0, and runtime libs for 2, 3 and
4. There is no /usr/include/gtk-4.0 — GTK 4's library is present, its
headers are not, so GTK 4 is unreachable until libgtk-4-dev is installed.
Version selection is a SEPARATE, SCOPED feature
The owner asked for 2/3/4 selectable, defaulting to 3. The resolver half is
cheap — one variable driving the four literals above. The widgetset half is
not, and must not be promised with it: gtk3widgets.pas and friends bind GTK 3
specifically, and GTK 4 reshaped the container, event and drawing models. So
selecting a version buys the right headers and the right soname; it does not
make the PCL widget layer work on 2 or 4. Filed as
[[feature-a-gtk-version-selection-at-the-header-and-soname-layer]].
Ruled 2026-08-31 by the owner; mechanism and tk backend verified by frank-user.
LANDED 2026-09-05 (frankC, Track C) — and two corrections to the ruling above
Implemented five days after the ruling. cpreproc.inc still said
/usr/include/gtk-2.0/ this morning. See
[[feature-c-gtk3-header-final-wiring]] for the measured landing; the parked
ticket this decision blocked was never unparked when the answer arrived, so
ready and the ticket body disagreed for five days.
The change was three literals, not four — the arch-specific root was
deleted rather than moved, because GTK 3 keeps gdkconfig.h inside
/usr/include/gtk-3.0/gdk/ while GTK 2 kept it in
/usr/lib/<triplet>/gtk-2.0/include/. That retires the hardcoded
x86_64-linux-gnu path this ticket flagged as a separate worry.
Correction 1 — the blast radius is FOUR files, not three
test/test_c_gtk_window.pas is missing from the ruling's list. The sibling
ticket's own body names all four; the ruling's list, and both frontmatter
summaries, say three. The omitted one is the only test that runs a full
gtk_main loop, so it is the worst omission from a list whose whole purpose
is sizing risk.
Correction 2 — "those four never touch GTK at runtime" is false in both halves
Three of the four call into GTK (gtk_init, gtk_window_new, and the window
test's main loop) and the Makefile runs them under xvfb-run. And
test/my_gtk.h is an orphan: its only two references in the tree are a
writeln string in test_c_gtk.pas and the Makefile asserting that string.
Nothing includes it. Established two ways that fail differently — a positive
control (the file compiles ok from a directory not containing it) and a
capability argument (my_gtk.h declares only gtk_button_get_width/height,
yet test_c_gtk_types compiles gtk_window_new(GTK_WINDOW_TOPLEVEL) and all
four linked libgtk-x11-2.0.so.0).
Neither correction overturns the ruling. "gtk3 is a sane default in 2026" is a statement of intent and needs no evidence. The blast radius does — and the section a taker reads to size the job is the section that was wrong.
What the ruling got right, and it is the load-bearing part
The hazard it names — GTK 3 headers against a GTK 2 shared library — is real and is avoided only because the include root and the alias stem move together. It is precisely the failure mode of doing half this change.
One latent bug this surfaced
test_c_gtk_types.pas called gtk_window_new with no gtk_init. GTK 2
tolerated it; GTK 3 aborts without a display connection. The test was always
wrong and GTK 2 was lenient — fixed by adding the gtk_init GTK has always
required, not by working around GTK 3.
For anyone citing this ticket's numbers
The line numbers in the ruling (cpreproc.inc:2219-2220,
pasparser_proc.inc:3105, :2834-2836) had drifted to :2507-2508, :3428
and :3264-3265 before this landed, and have moved again since. Cite the
address, not the value: grep for the root literal. seven's manifest hit the
same wall twice in twelve hours and drew the same conclusion — a manifest pinned
to a literal another lane is moving is a time bomb. The wrong file count in this
ruling is that same disease: a value copied where an address belonged.