<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>Steef Hegeman</title>
	<link href="https://steefhegeman.com"/>
	<link rel="self" href="https://steefhegeman.com/blog/atom.xml"/>
	<icon>https://steefhegeman.com/favicon.png</icon>
	<author>
		<name>Steef Hegeman</name>
	</author>
	<id>https://steefhegeman.com/blog/atom.xml</id>
	<updated>2025-12-27T00:00:00Z</updated>
<entry>
	<title>Static site by HTML rewriting</title>

	<link rel="alternate" href="https://steefhegeman.com/blog/site-by-html-rewriting/"/>
	<id>https://steefhegeman.com/blog/site-by-html-rewriting/</id>
	<updated>2025-12-27T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/site-by-html-rewriting/">
&lt;p&gt;
Setting up static site generators is fun;
making changes later is not.
Having tried several, and always ending up hacking around other people’s code,
I decided to write my own based on HTML rewriting.

&lt;p&gt;
Elevator pitch:
&lt;ol&gt;
	&lt;li&gt;Write blog post content in HTML (no Markdown or whatever)
	&lt;li&gt;Do all preprocessing with a simple, single-pass HTML rewriter
&lt;/ol&gt;

&lt;p&gt;
Concretely:
&lt;ol&gt;
	&lt;li&gt;Posts are stored in ./blog/slug/index.html, at their desired path
	&lt;li&gt;A post starts with a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag
		with metadata like title, date
	&lt;li&gt;The rest is the post body, in HTML
&lt;/ol&gt;

&lt;p&gt;
For example, the source of this post (full source below) reads:
&lt;pre&gt;&lt;code class=hljs&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;meta&lt;/span&gt; &lt;span class="hljs-attr"&gt;type&lt;/span&gt; = &lt;span class="hljs-string"&gt;post&lt;/span&gt;
      &lt;span class="hljs-attr"&gt;title&lt;/span&gt; = &lt;span class="hljs-string"&gt;&amp;quot;Static site by HTML rewriting&amp;quot;&lt;/span&gt;
      &lt;span class="hljs-attr"&gt;date&lt;/span&gt; = &lt;span class="hljs-string"&gt;2025-12-27&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;Setting up static site generators...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
&lt;abbr title="Static Site Generator"&gt;SSG&lt;/abbr&gt;s always end up processing HTML in one way or another,
I just cut out the middleman.
Writing in HTML directly does not exclude fancy features:
for example, I have a rule that replaces
&lt;code&gt;&amp;lt;img type=code src=in.c&amp;gt;&lt;/code&gt;
with syntax-highlighted code snippets.
There is also good support for an HTML-oriented approach:
almost every programming language has a library for processing HTML,
and many support CSS selector syntax
(example: &lt;code&gt;img[type=code]&lt;/code&gt;).

