๐Ÿ’พ Archived View for boringcactus.com โ€บ feed.xml captured on 2021-12-03 at 14:04:38.

View Raw

More Information

โฌ…๏ธ Previous capture (2021-11-30)

-=-=-=-=-=-=-

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
    <link href="gemini://boringcactus.com/feed.xml" rel="self" type="application/atom+xml" />
    <link href="gemini://boringcactus.com/" rel="alternate" type="text/gemini" />
    <updated>2021-10-26T21:48:33.134323Z</updated>
    <id>gemini://boringcactus.com/feed.xml</id>
    <title>boringcactus</title>
    <subtitle>boringcactus's blog posts</subtitle>
    <author>
        <name>boringcactus / Melody Horn</name>
        <uri>/</uri>
    </author>
    
    <entry>
        <title>A 2021 Survey of Rust GUI Libraries</title>
        <link href="gemini://boringcactus.com/2021/10/24/2021-survey-of-rust-gui-libraries.gmi" rel="alternate" type="text/gemini" title="A 2021 Survey of Rust GUI Libraries" />
        <published>2021-10-24T00:00:00Z</published>
        <updated>2021-10-24T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/10/24/2021-survey-of-rust-gui-libraries.gmi</id>
        <content type="text/gemini">A year and a half ago I looked at a bunch of different Rust GUI libraries; that&#39;s a long time, so let&#39;s see if things have changed.

=&gt; /2020/08/21/survey-of-rust-gui-libraries.gmi looked at a bunch of different Rust GUI libraries

As before, some context:



The short version, if you&#39;re in a hurry:

=&gt; https://www.areweguiyet.com/ Are We GUI Yet

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ library โ”‚ easy setup? โ”‚ nonzero โ”‚ overall vibe check โ”‚

โ”‚ โ”‚ โ”‚ accessibility? โ”‚ โ”‚

โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก

โ”‚ Azul โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ Conrod โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ core-foundation โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ druid โ”‚ โœ… โ”‚ โŒ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ egui โ”‚ โœ… โ”‚ โŒ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ fltk โ”‚ โœ… โ”‚ โŒ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ GTK โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ Iced โ”‚ โœ… โ”‚ โŒ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ imgui โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ iui โ”‚ โœ… โ”‚ โœ… โ”‚ ๐Ÿ‘ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ KAS โ”‚ โœ… โ”‚ โŒ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ lvgl โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ native-windows-gui โ”‚ โœ… โ”‚ โœ… โ”‚ ๐Ÿคท โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ OrbTk โ”‚ โœ… โ”‚ โŒ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ qmetaobject-rs โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ qt_widgets โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ relm โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ rust-qt-binding- โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”‚ generator โ”‚ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ sciter-rs โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ SixtyFPS โ”‚ โœ… โ”‚ โŒ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ tauri โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

โ”‚ vgtk โ”‚ โŒ โ”‚ โ”‚ โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

## Azul

The installation guide says you need to separately download the precompiled library and make sure it&#39;s in the right folder and ughhhh that&#39;s more setup than adding it to `Cargo.toml` and i literally put in my rules that that&#39;s more work than i want to do. Rust already has a build system, I don&#39;t want to have to fuck with things independently of that.

=&gt; https://azul.rs/ Azul
=&gt; https://azul.rs/guide/1.0.0-alpha1/Installation installation guide

## Conrod

My previous adventures in Rust GUI development have led me to Conrod several times, and most of those times it&#39;s been a hassle, but we&#39;ll see if things have changed since then. The hello world tutorial appears to actually say which backends to use, which is a welcome and sorely-needed improvement.

=&gt; https://github.com/pistondevelopers/conrod Conrod
=&gt; https://docs.rs/conrod_core/0.75.0/conrod_core/guide/chapter_3/index.html hello world tutorial

Wait, what did that say there?

&gt; At the time of writing (September 2017),
Ok hold up, how outdated is this? It said to depend on `conrod 0.55.0`, but it looks like the latest version is actually `0.75.0`. And there is no `conrod 0.75.0`, apparently, because they split the crate into several different crates as of 0.62.0; allegedly I am &#34;most likely looking for `conrod_core`.&#34; So the guide, which is *part of `conrod_core`*, is so out of date that it uses the wrong name for itself. If I just use `conrod_core 0.75.0` where the guide said to use `conrod 0.55.0` my issues have not ceased, though:

=&gt; https://crates.io/crates/conrod allegedly

error: failed to select a version for `conrod_core`.

... required by package `conrod-demo v0.1.0 (D:\Melody\Projects\misc\gui-survey-2021\conrod-demo)`

versions that meet the requirements `^0.75.0` are: 0.75.0

the package `conrod-demo` depends on `conrod_core`, with features: `glium` but `conrod_core` does not have these features.


So the officially-recommended backends don&#39;t exist anymore, or at least they no longer exist in the same way and it&#39;s my job to figure out how to get them back. I don&#39;t want to debug the official tutorial that&#39;s linked in the README. As such, Conrod appears to not be for me.

## core-foundation

&#34;Bindings to Core Foundation for macOS&#34; are cool if I&#39;m using macOS. As established, I&#39;m not.

=&gt; https://github.com/servo/core-foundation-rs core-foundation

## druid

Previously, druid was one of the two libraries I was most impressed by; let&#39;s see if that still holds. The &#34;this is outdated, and should be replaced with a walkthrough of getting a simple app built and running&#34; message in the official guide persists, and upon further inspection there&#39;s been one release since my last blog post, with few new features.

=&gt; https://linebender.org/druid/ druid
=&gt; https://linebender.org/druid/get_started.html the official guide

The &#34;hello world&#34; code in the guide has broken - something that used to take a value now takes a closure returning that value - but that&#39;s an easy fix.

A harder fix, though, is that in the official &#34;hello world&#34; example, Windows Narrator can&#39;t find the text &#34;hello world&#34;. In my last post, I treated accessibility as an afterthought, to the point where I had to go in several months later and retroactively check for and add information about screen reader support; this was the wrong decision for me, and it&#39;s the wrong decision for druid. They list &#34;Offer robust accessibility support&#34; under their goals; maybe one day they&#39;ll get there.

## egui

This is the first entry on Are We GUI Yet that wasn&#39;t there last year, and it was seeing something about this on Twitter that got me interested in revisiting this topic in the first place. There&#39;s an official template that comes with some (bash, and thereby more-painful-on-Windows, but what can you do) scripts for managing WASM builds, but I&#39;m not interested in WASM at this point, so I&#39;ll just yoink the code and undo the multi-track drifting that allows WASM builds (which they probably don&#39;t actually need to do because wasm-bindgen can just do the right thing on binary crates due to work that i did almost exactly two years ago now, but whatever).

=&gt; https://github.com/emilk/egui egui
=&gt; https://github.com/rustwasm/wasm-bindgen/pull/1843 probably don&#39;t actually need to do because wasm-bindgen can just do the right thing on binary crates due to work that i did almost exactly two years ago now

It runs correctly, which is good, but out of the box, Windows Narrator can&#39;t tell what&#39;s going on. There is, however, work-in-progress screen reading support which we can opt into with the `screen_reader` feature. Unfortunately, this appears not to do anything, at least for me with my current setup and near-zero knowledge of egui. Hopefully this becomes more robust soon.

=&gt; https://github.com/emilk/egui/issues/167 work-in-progress screen reading support

## FLTK

The presence of the `fltk-bundled` feature is very much appreciated. Unfortunately, it doesn&#39;t make up for a lack of screen reader accessibility.

=&gt; https://github.com/fltk-rs/fltk-rs FLTK

## GTK

The GTK project appears to be putting a lot of effort towards providing good Rust bindings. Unfortunately,

=&gt; https://gtk-rs.org/ GTK

warning: Could not run `&#34;pkg-config&#34; &#34;--libs&#34; &#34;--cflags&#34; &#34;glib-2.0&#34; &#34;glib-2.0 &gt;= 2.48&#34;`


Full disclosure: I have tried several times to get GTK 3 or 4 working for development on this computer. I have failed several times, and succeeded none. I am disinclined to try and fail again.

## Iced

Iced was one of the two projects I was impressed by last time around. Unfortunately, that was before I started caring about accessibility as much; the counter example, when ran as a native binary, gives no information to Narrator.

=&gt; https://github.com/iced-rs/iced Iced

## imgui

The README doesn&#39;t have a self-contained example, but there&#39;s a hello world example that looks pretty reasonable. But that `mod support;` declaration looks like it&#39;s probably abstracting out some complexity, let me justโ€”oh god my eyes that&#39;s a lot of boilerplate. Am I supposed to copy and paste all that logic into my code? If so, why is it not just part of the library already? If not, why do all the examples have it?

=&gt; https://github.com/imgui-rs/imgui-rs imgui
=&gt; https://github.com/imgui-rs/imgui-rs/blob/v0.8.0/imgui-examples/examples/hello_world.rs hello world example
=&gt; https://github.com/imgui-rs/imgui-rs/blob/v0.8.0/imgui-examples/examples/support/mod.rs oh god my eyes

As stated, I don&#39;t already have a graphics pipeline set up, so I think I am not the target audience for imgui.

## iui

This one&#39;s also a new addition compared to my previous writeup. And I&#39;ll be damned, it actually works! Didn&#39;t have to mess around with compilers, worked just fine with Narrator.

=&gt; https://github.com/rust-native-ui/libui-rs iui

It&#39;s been three years since the last crates.io release, but I&#39;m writing this in the same order you&#39;re reading it, so this is the first one I&#39;ve seen that actually has any accessibility support whatsoever. If more than a handful of other libraries can clear that not-very-high bar, I&#39;ll fill in a more robust comparison, but for now this is pretty damn good.

## KAS

Runs fine, doesn&#39;t give Narrator any info.

=&gt; https://github.com/kas-gui/kas KAS

## lvgl

Setting up `DEP_LV_CONFIG_PATH` is 1. more work than just adding it to `Cargo.toml` and 2. apparently some bullshit based on C header configuration??

=&gt; https://github.com/rafaelcaricio/lvgl-rs lvgl

## native-windows-gui

Unsurprisingly, this works like a charm on Windows. No installation problems, no accessibility problems, no worries. It does, of course, only work on Windows, though, and I would love to wind up with code that theoretically should be cross-platform.

=&gt; https://github.com/gabdube/native-windows-gui native-windows-gui

## OrbTk

Works, no accessibility.

=&gt; https://github.com/redox-os/orbtk OrbTk

## qmetaobject-rs

Error: Failed to execute qmake. Make sure &#39;qmake&#39; is in your path!


## qt_widgets

failed to run command: &#34;qmake&#34; &#34;-query&#34; &#34;QT_VERSION&#34;


## relm

LINK : fatal error LNK1181: cannot open input file &#39;gtk-3.lib&#39;


## rust-qt-binding-generator

installing Qt is more work than i want to have to do.

=&gt; https://github.com/woboq/qmetaobject-rs qmetaobject-rs
=&gt; https://rust-qt.github.io/ qt_widgets
=&gt; https://github.com/antoyo/relm relm
=&gt; https://invent.kde.org/sdk/rust-qt-binding-generator rust-qt-binding-generator

## sciter-rs

downloading the SDK independent of Cargo is more work than i want to have to do.

=&gt; https://github.com/sciter-sdk/rust-sciter sciter-rs

## SixtyFPS

works, no accessibility

=&gt; https://sixtyfps.io/ SixtyFPS

## Tauri

whatever the hell any of this is is more work than i want to have to do. it looks like this wants you to have both npm/yarn and Cargo? and i don&#39;t think i like the implications of that.

=&gt; https://tauri.studio/en/ Tauri
=&gt; https://tauri.studio/en/docs/usage/development/integration#1-start-a-new-tauri-project whatever the hell any of this is

## vgtk

Could not run `&#34;pkg-config&#34; &#34;--libs&#34; &#34;--cflags&#34; &#34;glib-2.0&#34; &#34;glib-2.0 &gt;= 2.44&#34;`


## WebRender

what the goddamn hell is this supposed to be?

=&gt; https://github.com/bodil/vgtk vgtk
=&gt; https://github.com/servo/webrender WebRender
=&gt; https://github.com/servo/webrender/blob/master/examples/common/boilerplate.rs what the goddamn hell is this supposed to be

the basic example is doing shader fuckery! that doesn&#39;t seem basic to me!

## summary

Well, shit. Turns out if you want low-fuss Windows setup, nonzero accessibility, and theoretical cross-platform support, and you want them in the present rather than the future, there&#39;s only one option (at least among the things listed on Are We GUI Yet). Shout out to iui, in all its three-year-old-latest-release no-way-to-draw-images(-at-least-that-I-can-find) glory. Honorable mention to egui for having put in at least some effort towards accessibility; if I revisit this topic in another year or two, I suspect I&#39;ll be very impressed with egui. Additional reference to SixtyFPS who have made an entire other markup language for specifying UI structure; I&#39;m not sure it&#39;s a good or necessary approach, but it&#39;s eye-catching, and maybe at some point they&#39;ll start working on accessibility and I&#39;ll take a more thorough look. (I&#39;m not entirely sure the extent to which German and/or EU law requires commercial products meet accessibility standards, but if SixtyFPS is doing commercial licensing, they&#39;d better have looked into that.)

=&gt; https://github.com/rust-native-ui/libui-rs iui
=&gt; https://github.com/emilk/egui egui
=&gt; https://sixtyfps.io/ SixtyFPS

## whining

I&#39;ve worked with Python and Rust a lot, and those are my two languages of choice for new personal projects these days. Python, as an interpreted language, does not really go out of its way to give you binaries that you can just hand to end users; it&#39;s possible, but as of the last time I checked, the tools were all varying shades of pains in the ass to use. Rust seems like it&#39;d be far better suited for end-user application development, since at the end you have a .exe or whatever that you can hand off to anybody. It looks like the Rust ecosystem doesn&#39;t really have a solid foundation for GUI development, despite being a good language for GUI development. Python is set up such that it&#39;s easier to write tools for other developers (who are likely to have a Python interpreter installed) than for end users (who are not), and developers tend to prefer CLI programs to GUI ones, so it would be reasonable to expect GUI tools in Python to be worse than they are in Rust, right?

Let&#39;s check in on that:

after `pip install wxPython` and no other fuckery:

import wx

app = wx.App()

