i / gopher.someodd.zip 70 iWaffle: Gopher Client / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i __ __ __ __ _ _ / gopher.someodd.zip 70 i \ \ / / __ _ / _| / _| | | ___ (_) / gopher.someodd.zip 70 i \ \/\/ / / _` | | _| | _| | | / -_) _ / gopher.someodd.zip 70 i \_/\_/ \__,_| |_| |_| |_| \___| (_) / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i ___ _ / gopher.someodd.zip 70 i / __| ___ _ __ | |_ ___ _ _ / gopher.someodd.zip 70 i | (_ | / _ \ | '_ \ | ' \ / -_) | '_| / gopher.someodd.zip 70 i \___| \___/ | .__/ |_||_| \___| |_| / gopher.someodd.zip 70 i |_| / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i ___ _ _ _ / gopher.someodd.zip 70 i / __| | | (_) ___ _ _ | |_ / gopher.someodd.zip 70 i | (__ | | | | / -_) | ' \ | _| / gopher.someodd.zip 70 i \___| |_| |_| \___| |_||_| \__| / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i╔─*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──╗ / gopher.someodd.zip 70 i║1 .................................................. 1║ / gopher.someodd.zip 70 i║2* .................................................. *2║ / gopher.someodd.zip 70 i║3 .................................................. 3║ / gopher.someodd.zip 70 i║1 ................Posted: 2024-03-10................ 1║ / gopher.someodd.zip 70 i║2* Tags: nix gopher haskell debian showcase my_warez *2║ / gopher.someodd.zip 70 i║3 .................................................. 3║ / gopher.someodd.zip 70 i║1 .................................................. 1║ / gopher.someodd.zip 70 i╚──────────────────────────────────────────────────────────╝ / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iI made a Gopher client called waffle[1]. Basically, a web browser, but for the / gopher.someodd.zip 70 iGopher Protocol. I started it about 4 years ago now (2020?). / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iA truly big, complicated beast of a project for me. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iI feel like this project touched upon some core areas: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i* Rapid acceleration of my Haskell proficiency / gopher.someodd.zip 70 i* Ability to read and implement RFCs (namely RFC 1436[2]) / gopher.someodd.zip 70 i* Network programming / gopher.someodd.zip 70 i* Concurrency / gopher.someodd.zip 70 i* TUI (I used bricks, I think) / gopher.someodd.zip 70 i* Large project, requiring careful architecture and separation of concerns / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iSome other highlights: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i* I got to talk to one of the people who worked on the Gopher Protocol (P. / gopher.someodd.zip 70 i Lindner). / gopher.someodd.zip 70 i* Caching system / gopher.someodd.zip 70 i* Titlebar thing / gopher.someodd.zip 70 i* Search / gopher.someodd.zip 70 i* Profiling for efficiency, working with someone who made a tool I used to / gopher.someodd.zip 70 i analyze profiler output / gopher.someodd.zip 70 i* Bookmarks / gopher.someodd.zip 70 i* Color / gopher.someodd.zip 70 i* UTF-8 support, emojis / gopher.someodd.zip 70 i* Just using Cabal, basically, not Stack / gopher.someodd.zip 70 i* Build/run with nix! / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i## Examples of Waffle / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iI believe Waffle has an INI file for customization: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 gWaffle style configuration /assets/showcase/waffle/waffle-customization.gif gopher.someodd.zip 70 i / gopher.someodd.zip 70 iPlaying a hacking game in Waffle on mozz.us: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 gPlaying a hacking game with Waffle /assets/showcase/waffle/waffle-playing-hacking-game.gif gopher.someodd.zip 70 i / gopher.someodd.zip 70 iWaffle playing solitaire: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 IWaffle playing solitaire /assets/showcase/waffle/waffle-playing-solitaire.png gopher.someodd.zip 70 i / gopher.someodd.zip 70 iWaffle playing Zork: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 IWaffle playing Zork /assets/showcase/waffle/waffle-playing-zork.png gopher.someodd.zip 70 i / gopher.someodd.zip 70 iAn example of Waffle's search and refresh abilities (also kind of showing off / gopher.someodd.zip 70 icaching): / gopher.someodd.zip 70 i / gopher.someodd.zip 70 gSearch and refresh in Waffle /assets/showcase/waffle/waffle-search-and-refresh-quotes.gif gopher.someodd.zip 70 i / gopher.someodd.zip 70 i## The development journey / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iI found it very satisfying to create a project based around reading an RFC and / gopher.someodd.zip 70 iother research. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i### Gopher: High Level / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iThe Gopher client, hell, even the Gopher Protocol in general, is very / gopher.someodd.zip 70 ilines-based. As in everything is just composed by a serires of lines, it can / gopher.someodd.zip 70 ifeel. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iI believe the Gopher Protocol was designed around being simple, easy to hack / gopher.someodd.zip 70 ithings together for. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iYou browse Gopherspace using menus. Menus are basically a bunch of lines that / gopher.someodd.zip 70 iindicate some kind of option you can select to do something, like go to a new / gopher.someodd.zip 70 idirectory, download a file, or start a query. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iThe menus themselves are simple text files that are primarily tab-delimited, / gopher.someodd.zip 70 ilooking like this: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i``` / gopher.someodd.zip 70 1About Gopher /about gopher.server.com 70 0What's New! /whatsnew.txt gopher.server.com 70 1Gopher Clients /clients gopher.server.com 70 1Search Gopher Space /search gopher.server.com 70 0Gopher Protocol Specification /rfc1436.txt gopher.server.com 70 iThis is an information line fake 0 1Fun & Games /fun gopher.server.com 70 1University Information /university gopher.server.com 70 1Libraries around the World /libraries gopher.server.com 70 7Search a gopherhole /search gopher.example.org 70 i. / gopher.someodd.zip 70 i``` / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iAs you can see these menus are pretty easy to parse. Each line represents / gopher.someodd.zip 70 isomething actionable. For example if the first line is selected in the client, / gopher.someodd.zip 70 iwe know the following information: / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i* Represent the line as `About Gopher` to the user/client / gopher.someodd.zip 70 i* The line intends to bring the user to another gopher menu (I also see menus / gopher.someodd.zip 70 i called "directories"), because the first character on the line is `1`, which / gopher.someodd.zip 70 i is the directory (menu) type. Check out RFC1436, 3.8: Item Type Characters[3] / gopher.someodd.zip 70 i for a comprehensive list of such characters. / gopher.someodd.zip 70 i* The "selector string" is `/about` (the path to request) / gopher.someodd.zip 70 i* The server to make the request to is `gopher.server.com` on port `70` / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iAn `i` line is just an information line that's just there for display. There are / gopher.someodd.zip 70 ivarious kinds of downloadable files, like type `9` (binary file) and even a / gopher.someodd.zip 70 ispecific type for GIF (`g`). / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iMaybe the most interesting type is the `7` item type character. It indicates / gopher.someodd.zip 70 ithat the entry intends to perform a query if selected, where a query string is / gopher.someodd.zip 70 isent to the server and a response is (hopefully) given based off that query. / gopher.someodd.zip 70 iSome gopher enthusiasts, myself included, really enjoy abusing this feature for / gopher.someodd.zip 70 ieverything from games (I've included some screenshots and even video of games in / gopher.someodd.zip 70 iWaffle!) to, say, my gopher forum software[4]. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iClients are also kind of expected to view type `0` in the client, I think. / gopher.someodd.zip 70 iThat's text files. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i### Interesting bits / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i* I found it interesting that the way Gopher Clients, Waffle included, tend to / gopher.someodd.zip 70 i get the title bar text or the like, is from the label of the menu item which / gopher.someodd.zip 70 i lead to it. / gopher.someodd.zip 70 i* I decided to implement a loading screen (there's a whole interesting / gopher.someodd.zip 70 i `Progress.hs`[5] module), which uses `Brick.BChan`[6], and does some fun / gopher.someodd.zip 70 i networking/socket things / gopher.someodd.zip 70 i* Caching and history / gopher.someodd.zip 70 i* The TUI/BrickApp gets broken up into different states I call a `ModeAction`, / gopher.someodd.zip 70 i so this could be things like bookmark, help, homepage, menu, open, progress, / gopher.someodd.zip 70 i search, or stuff to do with the menu. The user interface is kind of thought of / gopher.someodd.zip 70 i in relation to a state machine. / gopher.someodd.zip 70 i* INI configuration and storage for bookmarks, styling, default associations, / gopher.someodd.zip 70 i homepage / gopher.someodd.zip 70 i* Can be piped with tor or whatever, so you can access hidden service / gopher.someodd.zip 70 i Gopherholes / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i### Profiling, benchmarking, optimizing / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iOne of the first major issues I came across was Waffle was extremely slow. I / gopher.someodd.zip 70 iwasn't sure why, because it felt like I was using brick[7] the way I was / gopher.someodd.zip 70 isupposed to. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 iI used a terminal-based viewer called profold[8] for GHC `.prof` files generated / gopher.someodd.zip 70 iby profiling with `+RTS -p`, made by someone I had the pleasure of talking to / gopher.someodd.zip 70 i(@garmelon[9]). I believe it allowed to to suss out what Waffle was spending the / gopher.someodd.zip 70 imost time doing and why, specifically, scrolling up and down felt so laggy and / gopher.someodd.zip 70 iintensive on very long menus. I can't remember what I was doing before, but the / gopher.someodd.zip 70 isolution turned out to be to instead use `BrickList` to display menus, / gopher.someodd.zip 70 ibasically. / gopher.someodd.zip 70 i / gopher.someodd.zip 70 i## Footnotes / gopher.someodd.zip 70 i / gopher.someodd.zip 70 h[1]: waffle: https://github.com/someodd/waffle URL:https://github.com/someodd/waffle gopher.someodd.zip 70 h[2]: RFC 1436: https://www.rfc-editor.org/rfc/rfc1436 URL:https://www.rfc-editor.org/rfc/rfc1436 gopher.someodd.zip 70 h[3]: RFC1436, 3.8: Item Type Characters: https://www.rfc-editor.org/rfc/rfc1436 URL:https://www.rfc-editor.org/rfc/rfc1436 gopher.someodd.zip 70 0[4]: my gopher forum software: /showcase/gopherden /showcase/gopherden gopher.someodd.zip 70 h[5]: `Progress.hs`: https://github.com/someodd/waffle/blob/master/src/BrickApp/ModeAction/Progress.hs URL:https://github.com/someodd/waffle/blob/master/src/BrickApp/ModeAction/Progress.hs gopher.someodd.zip 70 h[6]: `Brick.BChan`: https://hackage.haskell.org/package/brick-2.3.1/docs/Brick-BChan.html URL:https://hackage.haskell.org/package/brick-2.3.1/docs/Brick-BChan.html gopher.someodd.zip 70 h[7]: brick: https://hackage.haskell.org/package/brick URL:https://hackage.haskell.org/package/brick gopher.someodd.zip 70 h[8]: profold: https://github.com/Garmelon/profold URL:https://github.com/Garmelon/profold gopher.someodd.zip 70 h[9]: @garmelon: https://github.com/Garmelon/ URL:https://github.com/Garmelon/ gopher.someodd.zip 70