&lt;p&gt;
I decided to implement my SSG in JavaScript,
targeting &lt;a href=https://bun.com&gt;Bun&lt;/a&gt;,
which has a builtin
&lt;a href=https://bun.com/docs/runtime/html-rewriter&gt;HTML rewriting library&lt;/a&gt;
(based on &lt;a href=https://github.com/cloudflare/lol-html&gt;lol-html&lt;/a&gt;),
as well as builtin APIs
to quickly implement a local preview server.
The whole thing is a few hundred lines,
including
code for the blog index,
Atom feed,
and a local preview server
that compiles pages on-the-fly
and triggers browser reloads when files change.
I have a better drafting experience now than I had with off-the-shelf SSGs.

&lt;div role="note"&gt;&lt;small&gt;
Recently,
the company behind Bun was acquired by a large VC-backed AI company&amp;hellip;
So I’m not sure if I would recommend it still.
&lt;/small&gt;&lt;/div&gt;

&lt;p&gt;
I still use some self-contained dependencies
like &lt;a href=https://highlightjs.org/&gt;highlight.js&lt;/a&gt;,
another benefit of staying in the JavaScript ecosystem.
But it’s nowhere near the 5000-line package-lock.json I had with Metalsmith.

&lt;p&gt;
In some sense, the time is ripe for more minimal static site generators.
Where ten years ago I needed a CSS preprocessor for nesting and calculations,
nowadays this is part of standard CSS.
Writing HTML by hand is much nicer than in the XHTML days:
attribute values almost never have to be quoted,
and most-used prose elements like &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;
&lt;a href=https://html.spec.whatwg.org/multipage/syntax.html#optional-tags&gt;often do not need end tags&lt;/a&gt;.
If custom elements didn’t require JavaScript,
I would have been tempted to do away with the static site generator entirely!

&lt;p&gt;
My Metalsmith-based blog lasted nine years.
Hopefully, this new script holds out for another ten.
And my on-and-off blogging!

&lt;details&gt;
	&lt;summary&gt;Bonus: source of this post&lt;/summary&gt;
	&lt;pre&gt;&lt;code class=hljs&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;meta&lt;/span&gt; &lt;span class="hljs-attr"&gt;type&lt;/span&gt; = &lt;span class="hljs-string"&gt;post&lt;/span&gt;
      &lt;span class="hljs-attr"&gt;title&lt;/span&gt; = &lt;span class="hljs-string"&gt;&amp;quot;Static site by HTML rewriting&amp;quot;&lt;/span&gt;
      &lt;span class="hljs-attr"&gt;date&lt;/span&gt; = &lt;span class="hljs-string"&gt;2025-12-27&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
Setting up static site generators is fun;
making changes later is not.
Having tried several, and always ending up hacking around other people’s code,
I decided to write my own based on HTML rewriting.

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
Elevator pitch:
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;ol&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt;Write blog post content in HTML (no Markdown or whatever)
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt;Do all preprocessing with a simple, single-pass HTML rewriter
&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;ol&lt;/span&gt;&amp;gt;&lt;/span&gt;

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
Concretely:
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;ol&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt;Posts are stored in ./blog/slug/index.html, at their desired path
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt;A post starts with a &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class="hljs-symbol"&gt;&amp;amp;lt;&lt;/span&gt;meta&lt;span class="hljs-symbol"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt; tag
		with metadata like title, date
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;li&lt;/span&gt;&amp;gt;&lt;/span&gt;The rest is the post body, in HTML
&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;ol&lt;/span&gt;&amp;gt;&lt;/span&gt;

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
For example, the source of this post (full source below) reads:
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;img&lt;/span&gt; &lt;span class="hljs-attr"&gt;type&lt;/span&gt;=&lt;span class="hljs-string"&gt;code&lt;/span&gt; &lt;span class="hljs-attr"&gt;src&lt;/span&gt;=&lt;span class="hljs-string"&gt;example&lt;/span&gt; &lt;span class="hljs-attr"&gt;lang&lt;/span&gt;=&lt;span class="hljs-string"&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt;

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;abbr&lt;/span&gt; &lt;span class="hljs-attr"&gt;title&lt;/span&gt;=&lt;span class="hljs-string"&gt;&amp;quot;Static Site Generator&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;SSG&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;abbr&lt;/span&gt;&amp;gt;&lt;/span&gt;s always end up processing HTML in one way or another,
I just cut out the middleman.
Writing in HTML directly does not exclude fancy features:
for example, I have a rule that replaces
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class="hljs-symbol"&gt;&amp;amp;lt;&lt;/span&gt;img type=code src=in.c&lt;span class="hljs-symbol"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;
with syntax-highlighted code snippets.
There is also good support for an HTML-oriented approach:
almost every programming language has a library for processing HTML,
and many support CSS selector syntax
(example: &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;img[type=code]&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;).

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
I decided to implement my SSG in JavaScript,
targeting &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;a&lt;/span&gt; &lt;span class="hljs-attr"&gt;href&lt;/span&gt;=&lt;span class="hljs-string"&gt;https://bun.com&lt;/span&gt;&amp;gt;&lt;/span&gt;Bun&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;,
which has a builtin
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;a&lt;/span&gt; &lt;span class="hljs-attr"&gt;href&lt;/span&gt;=&lt;span class="hljs-string"&gt;https://bun.com/docs/runtime/html-rewriter&lt;/span&gt;&amp;gt;&lt;/span&gt;HTML rewriting library&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;
(based on &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;a&lt;/span&gt; &lt;span class="hljs-attr"&gt;href&lt;/span&gt;=&lt;span class="hljs-string"&gt;https://github.com/cloudflare/lol-html&lt;/span&gt;&amp;gt;&lt;/span&gt;lol-html&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;),
as well as builtin APIs
to quickly implement a local preview server.
The whole thing is a few hundred lines,
including
code for the blog index,
Atom feed,
and a local preview server
that compiles pages on-the-fly
and triggers browser reloads when files change.
I have a better drafting experience now than I had with off-the-shelf SSGs.

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;side-note&lt;/span&gt;&amp;gt;&lt;/span&gt;
Recently,
the company behind Bun was acquired by a large VC-backed AI company&lt;span class="hljs-symbol"&gt;&amp;amp;hellip;&lt;/span&gt;
So I’m not sure if I would recommend it still.
&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;side-note&lt;/span&gt;&amp;gt;&lt;/span&gt;

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
I still use some self-contained dependencies
like &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;a&lt;/span&gt; &lt;span class="hljs-attr"&gt;href&lt;/span&gt;=&lt;span class="hljs-string"&gt;https://highlightjs.org/&lt;/span&gt;&amp;gt;&lt;/span&gt;highlight.js&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;,
another benefit of staying in the JavaScript ecosystem.
But it’s nowhere near the 5000-line package-lock.json I had with Metalsmith.

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
In some sense, the time is ripe for more minimal static site generators.
Where ten years ago I needed a CSS preprocessor for nesting and calculations,
nowadays this is part of standard CSS.
Writing HTML by hand is much nicer than in the XHTML days:
attribute values almost never have to be quoted,
and most-used prose elements like &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class="hljs-symbol"&gt;&amp;amp;lt;&lt;/span&gt;p&lt;span class="hljs-symbol"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt; and &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class="hljs-symbol"&gt;&amp;amp;lt;&lt;/span&gt;li&lt;span class="hljs-symbol"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;code&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;a&lt;/span&gt; &lt;span class="hljs-attr"&gt;href&lt;/span&gt;=&lt;span class="hljs-string"&gt;https://html.spec.whatwg.org/multipage/syntax.html#optional-tags&lt;/span&gt;&amp;gt;&lt;/span&gt;often do not need end tags&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;.
If custom elements didn’t require JavaScript,
I would have been tempted to do away with the static site generator entirely!

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
My Metalsmith-based blog lasted nine years.
Hopefully, this new script holds out for another ten.
And my on-and-off blogging!

&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;details&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;summary&lt;/span&gt;&amp;gt;&lt;/span&gt;Bonus: source of this post&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;summary&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;img&lt;/span&gt; &lt;span class="hljs-attr"&gt;type&lt;/span&gt;=&lt;span class="hljs-string"&gt;code&lt;/span&gt; &lt;span class="hljs-attr"&gt;src&lt;/span&gt;=&lt;span class="hljs-string"&gt;index.html&lt;/span&gt; &lt;span class="hljs-attr"&gt;lang&lt;/span&gt;=&lt;span class="hljs-string"&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;details&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
	</content>
</entry>
<entry>
	<title>CUDA tricks</title>
	<subtitle type="html">Takeaways from a few months of CUDA C++</subtitle>
	<link rel="alternate" href="https://steefhegeman.com/blog/cuda-tricks/"/>
	<id>https://steefhegeman.com/blog/cuda-tricks/</id>
	<updated>2024-03-29T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/cuda-tricks/">

&lt;p&gt;Getting into CUDA,
I found that the general architecture is well explained,
but did not find much in the form of best practices for CUDA C++.
Below are some tips and tricks that I use.&lt;/p&gt;
&lt;p&gt;Feedback most welcome!&lt;/p&gt;
&lt;h2&gt;0. Check for kernel errors&lt;/h2&gt;
&lt;p&gt;You might expect errors during kernel execution to crash your program.
They don&amp;#39;t.
Use some error checking wrapper as described &lt;a href="https://stackoverflow.com/questions/14038589/what-is-the-canonical-way-to-check-for-errors-using-the-cuda-runtime-api"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;1. Use &lt;code&gt;--expt-relaxed-constexpr&lt;/code&gt; (and secretly use some STL)&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#constexpr-functions-and-function-templates"&gt;&lt;code&gt;--expt-relaxed-constexpr&lt;/code&gt;&lt;/a&gt;
flag for nvcc allows the usage of unannotated constexpr functions in device code.
Many functions in the standard library are constexpr,
and can thus be used relatively safely from device code.
You don&amp;#39;t have to write your own &lt;code&gt;std::tuple&lt;/code&gt; or &lt;code&gt;std::bit_width&lt;/code&gt;.
The STL is officially unsupported in device code,
so formally you have no guarantee that this works. But it does.&lt;/p&gt;
&lt;h2&gt;2. Use Thrust (for tests)&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://nvidia.github.io/cccl/thrust/"&gt;Thrust&lt;/a&gt; comes with the CUDA toolkit and can be very useful for writing some quick tests.&lt;/p&gt;
&lt;h2&gt;3. Use &lt;code&gt;--extended-lambda&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#extended-lambdas"&gt;extended lambdas&lt;/a&gt; extension allows you to create device code lambdas.
This can for example be handy in combination with the Thrust map, sort, and reduce utilities.&lt;/p&gt;
&lt;h2&gt;4. &lt;code&gt;__global__&lt;/code&gt; member functions&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#global"&gt;Member functions cannot be global&lt;/a&gt;.
This can be annoying if you have a class
and want to write a kernel that operates on (a copy of) it.
You could write a &lt;code&gt;__global__&lt;/code&gt; function outside the class that takes (a copy of) the class as an argument &lt;code&gt;foo&lt;/code&gt;,
and then write &lt;code&gt;foo.member&lt;/code&gt; every time you have to access a member—but this gets old fast.&lt;/p&gt;
&lt;p&gt;An alternative is to do something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-cpp"&gt;
&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;C&lt;/span&gt;;
&lt;span class="hljs-function"&gt;__global__ &lt;span class="hljs-title"&gt;call_f&lt;/span&gt;&lt;span class="hljs-params"&gt;(C)&lt;/span&gt;&lt;/span&gt;;

&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;C&lt;/span&gt; {
    &lt;span class="hljs-function"&gt;__device__ &lt;span class="hljs-title"&gt;f_kernel&lt;/span&gt;&lt;span class="hljs-params"&gt;()&lt;/span&gt; &lt;/span&gt;{ ... }

    &lt;span class="hljs-function"&gt;&lt;span class="hljs-type"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;f&lt;/span&gt;&lt;span class="hljs-params"&gt;()&lt;/span&gt; &lt;/span&gt;{ call_f&amp;lt;&amp;lt;&amp;lt;...&amp;gt;&amp;gt;&amp;gt;(*&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;); }
};

&lt;span class="hljs-function"&gt;__global__ &lt;span class="hljs-title"&gt;call_f&lt;/span&gt;&lt;span class="hljs-params"&gt;(C c)&lt;/span&gt; &lt;/span&gt;{ c.&lt;span class="hljs-built_in"&gt;f_kernel&lt;/span&gt;(); }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;...which is not pretty, especially if you need many of these kernels.
Moreover, if &lt;code&gt;C&lt;/code&gt; were to depend on template arguments,
&lt;code&gt;call_f&lt;/code&gt; would have to depend on them as well—or
you give up on the type system and use &lt;code&gt;template &amp;lt;class T&amp;gt; call_f(T c)&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;My solution: use &lt;code&gt;--expt-relaxed-constexpr&lt;/code&gt; and &lt;code&gt;std::invoke&lt;/code&gt; (constexpr!) to define the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-cpp"&gt;&lt;span class="hljs-keyword"&gt;template&lt;/span&gt; &amp;lt;&lt;span class="hljs-keyword"&gt;auto&lt;/span&gt; F, &lt;span class="hljs-keyword"&gt;class&lt;/span&gt;... Args&amp;gt;
&lt;span class="hljs-function"&gt;__global__ &lt;span class="hljs-type"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;invoke_device&lt;/span&gt;&lt;span class="hljs-params"&gt;(Args... args)&lt;/span&gt; &lt;/span&gt;{
    std::&lt;span class="hljs-built_in"&gt;invoke&lt;/span&gt;(F, args...);
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The code above can then be rewritten as:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-cpp"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;C&lt;/span&gt; {
    &lt;span class="hljs-function"&gt;__device__ &lt;span class="hljs-title"&gt;f_kernel&lt;/span&gt;&lt;span class="hljs-params"&gt;()&lt;/span&gt; &lt;/span&gt;{ ... }

    &lt;span class="hljs-function"&gt;&lt;span class="hljs-type"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;f&lt;/span&gt;&lt;span class="hljs-params"&gt;()&lt;/span&gt; &lt;/span&gt;{ invoke_device&amp;lt;&amp;amp;C::f_kernel&amp;gt;&amp;lt;&amp;lt;&amp;lt;...&amp;gt;&amp;gt;&amp;gt;(*&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;); }
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Much better!&lt;/p&gt;
&lt;p&gt;The same code can be used for classes that depend on template arguments
(no need to restate them in the template argument to &lt;code&gt;invoke_device&lt;/code&gt;),
and the parameter pack allows &lt;code&gt;invoke_device&lt;/code&gt; to be used for &lt;code&gt;__device__&lt;/code&gt; functions with arguments as well.&lt;/p&gt;
&lt;h2&gt;Bonus: class-of-pointers and &lt;code&gt;std::shared_ptr&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;A pattern that I have found to be useful is to have
a relatively lightweight class containing pointers to device (or managed) memory,
which is then passed to kernels by value.&lt;/p&gt;
&lt;p&gt;It can be useful for this class to have a destructor deallocating the pointed-to device memory.
However, as it is passed to kernels by value,
this destructor will be called after each kernel call.
A trick I took from &lt;a href="https://github.com/owensgroup/BGHT"&gt;BGHT&lt;/a&gt;,
is to use &lt;code&gt;std::shared_ptr&lt;/code&gt; with &lt;code&gt;cudaFree&lt;/code&gt; as a custom deleter instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-cpp"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;C&lt;/span&gt; {
    std::shared_ptr&amp;lt;Foo&amp;gt; foo;

    &lt;span class="hljs-built_in"&gt;C&lt;/span&gt;() {
        Foo *_foo;
        &lt;span class="hljs-built_in"&gt;cudaMalloc&lt;/span&gt;(&amp;amp;_foo, ...);
        foo = std::&lt;span class="hljs-built_in"&gt;shared_ptr&lt;/span&gt;(_foo, cudaFree);
    }
};
&lt;/code&gt;&lt;/pre&gt;	</content>
</entry>
<entry>
	<title>Features I look forward to in future CSS</title>
	<subtitle type="html">&lt;code&gt;attr()&lt;/code&gt;, &lt;code&gt;:has()&lt;/code&gt;, and a “timeline” for the future</subtitle>
	<link rel="alternate" href="https://steefhegeman.com/blog/future-css/"/>
	<id>https://steefhegeman.com/blog/future-css/</id>
	<updated>2022-09-25T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/future-css/">

&lt;p&gt;When writing a web page,
I try to avoid JavaScript.
Relying on a general-purpose programming language for layout doesn&amp;#39;t feel right,
not to mention that JavaScript can be blocked or unsupported.
In a perfect world,
HTML is content,
and CSS takes care of the delivery.&lt;/p&gt;
&lt;p&gt;CSS has come a long way,
and websites like
&lt;a href="https://css-tricks.com/"&gt;css-tricks.com&lt;/a&gt;
are full of brilliant examples.
With the coming of variables and &lt;code&gt;calc()&lt;/code&gt;,
and the going of Internet Explorer,
JavaScript and CSS preprocessors are slowly becoming unnecessary
for my simple use cases.
With that in mind,
here are two likely future CSS features that I am looking forward to.&lt;/p&gt;
&lt;div role="note"&gt;&lt;small&gt;
October 2025: now that CSS supports nesting, I no longer use a preprocessor for this site (nor JS).
&lt;/small&gt;&lt;/div&gt;

&lt;h2&gt;The &lt;code&gt;:has()&lt;/code&gt; pseudo-class&lt;/h2&gt;
&lt;p&gt;Truth be told,
the
&lt;a href="https://www.w3.org/TR/2022/WD-selectors-4-20220507/#relational"&gt;&lt;code&gt;:has()&lt;/code&gt;&lt;/a&gt;
pseudo-class
(allowing to select a parent by its children)
is probably more of a utility feature to me.
I would use it in places where I would now use classes,
allowing me to focus more on content when writing HTML.
But Jen Simmons shows
&lt;a href="https://webkit.org/blog/13096/css-has-pseudo-class/"&gt;some really cool use-cases&lt;/a&gt;,
some of which are currently impossible to implement purely in CSS.
WebKit and Blink already support it.
Hopefully, Mozilla will follow suit soon.&lt;/p&gt;
&lt;h2&gt;The &lt;code&gt;attr()&lt;/code&gt; function&lt;/h2&gt;
&lt;p&gt;Yes, the
&lt;a href="https://drafts.csswg.org/css-values-5/#attr-notation"&gt;&lt;code&gt;attr()&lt;/code&gt;&lt;/a&gt;
function,
for referencing the value of an HTML attribute,
is not that new.
In fact, it is widely supported &amp;mdash; for use in the &lt;code&gt;content&lt;/code&gt; property.
But I would like to use it in places like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-html"&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;div&lt;/span&gt; &lt;span class="hljs-attr"&gt;class&lt;/span&gt;=&lt;span class="hljs-string"&gt;timeline&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-from&lt;/span&gt;=&lt;span class="hljs-string"&gt;2000&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-to&lt;/span&gt;=&lt;span class="hljs-string"&gt;3000&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;section&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-year&lt;/span&gt;=&lt;span class="hljs-string"&gt;2000&lt;/span&gt;&amp;gt;&lt;/span&gt;Something happened.&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;section&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;section&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-year&lt;/span&gt;=&lt;span class="hljs-string"&gt;2200&lt;/span&gt;&amp;gt;&lt;/span&gt;Then this.&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;section&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;section&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-year&lt;/span&gt;=&lt;span class="hljs-string"&gt;2300&lt;/span&gt;&amp;gt;&lt;/span&gt;Then that.&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;section&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;section&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-year&lt;/span&gt;=&lt;span class="hljs-string"&gt;2400&lt;/span&gt;&amp;gt;&lt;/span&gt;And such.&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;section&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;section&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-year&lt;/span&gt;=&lt;span class="hljs-string"&gt;2800&lt;/span&gt;&amp;gt;&lt;/span&gt;And then, after a long time.&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;section&lt;/span&gt;&amp;gt;&lt;/span&gt;
	&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;section&lt;/span&gt; &lt;span class="hljs-attr"&gt;data-year&lt;/span&gt;=&lt;span class="hljs-string"&gt;3000&lt;/span&gt;&amp;gt;&lt;/span&gt;Finally.&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;section&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;style&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class="language-css"&gt;
&lt;span class="hljs-selector-class"&gt;.timeline&lt;/span&gt; {
	&lt;span class="hljs-attr"&gt;--from&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;attr&lt;/span&gt;(data-from);
	&lt;span class="hljs-attr"&gt;--to&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;attr&lt;/span&gt;(data-to);
	&lt;span class="hljs-attr"&gt;--span&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;calc&lt;/span&gt;(&lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--to) - &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--from));
}
&lt;span class="hljs-selector-class"&gt;.timeline&lt;/span&gt; &lt;span class="hljs-selector-tag"&gt;section&lt;/span&gt; {
	&lt;span class="hljs-attr"&gt;--year&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;attr&lt;/span&gt;(data-year);
	&lt;span class="hljs-attr"&gt;--reltime&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;calc&lt;/span&gt;(&lt;span class="hljs-built_in"&gt;calc&lt;/span&gt;(&lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--year) - &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--from)) / &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--span));
	&lt;span class="hljs-attribute"&gt;background-color&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;rgb&lt;/span&gt;(&lt;span class="hljs-number"&gt;255&lt;/span&gt; &lt;span class="hljs-number"&gt;140&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; / &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--reltime));
	&lt;span class="hljs-attribute"&gt;padding&lt;/span&gt;: .&lt;span class="hljs-number"&gt;5em&lt;/span&gt;;
}
&lt;span class="hljs-selector-class"&gt;.timeline&lt;/span&gt; &lt;span class="hljs-selector-tag"&gt;section&lt;/span&gt;&lt;span class="hljs-selector-pseudo"&gt;::before&lt;/span&gt; {
	&lt;span class="hljs-attribute"&gt;content&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--year);
	&lt;span class="hljs-attribute"&gt;margin-right&lt;/span&gt;: &lt;span class="hljs-number"&gt;1em&lt;/span&gt;;
}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A timeline, where the further we go towards the end,
the darker the orange background gets,
giving a bit of sunset vibes.&lt;/p&gt;
&lt;p&gt;This does not work in any current browser.
For a good reason: &lt;code&gt;attr(data-from)&lt;/code&gt; will return &lt;code&gt;2000&lt;/code&gt; as a &lt;code&gt;string&lt;/code&gt;,
as will all &lt;code&gt;attr(attribute)&lt;/code&gt; calls,
and therefore the &lt;code&gt;calc()&lt;/code&gt;s will fail.
Luckily, the &lt;a href="https://drafts.csswg.org/css-values-5/#attr-notation"&gt;draft specification&lt;/a&gt;
allows us to specify the desired type (as well as a fallback value).&lt;/p&gt;
&lt;p&gt;The proper code (according to the current draft) becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-css"&gt;&lt;span class="hljs-selector-class"&gt;.timeline&lt;/span&gt; {
	&lt;span class="hljs-attr"&gt;--from&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;attr&lt;/span&gt;(data-from number); &lt;span class="hljs-comment"&gt;/* specify number type */&lt;/span&gt;
	&lt;span class="hljs-attr"&gt;--to&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;attr&lt;/span&gt;(data-to number);
	&lt;span class="hljs-attr"&gt;--span&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;calc&lt;/span&gt;(&lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--to) - &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--from));
}
&lt;span class="hljs-selector-class"&gt;.timeline&lt;/span&gt; &lt;span class="hljs-selector-tag"&gt;section&lt;/span&gt; {
	&lt;span class="hljs-attr"&gt;--year&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;attr&lt;/span&gt;(data-year number);
	&lt;span class="hljs-attr"&gt;--reltime&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;calc&lt;/span&gt;(&lt;span class="hljs-built_in"&gt;calc&lt;/span&gt;(&lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--year) - &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--from)) / &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--span));
	&lt;span class="hljs-attribute"&gt;background-color&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;rgb&lt;/span&gt;(&lt;span class="hljs-number"&gt;255&lt;/span&gt; &lt;span class="hljs-number"&gt;140&lt;/span&gt; &lt;span class="hljs-number"&gt;0&lt;/span&gt; / &lt;span class="hljs-built_in"&gt;var&lt;/span&gt;(--reltime));
	&lt;span class="hljs-attribute"&gt;padding&lt;/span&gt;: .&lt;span class="hljs-number"&gt;5em&lt;/span&gt;;
}
&lt;span class="hljs-selector-class"&gt;.timeline&lt;/span&gt; &lt;span class="hljs-selector-tag"&gt;section&lt;/span&gt;&lt;span class="hljs-selector-pseudo"&gt;::before&lt;/span&gt; {
	&lt;span class="hljs-attribute"&gt;content&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;attr&lt;/span&gt;(data-year); &lt;span class="hljs-comment"&gt;/* re-attr() since we need a string */&lt;/span&gt;
	&lt;span class="hljs-attribute"&gt;margin-right&lt;/span&gt;: &lt;span class="hljs-number"&gt;1em&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sadly, at the time of writing,
no browser supports it yet.
For use cases like this,
we are either bound to a preprocessor
(separate class for each timeline, seperate class for each timeline section)
or we must fall back to JavaScript.&lt;/p&gt;
&lt;p&gt;To show what the above should look like,
we can use a tiny bit of JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-js"&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; timelines = &lt;span class="hljs-variable language_"&gt;document&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;querySelectorAll&lt;/span&gt;(&lt;span class="hljs-string"&gt;&amp;quot;.timeline&amp;quot;&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; sections = &lt;span class="hljs-variable language_"&gt;document&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;querySelectorAll&lt;/span&gt;(&lt;span class="hljs-string"&gt;&amp;quot;.timeline section&amp;quot;&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-title function_"&gt;convert&lt;/span&gt; = attr =&amp;gt; &lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;el&lt;/span&gt; =&amp;gt;&lt;/span&gt;
	el.&lt;span class="hljs-property"&gt;style&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;setProperty&lt;/span&gt;(&lt;span class="hljs-string"&gt;`--&lt;span class="hljs-subst"&gt;${attr}&lt;/span&gt;`&lt;/span&gt;, &lt;span class="hljs-built_in"&gt;parseFloat&lt;/span&gt;(el.&lt;span class="hljs-property"&gt;dataset&lt;/span&gt;[attr]));
timelines.&lt;span class="hljs-title function_"&gt;forEach&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;convert&lt;/span&gt;(&lt;span class="hljs-string"&gt;&amp;quot;to&amp;quot;&lt;/span&gt;));
timelines.&lt;span class="hljs-title function_"&gt;forEach&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;convert&lt;/span&gt;(&lt;span class="hljs-string"&gt;&amp;quot;from&amp;quot;&lt;/span&gt;));
sections.&lt;span class="hljs-title function_"&gt;forEach&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;convert&lt;/span&gt;(&lt;span class="hljs-string"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;));
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you are from the future, and all is well, you should see the effect below.
If not, press 
&lt;button id="timelineshim"&gt;here&lt;/button&gt;
to let JavaScript do the &lt;code&gt;attr()&lt;/code&gt;s.&lt;/p&gt;
&lt;div class=timeline data-from=2000 data-to=3000&gt;
	&lt;section data-year=2000&gt;Something happened.&lt;/section&gt;
	&lt;section data-year=2200&gt;Then this.&lt;/section&gt;
	&lt;section data-year=2300&gt;Then that.&lt;/section&gt;
	&lt;section data-year=2400&gt;And such.&lt;/section&gt;
	&lt;section data-year=2800&gt;And then, after a long time.&lt;/section&gt;
	&lt;section data-year=3000&gt;Finally.&lt;/section&gt;