frame = wx.Frame(None, title=&#34;wxPython Example&#34;)

panel = wx.Panel(frame)

text = wx.StaticText(panel, label=&#34;Hello World!&#34;)

frame.Show()

app.MainLoop()


This was easy to set up on Windows, correctly provides accessibility data to Windows Narrator, and was really concise to implement. So if I want a nice, powerful, elegant GUI library without losing my mind trying to set it up, I can have that, as long as I don&#39;t need other people to be able to conveniently run the program I make. How the goddamn fuck did we get here?

Unfortunately, I know the answer to that one. wxWidgets, the library wxPython is binding to, is a C++ library that is used primarily by inheriting from wxWidgets&#39;s classes. Rust bindings to C++ libraries are difficult to create in the best of scenarios, and inheritance is among the worst of scenarios, because Rust does not have any concepts to which C++ inheritance maps cleanly. The wxPython bindings are maintained by the wxWidgets team themselves, and Python is designed to be extended by C/C++ code. Rust&#39;s FFI with C is fine, but bindgen is really bad at handling C++ as used by wxWidgets, and manually authoring the thousands of bindings and then providing a safe abstraction over them would be extraordinarily not a good time.

So if wxWidgets isn&#39;t feasible to use from Rust, what about some other library? Well, the only reason wxPython is as easy to install as it is is that the maintainers made sure it comes with the fully-compiled wxWidgets binaries, especially on Windows where everything of that sort is harder to do by hand after the fact. Some Rust bindings to C libraries, like SDL2 or the aforementioned FLTK, will build the libraries they depend on automatically, encapsulating all that complexity and letting me just use them; most, like GTK, do not. I&#39;m not sure if there&#39;s a technical reason gtk-rs and the rest of these can&#39;t do that (or, for bonus points, just download a precompiled binary from the official releases and save a bunch of time), or if it&#39;s just that nobody&#39;s asked for it. Linux (and to some extent Mac I suppose) devs are accustomed to niceties like `pkg-config` and actual package managers that make it easy to install a C library; on Windows, we don&#39;t have that (unless we&#39;re working inside MSYS2 or what have you, but that&#39;s its own mess sometimes). So please: if you&#39;re writing Rust bindings to a C library, do your Windows users a favor, and give us a `bundled` feature.</content>
    </entry>
    
    <entry>
        <title>An Anti-License Manifesto</title>
        <link href="gemini://boringcactus.com/2021/09/29/anti-license-manifesto.gmi" rel="alternate" type="text/gemini" title="An Anti-License Manifesto" />
        <published>2021-09-29T00:00:00Z</published>
        <updated>2021-09-29T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/09/29/anti-license-manifesto.gmi</id>
        <content type="text/gemini">software licenses are unavoidably a legal tool. the legal system, in the US and approximately everywhere else, is not a machine that leads to justice. therefore, software licenses do not lead to justice.

we cannot software license our way to a better world. as such, we should and must software license our way to a *stranger* world. permissive licenses and copyleft licenses are both tools of the corporate status quo. we therefore reject all conventional software licenses, and instead champion the weird, the experimental, the decorative, the hostile, the absurd, the useless, the straight up unhinged.

some anti-licenses:


    </entry>
    
    <entry>
        <title>How to Develop a Deeply Unhealthy Relationship with Twitter</title>
        <link href="gemini://boringcactus.com/2021/05/25/how-to-develop-a-deeply-unhealthy-relationship-with-twitter.gmi" rel="alternate" type="text/gemini" title="How to Develop a Deeply Unhealthy Relationship with Twitter" />
        <published>2021-05-25T00:00:00Z</published>
        <updated>2021-05-25T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/05/25/how-to-develop-a-deeply-unhealthy-relationship-with-twitter.gmi</id>
        <content type="text/gemini">1. Follow some cool people on Twitter.
2. Spend time on Twitter.
3. See a cool person quote tweet some dipshit to be mad about them, or retweet a reply to some dipshit that dunks on or dismantles them, or say something controversial yet correct where you just know a bunch of the replies and quote tweets are going to be dipshits.
4. Think to yourself &#34;damn, it sucks that people on here are being dipshits. but hey, if I block all these people now, I&#39;ll never have them show up in my mentions later&#34;
5. Read through all the immediate (and, if your brain decides to, several levels deep) context, finding and blocking dipshits as they come up.
6. Repeat steps 3-5 several times. Importantly, never only do step 3, so that the subsequent quest to block all dipshits attaching themselves to the original tweet becomes a habit, and then a reflex, rather than a deliberate decision.
7. Get mad that you keep seeing dipshits doing dipshit things on Twitter. Consider leaving, but then say &#34;but the cool people are here! hell am I gonna do, convince them to stop arguing with dipshits and posting takes that dipshits will disagree with?&#34; Forget that you have any other options.
8. Wait for Discourse, when arguing and dipshittery are ubiquitous.
9. Reflexively seek out all the dipshits swarming around every time you see a new Discourse-related tweet.
10. Be miserable on Twitter because your experience is overwhelmingly dominated by seeing dipshits being wrong and obnoxious, because you built a habit of seeking those dipshits out in the first place.

In conclusion, as Fraxiom put it in food house&#39;s &#34;8 now&#34;,

=&gt; https://youtu.be/y-7qbdxIPWc?t=92 8 now

&gt; I need to delete Twitter &#39;cause it gives me fucking mental illness</content>
    </entry>
    
    <entry>
        <title>Non-FSF Copyleft Usage</title>
        <link href="gemini://boringcactus.com/2021/03/24/non-fsf-copyleft.gmi" rel="alternate" type="text/gemini" title="Non-FSF Copyleft Usage" />
        <published>2021-03-24T00:00:00Z</published>
        <updated>2021-03-24T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/03/24/non-fsf-copyleft.gmi</id>
        <content type="text/gemini">The Free Software Foundation has decided they&#39;d rather hang out with a sex pest than have an ounce of credibility, so fuck em. Let&#39;s look at the copyleft licenses they didn&#39;t write and see how they&#39;re used.

I&#39;ll be using the Blue Oak Council&#39;s list of copyleft licenses here, because I don&#39;t know of a good other way to find specifically copyleft licenses. I&#39;ll be searching GitHub for `filename:LICENSE &#34;&lt;some snippet from the license&gt;&#34;` or checking Wikipedia and then seeing if anything notable turns up.

=&gt; https://blueoakcouncil.org/copyleft Blue Oak Council&#39;s list of copyleft licenses

## Maximal Copyleft



## Network Copyleft



## Strong Copyleft



## Weak Copyleft



## Summary

for maximal copyleft (&#34;must publish source, even if only changing for internal use&#34;), Parity is probably the most promising future license.

=&gt; https://paritylicense.com/ its own list of users
=&gt; https://en.wikipedia.org/wiki/Reciprocal_Public_License basically it
=&gt; https://github.com/search?q=license%3Aeupl-1.2&amp;amp;type=Repositories well,
=&gt; https://en.wikipedia.org/wiki/Category:Software_using_the_European_Union_Public_Licence and
=&gt; https://github.com/search?q=filename%3ALICENSE+%22Non-Profit+Open+Software+License%22&amp;amp;type=code GitHub can&#39;t find any usage of it!
=&gt; https://en.wikipedia.org/wiki/Common_Development_and_Distribution_License#Adoption popular with enterprisey bullshit because it started at Sun
=&gt; https://en.wikipedia.org/wiki/Category:Software_using_the_CPL_license even less usage
=&gt; https://en.wikipedia.org/wiki/Eclipse_Public_License#Notable_projects a lot of other non-enterprise-bullshit (mostly Java) stuff apparently
=&gt; https://en.wikipedia.org/wiki/Mozilla_Public_License#Notable_users lots of users
=&gt; https://github.com/search?l=&amp;amp;o=desc&amp;amp;q=license%3Ams-rl&amp;amp;s=stars&amp;amp;type=Repositories not really widely used
=&gt; https://en.wikipedia.org/wiki/Sun_Public_License apparently it&#39;s the older, worse CDDL
=&gt; https://spdx.org/licenses/Parity-7.0.0.html Parity

for network copyleft (&#34;must offer source to users even across a network&#34;), the European Union Public License is already seeing some adoption.

=&gt; https://spdx.org/licenses/EUPL-1.2.html European Union Public License

for &#34;strong&#34; copyleft (&#34;must offer source to users even if just linking as a library&#34;), there are no good options because the GPL has been the only game in town for ages.

for &#34;weak&#34; copyleft (&#34;must offer source to users&#34;), the Mozilla Public License looks like it has a good chance of continuing to exist and be used by actual projeccts for a nice long while.

=&gt; https://spdx.org/licenses/MPL-2.0.html Mozilla Public License</content>
    </entry>
    
    <entry>
        <title>Cactus&#39;s Obvious, Intuitive Naming Scheme</title>
        <link href="gemini://boringcactus.com/2021/03/21/coins.gmi" rel="alternate" type="text/gemini" title="Cactus&#39;s Obvious, Intuitive Naming Scheme" />
        <published>2021-03-21T00:00:00Z</published>
        <updated>2021-03-21T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/03/21/coins.gmi</id>
        <content type="text/gemini">It&#39;s well-established that naming things is one of the two hardest problems in computer science (along with cache invalidation and off-by-one errors). So I solved naming. If you&#39;ve got a project that needs a name, here you go.

The ingredients are simple:

1. Your name.
2. The word &#34;obvious&#34;.
3. A good adjective.
4. The kind of thing you&#39;ve made.

The canonical example, from which this scheme was reverse-engineered, is Tom&#39;s Obvious, Minimal Language. People who haven&#39;t used TOML might think that &#34;obvious&#34; is functioning here to describe the language, but various attributes of TOML (non-ASCII keys requiring quotes, substantial distinctions between single- and double-quoted strings, array-of-tables syntax) indicate that &#34;obvious&#34; is merely part of the name template rather than an actual descriptor.

=&gt; https://en.wikipedia.org/wiki/TOML Tom&#39;s Obvious, Minimal Language
=&gt; https://toml.io/en/v1.0.0#keys non-ASCII keys requiring quotes
=&gt; https://toml.io/en/v1.0.0#string substantial distinctions between single- and double-quoted strings
=&gt; https://toml.io/en/v1.0.0#array-of-tables array-of-tables syntax

As an example, we can construct a name for this approach to assigning names to things. My name is *cactus* (for some value of &#34;name&#34;, and for that matter &#34;my&#34;), it would be good if this was *intuitive*, and this is a *naming scheme*. As such, the name this process assigns to itself is &#34;Cactus&#39;s Obvious, Intuitive Naming Scheme&#34;, which abbreviates nicely to &#34;COINS&#34;.

We can also construct COINS names for other things:



Despite the fact that Tom&#39;s Obvious, Minimal Language inspired this naming scheme, it actually suffers greatly from having used it. If I ever get my hands on a time machine, I will go back and tell Tom to call it &#34;My INI-Like Format&#34; instead.

=&gt; https://en.wikipedia.org/wiki/Linux_kernel Linus&#39;s Obvious, Free Kernel
=&gt; https://en.wikipedia.org/wiki/C%2B%2B Bjarne&#39;s Obvious, Powerful Language
=&gt; https://en.wikipedia.org/wiki/TeX Donald&#39;s Obvious, Expressive Language
=&gt; https://en.wikipedia.org/wiki/LaTeX Leslie&#39;s Obvious, Usable Macros
=&gt; https://en.wikipedia.org/wiki/Twitter Jack&#39;s Obvious, Nightmarish Hellsite
=&gt; https://en.wikipedia.org/wiki/Vim_(text_editor) Bram&#39;s Obvious, Terse Editor
=&gt; https://en.wikipedia.org/wiki/Toki_Pona Sonja&#39;s Obvious, Minimal Language
=&gt; https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Kurt&#39;s Obvious, Profound Theorem
=&gt; https://en.wikipedia.org/wiki/Primer_(film) [Miku]&#39;s Obvious, Engaging Film
=&gt; https://en.wikipedia.org/wiki/Wikipedia Jimmy&#39;s Obvious, Collective Encyclopedia
=&gt; https://en.wikipedia.org/wiki/Computer Alan&#39;s Obvious, Revolutionary Mistake</content>
    </entry>
    
    <entry>
        <title>Can We Please Move Past Git?</title>
        <link href="gemini://boringcactus.com/2021/02/22/can-we-please-move-past-git.gmi" rel="alternate" type="text/gemini" title="Can We Please Move Past Git?" />
        <published>2021-02-22T00:00:00Z</published>
        <updated>2021-02-22T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/02/22/can-we-please-move-past-git.gmi</id>
        <content type="text/gemini">&gt; Git is fundamentally a content-addressable filesystem with a VCS user interface written on top of it.
&gt; 
&gt; โ€” Pro Git ยง10.1
&gt; 
&gt; 
=&gt; https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain Pro Git ยง10.1
Most software development is not like the Linux kernel&#39;s development; as such, Git is not designed for most software development. Like Samuel Hayden tapping the forces of Hell itself to generate electricity, the foundations on which Git is built are overkill on the largest scale, and when the interface concealing all that complexity cracks, the nightmares which emerge can only be dealt with by the Doom Slayer that is Oh Shit Git. Of course, the far more common error handling method is start over from scratch.

=&gt; https://ohshitgit.com/ Oh Shit Git
=&gt; https://xkcd.com/1597/ start over from scratch

Git is bad. But are version control systems like operating systems in that they&#39;re all various kinds of bad, or is an actually good VCS possible? I don&#39;t know, but I can test some things and see what comes up.

## Mercurial

Mercurial is a distributed VCS that&#39;s around the same age as Git, and I&#39;ve seen it called the Betamax to Git&#39;s VHS, which my boomer friends tell me is an apt analogy, but I&#39;m too young for that to carry meaning. So let me see what all the fuss is about.

=&gt; https://www.mercurial-scm.org/ Mercurial

Well, I have some bad news. From the download page, under &#34;Requirements&#34;:

=&gt; https://www.mercurial-scm.org/downloads download page

&gt; Mercurial uses Python (version **2.7**). Most ready-to-run Mercurial distributions include Python or use the Python that comes with your operating system.
Emphasis theirs, but I&#39;d have added it myself otherwise. Python 2 has been dead for a very long time now, and saying you require Python 2 makes me stop caring faster than referring to &#34;GNU/Linux&#34;. If you&#39;ve updated it to Python 3, cool, don&#39;t say it uses Python 2. Saying it uses Python 2 makes me think you don&#39;t have your shit together, and in fairness, that makes two of us, but I&#39;m not asking people to use my version control system (so far, at least).

You can&#39;t be better than Git if you&#39;re that outdated. (Although you can totally be better than Git by developing a reputation for having a better UI than Git; word of mouth helps a lot.)

## Subversion

I am a fan of subverting things, and I have to respect wordplay. So let&#39;s take a look at Subversion (sorry, &#34;Apacheยฎ Subversionยฎ&#34;).

=&gt; https://subversion.apache.org/ Subversion

There are no official binaries at all, and the most-plausible-looking blessed unofficial binary for Windows is TortoiseSVN. I&#39;m looking through the manual, and I must say, the fact that branches and tags aren&#39;t actually part of the VCS, but instead conventions on top of it, isn&#39;t good. When I want to make a new branch, it&#39;s usually &#34;I want to try an experiment, and I want to make it easy to give up on this experiment.&#34; Also, I&#39;m not married to the idea of distributed VCSes, but I do tend to start a project well before I&#39;ve set up server-side infrastructure for it, and Subversion is not designed for that sort of thing at all. So I think I&#39;ll pass.

=&gt; https://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-repository.html#tsvn-repository-layout manual

You can&#39;t be better than Git if the server setup precedes the client setup when you&#39;re starting a new project. (Although you can totally be better than Git by having monotonically-ish increasing revision numbers.)

## Fossil

Fossil is kinda nifty: it handles not just code but also issue tracking, documentation authoring, and a bunch of the other things that services like GitHub staple on after the fact. Where Git was designed for the Linux kernel, which has a fuckton of contributors and needs to scale absurdly widely, Fossil was designed for SQLite, which has a very small number of contributors and does not solicit patches. My projects tend to only have one contributor, so this should in principle work fine for me.

=&gt; https://fossil-scm.org/ Fossil

However, a few things about Fossil fail to spark joy. The fact that repository metadata is stored as an independent file separate from the working directory, for example, is a design decision that doesn&#39;t merge well with my existing setup. If I were to move my website into Fossil, I would need somewhere to put `boringcactus.com.fossil` outside of `D:\Melody\Projects\boringcactus.com` where the working directory currently resides. The documentation suggests `~/Fossils` as a folder in which repository metadata can be stored, but that makes my directory structure more ugly. The rationale for doing it this way instead of having `.fossil` in the working directory like `.git` etc. is that multiple checkouts of the same repository are simpler when repository metadata is outside each of them. Presumably the SQLite developers do that sort of thing a lot, but I don&#39;t, and I don&#39;t know anyone who does, and I&#39;ve only ever done it once (back in the days when the only way to use GitHub Pages was to make a separate `gh-pages` branch). Cluttering up my filesystem just so you can support a weird edge case that I don&#39;t need isn&#39;t a great pitch.

=&gt; https://fossil-scm.org/home/doc/trunk/www/whyusefossil.wiki#definitions documentation

But sure, let&#39;s check this out. The docs have instructions for importing a Git repo to Fossil, so let&#39;s follow them:

=&gt; https://fossil-scm.org/home/doc/trunk/www/inout.wiki docs

PS D:\Melody\Projects\boringcactus.com&gt; git fast-export --all | fossil import --git D:\Melody\Projects\misc\boringcactus.com.fossil

]ad fast-import line: [S IN THE


Well, then. You can&#39;t be better than Git if your instructions for importing from Git don&#39;t actually work. (Although you can totally be better than Git if you can keep track of issues etc. alongside the code.)

## Darcs

Darcs is a distributed VCS that&#39;s a little different to Git etc. Git etc. have the *commit* as the fundamental unit on which all else is built, whereas Darcs has the *patch* as its fundamental unit. This means that a branch in Darcs refers to a set of patches, not a commit. As such, Darcs can be more flexible with its history than Git can: a Git commit depends on its temporal ancestor (&#34;parent&#34;), whereas a Darcs patch depends only on its logical ancestor (e.g. creating a file before adding text to it). This approach also improves the way that some types of merge are handled; I&#39;m not sure how often this sort of thing actually comes up, but the fact that it could is definitely suboptimal.

=&gt; http://darcs.net/ Darcs
=&gt; https://tahoe-lafs.org/~zooko/badmerge/simple.html some types of merge

So that&#39;s pretty cool; let&#39;s take a look for ourselves. Oh. Well, then. The download page is only served over plain HTTP - there&#39;s just nothing listening on that server over HTTPS - and the downloaded binaries are also served over plain HTTP. That&#39;s not a good idea. I&#39;ll pass, thanks.

=&gt; http://darcs.net/Binaries download page

You can&#39;t be better than Git while serving binaries over plain HTTP. (Although you can totally be better than Git by having nonlinear history and doing interesting things with patches.)

## Pijul

Pijul is (per the manual)

=&gt; https://pijul.org/ Pijul
=&gt; https://pijul.org/manual/why_pijul.html the manual

&gt; the first distributed version control system to be based on a sound mathematical theory of changes. It is inspired by Darcs, but aims at solving the soundness and performance issues of Darcs.
Inspired by Darcs but better, you say? You have my attention. Also of note is that the developers are also building their own GitHub clone, which they use to host pijul itself, which gives a really nice view of how a GitHub clone built on top of pijul would work, and also offers free hosting.

=&gt; https://nest.pijul.com/pijul/pijul host pijul itself

The manual gives installation instructions for a couple Linuces and OS X, but not Windows, and not Alpine Linux, which is the only WSL distro I have installed. However, someone involved in the project showed up in my mentions to say that it works on Windows, so we&#39;ll just follow the generic instructions and see what happens:

=&gt; https://pijul.org/manual/installing.html installation instructions
=&gt; https://twitter.com/nuempe/status/1359614145415548939 showed up in my mentions to say that it works on Windows

PS D:\Melody\Projects&gt; cargo install pijul --version &#34;~1.0.0-alpha&#34;

Updating crates.io index

Installing pijul v1.0.0-alpha.38

Downloaded &lt;a bunch of stuff&gt;

Compiling &lt;a bunch of stuff&gt;

error: linking with `link.exe` failed: exit code: 1181

|

= note: &#34;C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.27.29110\\bin\\HostX64\\x64\\link.exe&#34; &lt;lots of bullshit&gt;

= note: LINK : fatal error LNK1181: cannot open input file &#39;zstd.lib&#39;

error: aborting due to previous error


So it doesn&#39;t work for me on Windows. (There&#39;s a chance that instructions would help, but in the absence of those, I will simply give up.) Let&#39;s try it over on Linux:

UberPC-V3:~$ cargo install pijul --version &#34;~1.0.0-alpha&#34;

&lt;lots of output&gt;

error: linking with `cc` failed: exit code: 1

|

= note: &#34;cc&#34; &lt;a mountain of arguments&gt;

= note: /usr/lib/gcc/x86_64-alpine-linux-musl/9.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find -lzstd

/usr/lib/gcc/x86_64-alpine-linux-musl/9.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find -lxxhash

collect2: error: ld returned 1 exit status

error: aborting due to previous error

UberPC-V3:~$ sudo apk add zstd-dev xxhash-dev

UberPC-V3:~$ cargo install pijul --version &#34;~1.0.0-alpha&#34;

&lt;lots of output again because cargo install forgets dependencies immediately smdh&gt;

Installed package `pijul v1.0.0-alpha.38` (executable `pijul`)


Oh hey, would you look at that, it actually worked, and all I had to do was wait six months for each compile to finish (and make an educated guess about what packages to install). So for the sake of giving back, let&#39;s add those instructions to the manual, so nobody else has to bang their head against the wall like I&#39;d done the past few times I tried to get Pijul working for myself.

First, clone the repository for the manual:

=&gt; https://nest.pijul.com/pijul/manual repository for the manual

UberPC-V3:~$ pijul clone https://nest.pijul.com/pijul/manual

Segmentation fault


Oh my god. That&#39;s extremely funny. Oh fuck that&#39;s *hilarious* - I sent that to a friend and her reaction reminded me that Pijul is *written in Rust*. This VCS so profoundly doesn&#39;t work on my machine that it manages to segfault in a language that&#39;s supposed to make segfaults impossible. Presumably the segfault came from C code FFId with `unsafe` preconditions that weren&#39;t met, but still, that&#39;s just amazing.



=&gt; https://github.com/microsoft/WSL/issues/658 `mmap` on WSL is just broken
=&gt; https://nest.pijul.com/pijul/pijul/discussions/140#57f9eead-915f-45c1-a169-a5bd417d9213 the state of the art in getting Pijul to work on Windows
=&gt; https://github.com/boringcactus/pijul-windows-builds set up automated Windows builds using GitHub Actions

PS D:\Melody\Projects\misc&gt; pijul clone https://nest.pijul.com/pijul/manual pijul-manual

โœ“ Updating remote changelist

โœ“ Applying changes 47/47

โœ“ Downloading changes 47/47

โœ“ Outputting repository


Hey, that actually works! We can throw in some text to the installation page (and more text to the getting started page) and then use `pijul record` to commit our changes. That pulls up *Notepad* as the default text editor, which fails to spark joy, but that&#39;s a papercut that&#39;s entirely understandable for alpha software not primarily developed on this OS. Instead of having &#34;issues&#34; and &#34;pull requests&#34; as two disjoint things, the Pijul Nest lets you add changes to any discussion, which I very much like. Once we&#39;ve recorded our change and made a discussion on the repository, we can `pijul push boringcactus@nest.pijul.com:pijul/manual --to-channel :34` and it&#39;ll attach the change we just made to discussion #34. (It appears to be having trouble finding my SSH keys or persisting known SSH hosts, which means I have to re-accept the fingerprint and re-enter my Nest password every time, but that&#39;s not the end of the world.)

=&gt; https://nest.pijul.com/pijul/manual/discussions/34 discussion #34

So yeah, Pijul definitely still isn&#39;t production-ready, but it shows some real promise. That said, you can&#39;t be better than Git if you aren&#39;t production-ready. (Although you can totally be better than Git by having your own officially-blessed GitHub clone sorted out already.) (And maybe, with time, you can be eventually better than Git.)

## what next?

None of the existing VCSes that I looked at were unreservedly better than Git, but they all had aspects that would help beat Git.

A tool which is actually better than Git should start by being no worse than Git:



Then, to pick and choose the best parts of other VCSes, it should



And just for kicks, a couple of extra features that nobody has but everybody should:



I probably don&#39;t have the skills, and I certainly don&#39;t have the free time, to build an Actually Good VCS myself. But if you want to, here&#39;s what you&#39;re aiming for. Good luck. If you can pull it off, you&#39;ll be a hero. And if you can&#39;t, you&#39;ll be in good company.</content>
    </entry>
    
    <entry>
        <title>Every President Sucked</title>
        <link href="gemini://boringcactus.com/2021/02/15/every-president-sucked.gmi" rel="alternate" type="text/gemini" title="Every President Sucked" />
        <published>2021-02-15T00:00:00Z</published>
        <updated>2021-02-15T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/02/15/every-president-sucked.gmi</id>
        <content type="text/gemini">(Not exhaustive; based on cursory readings of parts of Wikipedia articles.)

=&gt; https://en.wikipedia.org/wiki/Category:Presidencies_of_the_United_States Wikipedia articles

Washington thought it was morally acceptable to own human beings as property.

Adams signed the Alien and Sedition Acts, which were xenophobic and authoritarian.

=&gt; https://en.wikipedia.org/wiki/Alien_and_Sedition_Acts Alien and Sedition Acts

Jefferson thought it was morally acceptable to own human beings as property.

Madison thought it was morally acceptable to own human beings as property.

Monroe thought it was morally acceptable to own human beings as property.

Quincy Adams thought partisanship could be solved by appointing his enemies to office (but aside from that he seems pretty decent).

Jackson thought it was morally acceptable to own human beings as property, and was extremely enthusiastic about Native American genocide.

Van Buren thought it was morally acceptable to own human beings as property.

Harrison thought it was morally acceptable to own human beings as property.

Tyler thought it was morally acceptable to own human beings as property.

Polk thought it was morally acceptable to own human beings as property.

Taylor thought it was morally acceptable to own human beings as property.

Fillmore signed the Compromise of 1850, which had numerous elements which propagated slavery, and wound up attached to the Know-Nothing Party.

Pierce supported the illegitimate pro-slavery government of the Kansas Territory.

Buchanan encouraged the Supreme Court&#39;s decision in *Dred Scott v Sandford* that the Constitution does not apply to Black people.

Lincoln



Johnson



Grant



Hayes



Garfield



Arthur



Cleveland



Harrison



McKinley



Roosevelt



Taft



Wilson



Harding



Coolidge



Hoover



Roosevelt



Truman



Eisenhower



Kennedy



Johnson



Nixon



Ford



Carter



Reagan



Bush



Clinton



Bush



Obama



Trump



Biden, for the like month he&#39;s been in there so far



happy (slightly late) presidents&#39; day!</content>
    </entry>
    
    <entry>
        <title>Not &#34;Any Purpose&#34;</title>
        <link href="gemini://boringcactus.com/2021/02/11/not-any-purpose.gmi" rel="alternate" type="text/gemini" title="Not &#34;Any Purpose&#34;" />
        <published>2021-02-11T00:00:00Z</published>
        <updated>2021-02-11T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2021/02/11/not-any-purpose.gmi</id>
        <content type="text/gemini">&gt; A program is free software if the program&#39;s users have the four essential freedoms:
&gt; * The freedom to run the program as you wish, **for any purpose** (freedom 0).
&gt; 
&gt; 
&gt; โ€“ the Free Software Definition, emphasis mine
&gt; 
&gt; 
=&gt; https://www.gnu.org/philosophy/free-sw.html the Free Software Definition
&gt; Goddamn, what the fuck?
What the fuck, what the fuck?
Goddamn, what the fuck?
What the fuck?
Goddamn, what the fuck
What the fuck, what theโ€”
What the fuck? What the fuck? What the fuck?
&gt; 
&gt; โ€“ 745 sticky (Black Dresses Remix)
&gt; 
&gt; 
=&gt; https://youtu.be/O1kTTOdHgC4 745 sticky (Black Dresses Remix)
My previous post on post-open source appeared to resonate with a lot of people, but unsurprisingly, not much has changed since then. Recently, the company Elastic, who makes the database Elasticsearch, changed the license on Elasticsearch from Apache to the Server-Side Public License, which (to oversimplify the analysis of the excellent Kyle E. Mitchell) is AGPL but even more so. And unsurprisingly, as happened with Redis and MongoDB before it, everyone got mad. When I wrote this snippet of my post-open source post, I was referring to Redis, but it&#39;s the exact same playbook that&#39;s happening again now:

=&gt; /2020/08/13/post-open-source.html post-open source
=&gt; https://writing.kemitchell.com/2019/06/13/SSPL-Not-Commons-Clause.html the analysis of the excellent Kyle E. Mitchell

&gt; you made a cool database server that&#39;s under an open source license?
&gt; amazon&#39;s selling it as a service now, and they&#39;re not paying you a fuckin dime.
&gt; you want to change your license to stop them from doing that?
&gt; now the open source people are yelling at you, because when they say they&#39;re apolitical they mean they support the status quo.
&gt; and the free software people are also yelling at you, because you didn&#39;t do it their way with their license, you did it a different way with a different license, and that goes against amazon&#39;s freedom to screw you over.
So we&#39;re back here again. Open source ideology leads to intra-corporate exploitation, the exploitees want to get out from under the exploiters, everyone gets mad that the status quo has been upset, and Amazon forks the last permissively-licensed version of the software and laughs all the way to the bank. The only way for Elastic to win would&#39;ve been to have licensed Elasticsearch under something restrictive like the SSPL from day one.

But since they didn&#39;t, we get shit like this:

&gt; Capitalism concerns itself with making monopolies โ€” FOSS instead concerns itself with the socialized creation of software wealth.
&gt; 
&gt; โ€“ some jackass who loves being wrong on the internet
FOSS is not socialist. The free software movement is right-libertarian / &#34;anarcho&#34;-capitalist, and the open source movement is neoliberal; neither of these is even particularly close to socialism. If you want a software ideology that is actually opposed to capitalism, instead of sucking up to capital, you might be looking for the Anti-Capitalist Software License. It&#39;s good, it links to my previous writing on post-open source (and the Fuck Around and Find Out License), and the FSF hate it:

=&gt; https://lipu.dgold.eu/original-sin.html right-libertarian / &#34;anarcho&#34;-capitalist
=&gt; https://anticapitalist.software/ the Anti-Capitalist Software License

&gt; The Anti-Capitalist Software License is a nonfree license because it extends the four freedoms only to some kinds of organizations, not to all.
&gt; Such a restriction in a software license, in the name of any cause whatsoever, imposes too much power over users.
&gt; Please don&#39;t use this license, and we urge you to avoid any software that has been released under it.
What&#39;s not to love! As an author of some software released under the Anti-Capitalist Software License, I also urge people who take the FSF seriously to avoid my software which has been released under the ACSL. And my software released under any other license. And just me in general.

Unsurprisingly, Richard M. Stallman (the &#34;M&#34; stands for &#34;Making female students extremely uncomfortable for 30 years&#34;) has done some handwringing about the vital importance of Freedom 0&#39;s &#34;for any purpose&#34; that we can make fun of. Essentially, his points are

=&gt; https://selamjie.medium.com/remove-richard-stallman-appendix-a-a7e41e784f88 for 30 years



First, lmao, &#34;don&#39;t bother doing this because enforcement is impossible&#34; applies equally to copyleft and the GPL in general. Second, if we set aside masturbatory trolling bullshit like Bruce Perens&#39;s &#34;Vaccine License&#34;, nobody makes licenses that are that narrowly tailored to one specific thing - and even the Vaccine License is still a world away from a hypothetical Country Music Only License or Vegan Software License or what have you. This is a textbook slippery slope argument, and if you think &#34;you can&#39;t use this software to violate human rights&#34; is anywhere near &#34;you can&#39;t use this software to listen to BTS&#34; then fuck all of the way off. (Although, one time I mentioned on IRC that I was throwing a simple script together in Python, and someone said &#34;Hopefully it&#39;s Python 2, but if not I&#39;ll just rewrite it in Python 2&#34;, so I threw together the Why The Fuck Would You Even Do That Holy Shit Public License to prohibit exactly that.) Third, god *damn* libertarians hate when anyone tells them that some actions are bad. If I was going to buy a pen, and I saw that it had a &#34;hey, don&#39;t use this pen to violate human rights&#34; sticker on it, I would go &#34;fair enough I guess&#34; and just buy the damn pen.

=&gt; https://git.sr.ht/~boringcactus/gotbruh/tree/main/item/LICENSE.md Why The Fuck Would You Even Do That Holy Shit Public License

The libertarian position here is that toolmakers should have no say in how those tools are used, and the only way they should control what can be done with their tools is by directly adding or removing capabilities from the tools. Unsurprisingly, the libertarian position here is bullshit. A tool is not a morally neutral thing, and toolsmithing is not an act which must by necessity end when the tool leaves the smith&#39;s hands.

Let me now list some of the ethical principles from the ACM Code of Ethics:

=&gt; https://ethics.acm.org ACM Code of Ethics

&gt; A computing professional should...
&gt; 1. Contribute to society and to human well-being, acknowledging that all people are stakeholders in computing.
&gt; 2. Avoid harm.
&gt; 3. Be honest and trustworthy.
&gt; 4. Be fair and take action not to discriminate.
&gt; 5. Respect the work required to produce new ideas, inventions, creative works, and computing artifacts.
Points 1 and 2 are broadly aligned with the idea that software should be used for good, and point 5 suggests that authors should be compensated for their work. As such, ethical restrictions in software licenses are perfectly compatible with this code of ethics, as are restrictions on commercial use. It is possible to go even further than this, and I think I will, because I&#39;m in the mood to start some fights:

In my view, **it conflicts with the ACM Code of Ethics to release software under a license which permits ethical misconduct and economic exploitation.**</content>
    </entry>
    
    <entry>
        <title>Crowbar: Turns out, language development is hard</title>
        <link href="gemini://boringcactus.com/2020/10/19/crowbar-3-this-is-tough.gmi" rel="alternate" type="text/gemini" title="Crowbar: Turns out, language development is hard" />
        <published>2020-10-19T00:00:00Z</published>
        <updated>2020-10-19T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/10/19/crowbar-3-this-is-tough.gmi</id>
        <content type="text/gemini">(Previously in Crowbar: Defining a good C replacement, Simplifying C&#39;s type names)

=&gt; /2020/09/28/crowbar-1-defining-a-c-replacement.gmi Defining a good C replacement
=&gt; /2020/10/13/crowbar-2-simplifying-c-type-names.gmi Simplifying C&#39;s type names

Originally, I hadn&#39;t decided whether Crowbar should be designed with an eye towards compiling to C or with an eye towards compiling directly. Compiling to C massively cuts down the scope of Crowbar as a project, but compiling directly gives me more comprehensive control over what all happens.

I figured I wouldn&#39;t need comprehensive control over everything, so I chose compiling to C, and then almost immediately ran into a pile of issues that compiling to C brings with it.

## libc is part of the problem

One of the goals I had for Crowbar was memory safety - most of the footguns in C are of the dubious-memory-operations variety - but it turns out you can&#39;t just duct tape memory safety to an existing language and call it a day. Among the most easily-exploited class of dubious memory operations is the buffer overflow, and the most straightforward fix is bounds checking.

However, most of the C standard library doesn&#39;t perform bounds checking, because the C standard library was designed in the 1600s when every CPU cycle took six months and compiler optimizations hadn&#39;t been invented yet.

The C11 specification actually defines bounds-checking-performing alternatives to some of the standard library APIs, but it&#39;s optional (filed away in Annex K) and fuckin nobody can be bothered to implement it. Some of the spec authors wrote a whole investigation of why nobody uses Annex K, and it all boils down to &#34;error handling in C is broken-by-default&#34; - which is the other big issue Crowbar will have to solve, and I haven&#39;t even begun to think about how to do that.

=&gt; http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1969.htm wrote a whole investigation

But. We live in a world where C already exists in all its mixed glory, and in that world, nothing supports Annex K. There exist some tacked-on implementations, but the most complete-looking one is licensed under the regular GPL, not even the LGPL, which would be a problem for a lot of software. So if I want Crowbar to be designed to compile into C, I need to either reimplement Annex K my damn self or design some equivalent APIs and implement those.

=&gt; https://github.com/sbaresearch/slibc most complete-looking one

In either case, now Crowbar has a runtime library on top of libc, and nobody&#39;s going to already have it so it&#39;ll have to figure out how to ensure that its runtime library is available wherever needed. And that&#39;s a pain in the ass.

## you can&#39;t win

Okay, so if compiling to C would still require a runtime library, and ensuring that the runtime library still worked in mixed-Crowbar-and-C and fully-Crowbar projects without requiring system-wide installation or anything would be a nuisance, why not just simplify some things and skip over C?

Well, for an ordinary language, that&#39;d work rather well, which is why fucking nothing uses C as a compile target. But one of the nonnegotiable goals of Crowbar is to have low- or no-effort C interoperability. As such, you need to be able to include regular C headers. But loading and parsing regular C headers means the Crowbar compiler needs to be able to understand all of C, and either implement or shell out to a C preprocessor that can use conventional command line arguments to define include directories. And if the compiler has to encompass all of the complexity of C, then Crowbar just went from a subset of C to a superset of C. I do not want to write a C compiler. If Crowbar is supposed to be simpler than C, then writing a Crowbar compiler should be simpler than writing a C compiler, not more complicated.

So compiling to C creates problems, and compiling not-to-C creates problems. This sucks. Send help.

No, seriously, if you have advice, even if it&#39;s just &#34;well I&#39;d probably do this because it seems more intuitive to me&#34;, send it in.

=&gt; mailto:~boringcactus/crowbar-lang-devel@lists.sr.ht send it in</content>
    </entry>
    
    <entry>
        <title>Crowbar: Simplifying C&#39;s type names</title>
        <link href="gemini://boringcactus.com/2020/10/13/crowbar-2-simplifying-c-type-names.gmi" rel="alternate" type="text/gemini" title="Crowbar: Simplifying C&#39;s type names" />
        <published>2020-10-13T00:00:00Z</published>
        <updated>2020-10-13T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/10/13/crowbar-2-simplifying-c-type-names.gmi</id>
        <content type="text/gemini">(Previously in Crowbar: Defining a good C replacement.)

=&gt; /2020/09/28/crowbar-1-defining-a-c-replacement.gmi Defining a good C replacement

I&#39;ve been working intermittently on drawing up a specification for Crowbar, a C replacement aiming to be both simpler and safer. I&#39;m still nowhere near done, but I&#39;m proud of the concept I&#39;ve reached for type names, and I want to explain it in depth here.

=&gt; https://sr.ht/~boringcactus/crowbar-lang/ Crowbar

## The Problem

C declarations are known to be a nuisance in nontrivial cases. There&#39;s a mid-90s &#34;clockwise/spiral rule&#34; that I&#39;ve seen referenced a few times, but three steps are two too many for reading a declaration. Function pointers in particular have a reputation for being legendarily impossible to visually parse. I don&#39;t know what `void (*signal(int, void (*fp)(int)))(int);` is declaring, but it&#39;s the most complicated example listed on the spiral rule page, and I&#39;m pretty sure just pasting it into this blog post has already summoned some eldritch abomination.

=&gt; http://c-faq.com/decl/spiral.anderson.html &#34;clockwise/spiral rule&#34;

## A Solution

So we have this syntax which is well-established, and for simple cases well-understood, but in complex cases quickly becomes unmanageable. Ideally, we can preserve the syntax as is for simple cases, while cutting down on that complexity in the more difficult cases.

As of right now, the Crowbar specification gives the syntax as a parsing expression grammar, which I&#39;ll give an excerpt from here:

=&gt; https://en.wikipedia.org/wiki/Parsing_expression_grammar parsing expression grammar

Type โ† &#39;const&#39; BasicType /

BasicType &#39;*&#39; /

BasicType &#39;[&#39; Expression &#39;]&#39; /

BasicType &#39;function&#39; &#39;(&#39; (BasicType &#39;,&#39;)* &#39;)&#39; /

BasicType

BasicType โ† &#39;void&#39; /

&#39;int&#39; /

&#39;float&#39; /

&#39;bool&#39; /

&#39;(&#39; Type &#39;)&#39;


So essentially, basic types can be used as-is, and pointers-to or arrays-of those basic types require no additional syntax. But if you want to do something nontrivial, you&#39;ll need to parenthesize the inner type.

I didn&#39;t think this would wind up being quite as elegant as it turned out to be, but it handles a lot of edge cases gracefully and intuitively.

## In Motion

I&#39;ll just lift some examples straight from the Spiral Rule page.

char *str[10];


Evidently this means &#34;str is an array 10 of pointers to char&#34;. How would we express that in Crowbar (as it hypothetically exists so far)?

(char *)[10] str;


Now that&#39;s more like it. We can look at it and tell right away that the array is the outermost piece and so `str` is an array. In C, I&#39;m not sure how we&#39;d express a pointer-to-arrays-of-10-chars, but in Crowbar it&#39;s also straightforward:

(char[10])* str;


Now let&#39;s kick it up a notch, and look at those legendarily-awful aspects of C&#39;s syntax, function pointers. The Spiral Rule offers up

char *(*fp)( int, float *);


which supposedly means &#34;fp is a pointer to a function passing an int and a pointer to float returning a pointer to a char&#34;. That&#39;s not extremely dreadful, merely somewhat off-putting, but let&#39;s see how it looks in Crowbar.

((char *) function(int, (float *),)* fp;


I hate that way less. It&#39;s less terse, certainly, but it&#39;s more explicit. The variable name is where it belongs, instead of nestled three layers deep inside the declaration. The fact that the `char *` and `float *` need to be parenthesized here is probably unnecessary, but you could imagine situations where those parentheses would be vital. And introducing `function` as a keyword means you can look at it and know instantly that it&#39;s a pointer-to-a-function, instead of going &#34;wait what&#39;s that syntax where there are more parentheses than you&#39;d think you&#39;d want? oh yeah it&#39;s function pointers.&#34;

So let&#39;s take a look at the worst thing C can offer. The Spiral Rule calls it the &#34;ultimate&#34;, and I don&#39;t think that&#39;s a misnomer:

void (*signal(int, void (*fp)(int)))(int);


That fractal mess is &#34;a function passing an int and a pointer to a function passing an int returning nothing (void) returning a pointer to a function passing an int returning nothing (void)&#34;. My eyes glaze over reading that description even more than they do reading the original C. Can we make this not look awful?

((void function(int,))*) signal(int, ((void function(int,))*),);


This is beautiful. (Well, no it isn&#39;t, but it&#39;s way less ugly than the original.) It&#39;s clear which things are functions and which things are not, the nesting is all transparent and visible, and *you can tell what the return type is without a PhD in Deciphering C Declarations*. Plus, importantly, it&#39;s clear that this is a function prototype and not a function pointer declaration, which is a massive improvement over the original.

## Bonus Round

Just for kicks, another less-awful-but-still-not-great thing about C type syntax is the pointer-to-constant vs constant-pointer dichotomy.

const int * points_to_const; // can never do *points_to_const = 8;

int * const const_pointer; // can never do const_pointer = &amp;x;


You have to remember which is which. And why memorize when you can read?

(const int)* points_to_const;

const (int *) const_pointer;


Much, much better.

## Looking Forwards

This syntax is simpler than C&#39;s without losing any expressive power. That makes me very happy.

If you&#39;re curious what&#39;s coming next for Crowbar, watch this space for when I eventually write another Crowbar-related blog post, or join the mailing list. (But don&#39;t get your hopes up; Crowbar is a project I&#39;m working on in my spare time on a when-I-feel-like-it basis.)

=&gt; https://sr.ht/~boringcactus/crowbar-lang/lists join the mailing list</content>
    </entry>
    
    <entry>
        <title>Crowbar: Defining a good C replacement</title>
        <link href="gemini://boringcactus.com/2020/09/28/crowbar-1-defining-a-c-replacement.gmi" rel="alternate" type="text/gemini" title="Crowbar: Defining a good C replacement" />
        <published>2020-09-28T00:00:00Z</published>
        <updated>2020-09-28T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/09/28/crowbar-1-defining-a-c-replacement.gmi</id>
        <content type="text/gemini">I like Rust a lot. That said, the always-opinionated, ~~often-correct~~ occasionally-correct Drew DeVault raises some good points in his blog post Rust is not a good C replacement. He names some attributes that C has and Rust lacks which he thinks are required in a good C replacement. So what can we say are some features of a hypothetical good C replacement?

=&gt; https://web.archive.org/web/20201020022457if_/https://cmpwn.com/@sir/104946211496582150 occasionally-correct
=&gt; https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-replacement.html Rust is not a good C replacement

## Portability

Per Drew, &#34;C is the most portable programming language.&#34; Rust, via LLVM, supports a lot of target architectures, but C remains ubiquitous. Anything less portable than LLVM is, therefore, incapable of being a good C replacement, and to be a truly great C replacement a language should be as portable as C.

The lifehack that makes sense here to me, difficult as it might be to implement, is to have the compiler for your C replacement be able to emit C as a compile target. It might not be the same C that a competent C programmer would write by hand, the result might not initially be as fast as the compiler would be if it were running the full toolchain directly, but in principle a self-hosting compiler that could also emit C would be extremely easy to bootstrap on any new architecture.

## Specification

The C specification, while large and unwieldy and expensive, does at least exist. This creates a clear definition for how a compiler *should* work, whereas the behavior of Rust-the-language is defined to be whatever rustc-the-compiler does. Rust&#39;s lack of a specification allows it to move fast and not break things, which is cool for Rust (shout out to async/await), but there&#39;s virtue in moving slow, too. I don&#39;t think Rust itself necessarily needs a specification, but I do think that a good C replacement should be formally specified, and move at the speed of bureaucracy to ensure only the best changes get actually made.

The C99 specification is 538 pages and costs more money than I&#39;d want to spend to get a legitimate copy of. A good C replacement should probably have a specification that fits in half the size - hell, call it 200 pages for a nice round number - and that specification should be released free of charge and under a permissive license.

## Diverse Implementations

I think this is a result of having a specification more than a cause of its own, but Drew points out that the various C compilers keep each other and the spec honest and well-tested. I think in principle a good C replacement could start out with only one compiler, but it would need to be fairly straightforward to write a new compiler for - which is certainly not the case for C. It might be helpful to have several different reference compilers, themselves written in different languages and for different contexts. (It would be a fun party trick to have a WebAssembly-based compiler that people could poke around at in their browsers.)

## Zero-Effort FFI

Drew calls this point &#34;a consistent and stable ABI,&#34; but I think the context in which that matters is being able to use other libraries from your code, and being able to compile your code as a library that other people can use in turn, without needing to jump through extra hoops like writing glue layers manually or recompiling everything from source. The easy (&#34;easy&#34;) solution is to just build libraries that C code can just use directly, and use the same ABI that C libraries in turn would export, so that there&#39;s no such thing as FFI because no functions exist which are foreign.

## Composable Build Tools

I don&#39;t give a shit about this one, but in fairness to Drew I could understand why he would. If I have a project that starts out being entirely C, and I&#39;m like &#34;oh lemme try out Rust for some of this&#34;, I don&#39;t want to start by swapping out my build system and everything, I want to keep my Makefiles and make like one change to them and call it a day. (Makefiles also suck, but they don&#39;t dual-wielding-footguns suck, so it doesn&#39;t matter.) But cargo is very decidedly not built to be used that way, and rustc is very decidedly not built to be called from things that aren&#39;t cargo. So a good C replacement should have a compiler that works the way C compilers tend to, i.e. you pass in a file or several and a bunch of arguments and it builds the file you need it to build.

## *fart noises*

&gt; Concurrency is generally a bad thing.
## Safety Is Just Another Feature

In light of all the problems he has with Rust, Drew declares

&gt; Yes, Rust is more safe. I donโ€™t really care. In light of all of these problems, Iโ€™ll take my segfaults and buffer overflows.
And like. On one hand, those are all extremely avoidable problems, and the way to avoid them is to start using a memory safe language. And they do cause *rather more substantial problems* than not having a specification.

But also. On the other hand, I don&#39;t eat my vegetables, I only comment my code half the time, I use Windows and I don&#39;t hate it. We&#39;re all of us creatures of habit, we do what&#39;s familiar, and we&#39;ll keep doing things in dangerous ways if switching to safe ways would have too high a learning curve. So as much of a bruh moment as this is in the abstract, I&#39;ve been there, I&#39;ll likely be there again, and I can&#39;t really judge all that harshly.

## Crowbar

The conclusion to Drew&#39;s blog post opens like this:

&gt; C is far from the perfect language - it has many flaws.
&gt; However, its replacement will be simpler - not more complex.
And those sentences have been rattling around in my brain for a while now. Because he&#39;s right about that much. If we want C programmers to stop using C, we have to give them something that&#39;s only as different from C as it actually needs to be. We need C: The Good Parts Plus Barely Enough To Add Memory Safety And Good Error Handling.

And as someone with extraordinarily poor time management skills, I think I kinda want to work on building that.

Naming things is widely known to be the hardest problem in computer science, but since the goal is to pry C diehards away from C, I think a good name for this project would be

Crowbar: the good parts of C, with a little bit extra.

=&gt; https://sr.ht/~boringcactus/crowbar-lang/ Crowbar: the good parts of C, with a little bit extra

I don&#39;t know if this will get any of my time and attention. It really shouldn&#39;t - I&#39;ve got so much other shit I should be doing instead - but also this is fun so who knows. Join the mailing lists if you&#39;re interested in participating.</content>
    </entry>
    
    <entry>
        <title>A Survey of Rust Embeddable Scripting Languages</title>
        <link href="gemini://boringcactus.com/2020/09/16/survey-of-rust-embeddable-scripting-languages.gmi" rel="alternate" type="text/gemini" title="A Survey of Rust Embeddable Scripting Languages" />
        <published>2020-09-16T00:00:00Z</published>
        <updated>2020-09-16T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/09/16/survey-of-rust-embeddable-scripting-languages.gmi</id>
        <content type="text/gemini">Rust is a nice programming language. But it&#39;s only ever compiled, so if you want behavior that can be edited at runtime, you have to either anticipate and implement every single knob you think might need adjusting or let users write code in some other language that you then call from Rust. Lua is (to my knowledge) the most common language for this use case in general, but I don&#39;t really like using Lua all that much, so let&#39;s take a more comprehensive look at some of our options.

We&#39;ll need a use case in mind before we can really get going here, so here&#39;s our motivating example. We&#39;ve got an event enum that our host can send to our script:

pub enum Event {

Number(i64),

Word(String),

}


And we&#39;ve got some operation that makes sense in the context of our host, which the script can call:

pub fn print_fancy(text: &amp;str) {

println!(&#34;โœจ{}โœจ&#34;, text);

}


It&#39;d be nice to reuse most of this structure, so let&#39;s define a consistent interface:

pub trait ScriptHost {

type ScriptContext;

type Error;

fn new_context() -&gt; Result&lt;Self::ScriptContext, Self::Error&gt;;

fn handle_event(context: &amp;mut Self::ScriptContext, event: Event) -&gt; Result&lt;(), Self::Error&gt;;

}


And it&#39;d be nice if almost all the logic was generic over `ScriptHost` so our individual examples could just call it as is:

pub fn run_main&lt;SH: ScriptHost&gt;() -&gt; Result&lt;(), SH::Error&gt; {

let stdin = stdin();

let mut context = SH::new_context()?;

loop {

println!(&#34;Type a number or some text to fire an event, or nothing to exit.&#34;);

let mut input = String::new();

stdin.read_line(&amp;mut input).unwrap();

let input = input.trim();

if input.is_empty() {

break;

}

let event = match input.parse() {

Ok(i) =&gt; Event::Number(i),

Err(_) =&gt; Event::Text(input.to_string())

};

SH::handle_event(&amp;mut context, event)?;

}

Ok(())

}


And specifically, where possible, we&#39;d like to have the script simply export a function we can call and pass in an event. That way, the script can maintain its own long-running state if it wants to.

So that&#39;s the context in which we are working. If your context is not like this, then some libraries that don&#39;t work here might work for you. (If you&#39;re curious, you can check out the full source code for these examples here.)

=&gt; https://git.sr.ht/~boringcactus/rust-scripting-languages here

Our contenders will be pulled from the top crates.io results for the tags &#34;script&#34;, &#34;scripting&#34;, and &#34;lua&#34;, sorted by Recent Downloads as of when I&#39;m starting this blog post and with a cutoff of whenever I feel like stopping. Specifically (these are links to the sections where I discuss them, if you&#39;ve got one in mind you&#39;re curious about) we have



That seems like a lot. Let&#39;s hope I don&#39;t regret this.

## mlua and rlua

mlua and rlua are both Lua bindings - mlua is apparently a fork of rlua - so we&#39;ll look at them together.

=&gt; https://crates.io/crates/mlua mlua
=&gt; https://crates.io/crates/rlua rlua

Reading through the mlua README, we see something unfortunate:

&gt; On Windows `vendored` mode is not supported since you need to link to a Lua dll.
&gt; Easiest way is to use either MinGW64 (as part of MSYS2 package) with `pkg-config` or MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
I would really rather not have to go to all that work and expect any potential contributors to do the same. So there goes that one.

Since mlua is an rlua fork, I was guessing &#34;not a pain in the ass on Windows&#34; isn&#39;t a feature rlua has either, but apparently rlua works just fine on Windows with zero extra pain. So that&#39;s interesting.

Oh hey, less than a hundred lines of glue and we&#39;re fuckin set! The rlua crate has its own script host, so our choice of ScriptContext is really simple:

struct LuaScriptHost;

impl ScriptHost for LuaScriptHost {

type ScriptContext = Lua;

type Error = Error;


When we make a new context, we want to create a binding to our `print_fancy` method. Aside from having to use a `String` instead of `&amp;str` it&#39;s pretty straightforward:

fn new_context() -&gt; Result&lt;Lua, Error&gt; {

let lua = Lua::new();

lua.context::&lt;_, Result&lt;(), Error&gt;&gt;(|lua_ctx| {

let print_fancy = lua_ctx.create_function(|_, text: String| {

print_fancy(&amp;text);

Ok(())

})?;

lua_ctx.globals()

.set(&#34;print_fancy&#34;, print_fancy)?;

lua_ctx

.load(&amp;(read_to_string(&#34;scripts/rlua-sample.lua&#34;).unwrap()))

.set_name(&#34;rlua-sample.lua&#34;)?

.exec()?;

Ok(())

})?;

Ok(lua)

}


(We&#39;re hard coding the path to the script here, because autodiscovering those is beyond the scope of this blog post, but you could totally iterate a directory and either run all those scripts in the same context or give them each their own context.) We&#39;re going to need a little more glue on the `Event` enum, though since Lua doesn&#39;t exactly have anything like enums natively. (And that glue is more complicated if we&#39;re using Cargo examples, because they aren&#39;t technically the same crate, so Rust&#39;s orphan rules kick in.) The easiest way to do that, I think, is to just add `get_number` and `get_text` methods that will return `nil` if the `Event` wasn&#39;t of that type:

struct EventForLua(Event);

impl UserData for EventForLua {

fn add_methods&lt;&#39;lua, M: UserDataMethods&lt;&#39;lua, Self&gt;&gt;(methods: &amp;mut M) {

methods.add_method(&#34;get_number&#34;, |ctx, this, _: ()| {

match this.0 {

Event::Number(number) =&gt; Ok(number.to_lua(ctx)?),

_ =&gt; Ok(Nil)

}

});

methods.add_method(&#34;get_text&#34;, |ctx, this, _: ()| {

match &amp;this.0 {

Event::Text(text) =&gt; Ok(text.clone().to_lua(ctx)?),

_ =&gt; Ok(Nil)

}

});

}

}


Armed with all these tools, we can now actually write our Lua script:

function handle_event(event)

local number = event:get_number()

if number ~= nil then

print(&#34;number!&#34;, number)

end

local text = event:get_text()

if text ~= nil then

print(&#34;text!&#34;, text)

end

print_fancy(&#34;got an event!&#34;)

end


And now that we have our function, since we already ran this script, to handle an event all we need to do is call that function:

fn handle_event(context: &amp;mut Lua, event: Event) -&gt; Result&lt;(), Error&gt; {

context.context::&lt;_, Result&lt;(), Error&gt;&gt;(|lua_ctx| {

let handle_event: Function = lua_ctx.globals().get(&#34;handle_event&#34;)?;

handle_event.call::&lt;_, ()&gt;(EventForLua(event))?;

Ok(())

})?;

Ok(())

}

}


And that&#39;s all there is to it! That was surprisingly painless, all things considered. The only part that hurt a bit was the glue from `Event` into Lua, which wasn&#39;t even that bad for my case, but depending on what you&#39;re doing it might be.

## duckscript

So Lua is a language I know I don&#39;t like all that much, and the gnarliest part of using it here was getting Rust types into it. Let&#39;s see if Duckscript, a &#34;simple, extendable and embeddable scripting language&#34;, will be even simpler.

=&gt; https://crates.io/crates/duckscript Duckscript

Well, this is a minor nitpick, but if I want to write a custom command (which, I think, is the right way to get `print_fancy` available to our script) the trait for that requires that I manually write

fn clone_and_box(&amp;self) -&gt; Box&lt;dyn Command&gt; {

Box::new(self.clone())

}


even though *anything that is `Clone` can already be cloned into a box*. So that&#39;s a (very) minor nuisance.

The less-minor nuisance, it turns out, is that I can&#39;t figure out how to actually run a function defined in a script. So I guess in Duckscript you can&#39;t do that, and we&#39;ll have to just rerun the whole script when the event gets fired.

But once we&#39;ve discovered and adapted to those issues, there&#39;s not much actual code that needs writing. Conveniently, since we can&#39;t have any persistent state anyway, there&#39;s no script context to store:

struct DuckscriptHost;

impl ScriptHost for DuckscriptHost {

type ScriptContext = ();

type Error = ScriptError;

fn new_context() -&gt; Result&lt;Self::ScriptContext, ScriptError&gt; {

Ok(())

}


The glue to expose our `print_fancy` is pretty terse as well:

[derive(Clone)]

struct FancyPrintCommand;

impl Command for FancyPrintCommand {

fn name(&amp;self) -&gt; String {

&#34;fancy_print&#34;.to_string()

}

fn clone_and_box(&amp;self) -&gt; Box&lt;dyn Command&gt; {

Box::new(self.clone())

}

fn run(&amp;self, arguments: Vec&lt;String&gt;) -&gt; CommandResult {

print_fancy(&amp;arguments.join(&#34; &#34;));

CommandResult::Continue(None)

}

}


Once we&#39;ve got this, we can handle events by setting up everything from scratch:

fn handle_event(_context: &amp;mut Self::ScriptContext, event: Event) -&gt; Result&lt;(), ScriptError&gt; {

// Make a context

let mut context = Context::new();

// Add the standard library

duckscriptsdk::load(&amp;mut context.commands)?;

// Add our print_fancy command

context.commands.set(Box::new(PrintFancyCommand))?;

// Pick some strings to put in the context to represent our event

let (event_type, event_value) = match event {

Event::Number(value) =&gt; (&#34;number&#34;, format!(&#34;{}&#34;, value)),

Event::Text(value) =&gt; (&#34;text&#34;, value)

};

context.variables.insert(&#34;event.type&#34;.to_string(), event_type.to_string());

context.variables.insert(&#34;event.value&#34;.to_string(), event_value);

// Run the script

run_script_file(&#34;scripts/duckscript-sample.ds&#34;, context)?;

Ok(())

}

}


That&#39;s a lot of steps there. As you may notice, we have to put the number into a string before we can add it to the context. This is because all values in Duckscript are strings.

Well, now that we&#39;ve got all the code to pass in variables and run it, let&#39;s write our event handler script:

if eq ${event.type} &#34;number&#34;

echo &#34;number!&#34; ${event.value}

else

echo &#34;text!&#34; ${event.value}

end

print_fancy &#34;Got an event&#34;


Technically, this all works. But I don&#39;t like it. It might be enough for your use case, though, and if it is, it&#39;ll work.

## rhai

We&#39;re still in search of a Rust-centric scripting language that&#39;s good for our use case, so let&#39;s see if Rhai, &#34;an embedded scripting language and evaluation engine for Rust that gives a safe and easy way to add scripting to any application&#34;, will be that. It&#39;s got an entire documentation book, which is always nice to see, and WebAssembly support. So hopefully this one is as cool as it looks.

=&gt; https://crates.io/crates/rhai Rhai
=&gt; https://schungx.github.io/rhai/ an entire documentation book

Page four of said documentation book gives some really helpful clarity:

&gt; Rhaiโ€™s purpose is to provide a dynamic layer over Rust code, in the same spirit of zero cost abstractions. It doesnโ€™t attempt to be a new language.
That also sounds like exactly what we need.

Just take a minute and *look at how fucking gorgeous this code is*:

struct RhaiScriptHost;

impl ScriptHost for RhaiScriptHost {

type ScriptContext = (Engine, Scope&lt;&#39;static&gt;, AST);

type Error = Box&lt;EvalAltResult&gt;;

fn new_context() -&gt; Result&lt;Self::ScriptContext, Self::Error&gt; {

let mut engine = Engine::new();

engine.register_fn(&#34;print_fancy&#34;, print_fancy);

let mut scope = Scope::new();

let ast = engine.compile_file_with_scope(&amp;mut scope, PathBuf::from(&#34;scripts/rhai-sample.rhai&#34;))?;

// if there&#39;s some global state or on-boot handling, make sure it runs

engine.consume_ast_with_scope(&amp;mut scope, &amp;ast)?;

Ok((engine, scope, ast))

}

fn handle_event((engine, scope, ast): &amp;mut Self::ScriptContext, event: Event) -&gt; Result&lt;(), Self::Error&gt; {

let argument = match event {

Event::Number(number) =&gt; Dynamic::from(number),

Event::Text(text) =&gt; Dynamic::from(text),

};

engine.call_fn(scope, ast, &#34;handle_event&#34;, (argument,))?;

Ok(())

}

}


That&#39;s all of it. That&#39;s the whole damn thing. Our script is pretty darn straightforward, too, unsurprisingly:

fn handle_event(data) {

if type_of(data) == &#34;i64&#34; {

print(&#34;number! &#34; + data);

} else {

print(&#34;text! &#34; + data);

}

print_fancy(&#34;got an event!&#34;);

}


Rhai doesn&#39;t have pattern matching, so we can&#39;t use our `Event` enum directly, but it does have runtime type introspection, so we can simply pass in either a number or a string and let the script sort it out. (If this is possible in Lua, too, then we could&#39;ve made our Lua implementation a little simpler, but I&#39;m not as familiar with Lua.)

There&#39;s so much to love here, but my favorite part is that we don&#39;t need any extra glue at all around `print_fancy` to be able to expose it to our Rhai script. This is *excellent*. Well done to Stephen Chung, Jonathan Turner, and the rest of the contributors. This is gonna be tough to beat.

=&gt; https://github.com/jonathandturner/rhai/graphs/contributors the rest of the contributors

## dyon

Ooh, dyon was built by one of the two big Rust game engine projects. (In fairness, though, rlua was built by the other one.)

=&gt; https://crates.io/crates/dyon dyon

On the upside, this is almost as straightforward as Rhai was:

struct DyonScriptHost;

impl ScriptHost for DyonScriptHost {

type ScriptContext = Arc&lt;Module&gt;;

type Error = Error;

fn new_context() -&gt; Result&lt;Self::ScriptContext, Error&gt; {

let mut module = Module::new();

dyon_fn!{fn dyon_print_fancy(x: String) {

print_fancy(&amp;x);

}}

module.add_str(&#34;print_fancy&#34;, dyon_print_fancy, Dfn::nl(vec![Type::Str], Type::Void));

load(&#34;scripts/dyon-sample.dyon&#34;, &amp;mut module)?;

Ok(Arc::new(module))

}

fn handle_event(context: &amp;mut Self::ScriptContext, event: Event) -&gt; Result&lt;(), Error&gt; {

let call = Call::new(&#34;handle_event&#34;);

let call = match event {

Event::Number(value) =&gt; call.arg(value as f64),

Event::Text(value) =&gt; call.arg(value),

};

call.run(&amp;mut Runtime::new(), context)?;

Ok(())

}

}


Our script is likewise pretty straightforward:

fn handle_event(data) {

if typeof(data) == &#34;number&#34; {

println(&#34;number! &#34; + str(data))

} else {

println(&#34;text! &#34; + data)

}

print_fancy(&#34;got an event!&#34;)

}


But the API took a really long time for me to actually figure out, and there are a few language features that I extremely don&#39;t get. Like, they&#39;ve got secrets that can be attached to a bool or number, so you can ask for a retroactive explanation for why a function returned the value it did, or where a value came from. And that&#39;s neat and all, but like. why. Honestly I&#39;m just not as impressed by this as I am by Rhai. Maybe you are, though, in which case this might work well for you.

=&gt; http://www.piston.rs/dyon-tutorial/secrets.html secrets

## gluon

gluon, unlike our previous contestants, is a statically typed functional language. Static types, for this context, are nice to have. Functional programming... may or may not be, depending on how it&#39;s implemented.

=&gt; https://crates.io/crates/gluon gluon

Oh hey, at time of writing their website is down. That&#39;s always a good sign.

An even better sign, of course, is the fact that trying to declare our Event type in Gluon causes a stack overflow. In theory, since it&#39;s got algebraic data types, we could just declare it as is and bring it right on in, but unfortunately the code to do that 100% automatically appears to not compile, and the code to do that only almost automatically still requires us to write the type definition on the Gluon side manually:

vm.load_script(&#34;rust-scripting-languages.event&#34;, &#34;type Event = Number Int | Text String&#34;)?;


I&#39;m not sure why this causes a stack overflow either. But it does. As such, I have no clue how good this actually would be to use.

## ketos

ketos describes itself as &#34;a Lisp dialect functional programming language&#34;. So that ought to be interesting.

=&gt; https://crates.io/crates/ketos ketos

The Rust function glue all assumes your functions return a `Result` and that&#39;s a minor nuisance, but that&#39;s not insurmountable. Overall, this is pretty terse:

struct KetosScriptHost;

impl ScriptHost for KetosScriptHost {

type ScriptContext = Interpreter;

type Error = Error;

fn new_context() -&gt; Result&lt;Self::ScriptContext, Error&gt; {

let interp = Interpreter::new();

let scope = interp.scope();

fn print_fancy_fallible(text: &amp;str) -&gt; Result&lt;(), Error&gt; {

print_fancy(text);

Ok(())

}

ketos_fn! { scope =&gt; &#34;print-fancy&#34; =&gt; fn print_fancy_fallible(text: &amp;str) -&gt; () }

interp.run_file(&amp;PathBuf::from(&#34;scripts/ketos-sample.ket&#34;))?;

Ok(interp)

}

fn handle_event(context: &amp;mut Self::ScriptContext, event: Event) -&gt; Result&lt;(), Error&gt; {

let value = match event {

Event::Number(n) =&gt; Value::from(n),

Event::Text(t) =&gt; Value::from(t),

};

context.call(&#34;handle-event&#34;, vec![value])?;

Ok(())

}

}


Our script itself is... definitely Lisp-y.

(define (handle-event event)

(do

(println &#34;~a ~a&#34; (type-of event) event)

(print-fancy &#34;got an event!&#34;)))


This all works, and if you love Lisp and want your users to write a bunch of Lisp then this&#39;ll work perfectly for you. But it&#39;s not really to my tastes.

## rune

rune lists pattern matching in their README as a feature, so that&#39;s a good sign. And they&#39;ve got a Book that starts by giving a shout out to Rhai as a major inspiration, which is also neat.

=&gt; https://crates.io/crates/rune rune

Turns out the pattern matching doesn&#39;t let you match on enum variants even though Rune *has enums*. So we&#39;ve gotta do the same dynamic typing stuff we&#39;ve been doing before. But this is pretty clean:

struct RuneScriptHost;

impl ScriptHost for RuneScriptHost {

type ScriptContext = Vm;

type Error = Box&lt;dyn Error + &#39;static&gt;;

fn new_context() -&gt; Result&lt;Vm, Self::Error&gt; {

let mut context = rune::default_context()?;

let mut module = runestick::Module::default();

module.function(&amp;[&#34;print_fancy&#34;], print_fancy)?;

context.install(&amp;module)?;

let script = read_to_string(&#34;scripts/rune-sample.rn&#34;)?;

let mut warnings = rune::Warnings::new();

let unit = rune::load_source(&amp;context, &amp;Default::default(), Source::new(&#34;scripts/rune-sample.rn&#34;, &amp;script), &amp;mut warnings)?;

let vm = Vm::new(Arc::new(context), Arc::new(unit));

Ok(vm)

}

fn handle_event(vm: &amp;mut Vm, event: Event) -&gt; Result&lt;(), Self::Error&gt; {

let arg = match event {

Event::Number(n) =&gt; Value::from(n),

Event::Text(t) =&gt; Value::from(t)

};

vm.clone().call(&amp;[&#34;handle_event&#34;], (arg,))?.complete()?;

Ok(())

}

}


Again, we have zero glue required for our `print_fancy` function, which is nice. Our script looks pretty decent too:

fn handle_event(event) {

match event {

n if n is int =&gt; println(`Number! {n}`),

t =&gt; println(`Text! {t}`),

}

print_fancy(&#34;got an event!&#34;)

}


The one issue I notice is that `Vm::call` takes `self` by value, which means we&#39;re forced to `clone()` our VM every time we want to handle a new event instead of handling them all in the same VM. This probably means a more complicated script with some global state to maintain wouldn&#39;t be able to do that. So if that&#39;s something you know you&#39;ll want to support, Rune might not be for you, unless there&#39;s a different way to do this that I didn&#39;t find in the docs. But if you know you don&#39;t need that, Rune might work well for you.

## ruwren

ruwren provides bindings to the Wren embeddable scripting language, which looks kinda neat:

=&gt; https://crates.io/crates/ruwren ruwren
=&gt; https://wren.io/ Wren

&gt; Think Smalltalk in a Lua-sized package with a dash of Erlang and wrapped up in a familiar, modern syntax.
I think the example presented in the README at time of writing is incomplete and buggy, but I figured out how to fix it and sent in a pull request.

We do have to write some glue for our `print_fancy` method; since Wren is inspired by Smalltalk, everything is objects, so we need a class within which `print_fancy` can be a static method:

struct Demo;

impl Class for Demo {

fn initialize(_: &amp;VM) -&gt; Self {

Demo

}

}

impl Demo {

fn print_fancy(vm: &amp;VM) {

let text = get_slot_checked!(vm =&gt; string 1);

print_fancy(&amp;text);

}

}

create_module! {

class(&#34;Demo&#34;) crate::Demo =&gt; foo {

static(fn &#34;print_fancy&#34;, 1) print_fancy

}

module =&gt; demo

}


Armed with this, the actual integration is pretty straightforward:

struct RuwrenScriptHost;

impl ScriptHost for RuwrenScriptHost {

type ScriptContext = VMWrapper;

type Error = Box&lt;dyn Error + &#39;static&gt;;

fn new_context() -&gt; Result&lt;Self::ScriptContext, Self::Error&gt; {

let mut modules = ModuleLibrary::new();

demo::publish_module(&amp;mut modules);

let vm = VMConfig::new()

.library(&amp;modules)

.build();

vm.interpret(&#34;demo&#34;, r##&#34;

foreign class Demo {

foreign static print_fancy(string)

}

&#34;##)?;

let script = read_to_string(&#34;scripts/ruwren-sample.wren&#34;)?;

vm.interpret(&#34;main&#34;, &amp;script)?;

Ok(vm)

}

fn handle_event(vm: &amp;mut VMWrapper, event: Event) -&gt; Result&lt;(), Self::Error&gt; {

vm.execute(|vm| {

vm.ensure_slots(2);

vm.get_variable(&#34;main&#34;, &#34;EventHandler&#34;, 0);

match &amp;event {

Event::Number(n) =&gt; vm.set_slot_double(1, *n as f64),

Event::Text(t) =&gt; vm.set_slot_string(1, t),

}

});

vm.call(FunctionSignature::new_function(&#34;handleEvent&#34;, 1))?;

Ok(())

}

}


And our test script in Wren looks nice:

import &#34;demo&#34; for Demo

class EventHandler {

static handleEvent(data) {

if (data is Num) {

System.print(&#34;Number! %(data)&#34;)

} else {

System.print(&#34;Text! %(data)&#34;)

}

Demo.print_fancy(&#34;got an event&#34;)

}

}


Overall, I think this is pretty good. This specific use case isn&#39;t particularly well suited to Smalltalk-style object orientation, but if yours is then this will work well for you. Plus, since Wren isn&#39;t Rust-specific, it might be slightly more portable, if that&#39;s a concern for you.

## summary

So those are our options. A convenient bullet points tl;dr for you:


    </entry>
    
    <entry>
        <title>A Survey of Rust GUI Libraries</title>
        <link href="gemini://boringcactus.com/2020/08/21/survey-of-rust-gui-libraries.gmi" rel="alternate" type="text/gemini" title="A Survey of Rust GUI Libraries" />
        <published>2020-08-21T00:00:00Z</published>
        <updated>2020-08-21T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/08/21/survey-of-rust-gui-libraries.gmi</id>
        <content type="text/gemini">a popular trend in the Rust community is to ask &#34;Are We X Yet&#34; for various things that it would be nice to be able to develop easily in Rust - game and web are the most prominent ones as far as i can tell - and one such question is Are We GUI Yet. that&#39;s a good question; *are* we GUI yet? Are We GUI Yet has a list of libraries for building GUIs: let&#39;s go through them in alphabetical order and see if we can build a simple to-do list with them without too much struggle.

=&gt; https://arewegameyet.rs/ game
=&gt; https://www.arewewebyet.org/ web
=&gt; https://areweguiyet.com/ Are We GUI Yet

some notes before we get started.

1. this is all extremely subjective.
2. the only ui toolkits i have used and not hated are Swing (i know), Electron (*i know*), and wxWidgets, which doesn&#39;t have Rust bindings because Rust bindings to C++ libraries are generously described as a nuisance to create (i&#39;ve tried, and i&#39;m writing this post instead of trying harder). as such, i might be using some of these wrong, who knows.
3. i use windows, and so anything that&#39;s a nuisance to set up on windows is not going to fare well regardless of how cool it is once you get it working. it could be the best thing since sliced bread or Meteor on release and i wouldn&#39;t care. do not @ me.
4. the people who wrote these libraries have done more than i have to make the rust gui ecosystem not suck, and i don&#39;t want any of this to come across as suggesting that they suck and their work is bad. the strongest thing i want to say is that a library is not designed in a way that i would want it to be designed, or that it doesn&#39;t work for me. doing this shit at all is really goddamn difficult, and i don&#39;t want to minimize that by being unhappy with the results.
5. i started drafting this post in early July 2020, and finished it in late August 2020. some things may have changed in the meantime while i wasn&#39;t paying attention.

## azul

first on our list is azul:

=&gt; https://azul.rs/ azul

&gt; A free, functional, immediate-mode GUI framework for rapid development of desktop applications written in Rust, supported by the Mozilla WebRender rendering engine.
the wiki says we need cmake installed, which is never a good sign, but conveniently, i&#39;ve already got that set up on my computer, for reasons i forget but probably didn&#39;t enjoy. the runtime dependencies on linux are a mile long, but fortunately i don&#39;t have to care. azul isn&#39;t currently available on crates.io for reasons that presumably exist but are difficult to explain, so we have to add it directly as a git dependency.

once we&#39;ve got it added as a dependency, we can attempt to run our test crate, just to make sure everything&#39;s not on fire. unfortunately:

error: failed to run custom build command for `servo-freetype-sys v4.0.5`

Caused by:

process didn&#39;t exit successfully: `D:\Melody\Projects\we-are-not-gui-yet\target\debug\build\servo-freetype-sys-1fae054761ff82c5\build-script-build` (exit code: 101)

--- stdout

running: &#34;cmake&#34; &lt;snip&gt;

--- stderr

CMake Error: Could not create named generator Visual Studio 16 2019


this is an inauspicious beginning. one version history crawl later and it looks like my cmake is from April 2019, which is not all that old but maybe they hadn&#39;t caught up on the latest visual studio yet, who knows. this is already more work than i was prepared to do, but i&#39;ve come this far, so it&#39;s time to update my cmake.

okay one installer later and it&#39;s time to try again. armed with a cmake from May 2020, let&#39;s give this another shot:

error[E0433]: failed to resolve: could not find `IoReader` in `bincode`

--&gt; C:\Users\Melody\.cargo\registry\src\github.com-1ecc6299db9ec823\webrender_api-0.60.0\src\display_list.rs:270:35

|

270 | let reader = bincode::IoReader::new(UnsafeReader::new(&amp;mut self.data));

| ^^^^^^^^ could not find `IoReader` in `bincode`


welp. the cmake update fixed things, i guess, but now we&#39;ve got a whole other pile of mess. this might be fixable, it may have been fixed by the time you read this. regardless, this library does not work for me.

## conrod

next up is conrod:

=&gt; https://github.com/pistondevelopers/conrod conrod

&gt; An easy-to-use, 2D GUI library written entirely in Rust.
i&#39;ve actually used this one before in a couple of projects, but it&#39;s been a minute, so i forget the details.

they do not have a real tutorial, which is unfortunate, but they do have some examples. unfortunately, step one is to pick which of the half dozen backends i want. do i want glium or vulkano or rendy or piston? do i look like i know what a vulkano is? i just want a picture of a god dang hot dog. okay that&#39;s not quite fair, i recognize three of those and can infer from context what the fourth one is, but that&#39;s only because i&#39;ve been down this road before, and i still have no clue which one is the right one to pick. all the examples live under glium, though, so let&#39;s go with that.

=&gt; https://youtu.be/EvKTOHVGNbg god dang hot dog

wait actually i&#39;m staring at these examples and there&#39;s an entire ass event loop in the support code for the examples. something in here mentions a `GliumDisplayWinitWrapper` and i&#39;m scared. i have literally used this library before - on two different projects - and i&#39;m at a loss. i don&#39;t want to just copy and paste the examples without actually understanding what&#39;s going on, i can&#39;t understand what&#39;s going on in the examples, and there&#39;s nowhere else to get started. so there goes that i guess.

## core-foundation

&gt; Bindings to Core Foundation for macOS.
oh hey, it&#39;s an OS i don&#39;t have access to at all. next.

## druid

our next contender is druid:

=&gt; https://linebender.org/druid/ druid

&gt; Druid is a framework for building simple graphical applications.
apparently this sprung out of that vi-like text editor a couple googlers were working on, so apparently it&#39;s at least possible to use it for real software. there&#39;s a tutorial, they&#39;re on crates.io, they&#39;re describing it as &#34;conceptually simple and largely non-magical&#34; which i am always a fan of, i am cautiously optimistic. if we throw it in our dependencies and just see if anything breaks, we find the surprising result that everything just works.

oh hey the first real chapter in the tutorial starts with

&gt; this is outdated, and should be replaced with a walkthrough of getting a simple app built and running.
you love to see it. fortunately, we can just ignore that and skip to the hello world example, reproduced here in its entirety:

use druid::{AppLauncher, WindowDesc, Widget, PlatformError};

use druid::widget::Label;

fn build_ui() -&gt; impl Widget&lt;()&gt; {

Label::new(&#34;Hello world&#34;)

}

fn main() -&gt; Result&lt;(), PlatformError&gt; {

AppLauncher::with_window(WindowDesc::new(build_ui)).launch(())?;

Ok(())

}


and somehow, this actually works.

the tutorial ends here, which is unfortunate, but there&#39;s more documentation, including explanations of core concepts with examples that are... todo lists! with even more features than what i was planning to include here! so that&#39;s convenient. the UI hierarchy is based on CSS Flexbox, which i also appreciate. this is what peak UI layout API looks like:

fn build_new_todo() -&gt; impl Widget&lt;TodoState&gt; {

let textbox = TextBox::new()

.with_placeholder(&#34;New todo&#34;)

.lens(TodoState::next_todo);

let button = Button::new(&#34;Add&#34;)

.on_click(|_, data: &amp;mut TodoState, _| data.create_todo());

Flex::row()

.with_flex_child(textbox.expand_width(), 1.0)

.with_child(button)

}


1 hour and 80 lines of code later, we&#39;ve got ourselves a perfectly valid and working todo list!

=&gt; /assets/2020-08-21-survey-of-rust-gui-libraries-1.png our sample druid application, showing a todo list [IMG]

i&#39;m not quite happy with this, though: we can type text and hit the button and it adds the todo, but pressing enter in the text field doesn&#39;t do anything. there&#39;s no way out-of-the-box to make that happen; let&#39;s see if we can build that ourselves.

~30 lines of code later, we&#39;ve got it! the `Controller` trait is designed for exactly this sort of thing, when you need to wrap the behavior of an existing component and intercept some events to inject your own logic.

if you&#39;re curious, you can take a look at the source for our druid example.

=&gt; https://git.sr.ht/~boringcactus/survey-of-rust-gui-libraries/tree/main/druid-test the source for our druid example

so apparently druid is actually pretty darn usable. i only have a couple tiny issues with it:

1. it doesn&#39;t use platform native UI widgets, so it doesn&#39;t look quite like a windows app should, and it won&#39;t look quite like a mac or linux app should either if i test it there. this one is a feature as far as some people are concerned, but i am not on that list.
2. accessibility features like being able to tab between UI widgets (*update 2020-12-14*: and also literally any screen reader support) are missing, so you&#39;d have to roll those yourself in a real application. maybe they&#39;ll add that by default in future versions, maybe not, but it would be neat if it existed.
3. high-level documentation is incomplete. the individual struct/function docs are really good, but at a high level you don&#39;t really have a convenient place to jump in.

i was about to add &#34;no support for web&#34; to that list, but even though the high-level docs don&#39;t mention it, the crate root docs and the examples do. on the plus side, it just works, and i didn&#39;t have to make any changes to my code because i use this patch to wasm-pack that lets you just use binary crates in wasm-pack even though it hasn&#39;t been merged yet upstream. on the minus side, it points everything at a `&lt;canvas&gt;` tag, which means you get none of the accessibility features of actually using the DOM. so that one&#39;s a mixed bag.

=&gt; https://github.com/rustwasm/wasm-pack/pull/736 this patch to wasm-pack that lets you just use binary crates in wasm-pack

but yeah, overall druid is perfectly usable for gui development. i was originally calling this post &#34;we are not gui yet&#34; but i guess we are at least a little bit gui already. pleasant surprises are the best kind.

## fltk

following up that success is fltk:

=&gt; https://github.com/MoAlyousef/fltk-rs fltk

&gt; The FLTK crate is a crossplatform lightweight gui library which can be statically linked to produce small, self-contained and fast gui applications.
cross-platform and statically linked are both good things. the upstream FLTK website makes my eyes bleed, which is never a good sign for a UI library, but that doesn&#39;t mean much one way or the other. the simple hello world example is once again a mere handful of lines:

use fltk::{app::*, window::*};

fn main() {

let app = App::default();

let mut wind = Window::new(100, 100, 400, 300, &#34;Hello from rust&#34;);

wind.end();

wind.show();

app.run().unwrap();

}


a downside i&#39;m noticing already, at least compared to druid, is that everything has to be positioned manually, and we don&#39;t get any layout stuff calculated for free.

a lot of wrestling later, we have a technically working implementation (source code).

=&gt; https://git.sr.ht/~boringcactus/survey-of-rust-gui-libraries/tree/main/fltk-test source code

=&gt; /assets/2020-08-21-survey-of-rust-gui-libraries-2.png our sample fltk application, showing a todo list [IMG]

it&#39;s half as much code as the druid implementation, but part of that&#39;s because the druid implementation also preserves state information, so we could easily have added persistence without all that much work, but our fltk version does not do that and is just a pile of ui widgets. some of that code, i will say, fails to spark joy:

add_todo.set_callback(Box::new(move || {

let text = next_todo.value();

next_todo.set_value(&#34;&#34;);

let done = CheckButton::new(0, top, 400, 30, &amp;text);

wind.add(&amp;done);

wind.redraw();

top += 30;

}));


we have to drag that position and size around manually. i don&#39;t like that.



overall, this technically works i guess, but i think the code is ugly and the style of the resulting application is also ugly. we do get tab and space and everything working out of the box on buttons, which is always appreciated, though. not broken or anything, not something i&#39;d be likely to choose to use though either.

## gtk

next on our list is another pile of bindings to an existing ui library, gtk:

=&gt; https://gtk-rs.org/ gtk

&gt; Rust bindings and wrappers for GLib, GDK 3, GTK+ 3 and Cairo.
however. the second meaningful sentence in the README says

&gt; gtk expects GTK+, GLib and Cairo development files to be installed on your system.
and i have been down that road before and mother of god once is enough. maybe on things-that-are-not-windows this isn&#39;t a nightmare, but i do not use things that are not windows. the windows instructions are a nightmare even in the happy path that their instructions explain, which last time around i failed to hit, making the whole process even more nightmarish. so i think i will pass.

## iced

our next contestant is iced:

=&gt; https://github.com/hecrj/iced iced

&gt; A cross-platform GUI library for Rust focused on simplicity and type-safety. Inspired by Elm.
cross-platform and simple are good. inspired by elm is a tentative &#34;nice&#34; - my experiment with elm way back in the day had mixed results, but it&#39;s not clear how much of that was my fault.

iced compiles just fine, and it looks like we&#39;ve got a vaguely MVC-ish architecture here. it looks like you write your logic in a highly portable way and then glue it together in ways that vary based on whether you&#39;re building for native or for Web.

conveniently, there&#39;s a todo list example! but we don&#39;t even need it; the example given in the README, with some of the details elided there, is enough context to have an entire todo list application in 100 lines of Rust.

=&gt; https://git.sr.ht/~boringcactus/survey-of-rust-gui-libraries/tree/main/iced-test 100 lines of Rust

=&gt; /assets/2020-08-21-survey-of-rust-gui-libraries-3.png our sample iced application, showing a todo list [IMG]

notably, our checkboxes aren&#39;t aligned to the right of the window. i couldn&#39;t figure out how to make that happen. however, we do have built-in support for &#34;do a thing when the user presses enter in the text area,&#34; which we had to write ourself in other frameworks. so that one is nice.

compared to druid, i&#39;d say the logic is a little more intuitive, the layout controls are less intuitive, and the web support is way better. the native build once again doesn&#39;t use native widgets and so once again doesn&#39;t get tab-between-fields or other accessibility features, but the web build uses actual HTML elements and so gets tab-between-fields for free. high-level documentation is a little more robust here, plus the concepts are less complicated in the first place.



so it&#39;s a little easier to get off the ground than with druid, and the results on the web are way better, but it&#39;s more difficult to make it look decent. maybe that&#39;s just a documentation issue, but it&#39;s not ideal. regardless, yet again we have a perfectly usable library.

## imgui

up next, another binding to an existing library, imgui:

=&gt; https://github.com/Gekkio/imgui-rs imgui

&gt; Rust bindings for Dear ImGui
further down the readme, we see

&gt; Almost every application that uses imgui-rs needs two additional components in addition to the main imgui crate: a backend platform, and a renderer.
and immediately i no longer give a shit. i&#39;m pretty sure imgui is designed for, like, diy game engines etc where you already have a backend and a renderer set up, which is a really specific use case that i don&#39;t currently meet. goodbye.

## kas

this one is not a binding to something else, it&#39;s new from scratch, it&#39;s kas:

=&gt; https://github.com/kas-gui/kas kas

&gt; KAS, the toolKit Abstraction System, is a general-purpose GUI toolkit.
the readme has a lot of screenshots, which is always nice to see. no tutorial, apparently, but several examples.

the guts of kas are mostly macro-based, which doesn&#39;t combine well with the lack of high-level documentation, but the examples are enough to let me bullshit my way towards something almost usable.

=&gt; /assets/2020-08-21-survey-of-rust-gui-libraries-4.png our sample kas application, showing a todo list [IMG]

why almost? because clicking in the text entry field to give it focus causes an explosion:

thread &#39;main&#39; panicked at &#39;called `Option::unwrap()` on a `None` value&#39;, C:\Users\Melody\.cargo\registry\src\github.com-1ecc6299db9ec823\kas-text-0.1.3\src\prepared.rs:465:9


that&#39;s bad. and i don&#39;t feel like chasing down why that happens, especially because my gut says my code isn&#39;t the problem. shame, though, the widgets sure look pretty.

=&gt; https://git.sr.ht/~boringcactus/survey-of-rust-gui-libraries/tree/main/kas-test my code

## neutrino

next up we have neutrino:

=&gt; https://github.com/alexislozano/neutrino neutrino

&gt; Neutrino is a MVC GUI framework written in Rust.
ah, ol&#39; reliable, MVC. the wiki has an actual tutorial, too, which you love to see.

=&gt; https://knowyourmeme.com/memes/ol-reliable ol&#39; reliable

most of the other libraries have not made me throw around `Rc&lt;RefCell&lt;T&gt;&gt;` everywhere myself, though. but neutrino has that just all over the place. and it gets worse than you&#39;d think. building the same example to-do list required a `Rc&lt;RefCell&lt;Vec&lt;Rc&lt;RefCell&lt;TodoItem&gt;&gt;&gt;&gt;&gt;` and i feel like that&#39;s bad. it definitely makes my code look terrible.

=&gt; https://git.sr.ht/~boringcactus/survey-of-rust-gui-libraries/tree/main/neutrino-test my code

excitingly, we now have a demo that looks bad and also doesn&#39;t work:

=&gt; /assets/2020-08-21-survey-of-rust-gui-libraries-5.png our sample neutrino application, showing a todo list [IMG]

excitingly, when we type some text and hit the &#34;add&#34; button, the text gets lost in the created todo, and i have no goddamn clue where it&#39;s going or what to do to fix it.



the approach is interesting, though. as i&#39;m writing this neutrino is unmaintained and seeking a new maintainer, so hopefully somebody has the time and energy to steer it forwards.

## orbtk

our next contestant is OrbTK:

=&gt; https://github.com/redox-os/orbtk OrbTK

&gt; The Orbital Widget Toolkit is a cross-platform (G)UI toolkit for building scalable user interfaces with the programming language Rust.
apparently this is attached to Redox, the OS written in Rust. so that&#39;s neat.

again, no tutorial, some examples that are far from self-explanatory.

it does let us build a working todo list, and one that looks pretty nice:

=&gt; /assets/2020-08-21-survey-of-rust-gui-libraries-6.png our sample orbtk application, showing a todo list [IMG]

i can&#39;t for the life of me figure out how to make the text field take up the entire width available to it. but everything works, and we get built-in support for adding the todo on Enter in the text field, which is nice.



in theory, there&#39;s web support, but when i tried it it very loudly didn&#39;t work:

error[E0405]: cannot find trait `StdError` in module `serde::de`

--&gt; C:\Users\Melody\.cargo\registry\src\github.com-1ecc6299db9ec823\serde_json-1.0.46\src\error.rs:317:17

|

317 | impl serde::de::StdError for Error {

| ^^^^^^^^ not found in `serde::de`


so yeah, we&#39;ve got some nice-looking widgets, with unintuitive layout settings, broken web support, and a *lot* of glue i had to write by hand that makes the source code cluttered and messy. don&#39;t think i&#39;d use it for anything more serious, at least as it exists right now.

=&gt; https://git.sr.ht/~boringcactus/survey-of-rust-gui-libraries/tree/main/orbtk-test the source code

## qmetaobject

&gt; The qmetaobject crate is a crate which is used to expose rust object to Qt and QML.
i don&#39;t want to install Qt. that sounds like a nuisance, and more importantly, if i want Travis or whatever to give me automated CI builds, i don&#39;t think it&#39;s easy to make sure Qt exists on all platforms on Travis.

## qt_widgets

oh hey, more Qt API bindings! i still don&#39;t want to install Qt.

## relm

&gt; Asynchronous, GTK+-based, GUI library, inspired by Elm, written in Rust.
as established, GTK+ setup on Windows is a scary nightmare hellscape.

## rust-qt-binding-generator

i am so tired.

## sciter-rs

i think sciter is a thing actual programs use, which is nice. however, we need not only the sciter sdk installed and available, but also GTK+, and god damn i do not want to do that.

## WebRender

last, but hopefully not least, we have webrender:

=&gt; https://github.com/servo/webrender webrender

&gt; WebRender is a GPU-based 2D rendering engine written in Rust.
&gt; Firefox, the research web browser Servo, and other GUI frameworks draw with it.
pour one out for Servo, btw.

unfortunately, the &#34;basic&#34; example is still 300+ lines of code. so i doubt that&#39;s gonna be useful.

=&gt; https://github.com/servo/webrender/blob/master/examples/basic.rs &#34;basic&#34; example

## so *are* we GUI yet?

well, kinda. druid works well if you want a straightforward layout experience. iced works well if you want a straightforward render-update architecture, or actual HTML elements on Web. everything else is, as of today, broken and/or more complex than i want. and if you want native ui widgets to match your platform&#39;s look and feel (*update 2020-12-14*: or be accessible at all), that&#39;s gonna be like a year away at least.</content>
    </entry>
    
    <entry>
        <title>Post-Open Source</title>
        <link href="gemini://boringcactus.com/2020/08/13/post-open-source.gmi" rel="alternate" type="text/gemini" title="Post-Open Source" />
        <published>2020-08-13T00:00:00Z</published>
        <updated>2020-08-13T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/08/13/post-open-source.gmi</id>
        <content type="text/gemini">i&#39;m writing this like a day after big mozilla layoffs that included a lot of people working on cool and important shit. the consensus i&#39;m seeing is that it reflects mozilla&#39;s search for profit over impact, mismanagement, and disproportionate executive compensation. this is taking place in a larger trend of corporatization of open source over the past several years, an ongoing open source sustainability crisis, and of course COVID-19, the all-consuming crisis that makes all our other crises worse. all of this was summed up most concisely by Kat Marchรกn:

=&gt; https://www.fastcompany.com/90539632/mozilla-vows-mdn-isnt-going-anywhere-as-layoffs-cause-panic-among-developers big mozilla layoffs
=&gt; https://twitter.com/zkat__/status/1293626135142477825 Kat Marchรกn

&gt; Imo, open source as a community endeavor is falling apart right before our eyes, and being replaced by open source as Big Corp entrenchment strategy.
&gt; 
&gt; I mean it&#39;s been happening for a while, but seeing Mozilla sinking like this is just driving the point home for me.
&gt; 
&gt; FOSS is dead
how did we get here? where even are we? what happens next?

i am incredibly unqualified to answer any of this - i didn&#39;t show up until right around the peak of SourceForge, i wasn&#39;t there for most of this - but i&#39;m not gonna let that stop me.

## names

to start this funeral service for FOSS, we have to unpack the term itself. &#34;free and open source software&#34; as a term already contains multitudes. on one hand, &#34;free software&#34;, an explicitly political movement with a decidedly anti-charismatic leader. on the other hand, &#34;open source software&#34;, defanged and corporate-friendly by design. the free software people (correctly) criticize &#34;open source&#34; as milquetoast centrism. the open source people (correctly) criticize &#34;free software&#34; as stubborn idealism fighting tooth and nail to reject the real world as it actually exists. they have as much in common as leftists and liberals (but they&#39;re more prepared to work together), and although their short-term goals were similar enough that it made sense to lump them together (hence the cooperation), now that the movement is dead i think there&#39;s more to gain from considering them separately. most software licenses that i&#39;m going to bring up technically qualify as both, but they&#39;re popular with one or the other, so i&#39;ll refer to &#34;free software licenses&#34; and &#34;open source licenses&#34; as licenses that are more directly tied to those movements, even though any given license likely meets both definitions.

i&#39;d say free software died a while ago, and open source went horribly right.

## freedom

the free software movement, for all its faults, has always known what it&#39;s about:

=&gt; https://www.gnu.org/philosophy/free-sw.html.en what it&#39;s about

&gt; 0. The freedom to run the program for any purpose.
&gt; 1. The freedom to study how the program works, and change it to make it do what you wish.
&gt; 2. The freedom to redistribute and make copies so you can help your neighbour.
&gt; 3. The freedom to improve the program, and release your improvements (and modified versions in general) to the public, so that the whole community benefits.
it&#39;s concise, it&#39;s understandable, and it&#39;sโ€ฆ kinda useless. this point was raised better by actual lawyer Luis Villa (Karl Marx slander notwithstanding), but those freedoms don&#39;t actually mean shit to the average end user. only programmers care if they have access to the source code, and most people aren&#39;t programmers. and i *am* a programmer, and i don&#39;t give a shit. the freedom to not think about my operating system and just get work done overrules all of those for me, so i use windows. like, yeah, those things are all in principle nice to have, and between two otherwise equally good programs i&#39;d take the free one. but they&#39;re really fuckin specific things, and even if i have the freedom to do them i&#39;m not likely to have the ability or desire to do them, so there&#39;s no good reason for me as a user to use software that&#39;s worse in other ways because it gives me freedoms i don&#39;t need.

=&gt; https://lu.is/blog/2016/03/23/free-as-in-my-libreplanet-2016-talk/ raised better by actual lawyer Luis Villa

the free software movement is explicitly political, but its politics suck. it&#39;s a movement by and for ideological diehards but the ideology is extremely esoteric. theirs was a losing battle from day one. so what was it that actually killed them? i think in a very real way it was the GPLv3.

## losing

the flagship projects of the free software movement are probably Linux and the GNU pile of tools. the Linux kernel being released under a free software license doesn&#39;t directly create more free software, though, since even things that tie closely to the kernel aren&#39;t obligated to also be free software, and of course user-level applications can have whatever license they want. and also most of the people using Linux right now are using it by accident, distributed as ChromeOS or Android, neither of which is free software. so Linux is a win for the free software movement but a useless one.

the GNU userland tools are, for the most part, even more underwhelming. it may be technically more accurate to call it GNU/Linux, but the only time i remember my linux userland tools are GNU or free software at all is when there&#39;s some weird inconsistency between a GNU tool and its BSD equivalent, and that&#39;s not exactly ideal. gcc had, as far as i can tell, been basically *the* C compiler for a while, if you weren&#39;t stuck with MSVC or something worse. the free software movement were stubborn ideologues with weird priorities, but they still had one big technical advantage. then the GPLv3 happened.

=&gt; https://twitter.com/boring_cactus/status/1166408436386430976 some weird inconsistency between a GNU tool and its BSD equivalent

the GPLv2 was pretty popular at the time, but there were a couple notable loopholes some big corporations had been taking advantage of, which the free software people wanted to close. a whole bunch of people thought the GPLv2 was fine the way it was, though - closing the loopholes as aggressively as the GPLv3 did cut off some justifiable security measures, and some people said that it could do more harm than good. the linux kernel, along with a lot more stuff, declared it was sticking with the GPLv2 and not moving to the GPLv3. when your movement says &#34;here is the new version of The Right Way To Do Things&#34; and several of your largest adherents say &#34;nah fuck you we&#39;re going with the old version&#34; that is not a good sign. around the same time, free software organizations were starting to successfully sue companies who were using free software but not complying with the license. so big companies, like Apple, saw new restrictions coming in at the same time as more aggressive enforcement, and said &#34;well shit, we want to base our software on these handy convenient tools like GCC but we can&#39;t use GPLv3 software while keeping our hardware and software as locked together as we&#39;d like.&#34; so they started pouring money into a new C compiler, LLVM, that was instead open source.

and LLVM became at least as good as GCC, and a less risky decision for big companies, and easier to use to build new languages. so the free software movement&#39;s last technical advantage was gone. its social advantages also kinda went up in flames with the GPLv3, too: the software that was the foundation for the GPL enforcement lawsuits stuck with the GPLv2. the discourse over that decision was so nasty that the lead maintainer (Rob Landley; he&#39;ll come up later) started an identical project which he wound up relicensing under an open source license because the lawsuits had completely backfired: instead of complying with the terms of the GPL, companies were just avoiding GPL software.

the free software movement, in the end, burned itself out, by fighting for a tiny crumb of success and then turning around and lighting that success on fire. the death of free software tells us that we can&#39;t use a license to trick corporations into sharing our values: they want to profit, and if good software has a license that puts a limit on how much they can do that, they&#39;ll put more resources into writing their own alternative than they would spend complying with the license in the first place.

## openness

the open source movement manages to share the same short term goals as the free software movement but be bad in almost entirely disjoint ways. the mission of the Open Source Initiative says

=&gt; https://opensource.org/about mission of the Open Source Initiative

&gt; Open source enables a development method for software that harnesses the power of distributed peer review and transparency of process.
&gt; The promise of open source is higher quality, better reliability, greater flexibility, lower cost, and an end to predatory vendor lock-in.
this is so profoundly different from the free software definition that it&#39;s almost comical. where free software says &#34;we value freedom, which we define in these ways,&#34; open source says &#34;your code will get better.&#34; the free software movement was prepared to start fights with corporations that used their work but didn&#39;t play by their rules. the open source movement was invented to be a friendly, apolitical, pro-corporate alternative to the free software movement.

the contrast between &#34;use free software because it preserves your freedom&#34; and &#34;use open source software because it&#39;s better&#34; is profound and honestly a little disappointing to revisit this explicitly. free software preserves freedoms i don&#39;t need or care about as a user, but it does at least do that. open source software is frequently not in fact better than closed source alternatives, and &#34;use open source software because on rare occasions it manages to be almost as good&#34; is an even more underwhelming sales pitch than anything free software can give.

where free software is misguided and quixotic, open source is spineless and centrist. and as tends to happen with spineless centrism, it has eaten the world.

## winning

if there&#39;s anything corporations love more than rewriting software so it lets them make all the money they can dream of, it&#39;s letting other people do that work for them. it took a while to take off, because the conservative approach of &#34;keep things closed source&#34; was pretty solidly entrenched in a lot of places, but now even the once conservative holdouts have accepted the gospel of centrism. corporations have little to nothing to lose by publishing existing source code, and can gain all sorts of unpaid volunteer labor. if they start a new internal project, important enough that they&#39;re prepared to put effort into it but not so important that someone could run off with it and compete with them, then now they&#39;ll likely open source it. worst case scenario, they do all the work they were already prepared to do. best case scenario, their library turns into the single most popular library of its type, with thousands of unpaid volunteers donating their time to you. more labor for free, community goodwill for having started the project everybody uses, the benefits if it goes well are countless. free software is not in principle anti-corporate, but corporations are very cautious getting caught up in the free software movement, because that actually creates obligations for them. open source gives corporations a shot at improving their code for free, so as long as they don&#39;t share so much someone could start a competitor, so there&#39;s zero reason for a corporation to not get into open source.

the best part for corporations is they don&#39;t even have to be the ones to start a project. if you&#39;re just some random small time developer, they can just show up. you made a cool database server that&#39;s under an open source license? amazon&#39;s selling it as a service now, and they&#39;re not paying you a fuckin dime. you want to change your license to stop them from doing that? now the open source people are yelling at you, because when they say they&#39;re apolitical they mean they support the status quo. and the free software people are also yelling at you, because you didn&#39;t do it their way with their license, you did it a different way with a different license, and that goes against amazon&#39;s freedom to screw you over.

github itself is arguably the epitome of the open source movement. the platform itself is closed source, because they don&#39;t want people to compete with them running their code, and also they sell the very expensive self-hosted version to corporations. opening up the source for github itself would take a chunk out of github&#39;s profits. can&#39;t have that. but they don&#39;t even need to start or adopt an open source component to profit off other people&#39;s labor: *literally every project on github* makes github more valuable. popular projects get people in, network effects bring their colleagues in, and then when it&#39;s time for something that you&#39;d rather have closed source you and everyone else are already on github so you might as well spring for the paid tier. if they believed open source was in principle better, they&#39;d be open source themselves. they believe open source is profitable for them, and corporate profit is by definition value generated by labor but not paid to the laborer.

what&#39;s good for corporations is, of course, bad for people. random individual contributors almost never get paid for their work, even when a corporation or several will profit substantially from those changes. maintainers of vital infrastructure libraries generally only get paid if they wrote the library for or under the control of the company they worked for anyway. professional, corporate maintainers can offer more to the community since they&#39;re getting paid for it, which heightens expectations on independent maintainers and leads to maintainer burnout. and if a company runs off with some existing open source software, they can build their secret competitive advantage around it without giving any of that work back to the original authors.

all of these individual crises are by design: this was always the endgame of the open source movement. the free software movement was transparent with its greatest value: &#34;we believe users should have the freedom to mess with and contribute to the source code of the programs they use.&#34; the open source movement had a far subtler value: &#34;we believe corporations should have the freedom to exploit the labor of developers.&#34; the fact that individual developers were ever on board with the open source movement speaks to the pernicious branding it employs. but people are starting to notice that this isn&#39;t actually good at all.

the free software movement was on occasion writing actually good software; corporations saw that and wanted to get in on it without having to actually have principles. so they embraced the nominal goals of the free software movement and extended it into a more corporate-friendly movement with a larger pile of software to draw from. the conventional step after embrace and extend is, naturally, extinguish. the free software movement died long ago, in no small part due to its own mistakes, so there&#39;s not much left to extinguish. that which is being extinguished, that which died with mozilla, is the idea that the open source movement could have any other principles than corporate exploitation.

=&gt; https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish naturally

i wouldn&#39;t say that the open source movement died per se. it was undead from the moment it began; it won, and with its victory it has stopped pretending to be anything other than a lich. the only meaningful lesson to learn from the open source movement is that letting corporations do whatever the hell they want ends poorly, which is not exactly news.

## not learning

open source won, and nothing got better. in an effort to fix this feature of the open source movement, some people have chosen to repeat the mistakes of the free software movement. as some smart german dude once said, everything in history happens twice, first as tragedy, then as farce.

=&gt; https://en.wikipedia.org/wiki/Karl_Marx some smart german dude

the free software movement declared that the user&#39;s freedom to tinker with and contribute to the software they use is supreme, and they wrote a license specifically built to preserve that in software applied to it, and to spread that freedom to software based on it. an uninspiring but at least well-defined goal, pursued somewhat decently, with at least some lasting success.

the &#34;ethical source&#34; movement declares that the UN&#39;s Universal Declaration of Human Rights is supreme, with relevant laws in whatever jurisdiction is relevant a close second, and the Hippocratic License says &#34;if the software author says you&#39;re violating human rights you have to go through public arbitration or the license is void.&#34; the goal is at least in principle better, so that&#39;s something, at least. although i will say, if someone releases a data visualization library under the Hippocratic License and someone else uses that library to display leaked personal information of police officers who got away with murder, there are several articles of the Universal Declaration of Human Rights that&#39;d arguably be violated, so the library author would likely have grounds to make a nuisance of themself. and that sucks shit. the fact that the website for the Hippocratic License is `firstdonoharm.dev` kinda gives the whole thing away, because sometimes a little harm in one way prevents a much greater harm in some other way. there&#39;s a reason doctors don&#39;t use the hippocratic oath anymore.

even setting that aside, there&#39;s a far greater issue with the Hippocratic License. show me a corporate lawyer who&#39;ll look at a license that says &#34;i can drag you into arbitration proceedings that have to be public whenever i want and there&#39;s no consequences for me doing that in bad faith&#34; and say &#34;yeah that looks good, we can use this library&#34; and i&#39;ll show you a corporate lawyer who&#39;s gonna get fired tomorrow. the free software movement tried and failed to use a license to trick corporations into sharing their values. the ethical source movement appears to be trying to use a worse license to trick corporations into sharing less concretely defined values.

until all the talented people in that community start doing more useful things with their time, we can at least learn a few things from this preemptive failure. one, trying to bake the complexity of an ethical system into your license is a fool&#39;s errand that will not go well. two, if you&#39;re writing a license to coerce companies into behaving differently, don&#39;t scare them off right out of the gate with a poorly considered enforcement system.

## options

the term &#34;post-open source&#34; apparently was used by a couple people in like 2012 to refer to just not giving your code a license. it&#39;s got a wikipedia page that&#39;s had the &#34;this might not be notable enough for wikipedia&#34; box applied to it since 2013. i am declaring that Basically Dead and so i&#39;m using that term in a broader way now.

=&gt; https://en.wikipedia.org/w/index.php?title=Post_open_source&amp;amp;oldid=890953566 wikipedia page

so what do we do after open source has eaten the world? the retro option, apparently, is to skip the license entirely. it&#39;ll scare off the corporations, since they technically can&#39;t safely use your work if you maintain full copyright. and as actual lawyer Luis Villa pointed out at the time, the idea that you need to give other people permission to do things like modify your code for themselves is something we shouldn&#39;t automatically take for granted. (although i must say, for someone who claims to hate &#34;permission culture&#34; so much, Nina Paley sure does seem concerned with giving people permission to count as women. TERFs fuck off, now and forever.) not using a license at all can be interpreted as a conscious rejection not just of copyright but also of the endeavor to wield copyright as a tool for justice at all.

=&gt; https://lu.is/blog/2013/01/27/taking-post-open-source-seriously-as-a-statement-about-copyright-law/ pointed out at the time

however, not using a license at all also makes it complicated for actual human beings who want to use your software. Villa points to a favorite of mine, the Do What The Fuck You Want To Public License, as a way to make the implicit permissiveness of rejecting licensing altogether explicit while preserving the anti-serious aspect. however, once the corporations realize that they&#39;re allowed to use software that says fuck, they can and will exploit the shit out of WTFPL software, so this does not provide a long term solution for the problems with open source. (it is, however, really good, so i will count it as post-open source at heart even though it is essentially just open source). its nominally equivalent but more serious cousin, zero-clause BSD, was written by the same Rob Landley whose experience navigating GPLv2 vs GPLv3 was so unpleasant back in the day; it&#39;s no fun, and i wouldn&#39;t call it a post-open source license, but it is in a very real way a post-free software license, and the exact opposite of the GPL. and in fairness i&#39;d be trying to write the opposite of the GPL after that mess too.

=&gt; http://www.wtfpl.net/ Do What The Fuck You Want To Public License
=&gt; http://landley.net/toybox/license.html zero-clause BSD

the ethical source people are trying to use the hippocratic license to make it illegal to use certain software if you&#39;re doing bad things. the issues with that were the broad definition of &#34;bad things&#34; and the weird enforcement provisions. you can take both of those to the other extreme and get the JSON License. it&#39;s just a regular MIT/BSD/X11/whatever permissive license but with an extra caveat:

=&gt; https://www.json.org/license.html JSON License

&gt; The Software shall be used for Good, not Evil.
now, this is basically decorative (although evidently IBM paid the author for permission to do evil with that software, which is *fucking beautiful*), but it does also scare off corporations while letting normal people do whatever. i actually had a brief twitter exchange with the unparalleled jenn schiffer about the effectiveness of the json license a while back, but she understandably doesn&#39;t let ancient tweets linger forever, so whatever actual points were made there are lost to time. it does at least manage to solve the problems with the hippocratic license, though: the definition of evil is left completely implicit anyway, and the mechanism for enforcement is just copyright law like with any old license. now, since the vagueness is left implicit, there&#39;s room to argue that the clause is unenforceable. nobody has tested it, but that&#39;s a loophole waiting to be exploited, and also it&#39;s not as fun as the WTFPL. as such, right before i started writing this blog post i wrote the fuck around and find out license v0.1 (or FAFOL for short), which replaces the json license&#39;s ethics disclaimer with something more clear:

=&gt; https://twitter.com/boring_cactus/status/1090803883230679040 brief twitter exchange
=&gt; https://git.sr.ht/~boringcactus/fafol/tree/master/LICENSE-v0.1.md fuck around and find out license v0.1

&gt; the software shall be used for Good, not Evil. the original author of the software retains the sole and exclusive right to determine which uses are Good and which uses are Evil.
now it is unambiguous in its intent, and also, it says fuck in the title. as such, it is the only good software license.



=&gt; https://git.sr.ht/~boringcactus/fafol/tree/master/README.md broader infrastructure around the Fuck Around and Find Out License

on a more sincere note, some licenses are trying to solve the problem of corporate exploitation by bringing back into fashion the idea of public-private licenses, where the default license is principled and corporations can simply pay for an exception and be covered by a different license instead. the most interesting of these projects, at least as of August 2020, is license zero, run by actual lawyer Kyle E. Mitchell, which offers two different public licenses, one standard private license template, and infrastructure for automatically selling exceptions. their Parity license is a share-alike license that allows any use that is also published under an open license. their Prosperity license allows any use as long as it is not commercial in nature; as such, it technically doesn&#39;t satisfy the Open Source Definition and is thus in a very concrete sense a post-open source license. their Patreon license, which isn&#39;t linked on their homepage at all, grants an automatic license exception for certain financial supporters. Kat Marchรกn, whose tweet i opened this blog post with, has a blog post of their own explaining one approach to using license zero&#39;s tools as a solution to the open source sustainability crisis. license zero the project appears to be currently working through some branding issues, and might have a different name and structure by the time you&#39;re reading this. but as it stands it&#39;s at least relevant.

=&gt; https://licensezero.com/ license zero
=&gt; https://dev.to/zkat/a-system-for-sustainable-foss-11k9 blog post of their own

an additional concept comes once more from actual lawyer Luis Villa, who was talking about data and not code when he said all this, but i think it can be applied to code too. i&#39;ll let him summarize his post himself:

=&gt; https://lu.is/blog/2016/09/26/public-licenses-and-data-so-what-to-do-instead/ talking about data and not code

&gt; tl;dr: say no to licenses, say yes to norms.
a license is a tool of the law, but the law is not actually very good at delineating the exact boundaries of ethical behavior (in either direction). as such, the approach Villa describes is to tell the law to mind its own damn business and use a maximally permissive license, and then use social norms to delineate what behavior you do and do not find acceptable. norms are tough to start from scratch, but sociologically they can fill a similar role in principle to laws while maintaining flexibility. i&#39;m not quite sure what a normative approach to post-open source software would look like - i&#39;m not aware of anyone attempting to implement it, and i&#39;m not sure i&#39;m ready to be the first - but most likely it&#39;d combine the WTFPL (or, more plausibly, zero-clause BSD) with an ecosystem of standard sets of norms similar to the current varieties in codes of conduct.



=&gt; https://writing.kemitchell.com/2019/03/15/Ethical-Subcommons.html that proposal

and since i&#39;ve been quoting him the whole time, i should probably also give a shout out to actual lawyer Luis Villa&#39;s current project, Tidelift, which is trying to address open source funding at both ends. for corporate clients, (it looks like) tidelift is selling known-good, actively maintained, secure dependency subscriptions, and for open source maintainers, they&#39;re (i think) offering not just a proportional cut of the subscription revenue but also resources for keeping projects good, maintained, and secure. i haven&#39;t used it on either end, i&#39;m just paraphrasing their marketing copy, but they do exist.

=&gt; https://tidelift.com/ Tidelift

## evaluation

so those are some options for what we do next. which ones are good? here we venture even deeper into my arbitrary, poorly informed, untrustworthy opinion than we already were.

rejecting licenses altogether is fun but feels kinda halfhearted. like, if you think licenses are a waste of time, you can just use the WTFPL and get in principle the same effect but with more gratuitous profanity. also, you can&#39;t refer to software that doesn&#39;t have a license as &#34;unlicensed&#34; because some chucklefucks decided to make a license called The Unlicense and so it&#39;s ambiguous now. &#34;license-free&#34; just doesn&#39;t have the same ring to it. and more concretely, if you have a real organization that wants to do actual good with your work, they probably still have a lawyer telling them copyright exists, and it&#39;d be good to give them explicit permission to be doing good things.

the zero-clause BSD license is the most open of open source licenses, and as such it inherits all the issues of the open source movement it is attached to. it&#39;s essentially the same as releasing your software to the public domain, skipping copyright entirely and giving basically unlimited permissions, and therefore providing no protection from exploitation. the WTFPL is in practice probably equivalent, but in theory scares off lawyers by being impossible to take seriously. however, i remember when i got into dogecoin back when dogecoin was a Thing i thought it was impossible to take seriously and therefore immune from the cryptocurrency true believers, and that wound up pretty decidedly not happening. so a surface-level anti-serious tone is not foolproof protection against bad things. i&#39;ve released software under the WTFPL, and i&#39;d likely do it again for code i have no interest in maintaining for other people&#39;s benefit, but i would never use it as a license for a library i wanted other people to use.

if i recall correctly, the JSON license was explicitly intended as a jab at corporate lawyers, and so it is close in spirit to the WTFPL. and my FAFOL makes that harmony even greater. but as we (or at least some of us) learned from the free software movement, using a license as a tool of enacting corporate ethics by proxy is essentially impossible; corporations interpret ethics as damage and route around them. so as funny as it is, it&#39;s not actually useful.

license zero&#39;s work has a chance to succeed at creating funding opportunities for software maintainers. if you&#39;re going to want to cut into a corporation&#39;s profits, the best way that can go for you is if they can budget in the cost of your exception. i&#39;m not aware of instances of that actually working super well for anybody, but maybe it&#39;s happened and i haven&#39;t heard, or maybe it&#39;ll happen as time goes on. same goes for tidelift, honestly. trying to solve specifically the maintainer-side economic issues is an approach i&#39;m not qualified to evaluate, but it definitely feels more like a medium-term patch to the open source movement than a long-term fix.

i think the normative approach is at least in principle worth exploring, and maybe the next project i release will be stapled to an experiment with that. it definitely feels like it has potential, and could actually supplant the open source model if it works well. plus the idea of replacing laws with explicit but informal expectations and letting the community self-regulate warms my anarchist heart.

## conclusion

FOSS is dead. free software died long ago, and open source software was a lich the whole time, only now claiming victory and beginning to pull up the ladder behind it. what will come next?

i can&#39;t predict every aspect of the post-open source movement, but i can tell you one thing it&#39;ll absolutely require if it&#39;s going to be meaningful.

what really killed mozilla? what really killed free software? what really gave us the already-dead open source movement?

optimizing for profit at the expense of any other consideration. chasing short-term gains and ignoring long-term sustainability or justice. squeezing every drop of surplus value out of every person within reach and putting it in the hands of a dozen investors and overpaid executives.

in a word, capitalism.

if post-open source wants to not die the same death, it will need to explicitly and aggressively fight its greatest existential threat.</content>
    </entry>
    
    <entry>
        <title>Monads, Explained Without Bullshit</title>
        <link href="gemini://boringcactus.com/2020/07/18/monads-without-the-bullshit.gmi" rel="alternate" type="text/gemini" title="Monads, Explained Without Bullshit" />
        <published>2020-07-18T00:00:00Z</published>
        <updated>2020-07-18T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/07/18/monads-without-the-bullshit.gmi</id>
        <content type="text/gemini">there&#39;s a CS theory term, &#34;monad,&#34; that has a reputation among extremely online programmers as being a really difficult to understand concept that nobody knows how to actually explain. for a while, i thought that reputation was accurate; i tried like six times to understand what a monad is, and couldn&#39;t get anywhere. but then a friend sent me Philip Wadler&#39;s 1992 paper &#34;Monads for functional programming&#34; which turns out to be a really good explanation of what a monad is (in addition to a bunch of other stuff i don&#39;t care about). so i&#39;m gonna be repackaging the parts i like of that explanation here.

=&gt; https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf Philip Wadler&#39;s 1992 paper &#34;Monads for functional programming&#34;

math jargon is pretty information-dense for me, though, and my eyes tend to glaze over pretty quickly, so i&#39;ll be using Rust (or an idealized version thereof) throughout this post instead of math.

=&gt; https://www.rust-lang.org Rust

so, a monad is a specific kind of type, so we can think of it like a `trait`:

trait Monad {

// TODO

}


Rust has two types that will be helpful here, because (spoilers) it turns out they&#39;re both monads: `Vec` and `Option`. now, if you&#39;ve worked with Rust before, you might be thinking &#34;wait, don&#39;t you mean `Vec&lt;T&gt;` and `Option&lt;T&gt;`?&#34; and that&#39;s a reasonable question to ask, since Rust doesn&#39;t really let you just say `Vec` or `Option` by themselves. but as it happens, the monad-ness applies not to a specific `Vec&lt;T&gt;` but to `Vec` itself, and the same goes for `Option`. which means what we&#39;d like to do is say

impl Monad for Vec {}

impl Monad for Option {}


but Rust won&#39;t let us do that because we can&#39;t talk about `Vec`, only `Vec&lt;T&gt;`. this is (part of) why Rust doesn&#39;t have monads. so let&#39;s just kinda pretend that&#39;s legal Rust and move on. what operations make a monad a monad?

## new

Wadler calls this operation `unit`, and Haskell calls it `return`, but i think it is easier to think of it as `new`.

trait Monad {

fn new&lt;T&gt;(item: T) -&gt; Self&lt;T&gt;;

}


`new` takes a `T` and returns an instance of whatever monad that contains that `T`. it&#39;s pretty straightforward to implement for both `Option` and `Vec`:

impl Monad for Option {

fn new&lt;T&gt;(item: T) -&gt; Self&lt;T&gt; { Some(item) }

}

impl Monad for Vec {

fn new&lt;T&gt;(item: T) -&gt; Self&lt;T&gt; { vec![item] }

}


we started out with a stuff, and we made an instance of whatever monad that contains that stuff.

## flat_map

Wadler calls it `*`, Haskell calls it &#34;bind&#34; and spells it `&gt;&gt;=`, but i think `flat_map` is the best name for it.

trait Monad {

fn flat_map&lt;T, U, F: Fn(T) -&gt; Self&lt;U&gt;&gt;(data: Self&lt;T&gt;, operation: F) -&gt; Self&lt;U&gt;;

}


we have an instance of our monad containing data of some type `T`, and we have an operation that takes in a `T` and returns the same kind of monad containing a different type `U`. we get back our monad containing a `U`.

as you may have guessed by how i named it, `flat_map` is basically just `Iterator::flat_map`, so implementing it for `Vec` is fairly straightforward. for `Option` it&#39;s literally just `and_then`.

impl Monad for Option {

fn flat_map&lt;T, U, F: Fn(T) -&gt; Self&lt;U&gt;&gt;(data: Self&lt;T&gt;, operation: F) -&gt; Self&lt;U&gt; {

data.and_then(operation)

}

}

impl Monad for Vec {

fn flat_map&lt;T, U, F: Fn(T) -&gt; Self&lt;U&gt;&gt;(data: Self&lt;T&gt;, operation: F) -&gt; Self&lt;U&gt; {

data.into_iter().flat_map(operation).collect()

}

}


so in theory, we&#39;re done. we&#39;ve shown the operations that make a monad a monad, and we&#39;ve given their implementations for a couple of trivial monads. but not every type implementing this trait is really a monad: there are some guarantees we need to make about the behavior of these operations.

## monad laws

(written with reference to the relevant Haskell wiki page)

=&gt; https://wiki.haskell.org/Monad_laws the relevant Haskell wiki page

like how there&#39;s nothing in Rust itself to ensure that your implementation of `Add` doesn&#39;t instead multiply, print a dozen lines of nonsense, or delete System32, the type system is not enough to guarantee that any given implementation of `Monad` is well-behaved. we need to define what a well-behaved implementation of `Monad` does, and we&#39;ll do that by writing functions that assert our `Monad` implementation is reasonable. we&#39;re going to have to also cheat a bit here and deviate from actual Rust by using `assert_eq!` to mean &#34;assert equivalent&#34; and not &#34;assert equal&#34;; that is, the two expressions should be interchangeable in every context.

first off, we have the &#34;left identity,&#34; which says that passing a value into a function through `new` and `flat_map` should be the same as passing that value in directly:

fn assert_left_identity_holds&lt;M: Monad&gt;() {

let x = 7u8; // this should hold for any value

let f = |n: u8| M::new((n as i16) + 3); // this should hold for any function

assert_eq!(M::flat_map(M::new(x), f), f(x));

}


next, we have the &#34;right identity,&#34; which says that &#34;and then make a new monad instance&#34; should do nothing to a monad instance:

fn assert_right_identity_holds&lt;M: Monad&gt;() {

let m = M::new(&#39;z&#39;); // this should hold for any instance of M

assert_eq!(M::flat_map(m, M::new), m);

}


and last but by no means least we have associativity, which says it shouldn&#39;t matter the sequence in which we apply `flat_map` as long as the arguments stay in the same order:

fn assert_associativity_holds&lt;M: Monad&gt;() {

let m = M::new(false); // this should hold for any instance of M

let f = |data: bool| if data { M::new(3usize) } else { M::new(7usize) }; // this should hold for any function

let g = |data: usize| M::new(vec![&#34;hello&#34;; data]); // this should hold for any function

assert_eq!(

M::flat_map(M::flat_map(m, |x: bool| f(x)), g),

M::flat_map(m, |x: bool| M::flat_map(f(x), g))

);

}


so now we can glue all those together and write a single function that ensures any given monad actually behaves as it should:

fn assert_well_behaved_monad&lt;M: Monad&gt;() {

assert_left_identity_holds::&lt;M&gt;();

assert_right_identity_holds::&lt;M&gt;();

assert_associativity_holds::&lt;M&gt;();

}


## but. why

well. monads exist in functional programming to encapsulate state in a way that doesn&#39;t explode functional programming (among other things, please do not @ me). Rust isn&#39;t a functional programming language, so we have things like `mut` to handle state.

there&#39;s a bit of discussion in Rust abt how monads would be actually implemented - the hypothetical extended Rust that i use here is not actually what anyone advocates for, you can look around for yourself if you care - but even the people in that discussion seem to not really explain why Rust needs monads. so all of this doesn&#39;t really build up to anything. but hey, now (with luck) you understand what monads are! i hope you find that rewarding for its own sake. i hope i do, too.</content>
    </entry>
    
    <entry>
        <title>What, Then, Shall We Do?</title>
        <link href="gemini://boringcactus.com/2020/07/15/what-then-shall-we-do.gmi" rel="alternate" type="text/gemini" title="What, Then, Shall We Do?" />
        <published>2020-07-15T00:00:00Z</published>
        <updated>2020-07-15T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/07/15/what-then-shall-we-do.gmi</id>
        <content type="text/gemini">america is at a crossroads of sorts. widespread dissatisfaction with how governments at all levels handled the COVID-19 pandemic, decades of police brutality overwhelmingly targeting POC, two presidential candidates nobody (or at least nobody worth a damn) really likes. we (by which i mean the american left, broadly speaking) see these futures unfold before us, and ask ourselves &#34;what, then, shall we do?&#34; i&#39;m just some random dipshit, i&#39;m not qualified to give an answer, but i can think about what we shall do specifically when it comes to the upcoming presidential election.

## the very marginally lesser of two evils

obviously voting for donald trump is inexcusable. he&#39;s a deranged senile incestuous racist rapist whose occasional halfhearted gestures towards the working class are transparently borne of a desire for self-preservation rather than any actual good will. he may occasionally claim to be an ally for queer people, or people of color, or women, but his record stands in contrast to any of these claims - he has done more material harm to these groups than he has even bothered to acknowledge - and so only profound gullibility could lead anyone to believe this. even the most negligible support of him is also a tacit acceptance of the entire political system that allowed him to gain what power he already has, and trusting that system to hold him in check is foolish.

oh shit did i say donald trump there? i meant joe biden. and also donald trump.

## the least of a dozen evils

so joe biden sucks, and we cannot start ridin with biden. the democratic primary was what could generously be described as a mess, with the maybe-depending-on-who-you-ask winner of the first primary dropping out early to try to donate his momentum to an even more useless, even more racist moderate. the party establishment threatened to punish states that postponed their primaries in light of an ongoing pandemic that made it unsafe to vote, contributing to widespread voter suppression. clearly the democratic party is no friend to the left, and cannot be trusted. so what other parties can we support?

well, about that. let&#39;s look at the third parties that were around in 2016. the libertarian party is - setting aside the libsoc caucus, who are probably cool but wasting their time - just for conservatives who want to smoke weed, and the constitution party is for religious conservatives who are too cool for the republican party, so neither of those is worth a damn. we can also ignore the tiny parties that only exist in like three states. that leaves us with like two third parties that could conceivably be worth caring about.

=&gt; https://en.wikipedia.org/wiki/Third-party_and_independent_candidates_for_the_2016_United_States_presidential_election third parties that were around in 2016

most notably, we&#39;ve got the green party. on the ballot almost everywhere, which is nice. they got like 2.5% of national votes back in 2000, and they cleared 1% in 2016. their 2020 nominee is Howie Hawkins, who apparently worked with Murray Bookchin for a while, and as a communalist myself i gotta say that&#39;s pretty epic. however. according to somebody who was running in the green primary and lost, there were some party-level shenanigans pulled to swing the process towards hawkins - local parties officially hosting hawkins&#39; campaign events before the primary was held, party officials also being hawkins campaign staff, etc. considering that a lot of ppl are mad at the democratic party for pulling the same shit, that&#39;s not great. and also hawkins has been kinda both sidesy abt antivaxx stuff, which is cringe. you&#39;re allowed to not care if you want, but i&#39;m not sure &#34;get the greens past the 5% threshold so they get federal funding in 2024&#34; is really a capital-S Solution.

in the other corner we have the Party for Socialism and Liberation. socialism and liberation are both cool things to have. also their vice presidential candidate is Leonard Peltier, a political prisoner allegedly involved in the murder of two FBI agents, so that&#39;s pretty based. however, they were only on the ballot in a handful of states in 2016, so they may or may not even have enough reach to be worth caring about even if they nominated Marx and Engels themselves. also even in the states where they exist they haven&#39;t managed to achieve any meaningful electoral results. and one of their most prominent members, Michael Prysner, is a military intelligence war crimes doer; transformative justice is a cool thing and all but for fuck&#39;s sake assume military intelligence dudes are compromised six ways from sunday and don&#39;t let them have major influence in your vanguard. again, feel free to not care, but i do.

there are non-political-party organizations like the DSA, which i&#39;m a dues-paying member of, but i don&#39;t think pivoting one of them into a political party or spinning up something new would really help either. ballot access is key to making a case for having a chance to win despite being a third party, and even the DSA couldn&#39;t get that in most states on a shorter than several years scale.

so. the green party is recreating the favoritism that was part of why we&#39;re pissed off at the democratic party, and the PSL is hanging out with war criminals while being missing or useless everywhere, and anything else will take eons to get off the ground and so doesn&#39;t give us anything to do in the meantime. what, then, shall we do?

## no fate but what we make for ourselves

maybe electoralism is a waste of energy. maybe voting is a waste of time. when we vote, we don&#39;t get shit. when we take to the streets and burn shit down, we are heard.

politicians already don&#39;t feel obligated to try to earn your vote. make them earn peace. they should be afraid of us, them and the cops and everyone who works to defend and prop up the indefensible on a daily basis. there are more of us than there are of them. violence is bad but complacency is worse. in the 60s it was the ballot or the bullet, and comrades, i don&#39;t think the ballot has done what it needed to.

=&gt; https://en.wikipedia.org/wiki/The_Ballot_or_the_Bullet the ballot or the bullet</content>
    </entry>
    
    <entry>
        <title>Setting Up A Police Scanner With An RTL-SDR</title>
        <link href="gemini://boringcactus.com/2020/06/26/police-scanner-setup.gmi" rel="alternate" type="text/gemini" title="Setting Up A Police Scanner With An RTL-SDR" />
        <published>2020-06-26T00:00:00Z</published>
        <updated>2020-06-26T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/06/26/police-scanner-setup.gmi</id>
        <content type="text/gemini">so my city has a community-run police scanner broadcast on the internet, but the person who runs it is a bootlicker who&#39;s been threatening to shut it down if people are using it to make trouble for the cops. so i figured i&#39;d set up my own. this is how i did it, hope it&#39;s useful.

## shopping

you&#39;ll need an RTL-SDR unit. i recommend the dipole antenna kit as well, so you don&#39;t need to make any additional purchases. if you&#39;re a radio enthusiast already, you might have a better antenna available, but if you&#39;re like me you do not and it&#39;s worth the US$10. mine took a bit over a week to arrive. if you&#39;re extremely unlucky, you might need two of them, but i was fine with just one.

=&gt; https://www.rtl-sdr.com/buy-rtl-sdr-dvb-t-dongles/ an RTL-SDR unit

## basic setup

once your RTL-SDR arrives, you&#39;ll want to put together your antenna. if you&#39;re lucky, like i am, you can just extend the antennas arbitrarily and it&#39;ll work fine; if you&#39;re cursed, the RTL-SDR website has resources on how long is ideal for various frequencies.

connect the antenna to the RTL-SDR unit, plug it in, and follow the RTL-SDR quick start guide. SDRSharp will work, or any of the other Windows options. some of what we&#39;ll need is only available on windows.

=&gt; https://www.rtl-sdr.com/rtl-sdr-quick-start-guide/ quick start guide

once your RTL-SDR&#39;s drivers are sorted out, find the specifications for police radio in your area on RadioReference. click your state, click your county, scroll down and see if there&#39;s a link above a frequency table for you. if you&#39;re lucky, there is, and if you click it there&#39;s a page with a table with System Type and System Voice entries at the top. mine has a system type of EDACS Networked Standard and a system voice of ProVoice and Analog, so the rest of this assumes that&#39;s what you&#39;ve got as well. if not, good luck.

=&gt; https://www.radioreference.com/apps/db/ RadioReference

there should be a table for System Frequencies on your RadioReference page. start up SDRSharp and tune your radio to the first frequency listed there. you&#39;ll probably hear a bunch of static and the UI will look something like this:

=&gt; /assets/2020-06-26-police-scanner-setup-1.png one constant signal and a bunch of other signals coming in and out at other frequencies [IMG]

see how there&#39;s one constant signal and a bunch of other signals that appear and disappear all over the place? well, that&#39;s trunking, and the constant signal is our *control channel*. if you don&#39;t see it, you can click and drag on the bottom axis of the top panel to change the view. once you&#39;ve found that constant signal, click on it to get the approximate frequency, go back to your frequency table and the closest thing to that will be the exact frequency. it should sound like a series of weird beeps instead of static. remember that frequency, it&#39;ll be important later.



## specific setup

EDACS is a trunked system, so we&#39;re using RTL-SDR&#39;s trunked radio tutorial as our guide, mostly. that guide assumes we have two RTL-SDRs, but there&#39;s a piece at the end explaining how to do it with just one. that sucks. i&#39;m going to paraphrase it here.

=&gt; https://www.rtl-sdr.com/rtl-sdr-tutorial-following-trunked-radio-unitrunker/ RTL-SDR&#39;s trunked radio tutorial

first, we&#39;re going to download the software we need: Unitrunker, VB-Cable, and DSD+ (extract both the regular and DLL downloads to the same folder). install unitrunker and VB-Cable and extract dsd+ somewhere convenient. you might need to reboot after installing VB-Cable because computers are bad. VB-Cable might set your default input and output devices to the wrong things when you install it, so switch them back if it does.

=&gt; http://www.unitrunker.com/ Unitrunker
=&gt; http://www.vb-cable.com/ VB-Cable
=&gt; https://www.dsdplus.com/ DSD+

open up dsd+. it&#39;ll open four different windows, one of them should have a list of audio input and output devices. check the number in the input list that goes with CABLE Output - for me it&#39;s 3. pull up notepad and make a new file. since my input was number 3, i&#39;m typing

DSDPlus.exe -i3M


in that file: if yours is not 3, put whatever the correct number is for you instead of 3. then, save the file, find your DSDPlus folder, make sure the type is set to &#34;All Files&#34;, and name the file `run.bat`. close dsd+, go to that folder, and open that `run.bat` file you just created. it should pull up dsd+ and if you&#39;re lucky it&#39;ll print

audio input device #3 (CABLE Output (VB-Audio Virtual ) initialized


or something like that. leave that open.

open up unitrunker. click the `+` to add a new receiver, and click the RTL2832 button to add your RTL-SDR. set your settings around like this:

=&gt; /assets/2020-06-26-police-scanner-setup-2.png the RTL-SDR settings [IMG]

the most important things are the RTL Device, the sample rate (2.56 msps), and the VCOs (2 VCOs). i do not know what a VCO is and i do not care enough to find out. we should now have two VCO tabs next to our info tab. the first one needs to look kinda like this:

=&gt; /assets/2020-06-26-police-scanner-setup-3.png the RTL-SDR VCO 1 settings [IMG]

the important things are the Role being Signal, the Park frequency being the control channel we found earlier (mine is 851.7625), and the Mute box being checked.



the second VCO should look kinda like this:

=&gt; /assets/2020-06-26-police-scanner-setup-4.png the RTL-SDR VCO 2 settings [IMG]

the important things are the Role being Voice, the Deemphasis box being unchecked, and the Digital Output being set to your CABLE Input. this means it will connect up with dsd+ listening to our CABLE Output.

press Play now; it should pull up a window with a Channels tab. the Channels tab should look something like this:

=&gt; /assets/2020-06-26-police-scanner-setup-5.png the unitrunker channels tab [IMG]

but the Frequency column will all be zeroes except for the control frequency we found earlier. you&#39;ll need to copy over the rest of the frequencies manually from the RadioReference site.



press the stop button and the play button again, and everything should in theory be working. ideally, the Call History tab will be crowded and updating pretty frequently, and unitrunker will be passing things along to dsd+ which will give us the audio we want. technically, this is enough.

## groups

the thing, though, is we don&#39;t have context for any of this. for now, at least. RadioReference should have a table or several of talkgroups - the &#34;list all in one table&#34; button may come in handy - and we can use that information to figure out who we&#39;re hearing, and have at least some control over who takes priority if multiple people in different contexts are talking at once.

find the main unitrunker window - it&#39;s titled &#34;Universal Trunker&#34; and if you don&#39;t have it open just click the home button a bunch until it opens - and then open the Systems tab and double-click the one that exists. open the Groups tab in that window, and it should give you a massive list with columns for ID, Label, and a bunch of stuff we don&#39;t care about right now. the ID matches up with the DEC column in the RadioReference table, and the Label can be either &#34;Description&#34; or &#34;Alpha Tag&#34; or something you make up yourself if you feel creative. if you pay RadioReference $15 for a Premium subscription then unitrunker can import that data automatically.

once you&#39;ve filled that all in, open the Sites tab and double-click the entry you see there, then open the Call History tab. the group labels you added should now be appearing in the Audience column; the LCN and Frequency should turn green for what unitrunker is currently listening to.

back in the Groups tab, you can edit the Priority values to control which groups will be chosen more often - as far as i can tell, higher priority groups will interrupt lower priority groups, and equal priority groups will just play whoever started talking first.

## broadcasting

this setup lets you listen to things locally, but what if you want your comrades with no hardware to be able to also listen? the laziest option is to just stream the Call History window on Twitch or something, but in theory there are better options. RadioReference runs Broadcastify, which is designed for hosting police scanner livestreams, but they have to manually approve your broadcast, which is annoying for short term activity. you could run an icecast server yourself or something, but that takes effort to configure. honestly all of those kinda suck but those are your options as far as i know.



=&gt; https://www.vb-audio.com/Cable/index.htm#DownloadCable VB-CABLE A+B
=&gt; https://danielnoethen.de/butt/ butt (broadcast using this tool)

you&#39;ll need to set DSD+ to output to &#34;CABLE-A Input&#34; like how you set it to input from &#34;CABLE Output&#34; - Cable A is the fourth output in DSD+, so my run.bat now looks like this:

DSDPlus.exe -i3M -o4


run butt, pull up the settings, and under the Audio tab set the Input Device to &#34;CABLE-A Output&#34;. (for bonus points, set the Streaming Codec to AAC+.) under the Main tab, Add a new Server and put in whatever info your icecast server admin told you to use. now restart your DSD+ and hit butt&#39;s play button to start streaming, and you should be running a livestream of your police scanner that is accessible over the internet.</content>
    </entry>
    
    <entry>
        <title>Lifehack: Running An Entire Desktop Session Remotely With MobaXterm</title>
        <link href="gemini://boringcactus.com/2020/03/20/mobaxterm-desktop-session.gmi" rel="alternate" type="text/gemini" title="Lifehack: Running An Entire Desktop Session Remotely With MobaXterm" />
        <published>2020-03-20T00:00:00Z</published>
        <updated>2020-03-20T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2020/03/20/mobaxterm-desktop-session.gmi</id>
        <content type="text/gemini">Since my university has gone as remote as possible due to coronavirus, I was looking at ways to run an entire desktop session remotely over SSH, using MobaXterm because it is very cool. Here are the two steps to doing that.

1. Open your MobaXterm settings, go to the X11 tab, and make sure that the server display mode is set to windowed mode. If you run individual programs over X11 forwarding, this is worse, but for an entire desktop session it is better.
2. Duplicate your regular command line session that already works, and under the &#34;Advanced SSH settings&#34; tab, set &#34;Execute command&#34; to `env GNOME_SHELL_SESSION_MODE=ubuntu gnome-session --session=ubuntu`. (If you&#39;re not running the same setup I am, look around in `/usr/share/xsessions/`, pick something that looks reasonable, and use everything after `Exec=` on the line with that.)

At this point, you should be set. You&#39;ll need to hit the &#34;log out&#34; button to smoothly exit the connection. For me, this is extraordinarily slow, but that could easily be just because the machines I&#39;m connecting to are being used a lot.</content>
    </entry>
    
    <entry>
        <title>Rust 2020: Write Once, Run Anywhere</title>
        <link href="gemini://boringcactus.com/2019/11/03/rust-2020.gmi" rel="alternate" type="text/gemini" title="Rust 2020: Write Once, Run Anywhere" />
        <published>2019-11-03T00:00:00Z</published>
        <updated>2019-11-03T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2019/11/03/rust-2020.gmi</id>
        <content type="text/gemini">thing that is cool: writing the same codebase and having it run on desktop, mobile, and web

thing that is lame: JavaScript is the only language where people really do that right now, outside of big commercial game engines

things that need to happen for Rust to get there:

1. promote more platforms to tier 1, or maybe introduce a &#34;tier 1.5&#34; where std is guaranteed to work but rustc and cargo are not (although it&#39;d be cool for rustc to work on WebAssembly) * iOS: `aarch64-apple-ios`, `armv7-apple-ios`, `i386-apple-ios`, `x86_64-apple-ios` * Android: `aarch64-linux-android`, `arm-linux-androideabi`, `i686-linux-android`, `thumbv7neon-linux-androideabi`, `x86_64-linux-android` * WebAssembly: `wasm32-unknown-unknown` (or one of the other `wasm32` targets)
2. test platform abstractions (graphics libraries, game engines, UI frameworks) on all of those
3. get some high-level examples together of how to use Rust to write performant cross-platform code</content>
    </entry>
    
    <entry>
        <title>Email Notifications for SSH Logins From Scratch</title>
        <link href="gemini://boringcactus.com/2019/03/09/email-on-ssh-auth.gmi" rel="alternate" type="text/gemini" title="Email Notifications for SSH Logins From Scratch" />
        <published>2019-03-09T00:00:00Z</published>
        <updated>2019-03-09T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2019/03/09/email-on-ssh-auth.gmi</id>
        <content type="text/gemini">I just spent a while trying to make this happen, so I&#39;m putting this here so I don&#39;t have to redo all that research next time.

### Configuring Email

This guide from Linode explains how to install and configure Postfix, which you&#39;ll need. Be careful, though: when it says `[mail.isp.example]` the `[]` aren&#39;t just to indicate placeholders. You do need a literal `[]` around your hostname in your Postfix configuration.

=&gt; https://www.linode.com/docs/email/postfix/postfix-smtp-debian7/ This guide from Linode

Also, if your setup is like mine, if you try to send email from `x@&lt;your domain&gt;` to `y@&lt;your domain&gt;`, Postfix will unhelpfully try to deliver it locally. This Server Fault answer explains how to tell Postfix to not do that.

=&gt; https://serverfault.com/a/433305 This Server Fault answer

If you&#39;re really unlucky, you may also need to create local users `x` and `y` (with `useradd -M -N -s /bin/false &lt;username&gt;`). I did that before I fixed the Postfix config, so fixing the Postfix config may be enough.

### Configuring SSH

Thankfully, by the time you&#39;ve got the email configuration out of the way, this guide from VPSInfo fully explains how to set up SSH to send emails on login. This will send emails even if no login shell is run on the ssh connection.

=&gt; https://www.vpsinfo.com/tutorial/email-alert-ssh-login/ this guide from VPSInfo

Since you need to store your credentials in the Postfix configuration, a sufficiently motivated attacker could probably retrieve them. As such, if you&#39;re using email notifications to detect security breaches, I would suggest not sending them to the same address that they&#39;re being sent from.

As a security measure, this is purely reactive; you can know that someone has illegitimately connected, but whatever they&#39;re trying to do has already been done. A proactive measure would be to implement 2FA on SSH logins, as per this guide from DigitalOcean.

=&gt; https://www.digitalocean.com/community/tutorials/how-to-set-up-multi-factor-authentication-for-ssh-on-ubuntu-16-04 this guide from DigitalOcean</content>
    </entry>
    
    <entry>
        <title>Announcing vidslice</title>
        <link href="gemini://boringcactus.com/2019/02/27/vidslice.gmi" rel="alternate" type="text/gemini" title="Announcing vidslice" />
        <published>2019-02-27T00:00:00Z</published>
        <updated>2019-02-27T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2019/02/27/vidslice.gmi</id>
        <content type="text/gemini">I just released version 1.0 of vidslice, a wxPython GUI that wraps ffmpeg and youtube-dl to make &#34;give me from 1:03-1:15 of this youtube video&#34; really easy to do. More details are in the project README.

=&gt; https://github.com/boringcactus/vidslice vidslice</content>
    </entry>
    
    <entry>
        <title>Futures</title>
        <link href="gemini://boringcactus.com/2018/08/31/futures.gmi" rel="alternate" type="text/gemini" title="Futures" />
        <published>2018-08-31T00:00:00Z</published>
        <updated>2018-08-31T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2018/08/31/futures.gmi</id>
        <content type="text/gemini">I&#39;m going to be graduating from college in December and I&#39;m a little bit freaking out. Not because I don&#39;t have a plan, but because I have six plans.

## Melody the grad student

I may hate college, but that doesn&#39;t mean I&#39;m not good at it. I&#39;m still not entirely sure if it&#39;s college in general I hate or just my college in particular, but I&#39;ve got a hunch it&#39;s the latter. In that case, I could absolutely try to get a master&#39;s in computer science at a halfway decent university.

This would be the next step towards getting a Ph.D. and becoming a computer science professor, which is a thing that I think would be super cool. However, the step itself would take a whole bunch of work and I don&#39;t necessarily have all that much interest in CS theory. Someone suggested that I could pursue a master&#39;s in philosophy instead, which is an intriguing but counterintuitive prospect that I haven&#39;t necessarily thought all the way through yet.

I don&#39;t think I could take another semester of college right now, but most places are probably set up for people to start in the fall and not the spring, so that works out.

## Melody the indie miscellany dev

I am a walking collection of half-baked project ideas waiting to be built. Web application that bridges Twitter content into the ActivityPub/OStatus fediverse (Mastodon et al). No-code-required Discord bot toolkit. TodoMVC but for desktop GUI frameworks.

=&gt; http://todomvc.com/ TodoMVC

If I had the time, I could actually build some of those things. I&#39;d need to make money somehow, though; if this were me full time I&#39;d probably be on Patreon or trying to figure out non-shady ways to monetize this stuff. However, being solely responsible for things that people actually use would be super stressful. Plus I don&#39;t think I have the discipline to do anything if there&#39;s nobody to make me do it.

## Melody the indie game dev

Games are cool. I&#39;ve made a handful already for game jams and whatnot. Actually, the first thing I actually did as boringcactus was make a game for a game jam.

The trouble is that I don&#39;t have a big-I Idea that I would pursue if I decided I wanted to be an indie dev. Most of my jam games are either finished or not worth finishing. I&#39;d love to make something that&#39;s an indie success story like Night in the Woods or Gunpoint, but those games already exist. Plus the odds of taking off like that are kinda one-in-a-million.

## Melody the musician



What I&#39;d really like to do is live looping covers of things that weren&#39;t really designed to be replicated like that. I think something like this live performance of Marian Hill&#39;s *Down* would be super amazing to do with more complicated songs. I wrote a REAPER plugin that would help with stuff like that, but I haven&#39;t actually used it for anything yet.

=&gt; https://youtu.be/uc9Bi52ZQ7A?t=30s this live performance of Marian Hill&#39;s *Down*
=&gt; https://www.reaper.fm/ REAPER

## Melody the small tech co employee

This is a rant that I&#39;ll probably give in more detail some other time, but I think the Silicon Valley venture capital model is directly responsible for how shitty the tech industry is.

If I wanted traditional employment, the best environment for it would be a small company that isn&#39;t beholden to a VC firm. A company that can keep its standards and leave growth on the table if it&#39;d be unethical to pursue it is a company that I&#39;d probably be comfortable working for.

## Melody the sellout

Of course, small companies don&#39;t get that way by hiring every rando with a rรฉsumรฉ. Big companies with bad ethics are almost always hiring. And they&#39;re the most likely to still be around several years from now.</content>
    </entry>
    
    <entry>
        <title>Some Thoughts About Work</title>
        <link href="gemini://boringcactus.com/2018/07/17/some-thoughts-about-work.gmi" rel="alternate" type="text/gemini" title="Some Thoughts About Work" />
        <published>2018-07-17T00:00:00Z</published>
        <updated>2018-07-17T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2018/07/17/some-thoughts-about-work.gmi</id>
        <content type="text/gemini">I think there are three things that can make work rewarding:

1. Working with cool people
2. Working on cool things
3. Working in cool ways

Right now (summer of 2018) I am working with cool people but on boring things in less-than-ideal ways. I think that&#39;s the main reason I don&#39;t really enjoy this internship. But hey, it&#39;ll be over in two weeks.

## Working with cool people

It&#39;s nice to work with cool people. People I&#39;m more comfortable working with are people I&#39;ll work better with. People whose judgment I trust are people whose leadership I&#39;ll find easy to respect.

## Working on cool things

If I can explain the thing I&#39;m working on to somebody and get them excited about it, that&#39;s great. If I&#39;m excited about it but nobody else is, that&#39;s kinda enough. If even I think the project is kinda boring, I&#39;ll still get it done, but I won&#39;t enjoy the process.

## Working in cool ways

I&#39;m not sure there is a right way to run big projects, but I know there are a bunch of wrong ways. The more people are going to wind up touching your project, the more important it is to get the architecture right. If your project is going to be big, don&#39;t put all the important guts of it in the same 4000-line `switch` statement.

Line-by-line code reviews with the entire team can be helpful if everybody needs to know exactly what&#39;s going on, or if everybody might have something valuable to say or to learn. Line-by-line reviews of automatically generated XML are...less likely to be helpful.

## What that might mean

I don&#39;t know that I&#39;d function well if I were self-employed. I doubt I have the motivation or discipline to pull it off, plus then I wouldn&#39;t get to work with cool people.

I know &#34;cool&#34; is a relative term, but I think I might be a little too picky about the sort of thing I&#39;d want to work on. I think Silicon Valley venture capital culture is such that the money has gotten so dense it&#39;s formed a black hole from which no ethics and no perspective can escape. Advertising sucks, just about every blockchain was a mistake, and AI is going to spend the rest of the century wobbling between the almost-not-useless of voice assistants today and the actively-making-things-worse that YouTube has running demonetization and recommendations. Not all growth is worth it; it&#39;s not just OK but good to leave money on the table if you can walk away with your soul (this is the part VC tends to ruin). I don&#39;t know what&#39;s good, but I know what&#39;s bad, and frankly most stuff is bad.

Doing big projects right is hard. My solution would be to just not do big projects. Somebody has to, but it doesn&#39;t have to be me.</content>
    </entry>
    
    <entry>
        <title>Slicing and Dicing Images with GIMP and Python</title>
        <link href="gemini://boringcactus.com/2018/06/23/slicing-images-gimp-python.gmi" rel="alternate" type="text/gemini" title="Slicing and Dicing Images with GIMP and Python" />
        <published>2018-06-23T00:00:00Z</published>
        <updated>2018-06-23T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2018/06/23/slicing-images-gimp-python.gmi</id>
        <content type="text/gemini">Let&#39;s say you have one big image (say, a Telegram sticker) and you need to dice it into a bunch of smaller images (say, Discord emoji). GIMP can let you do that manually, but frankly so can simpler tools. GIMP also has powerful scripting support with Python (and also Scheme, but miss me with that) that can let us do that automatically.

## TL;DR how do i do the thing

1. Save your large image somewhere with a useful filename; this script will chuck `_1_1` and `_1_2` etc on the end of the existing filename.


2. Open that image in GIMP.


3. Go to the Filters menu, open Python-Fu, and hit Console.


4. Set up the width and height of your tiles. For 64x64 tiles, for example, type

5. Paste in this big ol&#39; block of code and let it build your tiles and print out the text you can enter in Discord to reconstitute your original image:


There are two minor issues with actually using this code to convert a Telegram sticker into Discord emoji that I&#39;ll get to later.

## The Code, Splained

I&#39;ll walk through each bit of the code segment above and explain why it&#39;s there.

We need the GIMP libraries, the Python 3 `print()` function (because as of GIMP 2.8.22 the GIMP console is still on Python 2), and some path manipulation functions.

from gimpfu import *

from __future__ import print_function

import os.path


We&#39;re going to crop an image with an X and Y offset and a width and height. The first step in generating the tile is telling GIMP to do the actual crop.

def crop(image, x, width, y, height):

pdb.gimp_image_crop(image, width, height, x, y)


The next step is to figure out the filename for this specific tile; here we&#39;re getting an index back from the offsets and width and height.

x_idx = x / width + 1

y_idx = y / width + 1

filename = pdb.gimp_image_get_filename(image)

dir, name = os.path.split(filename)

root, ext = os.path.splitext(name)

ext = &#34;.png&#34;

output_root = root + &#34;_&#34; + str(y_idx) + &#34;_&#34; + str(x_idx)

output_name = os.path.join(dir, output_root + ext)


Once we&#39;ve got a filename, we can save. For some reason GIMP&#39;s save functions all depend on both the image and the layer, and on two copies of the filename.

layer = pdb.gimp_image_get_active_layer(image)

pdb.file_png_save_defaults(image, layer, output_name, output_name)


Since the goal is to reconstitute the original image from Discord emoji, we assume that they won&#39;t be renamed. We need the Python 3 print function here to suppress any characters after the string is printed; the Python 2 `print &#34;foo&#34;,` trick still emits a space.

print(&#34;:&#34; + output_root + &#34;:&#34;, end=&#34;&#34;)


We might as well delete the image from GIMP. I don&#39;t know if this actually serves an important purpose or not.

pdb.gimp_image_delete(image)


We want to grab the original filename.

image = gimp.image_list()[0]

filename = pdb.gimp_image_get_filename(image)


Since we defined WIDTH and HEIGHT manually earlier, now we can loop through the image. I should probably go back in and make it grab the full image width and height, but fuck it, I don&#39;t want to.

for y in range(0, 512, WIDTH):

for x in range(0, 512, HEIGHT):


I don&#39;t know if GIMP doesn&#39;t expose undo in the Python API or if I just couldn&#39;t find it, but either way we don&#39;t have undo, so we pass in a fresh copy of the image instead.

crop(pdb.gimp_file_load(filename, filename), x, WIDTH, y, HEIGHT)


Since we&#39;re building up the emoji text for Discord one row at a time, we need to end the row at the end of a row.

print()


This is just there so the newline after the `for` loop gets pasted successfully.

pass


## The Plot Thickens

The first issue with this approach is that Discord (at time of writing, at least) sets a total of 2.25 pixels worth of horizontal margin between emoji, so your reconstituted image will have weird stripes. It might be feasible to adjust for these in the offsets so that the spacing isn&#39;t funky, but honestly that seems like a lot of work.

The second, and more interesting, issue is that Discord has a 50 emoji limit on each server. Slicing a 512x512 image into 32x32 tiles for a full size replica would generate 256 tiles, which might work if you had Discord Nitro and six different dummy servers, but nah. Slicing into 64x64 tiles that&#39;ll be rendered at half size only makes 64 tiles, which works out nicely numerically but is still more than can fit on one server. Unless we&#39;re clever.

I&#39;m not sure how well this generalizes, but for the sticker I&#39;m working with, 16 of those 64 tiles are fully transparent, and therefore identical. If we could detect this when slicing, we could avoid emitting 15 of those, at which point we come in nicely with 49 tiles, one under the Discord emoji limit. But how can we detect if an image is fully transparent?

Get histogram info for the alpha channel! We can use something like this to count how many pixels aren&#39;t fully transparent:

_, _, _, _, visible_count, _ = pdb.gimp_histogram(layer, HISTOGRAM_ALPHA, 1, 255)


So our final code can detect if each tile is fully transparent before it saves and treat all fully transparent tiles as equivalent to the very first one.

from gimpfu import *

from __future__ import print_function

import os.path

empty_tile_name = None

def crop(image, x, width, y, height):

global empty_tile_name

pdb.gimp_image_crop(image, width, height, x, y)

layer = pdb.gimp_image_get_active_layer(image)

_, _, _, _, visible_count, _ = pdb.gimp_histogram(layer, HISTOGRAM_ALPHA, 1, 255)

x_idx = x / width + 1

y_idx = y / width + 1

filename = pdb.gimp_image_get_filename(image)

dir, name = os.path.split(filename)

root, ext = os.path.splitext(name)

ext = &#34;.png&#34;

output_root = root + &#34;_&#34; + str(y_idx) + &#34;_&#34; + str(x_idx)

output_name = os.path.join(dir, output_root + ext)

if visible_count &gt; 0 or empty_tile_name is None:

pdb.file_png_save_defaults(image, layer, output_name, output_name)

if visible_count == 0:

if empty_tile_name is None:

empty_tile_name = output_root

else:

output_root = empty_tile_name

print(&#34;:&#34; + output_root + &#34;:&#34;, end=&#34;&#34;)

pdb.gimp_image_delete(image)

image = gimp.image_list()[0]

filename = pdb.gimp_image_get_filename(image)

for y in range(0, 512, WIDTH):

for x in range(0, 512, HEIGHT):

crop(pdb.gimp_file_load(filename, filename), x, WIDTH, y, HEIGHT)

print()

pass


The results are actually fairly impressive, all things considered:

=&gt; /assets/2018-06-23-slicing-images-gimp-python-1.png A halfway decent but slightly stripe-y replica as Discord emoji of the Telegram sticker of Pandora&#39;s Fox dabbing. [IMG]

(that sticker is by NL and of Pandora&#39;s Fox)

=&gt; https://twitter.com/NLDraws NL
=&gt; https://twitter.com/pandoras_foxo Pandora&#39;s Fox

But of course anyone with an ounce of sense would just upload the image so this whole project was a complete waste of three hours.</content>
    </entry>
    
    <entry>
        <title>Windows, Vim, and Python: An Unholy Trinity of Pain</title>
        <link href="gemini://boringcactus.com/2018/06/17/windows-vim-python.gmi" rel="alternate" type="text/gemini" title="Windows, Vim, and Python: An Unholy Trinity of Pain" />
        <published>2018-06-17T00:00:00Z</published>
        <updated>2018-06-17T00:00:00Z</updated>
        <id>gemini://boringcactus.com/2018/06/17/windows-vim-python.gmi</id>
        <content type="text/gemini">Last summer I figured I&#39;d learn Vim. That did not go well.

I started by stealing somebody&#39;s `.vimrc`, as is natural. In this case the person from whomst I lifted my `.vimrc` was a Python dev, and I was working in Python at the time, so that was a reasonable choice. But once I opened an actual Python file I got an error message that Vim couldn&#39;t find Python.

I did some research and it turned out that even though I&#39;d grabbed the latest version of Vim, it was looking for Python 3.5 and I had Python 3.6, which had been out for a while by then. So I uninstalled Python 3.6 and installed Python 3.5 and started getting a different error message.

A bit more research revealed that my Python was 64-bit but my Vim was 32-bit. Apparently Vim didn&#39;t provide official 64-bit Windows builds at that time, so for 64-bit Vim on Windows they just linked to a handful of third party distributions. I went ahead and uninstalled my 32-bit Vim so I could install 64-bit Vim, and then everything worked fine. (Except for all the minor Vim papercuts that eventually led me to write my own Nano clone instead.)

=&gt; https://github.com/mathphreak/mfte my own Nano clone

To get Vim and Python to play nice with each other, I had to reinstall both of them.

And that&#39;s basically what developing on Windows is like in a nutshell.

But it doesn&#39;t have to be this way. If more people treated Windows as a first class platform, the tools to develop on Windows wouldn&#39;t be so frustrating to use, and then more people would treat Windows as a first class platform.</content>
    </entry>
    
</feed>