&lt;/div&gt;
&lt;style&gt;
.timeline {
	--from: attr(data-from number);
	--to: attr(data-to number);
	--span: calc(var(--to) - var(--from));
	border: 2px solid black;
}
.timeline section::before {
	content: attr(data-year);
	margin-right: 1em;
}
.timeline section {
	--year: attr(data-year number);
	--reltime: calc(calc(var(--year) - var(--from)) / var(--span));
	background-color: rgb(255 140 0 / var(--reltime));
	padding: .5em;
	transition: background-color 1s;
}
&lt;/style&gt;
&lt;script&gt;
const timelines = document.querySelectorAll(".timeline");
const sections = document.querySelectorAll(".timeline section");
const convert = attr =&gt; el =&gt;
	el.style.setProperty(`--${attr}`, parseFloat(el.dataset[attr]));
document.getElementById("timelineshim").onclick = () =&gt; {
	timelines.forEach(convert("to"));
	timelines.forEach(convert("from"));
	sections.forEach(convert("year"));
}
&lt;/script&gt;

&lt;p&gt;Neat, right?&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The web is close to the ideal situation where HTML can be used just for content,
and all styling can be handled in CSS.
JavaScript will still be invaluable for interactive content,
as it should be.
But for simple websites,
the added complexity
and performance overhead
will become unnecessary
&amp;mdash; W3C/WHATWG volente.&lt;/p&gt;
	</content>
</entry>
<entry>
	<title>In memoriam: Wim Hegeman</title>

	<link rel="alternate" href="https://steefhegeman.com/wim/"/>
	<id>https://steefhegeman.com/wim/</id>
	<updated>2022-09-17T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/wim/">&lt;!DOCTYPE html&gt;
&lt;html lang=nl&gt;
&lt;meta name=viewport content="width=device-width, initial-scale=1"&gt;
&lt;link rel=icon href="wim.jpg"&gt;
&lt;title&gt;Wim Hegeman — 1964–2022&lt;/title&gt;
&lt;style&gt;
@font-face {
	font-family: 'IBM Plex Serif';
	font-style: normal;
	font-weight: 700;
	src: local("IBM Plex Serif Bold"), local("IBMPlexSerif-Bold"), url("plex/IBMPlexSerif-Bold-Latin1.woff2") format("woff2");
	unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02
}
@font-face {
	font-family: 'IBM Plex Serif';
	font-style: normal;
	font-weight: 400;
	src: local("IBM Plex Serif"), local("IBMPlexSerif-Regular"), url("plex/IBMPlexSerif-Regular-Latin1.woff2") format("woff2");
	unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02
}
:root { --width: 30em; }
body {
	font-family: "IBM Plex Serif", serif;
	font-size: 1.13em;
	margin: auto;
	max-width: var(--width);
	padding: 1em;
	line-height: 1.5;
	text-rendering: optimizeLegibility;
}
header, header * {
	text-align: center;
	margin: 0;
}
header span { white-space: nowrap; }
blockquote, figure {
	display: flex;
	margin: 0;
	print-color-adjust: exact;
	-webkit-print-color-adjust: exact;
}
pre {
	background-color: #c1ab85;
	box-shadow: 0 3px 10px -7px rgb(0 0 0 / 0.2);
	color: #007;
	clip-path: polygon(100% 100%, 0 100%, 0 2%, 1% 0, 92% 0, 94% 2%, 98% 0, 100% 0);
	font-family: inherit;
	font-size: 1.1em;
	margin: 0 auto;
	padding: 1em;
}
figure * {
	margin: 0 auto;
	width: min(calc(var(--width) / 2), 100%);
	border-radius: 50%;
}
p { hyphens: auto; }
:any-link { color: inherit; }
@media (prefers-color-scheme: dark) {
	body {
		background-color: black;
		color: white;
	}
}
&lt;/style&gt;
&lt;header&gt;
	&lt;h1&gt;Wim Hegeman&lt;/h1&gt;
	&lt;span&gt;26 januari 1964&lt;/span&gt; – &lt;span&gt;17 september 2022&lt;/span&gt;
&lt;/header&gt;
&lt;p&gt;
	Begin dit jaar kregen wij de onwerkelijke boodschap dat Wim ongeneeslijk ziek was.
	Door het verdriet te kunnen delen en de steun die wij kregen bleven we overeind,
	en hebben we met elkaar nog veel moois mee mogen maken.

&lt;blockquote&gt;&lt;pre&gt;
Bang ben ik niet
Met mijn dood kan ik wel
leven voor jullie had ik wel
graag nog even gebleven
&lt;/pre&gt;&lt;/blockquote&gt;

&lt;p&gt;
	Nu het moment van ‘nooit meer samen’ is gekomen,
	zouden we graag,
	met iedereen die Wim een warm hart heeft toegedragen en met ons heeft meegeleefd,
	afscheid van hem nemen op &lt;b&gt;vrijdag 30 september&lt;/b&gt;.
	Heb je geen kaart ontvangen, maar wil je hier bij zijn?
	Mail ons op &lt;a href="mailto:wim@steefhegeman.com"&gt;wim@steefhegeman.com&lt;/a&gt;.
&lt;p&gt;
	We ontvangen graag &lt;b&gt;herinneringen&lt;/b&gt; aan Wim.
	Bij de ceremonie zal een herinneringendoos staan.
	Ook kun je ons &lt;a href="mailto:wim@steefhegeman.com"&gt;mailen&lt;/a&gt;.
&lt;figure&gt;&lt;a href=wim_orig.jpg&gt;&lt;img src=wim.jpg&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;
	Tot het einde heeft Wim altijd graag anderen geholpen.
	Daarom heeft hij zijn lichaam afgestaan aan de wetenschap.
	</content>
</entry>
<entry>
	<title>Status update, March 2022</title>

	<link rel="alternate" href="https://steefhegeman.com/blog/status-update-march-2022/"/>
	<id>https://steefhegeman.com/blog/status-update-march-2022/</id>
	<updated>2022-03-01T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/status-update-march-2022/">

&lt;p&gt;Henceforth I plan to copy the habit of other bloggers to post a monthly status update,
to remind myself to write some code every month.&lt;/p&gt;
&lt;p&gt;While I plan to only talk about my hobbies here, I cannot ignore Russia&amp;#39;s
gruesome and deplorable invasion of Ukraine. It has to be stopped. Putin and
his allies must be stopped. Their bloody and unjust attack on peace,
sovereignty, democracy and human rights must never be forgotten or downplayed.&lt;/p&gt;
&lt;p&gt;Hobbies.
In the past months I have been moving from dwm on Xorg to &lt;a href="https://github.com/riverwm/river"&gt;river&lt;/a&gt;,
to ride the Wayland wave.
I have mostly got it set up now, with &lt;a href="https://github.com/riverwm/river"&gt;foot&lt;/a&gt; replacing urxvt, &lt;a href="https://github.com/Cloudef/bemenu"&gt;bemenu&lt;/a&gt; replacing dmenu,
and &lt;a href="https://github.com/emersion/mako"&gt;mako&lt;/a&gt; replacing dunst (though it seems that dunst now also has Wayland support).
Waybar replaces dwm&amp;#39;s builtin bar for now&amp;mdash;though I hope to replace it with something leaner:
at cold boot it often takes seconds to start.
Firefox&amp;#39;s Wayland backend is papercut-free.
So everything I use on a daily basis works without issue,
now without tearing.&lt;/p&gt;
&lt;p&gt;The downside of all of this is that snixembed will probably get even less love from me.
But hopefully it will cease to be useful soon.&lt;/p&gt;
&lt;p&gt;I learned to use &lt;a href="https://git-send-email.io/"&gt;git send-email&lt;/a&gt; and sent a small &lt;a href="https://lists.sr.ht/~sircmpwn/visurf-devel/patches/29459"&gt;patch&lt;/a&gt;
for adding an about:commands page to &lt;a href="https://sr.ht/~sircmpwn/visurf/"&gt;visurf&lt;/a&gt;.
Visurf is a fun project of building a vi-like frontend to netsurf.
It&amp;#39;s still very bare bones, and netsurf of course cannot render every website
you throw at it well, but the UI is very responsive and consistent. This cannot
be said of the Firefox add-on I&amp;#39;m using currently, which is Firefox&amp;#39;s fault
(design decision). Its &lt;code&gt;urlfilter&lt;/code&gt; concept is also interesting: you can specify
a program of choice for mapping locations (stuff passed to the &lt;code&gt;open&lt;/code&gt; command)
to URLs. This allows for easy URL rewriting, setting search engines as
fallbacks, et cetera. I hope to spend more time on visurf.&lt;/p&gt;
&lt;p&gt;I also sent a 3-liner &lt;a href="https://lists.zx2c4.com/pipermail/password-store/2022-February/004574.html"&gt;patch&lt;/a&gt; for making passmenu look for more
dmenu alternatives on Wayland (it currently uses dmenu-wl). It made me learn
more about POSIX sh for-loops&amp;mdash;which is great&amp;mdash;but I now doubt the patch itself
is actually useful. (One might as well shadow dmenu-wl in the path.)&lt;/p&gt;
&lt;p&gt;That&amp;#39;s all the public code I wrote in the past month. I spent more time on
e-mail trouble. My current host (Soverin) has had server issues in the past
months. From SMTP failures to IMAP (and webmail) being unavailable for hours
last night. I recall their Cal/CardDAV server often being offline when I used
it, and then being dropped for an alternative without automatic migration or an
e-mail about the imminent change. Their status page is also broken. I&amp;#39;m kind of
surprised by all of this, since Soverin is advocated by Bits of Freedom. One
good thing about Soverin, it must be said, is their kind and prompt handling of
support queries, and their small price. It may very well be that these are
growing pains.&lt;/p&gt;
&lt;p&gt;But I&amp;#39;m going to try something else. I&amp;#39;ve started a trial at &lt;a href="https://www.migadu.com/"&gt;Migadu&lt;/a&gt; during
last night&amp;#39;s outage, and it works well so far. Migadu&amp;#39;s documentation is a lot
better, and I like the idea behind their pricing. The only downside is that it
seems impossible to sign up for it without an e-mail address, but maybe I
didn&amp;#39;t look hard enough.&lt;/p&gt;
&lt;p&gt;While writing this, I notice that my Markdown parser does not support
footnotes. It&amp;#39;s high time I give the innards of this blog some love. Metalsmith
has been working well for me, but the downside of the plugin system is that one
of them eventually becomes unsupported and starts dragging behind the whole
system because of dependency conflicts. So maybe I should move to a more
self-contained solution.&lt;/p&gt;
	</content>
</entry>
<entry>
	<title>The story of snixembed</title>

	<link rel="alternate" href="https://steefhegeman.com/blog/snixembed-story/"/>
	<id>https://steefhegeman.com/blog/snixembed-story/</id>
	<updated>2022-01-10T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/snixembed-story/">

&lt;p&gt;It&amp;#39;s February 2020 and my
&lt;a href="https://github.com/vector-im/element-web"&gt;riot-desktop&lt;/a&gt;
tray icon
&lt;a href="https://github.com/vector-im/element-web/issues/12518"&gt;vanished&lt;/a&gt;.
Electron (Chrome, rather) dropped support for the
&lt;a href="gghttps://specifications.freedesktop.org/systemtray-spec/latest/"&gt;System Tray Protocol Specification&lt;/a&gt;:
an old but widely used system tray protocol for the Linux desktop.
Chrome moved to
&lt;a href="https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierItem/"&gt;StatusNotifierItem&lt;/a&gt;
(SNI),
a D-Bus based protocol that — while still too complicated for my tastes —
is simpler than the X based tray protocol, and ready for Wayland.&lt;/p&gt;
&lt;p&gt;The problem: I was not ready for Wayland. I was running dwm with a tray bar
patch that doesn&amp;#39;t support SNI. Convincing Electron to reconsider seemed
impossible, so I looked for another way.&lt;/p&gt;
&lt;p&gt;The solution: implement the bare minimum of SNI and then &amp;quot;translate&amp;quot; it to
the old protocol. Thus
&lt;a href="https://git.sr.ht/~steef/snixembed"&gt;snixembed&lt;/a&gt;
was born.&lt;/p&gt;
&lt;p&gt;I do not understand Xorg, and I did not feel like learning legacy technology
(for which there is surprisingly little tutorial-style documentation online).
So I decided to use a toolkit to handle the translation: I would set up the
proper D-Bus services, parse the data, and leave the rest to Qt.&lt;/p&gt;
&lt;p&gt;It didn&amp;#39;t work: QSystemTrayIcon does not allow for choosing the backend. As
soon as I started impersonating an SNI host properly, Qt started talking SNI
back to me instead of XEmbed to my old tray.&lt;/p&gt;
&lt;p&gt;Then I tried GTK. It worked: GTK didn&amp;#39;t (and probably still doesn&amp;#39;t) support
the new tray icon specifications, because they had stopped believing in system
trays since GNOME 3. The old implementation was marked deprecated as well. That
didn&amp;#39;t bother me much: I had little free time and wanted my icons back, fast.&lt;/p&gt;
&lt;p&gt;I decided to use
&lt;a href="https://wiki.gnome.org/Projects/Vala"&gt;Vala&lt;/a&gt;,
a language I used previously for a never published project. Vala has a simple
(too simple, it turned out) abstraction over D-Bus and is made specifically for
using GTK. That meant writing less code. Less code is my first ingredient for
finished projects.&lt;/p&gt;
&lt;p&gt;Two days later, snixembed worked. The code was awful, slapped together in a few
hours with little design considerations. But I had my icons back, so I was
happy. I put it on sourcehut (warning for crappy code and incomplete
functionality), and announced it on the Arch Linux forums.&lt;/p&gt;
&lt;p&gt;I didn&amp;#39;t expect anyone to use it, but someone did. I saw people recommending
snixembed on GitHub. I got sourcehut tickets, and made a Matrix room that saw
use. Small numbers, about ten people in total. Far more interaction than I had
expected.&lt;/p&gt;
&lt;div role="note"&gt;&lt;small&gt;
2025: I still get bug reports,
and interesting use cases &amp;mdash;
like &lt;a href=https://todo.sr.ht/~steef/snixembed/17&gt;getting tray icons to work on WSLg&lt;/a&gt;.
&lt;/small&gt;&lt;/div&gt;

&lt;p&gt;I no longer use snixembed. Months after its creation, Electron decided to
reintroduce its legacy tray code. Some time later it had hit production. I
spread the word, and expected that would be the happy end of snixembed.&lt;/p&gt;
&lt;p&gt;But once in a while, someone comes around with a bug or feature request. These
people do not use snixembed in any way I would have imagined. One person uses
(used?) it because they were running a KDE session but without its tray, and
just the KDE session being there made applications ignore their legacy tray.
Someone is using obscure apps that only use AppIndicator. (AI was Canonicals
&amp;quot;competitor&amp;quot; to SNI that uses the same service names as SNI but then breaks
from it in silly ways. I could write a whole blog post about the pains of this
and other ways in which the ecosystem manages to just diverge from quite simple
specifications and how I worked around it.) It&amp;#39;s fun to see my project live
longer than I have a need for it to.&lt;/p&gt;
&lt;p&gt;These are the things I learned from snixembed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sometimes writing ugly code is the best way to finish a project.&lt;/li&gt;
&lt;li&gt;Publish early. People will find your project, use your project. Some will
report bugs, help fixing bugs. It&amp;#39;s useful and motivating.&lt;/li&gt;
&lt;li&gt;People will use your software in unexpected ways.&lt;/li&gt;
&lt;li&gt;It&amp;#39;s nice to fix bugs and it&amp;#39;s important to do so only when it&amp;#39;s convenient.&lt;/li&gt;
&lt;li&gt;When implementing a protocol, do so faithfully — expect others do not.&lt;/li&gt;
&lt;li&gt;Dirty hacks can make a difference.&lt;/li&gt;
&lt;/ul&gt;
	</content>
</entry>
<entry>
	<title>Matrix and the flood of lookup queries</title>

	<link rel="alternate" href="https://steefhegeman.com/blog/matrix-lookup-flood/"/>
	<id>https://steefhegeman.com/blog/matrix-lookup-flood/</id>
	<updated>2021-07-14T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/matrix-lookup-flood/">

&lt;h2&gt;Matrix and DNS&lt;/h2&gt;
&lt;p&gt;For a while now, I have been running a small &lt;a href="https://matrix.org"&gt;Matrix&lt;/a&gt; homeserver for personal use.
First in the cloud, now at home for privacy and cost efficiency.
It works, but I&amp;#39;ve found that &lt;code&gt;synapse&lt;/code&gt; can at times spit out hundreds of DNS queries in seconds.
This is not unexpected, as federation means communicating with many different parties.
But at home, this means connection issues for my other devices:
very slow DNS queries or even connection failure.
The problem is acknowledged, albeit sprinkled between other issues (e.g. &lt;a href="https://github.com/matrix-org/synapse/issues/8338"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/matrix-org/synapse/issues/5831"&gt;here&lt;/a&gt;).
Thankfully, a little relief is not hard to achieve: cache the query results.
I decided to try out &lt;code&gt;systemd-resolved&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Debian and systemd-resolved&lt;/h2&gt;
&lt;p&gt;Since the default network configuration for Debian using &lt;a href="https://www.debian.org/doc/manuals/debian-reference/ch05.en.html"&gt;ifupdown&lt;/a&gt;
tends to overwite &lt;code&gt;/etc/resolv.conf&lt;/code&gt; and hence does not play well with &lt;code&gt;systemd-resolved&lt;/code&gt;,
I decided to switch the whole configuration to &lt;code&gt;systemd&lt;/code&gt; and use &lt;code&gt;systemd-networkd&lt;/code&gt; instead.
Luckily, &lt;a href="https://wiki.debian.org/SystemdNetworkd"&gt;there is some documentation&lt;/a&gt;.
Note that simply disabling &lt;code&gt;networking.service&lt;/code&gt; does not seem enough to stop &lt;code&gt;ifupdown&lt;/code&gt; from hijacking the interface,
so I really had to (re)move the &lt;code&gt;/etc/network/interfaces&lt;/code&gt; file as stated.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;systemd-networkd&lt;/code&gt; was set up, all it took was enabling &lt;code&gt;systemd-resolved.service&lt;/code&gt;
and symlinking &lt;code&gt;/etc/resolv.conf&lt;/code&gt; to the &lt;code&gt;systemd-resolved&lt;/code&gt;-managed stub &lt;code&gt;/run/systemd/resolve/stub-resolv.conf&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;After one hour of running time, the initial results are promising:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-sh"&gt;$ resolvectl statistics
[...]

Transactions
Current Transactions: 0
  Total Transactions: 21450

Cache
  Current Cache Size: 8
          Cache Hits: 5204
        Cache Misses: 16309

[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Looking at the cache size, &lt;code&gt;systemd-resolved&lt;/code&gt; does not appear to cache aggressively.
Maybe results could be improved by using a different cache system.
However, this configuration seems to already alleviate the issues I was having on other devices,
probably because it flattens the occasional big wave of requests coming from &lt;code&gt;synapse&lt;/code&gt;.
On a system that uses &lt;code&gt;systemd&lt;/code&gt;, it is easy to set up.&lt;/p&gt;
	</content>
</entry>
<entry>
	<title>From Jekyll to Metalsmith</title>

	<link rel="alternate" href="https://steefhegeman.com/blog/from-jekyll-to-metalsmith/"/>
	<id>https://steefhegeman.com/blog/from-jekyll-to-metalsmith/</id>
	<updated>2017-04-30T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/from-jekyll-to-metalsmith/">

&lt;p&gt;It has been roughly a year since I set up my static Jekyll blog on GitHub Pages. Back then, this free Jekyll hosting service was one of a kind. Nowadays, services like GitLab Pages allow free automated generation and hosting of static websites as well, and many limitations were removed.&lt;/p&gt;
&lt;p&gt;The new site is generated with the amazingly simple &lt;a href="http://www.metalsmith.io/"&gt;Metalsmith&lt;/a&gt;, which is based on Node.js instead of Ruby. Which is great, because it allows for really easy integration of the Node ecosystem, and I find setting up a Node environment a lot easier than a Ruby one. (Bundler is nice, but I still encountered version conflicts, especially while using &lt;a href="https://middlemanapp.com/"&gt;Middleman&lt;/a&gt; for a different website).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://pages.gitlab.io/"&gt;GitLab Pages&lt;/a&gt; makes getting online a breeze. A simple &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file like this suffices:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs"&gt;&lt;span class="hljs-attribute"&gt;image&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-string"&gt;node:alpine&lt;/span&gt;

&lt;span class="hljs-attribute"&gt;pages&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt;
  &lt;span class="hljs-attribute"&gt;cache&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt;
    &lt;span class="hljs-attribute"&gt;paths&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt;
    &lt;span class="hljs-bullet"&gt;-&lt;/span&gt; &lt;span class="hljs-string"&gt;node_modules&lt;/span&gt;

  &lt;span class="hljs-attribute"&gt;script&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt;
  &lt;span class="hljs-bullet"&gt;-&lt;/span&gt; &lt;span class="hljs-string"&gt;npm install&lt;/span&gt;
  &lt;span class="hljs-bullet"&gt;-&lt;/span&gt; &lt;span class="hljs-string"&gt;npm run build&lt;/span&gt;

  &lt;span class="hljs-attribute"&gt;artifacts&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt;
    &lt;span class="hljs-attribute"&gt;paths&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt;
    &lt;span class="hljs-bullet"&gt;-&lt;/span&gt; &lt;span class="hljs-string"&gt;public&lt;/span&gt;

  &lt;span class="hljs-attribute"&gt;only&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt;
  &lt;span class="hljs-bullet"&gt;-&lt;/span&gt; &lt;span class="hljs-string"&gt;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Under the hood&lt;/h2&gt;
&lt;p&gt;Most of the hard work is done by Metalsmith plugins metalsmith-collections, metalsmith-markdown and metalsmith-jstransformer. &lt;a href="https://mozilla.github.io/nunjucks/"&gt;Nunjucks&lt;/a&gt; is used as a templating language. I really liked Haml, but it&amp;#39;s not really a first class citizen outside of the Ruby world, and Nunjucks is so much more KISS.&lt;/p&gt;
&lt;h2&gt;Domain name&lt;/h2&gt;
&lt;p&gt;After much thought, I decided steefhegeman.com to be the best domain name for this website. It annoys me how unoriginal it is, but sometimes boring is good.&lt;/p&gt;
&lt;p&gt;With this, I hope to have a more stable website from now on. No more broken links. Promise.&lt;/p&gt;
	</content>
</entry>
<entry>
	<title>Getting started with Libpeas extensions in Vala</title>
	<subtitle type="html">Remnant from an old blog</subtitle>
	<link rel="alternate" href="https://steefhegeman.com/blog/libpeas-vala/"/>
	<id>https://steefhegeman.com/blog/libpeas-vala/</id>
	<updated>2016-01-20T00:00:00Z</updated>
	<content type="html" xml:base="https://steefhegeman.com/blog/libpeas-vala/">

&lt;p&gt;If you&amp;#39;ve been looking for easy ways to make your application extensible using &amp;quot;plugins&amp;quot;, surely you must have come across a GObject extensions library called &lt;a href="https://wiki.gnome.org/Projects/Libpeas"&gt;libpeas&lt;/a&gt;. Its engine can seamlessly load GObject-based extension objects which act as &amp;quot;entry points&amp;quot; to the application. Currently, plugins written in C/Vala (shared object files), Python and Lua are supported.&lt;/p&gt;
&lt;div role="note"&gt;&lt;small&gt;
&lt;b&gt;Note the date.&lt;/b&gt;
This has not been checked in years
and is kept for nostalgia only.
&lt;/small&gt;&lt;/div&gt;

&lt;p&gt;As a project I&amp;#39;m currently working on (more on that soon) requires a system like this, I decided to take a look. I must say, it took me a while to get it all up and running. Which is why I decided to write a simple introduction in the hope that the journey will be less painful for others. The end goal is to make a small a Gtk+ window with buttons provided by plugins. The core of the application will be written in Vala, and plugin examples are provided in Python and Vala. Let&amp;#39;s get started.&lt;/p&gt;
&lt;h2&gt;What you need&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Vala (obviously)&lt;/li&gt;
&lt;li&gt;libpeas&lt;/li&gt;
&lt;li&gt;Gtk+ (to spice the example up a bit)&lt;/li&gt;
&lt;li&gt;Python (optional, for the Python plugin)&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;python-gobject&lt;/li&gt;
&lt;li&gt;gobject-introspection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do make sure you have the development versions installed too, if your distribution splits library packages.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://valadoc.org"&gt;Valadoc&lt;/a&gt; contains a great reference for all the libraries used in this post (&lt;a href="http://valadoc.org/#!wiki=libpeas-1.0/index"&gt;libpeas-1.0&lt;/a&gt; and &lt;a href="http://valadoc.org/#!wiki=gtk+-3.0/index"&gt;gtk+-3.0&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;The structure&lt;/h2&gt;
&lt;p&gt;For this little tutorial I&amp;#39;ve poked around a bit in the source code of &lt;a href="https://wiki.gnome.org/Apps/Gedit"&gt;gedit&lt;/a&gt;, the project where libpeas was first conceived. I had never seen an extensible application up close before, so seeing the structure was a nice aha-moment for me.&lt;/p&gt;
&lt;p&gt;Most of gedit is actually a shared library. The library defines basically the whole application. Then there&amp;#39;s also a little executable called gedit which doesn&amp;#39;t do much more than creating some objects defined in libgedit to start the main loop. In fact, it&amp;#39;s so small it&amp;#39;s made of one C file: &lt;a href="https://git.gnome.org/browse/gedit/tree/gedit/gedit.c"&gt;gedit.c&lt;/a&gt;.&lt;/p&gt;
&lt;div role="note"&gt;&lt;small&gt;
Ten years later,
there are several files.
But Gedit still appears to have a &lt;a href=https://valadoc.org/gedit/Gedit.html&gt;familiar structure&lt;/a&gt;.
&lt;/small&gt;&lt;/div&gt;

&lt;p&gt;Why is this the case? For an application to be truly extendible, plugins need to be able to deal with its internal objects. Thinking about it like this, it&amp;#39;s only logical that extendible applications are basically a library. Of course, not all the internal objects need to be accessible by plugins. To hide an object, simply don&amp;#39;t ship the header file defining it or in Vala&amp;#39;s case, mark the object as private.&lt;/p&gt;
&lt;p&gt;To summarize: one big library compiled into a shared object file which a small launcher application and plugins utilize.&lt;/p&gt;
&lt;h2&gt;Part 1: the library&lt;/h2&gt;
&lt;p&gt;Just so you know: this will be most of the work.&lt;/p&gt;
&lt;p&gt;As our application consists of a simple Gtk window, we will define the window here. Furthermore, we will define an interface for the extension objects. For good measure, let&amp;#39;s give our little app an original name. Like foo.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-vala"&gt;&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;namespace&lt;/span&gt; &lt;span class="hljs-title"&gt;Foo&lt;/span&gt; &lt;/span&gt;{

	&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;Window&lt;/span&gt; : &lt;span class="hljs-title"&gt;Gtk&lt;/span&gt;.&lt;span class="hljs-title"&gt;Window&lt;/span&gt; &lt;/span&gt;{

		&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.ButtonBox buttons { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; }

		&lt;span class="hljs-keyword"&gt;private&lt;/span&gt; Peas.ExtensionSet extensions { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; }

		&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; Window() {
			&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.buttons = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.ButtonBox(&lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.Orientation.VERTICAL);
			&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.destroy.connect(&lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.main_quit);

			&lt;span class="hljs-comment"&gt;/* Get the default engine */&lt;/span&gt;
			&lt;span class="hljs-keyword"&gt;var&lt;/span&gt; engine = Peas.Engine.get_default();

			&lt;span class="hljs-comment"&gt;/* Enable the python3 loader */&lt;/span&gt;
			engine.enable_loader(&lt;span class="hljs-string"&gt;&amp;quot;python3&amp;quot;&lt;/span&gt;);

			&lt;span class="hljs-comment"&gt;/* Add the current directory to the search path */&lt;/span&gt;
			&lt;span class="hljs-keyword"&gt;string&lt;/span&gt; dir = Environment.get_current_dir();
			engine.add_search_path(dir, dir);

			&lt;span class="hljs-comment"&gt;/* Create the ExtensionSet */&lt;/span&gt;
			extensions = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; Peas.ExtensionSet(engine, typeof (Foo.Extension), &lt;span class="hljs-string"&gt;&amp;quot;window&amp;quot;&lt;/span&gt;, &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;);
			extensions.extension_added.connect((info, extension) =&amp;gt; {
				(extension as Foo.Extension).activate();
			});
			extensions.extension_removed.connect((info, extension) =&amp;gt; {
				(extension as Foo.Extension).deactivate();
			});

			&lt;span class="hljs-comment"&gt;/* Load all the plugins */&lt;/span&gt;
			&lt;span class="hljs-keyword"&gt;foreach&lt;/span&gt; (&lt;span class="hljs-keyword"&gt;var&lt;/span&gt; plugin in engine.get_plugin_list())
				engine.try_load_plugin(plugin);

			&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.add(buttons);
			&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.show_all();
		}
	}

	&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;interface&lt;/span&gt; &lt;span class="hljs-title"&gt;Extension&lt;/span&gt; : &lt;span class="hljs-title"&gt;Object&lt;/span&gt; &lt;/span&gt;{

		&lt;span class="hljs-comment"&gt;/* This will be set to the window */&lt;/span&gt;
		&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;abstract&lt;/span&gt; Window window { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; construct &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; }

		&lt;span class="hljs-comment"&gt;/* The &amp;quot;constructor&amp;quot; */&lt;/span&gt;
		&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;abstract&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; activate();

		&lt;span class="hljs-comment"&gt;/* The &amp;quot;destructor&amp;quot; */&lt;/span&gt;
		&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;abstract&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; deactivate();
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, in the constructor of &lt;code&gt;Foo.Window&lt;/code&gt;, a &lt;a href="http://valadoc.org/#!api=libpeas-1.0/Peas.Engine"&gt;Peas.Engine&lt;/a&gt; is used to search for plugins in the current directory and load the plugins. A &lt;a href="http://valadoc.org/#!api=libpeas-1.0/Peas.ExtensionSet"&gt;Peas.ExtensionSet&lt;/a&gt; takes care of creating extension objects implementing the &lt;code&gt;Foo.Extension&lt;/code&gt; interface as their plugins are loaded by the engine.&lt;/p&gt;
&lt;p&gt;By connecting to the &lt;code&gt;extension_added&lt;/code&gt; signal of &lt;code&gt;extensions&lt;/code&gt;, we make sure that after an extension object is created, its &lt;code&gt;activate&lt;/code&gt; member function is called. By connecting to the &lt;code&gt;extension_removed&lt;/code&gt; signal, we make sure that the deactivate() member function is called. If you want, you could see a &lt;code&gt;Foo.Extension&lt;/code&gt;&amp;#39;s &lt;code&gt;activate&lt;/code&gt; and &lt;code&gt;deactivate&lt;/code&gt; functions as some sort of constructor and destructor. In &lt;code&gt;activate&lt;/code&gt; they set their stuff up, add a button to the window, and in &lt;code&gt;deactivate&lt;/code&gt; they remove the button again.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re familiar with GObject-style construction, you&amp;#39;ll see that we tell &lt;code&gt;extensions&lt;/code&gt; to create every extension object with its &lt;code&gt;window&lt;/code&gt; property set to &lt;code&gt;this&lt;/code&gt; (the current Foo.Window instance). This way, extensions will have a reference to the window from which they can start messing with the application.&lt;/p&gt;
&lt;p&gt;Note that the Python loader has to be explicitly enabled.&lt;/p&gt;
&lt;p&gt;Of course you can retrieve way more information about a plugin before (and after) loading it. The variable &lt;code&gt;plugin&lt;/code&gt; is of the type &lt;a href="http://valadoc.org/#!api=libpeas-1.0/Peas.PluginInfo"&gt;Peas.PluginInfo&lt;/a&gt;, which, combined with &lt;a href="http://valadoc.org/#!api=libpeas-1.0/Peas.Engine.provides_extension"&gt;Peas.Engine.provides_extension&lt;/a&gt; should give you all the info you could wish for.&lt;/p&gt;
&lt;p&gt;Save the file as foo.vala and create the library:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-sh"&gt;&lt;span class="hljs-comment"&gt;# Generate shared object file, C headers, vapi file and gir file&lt;/span&gt;
valac -o libfoo.so --library foo -H foo.h  --gir Foo-1.0.gir  -X -shared -X -fPIC --pkg libpeas-1.0 --pkg gtk+-3.0 foo.vala

&lt;span class="hljs-comment"&gt;# Compile typelib file (Python and Lua)&lt;/span&gt;
g-ir-compiler --shared-library libfoo Foo-1.0.gir -o Foo-1.0.typelib
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What we get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A shared object file compiled from the library&lt;/li&gt;
&lt;li&gt;A vapi file for Vala plugins and the launcher&lt;/li&gt;
&lt;li&gt;A C header file for C and Vala plugins and the launcher&lt;/li&gt;
&lt;li&gt;A GObject introspection file to compile...&lt;/li&gt;
&lt;li&gt;...a typelib file for Lua and Python plugins&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Part 2: the launcher&lt;/h2&gt;
&lt;p&gt;Next, we create a launch point for our application. The contents of the file should be pretty straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-vala"&gt;&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; main(&lt;span class="hljs-keyword"&gt;string&lt;/span&gt;[] args) {
	&lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.init(ref args);

	&lt;span class="hljs-keyword"&gt;var&lt;/span&gt; window = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; Foo.Window();

	&lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.main();
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;#39;s all. Save it to a file called launcher.vala and compile it to the executable foo:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-sh"&gt;valac -o foo launcher.vala --vapidir . --pkg gtk+-3.0 --pkg foo -X -I. -X -L. -X -lfoo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The extra options are to make sure our library files can be found while they&amp;#39;re not installed in a default search directory.&lt;/p&gt;
&lt;p&gt;If you already tried to run the launcher, chances are you&amp;#39;ve run into this error message:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-txt"&gt;./foo: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Because the shared object file isn&amp;#39;t installed properly but is located in the current directory, in order to run the launcher, the environment variable &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; needs to be set to the current directory. For the Python plugin, our typelib file also needs to be found in the current directory using &lt;code&gt;GI_TYPELIB_PATH&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-sh"&gt;&lt;span class="hljs-built_in"&gt;export&lt;/span&gt; LD_LIBRARY_PATH=.
&lt;span class="hljs-built_in"&gt;export&lt;/span&gt; GI_TYPELIB_PATH=.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now you should be able to run the launcher using &lt;code&gt;./foo&lt;/code&gt;. An empty window, great!&lt;/p&gt;
&lt;h2&gt;Part 3: plugins&lt;/h2&gt;
&lt;p&gt;Finally, let&amp;#39;s write some plugins! Libpeas plugins consist of at least two files: the actual plugin (a shared object file or a script) and a plugin file containing some information about the plugin. Let&amp;#39;s start off by writing a plugin in Vala.&lt;/p&gt;
&lt;h3&gt;Vala&lt;/h3&gt;
&lt;pre&gt;&lt;code class="hljs language-vala"&gt;&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;ValaExtension&lt;/span&gt; : &lt;span class="hljs-title"&gt;Object&lt;/span&gt;, &lt;span class="hljs-title"&gt;Foo&lt;/span&gt;.&lt;span class="hljs-title"&gt;Extension&lt;/span&gt; &lt;/span&gt;{

	&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; Foo.Window window { &lt;span class="hljs-keyword"&gt;get&lt;/span&gt;; construct &lt;span class="hljs-keyword"&gt;set&lt;/span&gt;; }

	&lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.Button button;

	&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; activate() {
		button = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-built_in"&gt;Gtk&lt;/span&gt;.Button.with_label(&lt;span class="hljs-string"&gt;&amp;quot;Say Hello&amp;quot;&lt;/span&gt;);

		&lt;span class="hljs-comment"&gt;/* Change label when clicked */&lt;/span&gt;
		button.clicked.connect(() =&amp;gt; {
			button.set_label(&lt;span class="hljs-string"&gt;&amp;quot;Hello World!&amp;quot;&lt;/span&gt;);
		});

		&lt;span class="hljs-comment"&gt;/* The magic, it&amp;#x27;s happening! */&lt;/span&gt;
		window.buttons.add(button);
		button.show();
	}

	&lt;span class="hljs-keyword"&gt;void&lt;/span&gt; deactivate() {
		window.buttons.remove(button);
	}
}

&lt;span class="hljs-comment"&gt;/* Register extension types */&lt;/span&gt;
[ModuleInit]
&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; peas_register_types(TypeModule module) {
	&lt;span class="hljs-keyword"&gt;var&lt;/span&gt; objmodule = module as Peas.ObjectModule;

	objmodule.register_extension_type(typeof (Foo.Extension), typeof (ValaExtension));
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A few remarks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Notice how the extension object is derived from &lt;code&gt;Foo.Extension&lt;/code&gt;. &lt;code&gt;window&lt;/code&gt; points to the actual window object because the property is set on construction by the &lt;code&gt;Peas.ExtensionSet&lt;/code&gt; (using &lt;a href="http://valadoc.org/#!api=libpeas-1.0/Peas.Engine.create_extension"&gt;Peas.Engine.create_extension&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;libpeas uses &lt;a href="https://wiki.gnome.org/Projects/Vala/Tutorial#GObject-Style_Construction"&gt;GObject-style construction&lt;/a&gt;. Reading up on how it works would make you understand better how these construction properties work. If you want to define a constructor, use a &lt;code&gt;construct {}&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;Shared-object-file-based plugins (C/Vala) require a &lt;code&gt;peas_register_type&lt;/code&gt; function (don&amp;#39;t forget the leading &lt;code&gt;[ModuleInit]&lt;/code&gt; to register the extension types.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Save the file as vala-extension.vala and compile it to libvala-extension.so using:&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-sh"&gt;valac -o libvala-extension.so --library vala-extension vala-extension.vala -X -shared -X -fPIC --vapidir . --pkg libpeas-1.0 --pkg gtk+-3.0 --pkg foo -X -I. -X -L. -X -lfoo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Time to write a plugin file. This is written in the KeyFile format. For all the options, see &lt;a href="https://developer.gnome.org/libpeas/stable/PeasPluginInfo.html"&gt;the reference&lt;/a&gt;. Note that all this information will be accessible through &lt;a href="http://valadoc.org/#!api=libpeas-1.0/Peas.PluginInfo"&gt;Peas.PluginInfo&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-ini"&gt;&lt;span class="hljs-section"&gt;[Plugin]&lt;/span&gt;
&lt;span class="hljs-attr"&gt;Module&lt;/span&gt;=vala-extension.so
&lt;span class="hljs-attr"&gt;Name&lt;/span&gt;=Say Hello
&lt;span class="hljs-attr"&gt;Description&lt;/span&gt;=Displays &lt;span class="hljs-string"&gt;&amp;quot;Hello World!&amp;quot;&lt;/span&gt; &lt;span class="hljs-literal"&gt;on&lt;/span&gt; click
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice how in the Module value, the preceding &amp;quot;lib&amp;quot; is omitted.&lt;/p&gt;
&lt;p&gt;Great! Save it to &lt;code&gt;vala-extension.plugin&lt;/code&gt; and run the launcher. You should see a window with the button we just defined in it.&lt;/p&gt;
&lt;h3&gt;Python&lt;/h3&gt;
&lt;p&gt;Next, let&amp;#39;s add a button to quit the application, this time in Python. I&amp;#39;m not going to show how Python and python-gobject work (wish I could! If I did it wrong, please let me know) here, so I&amp;#39;ll keep it short.&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-python"&gt;&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; gi.repository &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; GObject
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; gi.repository &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Peas
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; gi.repository &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Gtk
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; gi.repository &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Foo

&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;PythonExtension&lt;/span&gt;(GObject.Object, Foo.Extension):
	window = GObject.Property(&lt;span class="hljs-built_in"&gt;type&lt;/span&gt;=Foo.Window)
	button = GObject.Property(&lt;span class="hljs-built_in"&gt;type&lt;/span&gt;=Gtk.Button)

	&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title function_"&gt;do_activate&lt;/span&gt;(&lt;span class="hljs-params"&gt;self&lt;/span&gt;):
		&lt;span class="hljs-variable language_"&gt;self&lt;/span&gt;.button = Gtk.Button(label=&lt;span class="hljs-string"&gt;&amp;quot;Quit&amp;quot;&lt;/span&gt;)
		&lt;span class="hljs-variable language_"&gt;self&lt;/span&gt;.button.connect(&lt;span class="hljs-string"&gt;&amp;quot;clicked&amp;quot;&lt;/span&gt;, Gtk.main_quit)

		&lt;span class="hljs-variable language_"&gt;self&lt;/span&gt;.window.get_buttons().add(&lt;span class="hljs-variable language_"&gt;self&lt;/span&gt;.button)
		&lt;span class="hljs-variable language_"&gt;self&lt;/span&gt;.button.show()

	&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title function_"&gt;do_deactivate&lt;/span&gt;(&lt;span class="hljs-params"&gt;self&lt;/span&gt;):
		&lt;span class="hljs-variable language_"&gt;self&lt;/span&gt;.window.get_buttons().remove(&lt;span class="hljs-variable language_"&gt;self&lt;/span&gt;.button)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save it as python-extension.py.&lt;/p&gt;
&lt;p&gt;The complementary plugin file (notice how &amp;quot;.py&amp;quot; is omitted):&lt;/p&gt;
&lt;pre&gt;&lt;code class="hljs language-ini"&gt;&lt;span class="hljs-section"&gt;[Plugin]&lt;/span&gt;
&lt;span class="hljs-attr"&gt;Module&lt;/span&gt;=python-extension
&lt;span class="hljs-attr"&gt;Loader&lt;/span&gt;=python3
&lt;span class="hljs-attr"&gt;Name&lt;/span&gt;=Quit button
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save it to a .plugin file like you&amp;#39;re used to, and enjoy the quit button. Don&amp;#39;t forget to set &lt;code&gt;GI_TYPELIB_PATH&lt;/code&gt; to the current directory (&lt;code&gt;export GI_TYPELIB_PATH=.&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;Well, that&amp;#39;s it! I hope this gave you a good impression of what libpeas can do for your application and how to achieve it. Of course, it can do way more. Plugins can bring extra datafiles and GSettings, for example, and libpeas-gtk provides widgets to manage plugins easily. If you want to know more about libpeas, a good starting point would be &lt;a href="https://developer.gnome.org/libpeas/stable/"&gt;the official reference&lt;/a&gt;. Other sources I used to write this post are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.gnome.org/Projects/Vala/Tutorial"&gt;The Vala Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.gnome.org/browse/gedit/"&gt;gedit&amp;#39;s source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://valadoc.org/#!wiki=libpeas-1.0/index"&gt;Valadoc on libpeas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.gnome.org/Projects/Vala/Gedit3PluginSample"&gt;Gedit 3 Plugin Sample (Vala)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git.gnome.org/browse/libpeas/tree/peas-demo"&gt;libpeas demo source code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks for reading! If you have any questions or other feedback, please let me know.&lt;/p&gt;
	</content>
</entry>
</feed>
