From 2c01d4daeff989a556083d26b7c6e5cf7f89b472 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Sat, 5 Feb 2022 17:41:36 -0500 Subject: Prefix old post file names with dates. --- posts/2013-06-08-hello-world.md | 29 ++ posts/2013-06-15-first-foss-contribution.md | 38 ++ posts/2013-06-30-diaspora.md | 53 +++ posts/2013-07-15-maine-2013.md | 41 ++ posts/2013-07-20-stumpwm.md | 74 +++ posts/2013-08-07-guile-2d-devlog-1.md | 44 ++ posts/2013-08-08-angularjs.md | 135 ++++++ posts/2013-08-11-little-schemer.md | 57 +++ posts/2013-08-17-pangocairo.md | 321 +++++++++++++ posts/2013-09-22-thinkpad-x220.md | 63 +++ posts/2013-09-27-guile-2d-0.1.md | 28 ++ posts/2013-09-30-gnu30.md | 62 +++ posts/2013-10-16-first-guix-package.md | 43 ++ posts/2013-11-22-first-guile-patch.md | 150 ++++++ posts/2013-12-17-rinari-jasmine.md | 36 ++ posts/2013-12-30-emacs-required-packages.md | 62 +++ posts/2014-01-24-guile-cooperative-repl.md | 73 +++ posts/2014-03-09-guile-2d-frp.md | 62 +++ posts/2014-05-04-guile-2d-live-asset-reload.md | 49 ++ posts/2014-08-11-guile-2d-is-now-sly.md | 33 ++ ...-11-08-reproducible-development-environments.md | 120 +++++ posts/2015-04-10-sxml-html-guile.md | 501 +++++++++++++++++++++ posts/2015-08-08-haunt-0.1-release.md | 37 ++ posts/2015-08-30-ruby-on-guix.md | 302 +++++++++++++ posts/2016-04-24-haunt-0.2-release.md | 32 ++ posts/2017-01-23-haunt-0.2.1-release.md | 24 + posts/2018-03-10-haunt-0.2.2-release.md | 23 + posts/angularjs.md | 135 ------ posts/diaspora.md | 53 --- posts/emacs-required-packages.md | 62 --- posts/first-foss-contribution.md | 38 -- posts/first-guile-patch.md | 150 ------ posts/first-guix-package.md | 43 -- posts/gnu30.md | 62 --- posts/guile-2d-0.1.md | 28 -- posts/guile-2d-devlog-1.md | 44 -- posts/guile-2d-frp.md | 62 --- posts/guile-2d-is-now-sly.md | 33 -- posts/guile-2d-live-asset-reload.md | 49 -- posts/guile-cooperative-repl.md | 73 --- posts/haunt-0.1-release.md | 37 -- posts/haunt-0.2-release.md | 32 -- posts/haunt-0.2.1-release.md | 24 - posts/haunt-0.2.2-release.md | 23 - posts/hello-world.md | 29 -- posts/little-schemer.md | 57 --- posts/maine-2013.md | 41 -- posts/pangocairo.md | 321 ------------- posts/reproducible-development-environments.md | 120 ----- posts/rinari-jasmine.md | 36 -- posts/ruby-on-guix.md | 302 ------------- posts/stumpwm.md | 74 --- posts/sxml-html-guile.md | 501 --------------------- posts/thinkpad-x220.md | 63 --- 54 files changed, 2492 insertions(+), 2492 deletions(-) create mode 100644 posts/2013-06-08-hello-world.md create mode 100644 posts/2013-06-15-first-foss-contribution.md create mode 100644 posts/2013-06-30-diaspora.md create mode 100644 posts/2013-07-15-maine-2013.md create mode 100644 posts/2013-07-20-stumpwm.md create mode 100644 posts/2013-08-07-guile-2d-devlog-1.md create mode 100644 posts/2013-08-08-angularjs.md create mode 100644 posts/2013-08-11-little-schemer.md create mode 100644 posts/2013-08-17-pangocairo.md create mode 100644 posts/2013-09-22-thinkpad-x220.md create mode 100644 posts/2013-09-27-guile-2d-0.1.md create mode 100644 posts/2013-09-30-gnu30.md create mode 100644 posts/2013-10-16-first-guix-package.md create mode 100644 posts/2013-11-22-first-guile-patch.md create mode 100644 posts/2013-12-17-rinari-jasmine.md create mode 100644 posts/2013-12-30-emacs-required-packages.md create mode 100644 posts/2014-01-24-guile-cooperative-repl.md create mode 100644 posts/2014-03-09-guile-2d-frp.md create mode 100644 posts/2014-05-04-guile-2d-live-asset-reload.md create mode 100644 posts/2014-08-11-guile-2d-is-now-sly.md create mode 100644 posts/2014-11-08-reproducible-development-environments.md create mode 100644 posts/2015-04-10-sxml-html-guile.md create mode 100644 posts/2015-08-08-haunt-0.1-release.md create mode 100644 posts/2015-08-30-ruby-on-guix.md create mode 100644 posts/2016-04-24-haunt-0.2-release.md create mode 100644 posts/2017-01-23-haunt-0.2.1-release.md create mode 100644 posts/2018-03-10-haunt-0.2.2-release.md delete mode 100644 posts/angularjs.md delete mode 100644 posts/diaspora.md delete mode 100644 posts/emacs-required-packages.md delete mode 100644 posts/first-foss-contribution.md delete mode 100644 posts/first-guile-patch.md delete mode 100644 posts/first-guix-package.md delete mode 100644 posts/gnu30.md delete mode 100644 posts/guile-2d-0.1.md delete mode 100644 posts/guile-2d-devlog-1.md delete mode 100644 posts/guile-2d-frp.md delete mode 100644 posts/guile-2d-is-now-sly.md delete mode 100644 posts/guile-2d-live-asset-reload.md delete mode 100644 posts/guile-cooperative-repl.md delete mode 100644 posts/haunt-0.1-release.md delete mode 100644 posts/haunt-0.2-release.md delete mode 100644 posts/haunt-0.2.1-release.md delete mode 100644 posts/haunt-0.2.2-release.md delete mode 100644 posts/hello-world.md delete mode 100644 posts/little-schemer.md delete mode 100644 posts/maine-2013.md delete mode 100644 posts/pangocairo.md delete mode 100644 posts/reproducible-development-environments.md delete mode 100644 posts/rinari-jasmine.md delete mode 100644 posts/ruby-on-guix.md delete mode 100644 posts/stumpwm.md delete mode 100644 posts/sxml-html-guile.md delete mode 100644 posts/thinkpad-x220.md diff --git a/posts/2013-06-08-hello-world.md b/posts/2013-06-08-hello-world.md new file mode 100644 index 0000000..9a8be46 --- /dev/null +++ b/posts/2013-06-08-hello-world.md @@ -0,0 +1,29 @@ +title: Hello, world! +date: 2013-06-08 17:18 +tags: hello +summary: I has a shiny new website +--- + +Hey! I have a shiny new website. I just purchased a cheap VPS from +[Digital Ocean](http://digitalocean.com) and registered the +[dthompson.us](https://dthompson.us) domain for this site. Instead of +the going the Wordpress route (or using similar blogging software), +I’ve decided to use a static site generator called +[Pelican](http://getpelican.com) after seeing that the +[Linux](http://kernel.org) kernel website was using it. I’m going to +use this site to host this blog, my resume, and information about my +software projects. + +A little about myself: I’m a software developer from Massachusetts. I +graduated from [Worcester State University](http://worcester.edu) in +2012 with a BS in Computer Science. I currently work as a junior +[Ruby on Rails](http://rubyonrails.org) developer at +[Vista Higher Learning](http://vistahigherlearning.com). I am an +advocate of free and open source software and I use GNU/Linux as my +operating system of choice. In my spare time, I like to write free +software (mostly my own never-to-be-finished projects). Oddly enough, +I’m still using a proprietary web service to host all of my software +projects, so check out my [Github](https://github.com/davexunit) page. + +Connect with me on [Diaspora](https://joindiaspora.com/u/davexunit) or +[Twitter](https://twitter.com/davexunit). diff --git a/posts/2013-06-15-first-foss-contribution.md b/posts/2013-06-15-first-foss-contribution.md new file mode 100644 index 0000000..32aa752 --- /dev/null +++ b/posts/2013-06-15-first-foss-contribution.md @@ -0,0 +1,38 @@ +title: My First Real FOSS Contribution +date: 2013-06-15 18:00 +tags: foss, mediagoblin, python, federated, decentralized, wsu +summary: I added a small feature! +--- + +I spend a lot of my free time writing code. I usually work on my own +personal projects that never really go anywhere. So, I decided to take +a detour from my normal hacking routine and contribute to an existing +free software project. My contribution was accepted awhile ago now, +but I wasn’t blogging then so I’m rambling about it now. + +It’s wise to find a project with a low barrier of entry. An active IRC +channel and/or mailing list with people willing to help newcomers is +ideal. I remembered hearing about +[GNU MediaGoblin](http://mediagoblin.org) at LibrePlanet 2012, so I +decided to check things out. MediaGoblin is a media sharing web +application written in Python. Their bug tracker marks tickets that +require little work and don’t require a deep understanding of +MediaGoblin as bitesized’. + +I chose to work on +[this ticket](http://issues.mediagoblin.org/ticket/453) because it +didn’t require any complicated database migrations or knowledge of the +media processing code. I added a new configuration option, +`allow_comments`, and a small amount of code to enforce the setting. + +Eventually, the ticket got reviewed and +[Christopher Webber](http://dustycloud.org) (MediaGoblin’s friendly +project leader) merged it: “Heya. Great branch, this works +perfectly. Merged!” + +It was a very small change, but I was happy to _finally_ have some +actual code of mine in a real free software project. I have a strong +passion for free software and the GNU philosophy, so it’s really great +to participate in the community. My job as a professional software +developer eats up a lot of my time these days, but I hope to find the +time to continue hacking and contributing. diff --git a/posts/2013-06-30-diaspora.md b/posts/2013-06-30-diaspora.md new file mode 100644 index 0000000..32a8991 --- /dev/null +++ b/posts/2013-06-30-diaspora.md @@ -0,0 +1,53 @@ +title: Find Me on Diaspora +date: 2013-06-30 15:00 +tags: foss, diaspora, federated, decentralized, rails, wsu +summary: I have started using and contributing to Diaspora. +--- + +With all of the recent news about the NSA’s widespread spying, I have +decided to ween myself off of proprietary, centralized web +services. Facebook, Google, and other such corporations hold onto +massive amounts of our data that we’ve willingly given to them via +status messages, “like” buttons, searches, and emails. Using and +contributing to free (as in freedom), decentralized (like email) web +services is a really great way to re-establish control of our +data. These services rely on many small, interconnected nodes to +operate, rather than a monolithic website that is under the control of +one entity. If the distinction between centralized and decentralized +isn’t clear, consider how email functions. There are many email +providers to choose from. Users can communicate with others that +choose to use a different email provider. This is how web services +should work, but unfortunately very few work this way now. + +The centralized web application that I spend too much time using is +Facebook. I have knowingly given Facebook a “frontdoor” into my life +for years now and I’m ready to move on. I think that the concept of a +“social network” is fun, so I wanted a Facebook replacement. +Fortunately, there is one: [Diaspora](http://diasporaproject.org/). + +Diaspora is a [free](https://github.com/diaspora/diaspora), +distributed, social networking web application written in Ruby using +the Rails framework. Diaspora is a community-run project. Its success +depends upon users, developers, technical writers, user interface +designers, etc. investing their time and/or money into making it +better. The Diaspora network is broken up into many servers, known as +[pods](http://podupti.me). Users have the choice of which pod to store +their data on. Pods assert no ownership over their user’s data, unlike +Facebook, and do not use that data for targeted +advertisements. Diaspora is still a rather young project, but it does +everything that I need it to do. Goodbye, Facebook! + +Since I’m a programmer, I naturally wanted to hack on some code and +contribute. The main developers are very friendly and give great +guidance to newcomers that want to help out. Every Monday is a “Bug +Mash Monday”, where a list of open issues is presented to entice +contributors to resolve them. In the past few weeks, I have made two +contributions to the Diaspora project: a +[bug fix](https://github.com/diaspora/diaspora/issues/2948) and a +[small feature](https://github.com/diaspora/diaspora/issues/2948). Diaspora +is very hackable and I encourage other developers with Ruby/Rails and +Javascript knowledge to join in. + +TL;DR: Diaspora is great. Create an account. Check out my +[profile](https://joindiaspora.com/u/davexunit). Start sharing. Happy +hacking. :) diff --git a/posts/2013-07-15-maine-2013.md b/posts/2013-07-15-maine-2013.md new file mode 100644 index 0000000..2e979f9 --- /dev/null +++ b/posts/2013-07-15-maine-2013.md @@ -0,0 +1,41 @@ +title: Maine! +date: 2013-07-15 22:00 +tags: maine, vacation +summary: My vacation in Maine +--- + +Every summer, my friends and I go to Maine for a week. We stay in the +Rockland area in an old house on a blueberry field. This year we hiked +a mountain in Camden, hiked Mt. Katahdin, and went to the Rockland +Blues Festival. + +Here are some pictures taken from my not-so-great cellphone camera: + +![View from the house](/images/maine-2013/view-from-house.jpg) + +The view from the driveway of our house. + +![Zeke's Lookout](/images/maine-2013/zekes-lookout.jpg) + +Sign at Zeke’s Lookout. Somewhere on a mountain near Camden. + +![Mt. Katahdin, foot of the Hunt trail](/images/maine-2013/katahdin-camp.jpg) + +Mt. Katahdin as seen from the foot of the Hunt trail. + +![Waterfall on Mt. Katahdin](/images/maine-2013/katahdin-waterfall.jpg) + +A beautiful waterfall that can be seen about a mile into the hike. + +![View from the mountain 1](/images/maine-2013/katahdin-view-1.jpg) +![View from the mountain 2](/images/maine-2013/katahdin-view-2.jpg) + +Views from the mountain. + +![Fog on the mountain](/images/maine-2013/katahdin-fog.jpg) + +Thick fog past the treeline. + +![Rockland Blues Festival](/images/maine-2013/blues-festival.jpg) + +Closed down street packed with people and bands in Rockland. diff --git a/posts/2013-07-20-stumpwm.md b/posts/2013-07-20-stumpwm.md new file mode 100644 index 0000000..e73412f --- /dev/null +++ b/posts/2013-07-20-stumpwm.md @@ -0,0 +1,74 @@ +title: StumpWM on Debian Wheezy +date: 2013-07-20 15:00 +tags: stumpwm, common lisp, debian, wheezy, wsu +summary: First steps with StumpWM on Debian Wheezy +--- + +Everyone that’s ever talked to me about software development knows +that I am in love with Emacs. Emacs has a wonderful keyboard driven +interface and is almost infinitely customizable via Emacs Lisp. I’ve +done a lot of programming in Emacs from my not-so-great laptop +lately. My laptop has a rather poor 1280x800 resolution and low +performing integrated graphics chip. Until today, I was running the +GNOME 3 desktop environment on it. Unlike most people (or perhaps just +a loud minority), I like GNOME 3. However, I wanted something that was +both less graphics intensive and more keyboard driven than GNOME +Shell and Mutter. + +Someone on IRC told me about +[StumpWM](http://www.nongnu.org/stumpwm/), a window manager written +entirely in Common Lisp. I had heard of StumpWM before, but back then +I wasn’t an Emacs user and I’ve never really stuck with any tiling +window manager that I’ve tried (DWM, Awesome). Now that I know the +power of a fully programmable environment thanks to Emacs, I decided +to give StumpWM a try. After some initial pains trying to get it to +run, I am now using it very happily. + +Here is what I had to do to get StumpWM running on Debian Wheezy. + +1) Install StumpWM + +```sh +sudo apt-get install stumpwm +``` + +2) Create an `.xinitrc` file in my home directory with the following + text + +```sh +exec stumpwm +``` + +3) Workaround clisp “component not found” errors + +I could not get StumpWM to start until I created the following +symbolic links: + +```sh +ln -s /usr/share/common-lisp/source/stumpwm/stumpwm.asd /usr/share/common-lisp/systems/stumpwm.asd +ln -s /usr/share/common-lisp/source/cl-ppcre/cl-ppcre.asd /usr/share/common-lisp/systems/cl-ppcre.asd +``` + +4) Start the X server + +```sh +startx +``` + +I use the GNOME Desktop Manager, so I also created a session file for +StumpWM in `/usr/share/xsessions/stumpwm.desktop`. + +``` +[Desktop Entry] +Encoding=UTF-8 +Name=StumpWM +Comment=This session logs you into StumpWM +Exec=stumpwm +TryExec=stumpwm +Icon= +Type=Application +``` + +I hope this brief installation guide can be of use to one of you out +there in Internet land. Perhaps in the future I will write an article +about customizing StumpWM with Common Lisp. diff --git a/posts/2013-08-07-guile-2d-devlog-1.md b/posts/2013-08-07-guile-2d-devlog-1.md new file mode 100644 index 0000000..1b36772 --- /dev/null +++ b/posts/2013-08-07-guile-2d-devlog-1.md @@ -0,0 +1,44 @@ +title: guile-2d - A 2D Game Development Framework for GNU Guile +date: 2013-08-07 23:00:00 +tags: foss, gnu, guile, scheme, gamedev, wsu +summary: I have started work on a 2D game framework +--- + +This is the very first devlog entry for my pet project, guile-2d. As +the title suggests, guile-2d is a 2D game development framework for +[GNU Guile](https://gnu.org/s/guile), a Scheme implementation that has +the honor of being the official extension language of the GNU +project. Guile is a language with a growing number of features, but it +still lacks a large assortment of libraries. I like to do 2D game +programming, and I saw a niche that needed to be filled. Python has +Pygame, Lua has Love, but there’s no fun and accessible game +programming library for Guile. Guile-2d is working to correct that. + +The goal of Guile-2d is to create an easy to use 2D game programming +framework. Libraries like SDL give the programmer a rather low-level +set of tools that they can use to build a game, guile-2d will provide +high-level tools over low-level SDL and OpenGL for commonly used +elements of 2D games: tile maps, sprite animation, particle systems, +collision detection, vector math, A* pathfinding, etc. Such features +will allow a game developer to very quickly produce a working +prototype with guile-2d. + +Guile-2d is a framework, which means that it has some opinion about +what the right way to do things is. The most apparent example of this +is the game loop. The game loop runs at 60 frames-per-second and uses +fixed timestep updates. Those that have read +[Fix Your Timestep](http://gafferongames.com/game-physics/fix-your-timestep/) +will know that this decision is a good thing. + +Perhaps the most important feature of guile-2d is the ability to do +“live coding”. When the game loop starts, a REPL +(read-eval-print-loop) server is started. Using the great +[Geiser](http://www.nongnu.org/geiser/) extension for Emacs to connect +to the REPL server, one can modify their game as it is running. This +gives users the power to evaluate some new code and see the changes +reflected immediately in the game window. No need to restart the game +unless you crash it! + +This has been a brief overview of some of the features and goals of +guile-2d. If this project interests you, you can check out the source +code on [Github](https://github.com/davexunit/guile-2d). diff --git a/posts/2013-08-08-angularjs.md b/posts/2013-08-08-angularjs.md new file mode 100644 index 0000000..0ea8c19 --- /dev/null +++ b/posts/2013-08-08-angularjs.md @@ -0,0 +1,135 @@ +title: AngularJS Post-mortem +date: 2013-08-08 11:00:00 +tags: web, javascript, angularjs, wsu +summary: AngularJS likes/dislikes and what went right/wrong +--- + +[AngularJS](http://angularjs.org/) is the new popular client-side +Javascript application framework developed by Google. We have recently +adopted it at Vista Higher Learning for building our latest features +that require a lot client-side logic. Now that I have a few +applications under my belt, it’s time to talk about my experience. + +If you want a quick TL;DR: I think AngularJS is good, but it has a +steep learning curve and there’s no well defined set of best +practices. + +Note: I will be using plenty of terms that will probably only make +sense for people that have used AngularJS. + +The Good Stuff +-------------- + +These are the things that went well. The things that made me glad that +we chose to use AngularJS. + +### Easier Testing + +Our Javascript test suite uses +[Jasmine](http://pivotal.github.io/jasmine/). AngularJS is built with +test frameworks like Jasmine in mind. AngularJS could tends to be +easily testable due to dependency injection. When the components of an +application don’t rely on global state, it is easier to mock services +for unit tests. + +### Separation of Concerns + +AngularJS stresses separating the view from the data structures and +logic behind it. I think everyone that’s written a somewhat complex +JQuery application can relate to the mess of CSS selectors and click +callbacks that the code quickly degenerates into. + +AngularJS allows you to break up the DOM into logical chunks that are +handled by separate controllers. Treating the application as many +small pieces working together rather than one giant DOM blob keeps the +code clean. Message passing via `$emit` and `$broadcast` keeps +controllers loosely coupled to each other. + +### No More JQuery Spaghetti + +Directives, the strategy for encapsulating DOM manipulation, are +wonderful. It is an elegant solution to the mess that was JQuery +selectors and event callbacks. AngularJS comes with a lot of +directives out of the box to handle the most common stuff like +showing/hiding elements, handling clicks, dynamically setting CSS +classes. + +### More Maintainable Code + +AngularJS is feature-rich. It does a lot on your behalf, which greatly +reduces the amount of boilerplate code needed to get a prototype up +and running. I had the opportunity to essentially rewrite an existing +JQuery application using AngularJS. The results were clear: The +AngularJS version had fewer lines of code, was more readable, and +was easier to debug. + +Bumps in the Road +----------------- + +These are the things that didn’t go smoothly. They boil down to +AngularJS having a steep learning curve and ill-informed software +design decisions on my part. + +### Understanding the Magic + +A lot of things seem to happen by magic. For example, it is possible +to make a asynchronous request and get a promise object in +return. When the promise is assigned to a variable on `$scope`, +AngularJS not only knows to ignore it while the request hasn’t +finished, but it will re-assign to that variable the value of the +asynchronous call when it completes. It is a nice feature, but it +takes some digging to find out what is really going on. + +### Poor Documentation + +I know I’m not the only one that hates the official AngularJS +documentation. The documentation is getting more complete, but its +still not very useful. Functions frequently have a blurb describing +what they do, but no explanation of the parameter list. It’s hard to +use a function that doesn’t describe what it expects for input. + +When the documentation confused us, which it did frequently, we turned +to the AngularJS book from +[O’Reilly](http://shop.oreilly.com/product/0636920028055.do) for +help. I need to get around to reading more of it. + +### RESTful Resources and Rails + +AngularJS claims to be targeted at CRUD applications, but using the +HTTP backend and the `Resource` abstraction that sits on top of it was +particularly difficult. A good amount of time was spent on trying to +get the HTTP requests from resources to conform to what our Rails +backend expects, like root-wrapping. + +### Bloated Controllers + +I frequently made controllers that had too much state and logic that +should have been extracted into services/factories/etc. A controller +would start out slim but would quickly grow to several hundred lines +of code as features were added. Controllers should be the middle man +between the view and the model and that’s it. + +Some tell-tale signs that a controller is becoming bloated: + +* There are a lot of private functions (not defined on `$scope`) + +* Functions are defined on `$scope` just so you can unit-test them, + but are never used in the template + +I attribute controller bloat to a lack of knowing the appropriate uses +for other AngularJS components. It was easy to just keep adding to the +controller. + +Conclusion +---------- + +Overall, I think things went well, but I (and the rest of my team) +made a lot of beginner mistakes. But that’s the learning process, +isn’t it? + +Now that I know more about AngularJS, it will be easier to make better +design decisions moving forward. + +I believe that as AngularJS continues to mature, some concensus in the +community about best practices will emerge that will make things +easier for beginners. diff --git a/posts/2013-08-11-little-schemer.md b/posts/2013-08-11-little-schemer.md new file mode 100644 index 0000000..d58c96b --- /dev/null +++ b/posts/2013-08-11-little-schemer.md @@ -0,0 +1,57 @@ +title: The Little Schemer +date: 2013-08-11 15:00:00 +tags: scheme, books, wsu +summary: I bought "The Little Schemer" +--- + +Yesterday, I took a trip to the MIT Press Bookstore and picked up a +copy of +[The Little Schemer](http://mitpress.mit.edu/books/little-schemer). I’ve +only spent a few hours reading and coding along with it, but I’ve had +a lot of fun. The following is a mini-review based on my experience +thus far. + +“The Little Schemer” teaches you to think recursively using an +interesting and comedic writing style and the Scheme programming +language. While Scheme is the language of choice, the real goal is to +teach you problem solving rather than the details of a specific +language. The book starts off simple, explaining what atoms, lists, +and S-expressions are. Rather than providing the definition and then +showing examples, it first gives examples in the form of a question +and answer. + +Example: + +> Is it true that this an atom? +> +> **atom** +> +> Yes, because **atom** is a string of characters beginning with a +> letter. + +From the examples given, a definition is created. In later examples, a +Scheme procedure is written that produces the correct answers for all +of the questions stated before it. It’s fun to build the procedure, +verify that it works for all cases, and compare your implementation +with the book’s. + +“The Little Schemer” defines ten commandments that are essential to +correctly solving the problems in the book. Some commandments are +first given in an incomplete form, and expanded later when a greater +level of understanding has been achieved. The problems that you solve +reinforce the commandments. You might notice that you start writing +procedures without thinking much about it, much like the muscle memory +earned from using Emacs a lot. Gerald J. Sussman was right when he +said that this book “can perform the same service that Hanon’s finger +exercises or Czerny’s piano studies perform for the student of the +piano.” I have no idea who Hanon and Czerny are, but I get it. For the +drummers out there, you could liken this book to +[Stick Control](http://www.amazon.com/Stick-Control-For-Snare-Drummer/dp/1892764040). + +The writing style is very informal, comedic, and food themed. Page 13 +has a space reserved for jelly stains, and page 52 tells you to “go +cons a piece of cake onto your mouth.” I have laughed a number of +times while reading. Oh, and let’s not forget about the cute elephant +drawings. This is definitely not your average boring, dry computer +science book. If you are interested in a unique and enjoyable learning +experience, then I highly recommend reading “The Little Schemer”. diff --git a/posts/2013-08-17-pangocairo.md b/posts/2013-08-17-pangocairo.md new file mode 100644 index 0000000..fd651b5 --- /dev/null +++ b/posts/2013-08-17-pangocairo.md @@ -0,0 +1,321 @@ +title: Font Rendering in OpenGL with Pango and Cairo +date: 2013-08-17 16:00:00 +tags: opengl, pango, cairo, font, wsu +summary: A brief tutorial for rendering fonts in OpenGL with libpangocairo +--- + +I am working towards a 0.1 release of my game development framework +for GNU Guile, [guile-2d](https://github.com/davexunit/guile-2d). One +of the few remaining blockers on my to-do list is font rendering. A +reddit user, [Madsy9](http://www.reddit.com/user/Madsy9), pointed me +in the right direction with this +[comment](http://www.reddit.com/r/scheme/comments/1k739l/guile_2d_game_programming_lib_for_scheme/cbmnyuk). There +are two libraries needed to perform nice font rendering with proper +internationalization support: [Pango](http://www.pango.org/), “a +library for laying out and rendering of text, with an emphasis on +internationalization,” and [Cairo](http://cairographics.org/), “a 2D +graphics library with support for multiple output devices.” + +It took me awhile to put together all of the pieces and build a +working sample program. The goal of this post is to help others that +may be trying to accomplish a similar task that have no prior +knowledge of Pango and Cairo. I will assume basic knowledge of C, SDL, +and OpenGL throughout this post. + +Let’s get the basic SDL and OpenGL initialization out of the way: + +```c +#include +#include +#include + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 +#define FONT "Sans Bold 18" +#define TEXT "The quick brown fox is so かわいい!" + +void +init_sdl () +{ + SDL_Init (SDL_INIT_EVERYTHING); + SDL_SetVideoMode (WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_OPENGL); +} + +void +init_gl () +{ + glClearColor (0.0f, 0.0f, 0.0f, 0.0f); + glDisable (GL_DEPTH_TEST); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_TEXTURE_2D); + glViewport (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, -1, 1); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); +} +``` + +`create_texture` simply creates an OpenGL texture given an array of +pixel data and the texture dimensions. Our Cairo surface will use BGRA +color. + +```c +unsigned int +create_texture (unsigned int width, + unsigned int height, + unsigned char *pixels) +{ + unsigned int texture_id; + + glGenTextures (1, &texture_id); + glBindTexture (GL_TEXTURE_2D, texture_id); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D (GL_TEXTURE_2D, + 0, + GL_RGBA, + width, + height, + 0, + GL_BGRA, + GL_UNSIGNED_BYTE, + pixels); + + return texture_id; +} +``` + +`draw_texture` clears the screen, renders a simple textured quad using +OpenGL’s immediate mode, and then swaps buffers. + +```c +void +draw_texture (int width, + int height, + unsigned int texture_id) +{ + /* Render a texture in immediate mode. */ + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + glClear (GL_COLOR_BUFFER_BIT); + glPushMatrix (); + glBindTexture (GL_TEXTURE_2D, texture_id); + glColor3f (1.f, 1.0f, 1.0f); + + glBegin (GL_QUADS); + glTexCoord2f (0.0f, 0.0f); + glVertex2f (0.0f, 0.0f); + glTexCoord2f (1.0f, 0.0f); + glVertex2f (width, 0.0f); + glTexCoord2f (1.0f, 1.0f); + glVertex2f (width , height); + glTexCoord2f (0.0f, 1.0f); + glVertex2f (0.0f, height); + glEnd (); + + glPopMatrix (); + SDL_GL_SwapBuffers(); +} +``` + +`create_cairo_context` is used to make a new Cairo context that draws +to a raw data surface. The return value, a `cairo_t`, is the main +object in Cairo. All drawing is done via a `cairo_t` object. A +context needs a surface to draw on. +`cairo_image_surface_create_for_data` creates a raw data surface for +us. We will be translating the surface into a texture later on. + +```c +cairo_t* +create_cairo_context (int width, + int height, + int channels, + cairo_surface_t** surf, + unsigned char** buffer) +{ + *buffer = calloc (channels * width * height, sizeof (unsigned char)); + *surf = cairo_image_surface_create_for_data (*buffer, + CAIRO_FORMAT_ARGB32, + width, + height, + channels * width); + return cairo_create (*surf); +} +``` + +`create_layout_context` also makes a new Cairo context, but this +context is for PangoLayout objects. In Pango, a layout describes the +style of a paragraph of text. The layout needs a context in order to +function. We use `cairo_image_surface_create` with dimensions of 0x0 +because we won’t actually be rendering to this surface. Instead, we +will layout our text and use `create_cairo_context` to build a context +with a surface that is the size of the rendered text. Cairo uses +reference counting for dynamically allocated objects, so we need to +call `cairo_surface_destroy` when we’re done with the temporary +surface. The context still maintains a reference to the surface, so +the memory for the surface will not be freed until the context is. + +```c +cairo_t* +create_layout_context () +{ + cairo_surface_t *temp_surface; + cairo_t *context; + + temp_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); + context = cairo_create (temp_surface); + cairo_surface_destroy (temp_surface); + + return context; +} +``` + +`get_text_size` tells us the size of the text that’s in the layout, in +pixels. Pango’s units are not in pixels, so we must divide by +`PANGO_SCALE` in order to get pixel units. + +```c + +void +get_text_size (PangoLayout *layout, + unsigned int *width, + unsigned int *height) +{ + pango_layout_get_size (layout, width, height); + /* Divide by pango scale to get dimensions in pixels. */ + *width /= PANGO_SCALE; + *height /= PANGO_SCALE; +} +``` + +`render_text` is where all of the magic happens. First, we create a +layout with a layout context and set the text that we will render with +this layout. `TEXT` is defined earlier in the program as "The quick +brown fox is so かわいい!" + +Then we create a `PangoFontDescription` object. This object +represents the font that we want to render. Earlier in the program, +`FONT` is defined as "Sans Bold 18". Pango is able to figure out how +to load a font from a string in this format. Your system must be able +to recognize the font family and font face, though. I haven’t yet +figured out how to have Pango render an arbitrary font from a `*.ttf` +file. + +Next, we create a rendering context by getting the layout’s size and +creating a context with a surface big enough to show all of the +rendered text. + +Finally, we set the font color to white, render the text to the +surface with `pango_cairo_show_layout`, and create an OpenGL texture +from the surface. We also clean up all the objects that we no longer +need before returning. + +```c +unsigned int +render_text (const char *text, + unsigned int *text_width, + unsigned int *text_height, + unsigned int *texture_id) +{ + cairo_t *layout_context; + cairo_t *render_context; + cairo_surface_t *temp_surface; + cairo_surface_t *surface; + unsigned char* surface_data = NULL; + PangoFontDescription *desc; + PangoLayout *layout; + + layout_context = create_layout_context (); + + /* Create a PangoLayout, set the font and text */ + layout = pango_cairo_create_layout (layout_context); + pango_layout_set_text (layout, text, -1); + + /* Load the font */ + desc = pango_font_description_from_string (FONT); + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + + /* Get text dimensions and create a context to render to */ + get_text_size (layout, text_width, text_height); + render_context = create_cairo_context (*text_width, + *text_height, + 4, + &surface, + &surface_data); + + /* Render */ + cairo_set_source_rgba (render_context, 1, 1, 1, 1); + pango_cairo_show_layout (render_context, layout); + *texture_id = create_texture(*text_width, *text_height, surface_data); + + /* Clean up */ + free (surface_data); + g_object_unref (layout); + cairo_destroy (layout_context); + cairo_destroy (render_context); + cairo_surface_destroy (surface); +} +``` + +`main` is pretty simple. We initialize SDL and OpenGL, render text +to a texture, and enter the rendering loop. The program will run +until you click the close button, press "enter", or press "q". + +```c +int main (int argc, char **argv) +{ + SDL_Event event; + int keep_running = 1; + unsigned int texture_id; + unsigned int text_width = 0; + unsigned int text_height = 0; + + init_sdl (); + init_gl (); + render_text(TEXT, + &texture_id, + &text_width, + &text_height); + + /* Update/render loop */ + while (keep_running) { + SDL_PollEvent (&event); + + switch (event.type) { + case SDL_QUIT : + keep_running = 0; + break; + + case SDL_KEYDOWN : + if (event.key.keysym.sym == SDLK_ESCAPE) + keep_running = 0; + if (event.key.keysym.sym == SDLK_q) + keep_running = 0; + break; + } + + draw_texture (texture_id, text_width, text_height); + SDL_Delay (16); + } + + /* Clean up */ + glDeleteTextures (1, &texture_id); + + SDL_Quit(); + + return 0; +} +``` + +And we’re done! You should now be able to render some text in an +OpenGL context. I hope this brief tutorial was helpful. Font rendering +isn’t easy, and it’s not really my area of interest. I’m glad that +Pango exists to do all of the real work for me so that I can more +quickly move on to the parts of graphics programming that I actually +enjoy. + +You can download the full source code [here](/src/pangocairo.tar.gz). diff --git a/posts/2013-09-22-thinkpad-x220.md b/posts/2013-09-22-thinkpad-x220.md new file mode 100644 index 0000000..fa69b98 --- /dev/null +++ b/posts/2013-09-22-thinkpad-x220.md @@ -0,0 +1,63 @@ +title: Liberating a Thinkpad X220 +date: 2013-09-22 21:00 +tags: thinkpad, free software, wsu +summary: I bought a used Thinkpad X220 +--- + +I had been looking for a suitable replacement to my old, slow Compaq +laptop that I purchased during my freshman year of college when I had +very little money. What I liked about my old laptop was that it played +well with free software. I had no trouble getting all of my hardware +to work out-of-the-box with fully free GNU/Linux distributions such as +Trisquel, and I wanted any future laptops of mine to play nicely, too. + +I have heard much praise for Thinkpads over the years. Solid build +quality, utilitarian design, and cheap to buy used. However, upon +further reading, I realized that most newer Thinkpads require nonfree +software in order to the drive the Intel wireless chip. Furthermore, +there was DRM present in the BIOS that would prevent the installation +of PCIe wireless chips that weren't in the whitelist. + +This really bummed me out, but I bought a Thinkpad anyway. I found a +great deal on a used X220 on ebay for $400. In order to liberate it, I +had to make a small deal with the devil: Use the pre-installed Windows +7 to flash a hacked BIOS that removes the whitelist. I could only find +the needed BIOS as a Windows executable, so I didn't have much +choice. This process left me hoping that coreboot gains wider +adoption. + +Once I had verified that I didn't brick my Thinkpad, I installed the +new wireless card. I purchased a Wireless N, half-height, mini PCIe +card from +[Thinkpenguin](https://www.thinkpenguin.com/gnu-linux/penguin-wireless-n-half-height-mini-pcie-card). It +uses an Atheros chipset and is free software compatible. I met Chris, +the owner of Thinkpenguin, at this year's Northeast GNU/Linux Fest at +Harvard. He is doing some great work and I wanted to support his +business. It was nice to buy from someone who could assure me that the +hardware I purchased is fully supported on a libre GNU/Linux +distribution. + +Now that my Thinkpad was free (sans BIOS, of course), it was time for +the final touch. I replaced the hard drive with a 128GB SSD and +installed Debian testing. It takes roughly 9 seconds to get from GRUB +to the GDM login screen. It feels very nice to have a device that +boots so quickly. + +Now that everything had been installed and configured, I was able to +start hacking and get a feel for things. The keyboard is the nicest +I've ever used on a laptop. The [TrackPoint](http://xkcd.com/243/) is +quite a nice way to move around once you get used to it. The +ThinkLight is pretty neat when you're in a dark area. The battery life +is extremely impressive. I don't know exactly how long it lasts yet +but I never have to charge it while I am using it. I was lucky if I +got 2 hours of battery life out of my old laptop, which caused me to +be constantly tethered to an AC adapter. The screen is matte, which is +awesome because it's very difficult to use a laptop outdoors when the +screen is glossy. 1366x768 is not an ideal resolution (16:9 be +damned), but I can live with it on a 12.5" screen. Last but not least, +I honestly like the aesthetics. A lot of people are enamored with the +brushed aluminum designs by that fruit company, but I love the flat +black, functional design of the Thinkpad. + +I hope to really break this thing in over the weekend at the +[GNU 30th Anniversary](https://www.gnu.org/gnu30/) hackathon. diff --git a/posts/2013-09-27-guile-2d-0.1.md b/posts/2013-09-27-guile-2d-0.1.md new file mode 100644 index 0000000..150a760 --- /dev/null +++ b/posts/2013-09-27-guile-2d-0.1.md @@ -0,0 +1,28 @@ +title: Guile-2D 0.1 Release +date: 2013-09-27 12:00:00 +tags: foss, gnu, guile, scheme, gamedev, wsu +summary: Official release of Guile-2D Version 0.1 +--- + +To celebrate the GNU Project’s 30th anniversary, I have decided to +make the very first release of my 2D game development framework for +[GNU Guile](https://gnu.org/s/guile). GNU Guile is a Scheme +implementation, and has the honor of being the official extension +language of the GNU project. Guile-2D is a layer above SDL, OpenGL, +FreeImage, and FTGL that provides abstractions for common 2D game +programming requirements such as sprites, tilesets, animations, +scripting, and collision detection. + +There is a lot of work to do in order to get Guile-2D up to snuff with +the game libraries for more popular languages like Python and Lua. I +am looking for contributors who share my vision of creating a fully +featured, easy to use game library in Scheme. + +Guile-2D currently supports GNU/Linux distributions. I am looking for +help to get it running on OS X and Windows. + +Please refer to the `INSTALL.org`, `README.org`, and texinfo files to +learn how to install Guile-2D, run example programs, and write your +own games. + +[Browse the source code on GitHub](https://github.com/davexunit/guile-2d) diff --git a/posts/2013-09-30-gnu30.md b/posts/2013-09-30-gnu30.md new file mode 100644 index 0000000..3d42bf3 --- /dev/null +++ b/posts/2013-09-30-gnu30.md @@ -0,0 +1,62 @@ +title: GNU 30th Anniversary Hackathon +date: 2013-09-30 22:00 +tags: gnu, fsf, free software +summary: Happy birthday, GNU! +--- + +I spent my weekend at MIT at the GNU 30th anniversary hackathon. I had +never participated in a hackathon before and was excited to see what +it was like. Developers from many GNU and non-GNU projects were there +to hack and help others get involved, and RMS was there to give the +keynote speech. + +On Saturday, I spent nearly the entire day in the GNU FM room. About a +year ago I wrote an +[installation guide](http://bugs.foocorp.net/projects/fm/wiki/How_to_install) +on the GNU FM wiki, and so Matt Lee asked me to walk some newcomers +through getting a development environment up and running. I was able +to help four people with this. They all had a functioning GNU FM +server and they were able to scrobble their music to it. Setting up +GNU FM can be quite a pain, and the guide I had written was missing +some information and gave some bad advice. I simplified and rewrote +some of it so that it’s easier to follow. Hopefully this will benefit +a future contributor to GNU FM. + +At 5PM on Saturday, RMS gave a talk about the future of free software +and the GNU project. He discussed the value of reverse engineering +proprietary applications and device drivers in order to write free +replacements. He also talked about the dangers of +software-as-a-service and the “iThings”. His +[new article](http://www.wired.com/opinion/2013/09/why-free-software-is-more-important-now-than-ever-before/) +on Wired covers much of the same subject matter. After his speech, he +raffled off a stuffed baby gnu and an “adorable” GNU 30th anniversary +mug. Chris, owner of ThinkPenguin, won both items! After the speech +came the reception in which I ate some delicious vegan cupcakes and +acquired two 3D printed gnu logos that were sitting atop each +cupcake. After the reception, I briefly went out to a pub with Matt +Lee and Matthew Garrett. Donald Robertson of the FSF joined in later. + +On Sunday, I spent the first couple of hours helping out more with GNU +FM because Matt Lee was sick. For lunch, I went with a large group to +a chinese restaurant. Included in the group was the John Eaton, the +GNU Octave author, and Zak Rogoff, Campaigns Manager at the FSF. It +was interesting to talk to John about the challenges that he faced and +continues to face when trying to keep up with Matlab and maintaining +compatibility even when the Matlab engineers make bad design decisions. + +After lunch, I met up with Mark Weaver, one of the GNU Guile +developers. He helped me write my first patch for Guile: a new REPL +option called “read-wrapper” that allows external code to hook into +the part of the REPL that waits for user input. Guile-2D needs this +functionality in order to create a REPL that plays nice with the game +event loop. Since the main thread is in an event loop, waiting for +user input at the REPL prompt would stop the game entirely. To get +around this, we used the “read-wrapper” option to pass the procedure +that reads user input into another thread so that Guile-2D’s event +loop can continue running. We achieved this functionality in less than +100 lines of code. This hack showed me how great it is to use a +language with first-class continuations. + +tl;dr: The hackathon was a great time. Happy birthday, GNU. + +![GNU 30th logo](/images/gnu30.jpg) diff --git a/posts/2013-10-16-first-guix-package.md b/posts/2013-10-16-first-guix-package.md new file mode 100644 index 0000000..1b5c704 --- /dev/null +++ b/posts/2013-10-16-first-guix-package.md @@ -0,0 +1,43 @@ +title: My First GNU Guix Patch +date: 2013-10-16 21:00 +tags: gnu, guix, scheme, guile, wsu +summary: I packaged libtheora for GNU Guix +--- + +Over the weekend, I decided to try out GNU Guix: A fully functional +package manager based on Nix and a distribution of the GNU system. I’m +a big proponent of GNU Guile, thus I was excited to see a DSL for +package management written with Guile. + +I was told that libtheora would be pretty easy to package, and it +was. Here’s what the package definition looks like: + +```scheme +(define libtheora + (package + (name "libtheora") + (version "1.1.1") + (source (origin + (method url-fetch) + (uri (string-append "http://downloads.xiph.org/releases/theora/libtheora-" + version ".tar.xz")) + (sha256 + (base32 + "0q8wark9ribij57dciym5vdikg2464p8q2mgqvfb78ksjh4s8vgk")))) + (build-system gnu-build-system) + (inputs `(("libvorbis" ,libvorbis))) + ;; The .pc files refer to libogg. + (propagated-inputs `(("libogg" ,libogg))) + (synopsis "Library implementing the Theora video format") + (description + "The libtheora library implements the ogg theora video format, +a fully open, non-proprietary, patent-and-royalty-free, general-purpose +compressed video format.") + (license license:bsd-3) + (home-page "http://xiph.org/theora/"))) +``` + +Pretty slick, eh? Now, I’m starting to work on packaging SDL (1.2 +and 2) and the SDL extensions (gfx, ttf, etc.), which are not quite as +easy. I hope to package all of the dependencies that guile-2d will +need to be available as a Guix package. diff --git a/posts/2013-11-22-first-guile-patch.md b/posts/2013-11-22-first-guile-patch.md new file mode 100644 index 0000000..d67c786 --- /dev/null +++ b/posts/2013-11-22-first-guile-patch.md @@ -0,0 +1,150 @@ +title: First GNU Guile Patch and More Guix Packages +date: 2013-11-22 21:00 +tags: gnu, guix, scheme, guile, wsu +summary: I added alist->hash-table procedures for Guile’s native hash table implementation and packaged SDL libraries for Guix. +--- + +I have spent some of the last month working on contributing to GNU +Guile and now I can finally say that I have contributed code to the +project. Guile has several hash table implementations: a Guile native +one, SRFI-69, and R6RS. SRFI-69 contains a handy procedure, +`alist->hash-table`, which allows for a sort of hash literal-like +syntax: + +```scheme +(alist->hash-table + '((foo . 1) + (bar . 2) + (baz . 3))) +``` + +However, I prefer to use Guile’s native hash implementation, and +SRFI-69 is incompatible with it, so I decided to port +`alist->hash-table`. Unfortunately, it was not as simple as writing +just one procedure. The native hash tables actually have 4 different +ways of doing key comparisons: Using `equal?`, `eq?`, `eqv?`, and +user-defined procedures. This is a design flaw because the user must +make sure to use the right procedures at all times rather than simply +set the `hash` and `assoc` procedures when creating the hash table +instance. + +Below is the final implementation. Since 3 of the 4 variations were +essentially the same, I wrote a short macro to reduce code +duplication. + +```scheme +;;;; Copyright (C) 2013 Free Software Foundation, Inc. +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Lesser General Public +;;;; License as published by the Free Software Foundation; either +;;;; version 3 of the License, or (at your option) any later version. +;;;; +;;;; This library is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;;;; Lesser General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Lesser General Public +;;;; License along with this library; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;;;; + +(define-syntax-rule (define-alist-converter name hash-set-proc) + (define (name alist) + "Convert ALIST into a hash table." + (let ((table (make-hash-table))) + (for-each (lambda (pair) + (hash-set-proc table (car pair) (cdr pair))) + (reverse alist)) + table))) + +(define-alist-converter alist->hash-table hash-set!) +(define-alist-converter alist->hashq-table hashq-set!) +(define-alist-converter alist->hashv-table hashv-set!) + +(define (alist->hashx-table hash assoc alist) + "Convert ALIST into a hash table with custom HASH and ASSOC +procedures." + (let ((table (make-hash-table))) + (for-each (lambda (pair) + (hashx-set! hash assoc table (car pair) (cdr pair))) + (reverse alist)) + table)) +``` + +Not only did I manage to get my code into Guile, I was given commit +access to GNU Guix by the lead developer, Ludovic Courtès. I have +never had commit access to any free software project repositories +besides my own. I feel quite honored, as cheesy as that may sound. + +Packages for SDL and SDL2 have been merged into master, and packages +for SDL_gfx, SDL_image, SDL_mixer, SDL_net, and SDL_ttf for SDL 1.2 +are pending review. + +I like to show off the elegance of Guix package recipes, so here’s +some code: + +```scheme +;;; Copyright © 2013 David Thompson +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define sdl + (package + (name "sdl") + (version "1.2.15") + (source (origin + (method url-fetch) + (uri + (string-append "http://libsdl.org/release/SDL-" + version ".tar.gz")) + (sha256 + (base32 + "005d993xcac8236fpvd1iawkz4wqjybkpn8dbwaliqz5jfkidlyn")))) + (build-system gnu-build-system) + (arguments '(#:tests? #f)) ; no check target + (inputs `(("libx11" ,libx11) + ("libxrandr" ,libxrandr) + ("mesa" ,mesa) + ("alsa-lib" ,alsa-lib) + ("pkg-config" ,pkg-config) + ("pulseaudio" ,pulseaudio))) + (synopsis "Cross platform game development library") + (description "Simple DirectMedia Layer is a cross-platform development +library designed to provide low level access to audio, keyboard, mouse, +joystick, and graphics hardware.") + (home-page "http://libsdl.org/") + (license lgpl2.1))) + +(define sdl2 + (package (inherit sdl) + (name "sdl2") + (version "2.0.0") + (source (origin + (method url-fetch) + (uri + (string-append "http://libsdl.org/release/SDL2-" + version ".tar.gz")) + (sha256 + (base32 + "0y3in99brki7vc2mb4c0w39v70mf4h341mblhh8nmq4h7lawhskg")))) + (license bsd-3))) +``` + +Much help is needed to package the GNU system for Guix. If you are +interested in helping, please drop by `#guix` on freenode, we are +friendly. :) diff --git a/posts/2013-12-17-rinari-jasmine.md b/posts/2013-12-17-rinari-jasmine.md new file mode 100644 index 0000000..cb94b1f --- /dev/null +++ b/posts/2013-12-17-rinari-jasmine.md @@ -0,0 +1,36 @@ +title: Jump to Jasmine Specs with Rinari +date: 2013-12-17 13:00 +tags: emacs, javascript, wsu +summary: I hacked rinari mode to jump between JS sources and jasmine tests +--- + +I use the [rinari](https://github.com/eschulte/rinari) Emacs mode to +assist me when working on rails projects. One of rinari’s most useful +features is the ability to quickly jump from one file to another +related file. I use this feature almost exclusively for jumping +between a ruby class file and its associated rspec file, but lately +I’ve been spending most of my time writing javascript. At VHL, we use +[jasmine](http://pivotal.github.io/jasmine/) for our unit testing +framework and the +[jasmine ruby gem](https://github.com/pivotal/jasmine-gem) to +integrate it with our rails projects. Rinari doesn’t have any +built-in jump settings for jasmine test files, so I wrote this quick +hack to make it work: + +```elisp +;; Make rinari jump to/from javascript source files and specs. +(setcdr (assoc 'javascript rinari-jump-schema) + '("j" + (("app/assets/javascripts/\\1.js" . "spec/javascripts/\\1_spec.js") + ("spec/javascripts/\\1_spec.js" . "app/assets/javascripts/\\1.js") + (t . "spec/javascripts/.*") + (t . "app/javascripts/.*")) + t)) +(rinari-apply-jump-schema rinari-jump-schema) +``` + +Now I can press `C-c ; f j` to jump between a javascript file in +`app/assets/javascripts/` and its jasmine test file in +`spec/javascripts/`. Perhaps I shouldn’t be overwriting the +predefined (but not very useful) javascript jump settings, but I +really wanted to use the `j` key. diff --git a/posts/2013-12-30-emacs-required-packages.md b/posts/2013-12-30-emacs-required-packages.md new file mode 100644 index 0000000..58874f8 --- /dev/null +++ b/posts/2013-12-30-emacs-required-packages.md @@ -0,0 +1,62 @@ +title: Syncing Required Packages in Emacs +date: 2013-12-30 11:30 +tags: emacs, wsu +summary: A simple way to keep Emacs packages synced across machines using package.el +--- + +I use Emacs on several different computers. To keep my configuration +consistent across all of them, I do what many people do and made the +`~/.emacs.d` directory a +[git repository](https://github.com/davexunit/.emacs.d). I don’t like +to keep copies of all of the Elisp extensions that I use, such as +paredit and geiser, in this repository. Instead, I prefer to use +package.el (introduced in Emacs 24) with the +[MELPA](https://melpa.org) repository. This saves me from having to +manually keep all of the extensions I use up-to-date, but requires +another method to keep useful packages in sync between computers. + +There’s a project called +[Pallet](https://github.com/rdallasgray/pallet) that solves this +problem, but it was too heavy for my liking. Instead, I wrote a short +function that simply iterates over a list of required packages and +installs those that are not currently installed. + +```elisp +;; Additional packages that I use. +(setq required-packages + '(better-defaults + elfeed + geiser + ido-ubiquitous + js2-mode + magit + paredit + rainbow-delimiters + smex)) + +(defun install-missing-packages () + "Install all required packages that haven’t been installed." + (interactive) + (mapc (lambda (package) + (unless (package-installed-p package) + (package-install package))) + required-packages) + (message "Installed all missing packages!")) +``` + +Now, it’s as easy as typing `M-x install-missing-packages RET` when +starting Emacs for the first time on a new computer to download all of +the extensions that I need. Note that before calling +`install-missing-packages` you must have already initialized the +package manager via the `package-initialize` function. This approach +does require some manual bookkeeping in order to keep the +`required-packages` list up-to-date with your workflow, but I haven’t +found it to be problematic. + +Update: If this solution is too simplistic for you, you should check +out [use-package](https://github.com/jwiegley/use-package), which +reddit user [lunayorn](http://www.reddit.com/user/lunaryorn) pointed +out to me. Thanks! + +Check out the comments on +[reddit](http://www.reddit.com/r/emacs/comments/1u0xr4/quick_hack_syncing_required_packages_in_emacs/). diff --git a/posts/2014-01-24-guile-cooperative-repl.md b/posts/2014-01-24-guile-cooperative-repl.md new file mode 100644 index 0000000..50cf39e --- /dev/null +++ b/posts/2014-01-24-guile-cooperative-repl.md @@ -0,0 +1,73 @@ +title: A Cooperative REPL Server for Guile 2.0.10 +date: 2014-01-24 22:00 +tags: gnu, scheme, guile, wsu +summary: Guile 2.0.10 will include a new type of REPL server suited for single-threaded applications. +--- + +The next release of GNU Guile, 2.0.10, is to be released “real soon +now”. My contribution to this release is the new `(system repl +coop-server)` module. This module introduces a useful variant of the +REPL server that I’ve named the “cooperative” REPL server. It is +cooperative because it can be integrated with single-threaded programs +without the thread synchronization issues present in the ordinary REPL +server. + +Using delimited continuations and mvars (another new feature coming in +Guile 2.0.10), it was possible to create a REPL server whose clients +all ran in the context of a single thread. The cooperative server +puts the user in control of when it is safe to evaluate expressions +entered at the REPL prompt. + +Here’s an example of how to use it: + +```scheme +(use-modules (system repl coop-server)) + +(define server (spawn-coop-repl-server)) + +(while #t + (poll-coop-repl-server server) + (sleep 1)) +``` + +That’s all it takes. There are only 2 public procedures! +`spawn-coop-repl-server` creates a new cooperative REPL server +object and starts listening for clients. `poll-coop-repl-server` +applies pending operations for the server. + +Now that I’ve explained the API, onto the implementation details! +Although the REPLs run in the context of a single thread, there are +other threads needed to make it all work. To avoid thinking too much +about thread synchronization issues, I used the new `(ice-9 mvars)` +module to provide thread-safe objects for read/write operations. I +used delimited continuations in order to yield control back to the +user from inside the loop. + +When the server is spawned, a new thread is created to listen for +client connections. When a client connects, a request to create a new +REPL is put into the server’s "evaluation" mvar, the storage place for +pending server operations. The job of `poll-coop-repl-server` is to +attempt to take from the evaluation mvar and execute the operation +stored within, if any. In this case, an operation called `new-repl` +is hanging out along with the client socket. The contents of the mvar +are removed, and a new REPL is started in the main thread. + +This new REPL is run within a "prompt", Guile’s implementation of +delimited continuations. The prompt allows the REPL to be paused +temporarily while waiting for user input. Just before the prompt is +aborted, a thunk containing the logic to read user input is stored +within the client’s "read" mvar for yet another thread to use. +Without this additional thread and the use of a prompt, the main +thread would block while waiting for input, defeating the purpose of +the cooperative REPL. The reader thread waits for this thunk to +appear in the read mvar. When it appears, the thunk is applied and +the resulting expression is stored as an `eval` operation within the +evaluation mvar. When `poll-coop-repl-server` is called, the REPL +prompt is resumed. The input expression is evaluated in the context +of the main thread, the result is printed, and the process repeats +itself. + +That’s all, folks. Thanks for following along. I’m very excited to +use the cooperative REPL server in guile-2d and I hope that others +find it useful as well. Many thanks to Mark Weaver for helping me out +when I got stuck and for all of the helpful code review. diff --git a/posts/2014-03-09-guile-2d-frp.md b/posts/2014-03-09-guile-2d-frp.md new file mode 100644 index 0000000..1970e73 --- /dev/null +++ b/posts/2014-03-09-guile-2d-frp.md @@ -0,0 +1,62 @@ +title: Functional Reactive Programming in Scheme with guile-2d +date: 2014-03-09 21:00:00 +tags: foss, gnu, guile, scheme, gamedev, wsu +summary: Introduction to FRP in Scheme with guile-2d +--- + +Last month, the [GNU Guile](https://gnu.org/s/guile) project +celebrated the 3rd anniversary of its 2.0 release with a hacker +[potluck](http://savannah.gnu.org/forum/forum.php?forum_id=7887). +Guilers were encouraged to bring a tasty hack to the mailing list to +share with everyone. My dish was a simple +[functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming) +library. + +Functional reactive programming (FRP) provides a way to describe +time-varying values and their relationships using a functional and +declarative programming style. To understand what this means, let’s +investigate a typical variable definition in Scheme. The expression +`(define c (+ a b))` defines the variable `c` to be the sum of +variables `a` and `b` at the time of evaluation. If `a` or `b` is +assigned a new value, `c` remains the same. However, for applications +that deal with state that transforms over time, it would be convenient +if we could define `c` to react to changes in `a` and `b` by +recomputing the sum. Contrast this approach with the more traditional +style of modeling dynamic state via events and callbacks. A lot of +programmers, myself included, have written code with so many callbacks +that the resulting program is unmaintainable spaghetti code. Callback +hell is real, but if you accept FRP into your heart then you will be +saved! + +By now you’re probably wondering: “What the hell does all this mean?” +So, here’s a real-world example of guile-2d’s FRP API: + +```scheme +(define-signal position + (signal-fold v+ (vector2 320 240) + (signal-map (lambda (v) + (vscale v 4)) + (signal-sample game-agenda 1 key-arrows)))) +``` + +In guile-2d, time-varying values are called “signals”. The above +signal describes a relationship between the arrow keys on the keyboard +and the position of the player. `signal-sample` is used to trigger a +signal update upon every game tick that provides the current state of +the arrow keys. `key-arrows` is a vector2 that maps to the current +state of the arrow keys, allowing for 8 direction movement. This +vector2 is then scaled 4x to make the player move faster. Finally, +the scaled vector is added to the previous player position via +`signal-fold`. The player’s position is at `(320, 240)` initially. +As you can see, there are no callbacks and explicit mutation needed. +Those details have been abstracted away, freeing the programmer to +focus on more important things. + +I think it’s helpful to see FRP in action to really appreciate the +magic. So, check out this screencast! + +![](/videos/guile-2d-frp-demo.webm) + +To see all of the juicy implementation details, check out the +[git repository](https://gitorious.org/sly/sly/source/sly/signal.scm). +Thanks for following along! diff --git a/posts/2014-05-04-guile-2d-live-asset-reload.md b/posts/2014-05-04-guile-2d-live-asset-reload.md new file mode 100644 index 0000000..f1e67b5 --- /dev/null +++ b/posts/2014-05-04-guile-2d-live-asset-reload.md @@ -0,0 +1,49 @@ +title: Live Asset Reloading with guile-2d +date: 2014-05-04 22:00:00 +tags: foss, gnu, guile, scheme, gamedev, wsu +summary: Automatically reload modified assets while developing guile-2d progams +--- + +Guile-2d provides a dynamic environment in which a developer can build +a game incrementally as it runs via the Guile REPL. It’s nice to be +able to hot-swap code and have the running game reflect the changes +made, but what about the game data files? If an image file or other +game asset is modified, it would be nice if the game engine took +notice and reloaded it automatically. This is what guile-2d’s live +asset reloading feature does. + +The new `(2d live-reload)` module provides the `live-reload` +procedure. `live-reload` takes a procedure like `load-texture` and +returns a new procedure that adds the live reload magic. The new +procedure returns assets wrapped in a signal, a time-varying value. A +coroutine is started that periodically checks if the asset file has +been modified, and if so, reloads the asset and propagates it via the +signal. Game objects that depend on the asset will be regenerated +immediately. + +Here’s some example code: + +```scheme +(define load-texture/live + (live-reload load-texture)) + +(define-signal texture + (load-texture/live "images/p1_front.png")) + +(define-signal sprite + (signal-map + (lambda (texture) + (make-sprite texture + #:position (vector2 320 240))) + texture)) +``` + +`load-texture/live` loads textures and reloads them when they change +on disk. Every time the texture is reloaded, the sprite is +regenerated using the new texture. + +Here’s a screencast to see live reloading in action: + +![](/videos/guile-2d-live-reload.webm) + +Guile-2d is ever-so-slowly approaching a 0.2 release. Stay tuned! diff --git a/posts/2014-08-11-guile-2d-is-now-sly.md b/posts/2014-08-11-guile-2d-is-now-sly.md new file mode 100644 index 0000000..4ec8255 --- /dev/null +++ b/posts/2014-08-11-guile-2d-is-now-sly.md @@ -0,0 +1,33 @@ +title: Guile-2D is now named “Sly” +date: 2014-08-11 19:00 +tags: foss, gnu, guile, scheme, gamedev, wsu +--- + +Guile-2D has been the working title for my game engine written in +Guile Scheme for over a year now. The name has become limiting since +I realized that it wouldn’t be much extra work to support 3D graphics. +After much indecision, I’ve finally decided on an official name: Sly. +I think it’s a great name. It’s short, easy to type, and slyness is +one of the definitions of “guile”. + +In other news: + +* Sly has a new contributor! + [Jordan Russel](https://gitorious.org/~seconded) has written a + [new module](https://gitorious.org/sly/sly/source/9231dca37261de8269149ccad4517acad41aa015:sly/joystick.scm) + that provides joystick input support. + +* I have written a + [module for describing animations](https://gitorious.org/sly/sly/source/9231dca37261de8269149ccad4517acad41aa015:sly/transition.scm). + +* I have been slowly working on a scene graph implementation that + plays well with the functional reactive programming API. + +* As mentioned above, 3D graphics support is on the way! So far, I + have implemented a perspective projection matrix, a "look at" + matrix, and a cube primitive. + +![3D Scene Graph](/images/sly/scene-graph-prototype.png) + +Check out the Sly source code repository on +[Gitorious](https://gitorious.org/sly/sly)! diff --git a/posts/2014-11-08-reproducible-development-environments.md b/posts/2014-11-08-reproducible-development-environments.md new file mode 100644 index 0000000..1672c78 --- /dev/null +++ b/posts/2014-11-08-reproducible-development-environments.md @@ -0,0 +1,120 @@ +title: Reproducible Development Environments with GNU Guix +date: 2014-11-08 22:00 +tags: gnu, guix, scheme, guile, wsu +summary: Easily acquire the dependencies needed to hack on a project +--- + +If you’re a software developer, then you probably know very well that +setting up a project’s development environment for the first time can +be a real pain. Installing all of the necessary dependencies using +your system’s package manager can be very tedious. To "solve" this +problem, we have resorted to inventing new package managers and +dependency bundlers for pretty much every programming language. Ruby +has rubygems and bundler, Python has pip and virtualenv, PHP has +composer, node.js has npm, and so on. Wouldn’t it be nice to instead +have a single package manager that can handle it all? Enter +[GNU Guix](https://gnu.org/s/guix), a purely functional package +manager and GNU/Linux distribution. Using Guix, you can easily create +a development environment for any software project using the `guix +environment` tool. + +`guix environment` is a new utility added in Guix 0.8, which should be +released in a few weeks. It accepts one or more packages as input and +produces a new shell environment in which all of the dependencies for +those packages are made available. For example, `guix environment +emacs` will get you everything you need to build GNU Emacs from a +source release tarball. By default, your `$SHELL` is spawned, but +you may opt to use any arbitrary shell command instead. If you’ve +used Nix before, you may notice that this sounds exactly like +`nix-shell`, and that’s because it was what inspired me to write +`guix environment`. + +Now, let’s take a look at an example. One of my hobby projects is +[Sly](https://gitorious.org/sly/sly), a game engine written in Guile +Scheme. Here’s the relevant Guix code that will produce a complete +development environment: + +```scheme + + ;;; Copyright (C) 2014 David Thompson + ;;; + ;;; Sly is free software: you can redistribute it and/or modify it + ;;; under the terms of the GNU General Public License as published by + ;;; the Free Software Foundation, either version 3 of the License, or + ;;; (at your option) any later version. + + (use-modules (guix packages) + (guix licenses) + (guix build-system gnu) + (gnu packages) + (gnu packages autotools) + (gnu packages guile) + (gnu packages gl) + (gnu packages pkg-config) + (gnu packages sdl) + (gnu packages maths) + (gnu packages image)) + + ;; The development environment needs a tweaked LTDL_LIBRARY_PATH + ;; for finding libfreeimage. + (define freeimage + (package (inherit freeimage) + (native-search-paths + (list (search-path-specification + (variable "LTDL_LIBRARY_PATH") + (directories '("lib"))))))) + + (package + (name "sly") + (version "0.0") + (source #f) + (build-system gnu-build-system) + (inputs + `(("pkg-config" ,pkg-config) + ("autoconf" ,autoconf) + ("automake" ,automake) + ("guile" ,guile-2.0) + ("guile-sdl" ,guile-sdl) + ("guile-opengl" ,guile-opengl) + ("gsl" ,gsl) + ("freeimage" ,freeimage) + ("mesa" ,mesa))) + (synopsis "2D/3D game engine for GNU Guile") + (description "Sly is a 2D/3D game engine written in Guile Scheme. + Sly differs from most game engines in that it emphasizes functional + reactive programming and live coding.") + (home-page "https://gitorious.org/sly/sly") + (license gpl3+)) +``` + +You may have noticed that the source field has been set to false. +This is because the package is not for building and installing. It’s +sole purpose is to provide the necessary software needed to build Sly +from a fresh git checkout. + +Assuming this code is in a file called `package.scm`, you can simply +run `guix environment -l package.scm` to spawn a shell for hacking +on Sly. By default, the environment created is an augmented version +of your pre-existing shell’s environment. This is convenient when you +want to use your installed software in the new environment, such as +git. However, it’s important to make sure that the environment really +does have everything needed for development without relying on any +impurities introduced by your existing environment. Without verifying +this, new developers might be frustrated to find out that the +environment provided to them is incomplete. To verify, pass the +`--pure` flag and build from scratch. + +So, `guix environment` is a pretty nice way to acquire all the +dependencies you need to work on a project, but there is still a lot +of room for improvement. What if you were working on a web +application that required a running PostgreSQL database, Redis server, +and XMPP server? It would be really great if Guix could handle +setting this up for you, too, a la Vagrant. To do so, `guix +environment` could use the existing features of `guix system` to spawn +a new virtual machine that shares the host system’s package store and +the project’s source tree, and then spawn a shell with your +development environment. I hope to implement this in the +not-too-distant future. Until next time, happy hacking! + +Read the discussion about this post on +[Hacker News](https://news.ycombinator.com/item?id=8616918). diff --git a/posts/2015-04-10-sxml-html-guile.md b/posts/2015-04-10-sxml-html-guile.md new file mode 100644 index 0000000..f600d7f --- /dev/null +++ b/posts/2015-04-10-sxml-html-guile.md @@ -0,0 +1,501 @@ +title: Rendering HTML with SXML and GNU Guile +date: 2015-04-10 18:00 +tags: gnu, guile, wsu +summary: With a little effort, SXML can be used for HTML templates +--- + +GNU Guile provides modules for working with XML documents called SXML. +SXML provides an elegant way of writing XML documents as s-expressions +that can be easily manipulated in Scheme. Here’s an example: + +```scheme +(sxml->xml '(foo (bar (@ (attr "something"))))) +``` + +```xml + +``` + +I don’t know about you, but I work with HTML documents much more often +than XML. Since HTML is very similar to XML, we should be able to +represent it with SXML, too! + +```scheme +(sxml->xml + '(html + (head + (title "Hello, world!") + (script (@ (src "foo.js")))) + (body + (h1 "Hello!")))) +``` + +```html + + + Hello, world! + + + +

Hello!

+ + +``` + +Note the use of the empty string in `(script (@ (src "foo.js")) "")`. +The output looks correct now, great! But what about the other void +elements? We’ll have to remember to use the empty string hack each +time we use one. That doesn’t sound very elegant. + +Furthermore, text isn’t even escaped properly! + +```scheme +(sxml->xml "Copyright © 2015 David Thompson ") +``` + +```html +Copyright © 2015 David Thompson <davet@gnu.org> +``` + +The `<` and `>` braces were escaped, but `©` should’ve been rendered +as `©`. Why does this fail, too? Is there a bug in SXML? + +There’s no bug. The improper rendering happens because HTML, while +similar to XML, has some different syntax rules. Instead of using +`sxml->xml`, a new procedure that is tailored to the HTML syntax is +needed. Introducing `sxml->html`: + +```scheme +(define* (sxml->html tree #:optional (port (current-output-port))) + "Write the serialized HTML form of TREE to PORT." + (match tree + (() *unspecified*) + (('doctype type) + (doctype->html type port)) + ;; Unescaped, raw HTML output + (('raw html) + (display html port)) + (((? symbol? tag) ('@ attrs ...) body ...) + (element->html tag attrs body port)) + (((? symbol? tag) body ...) + (element->html tag '() body port)) + ((nodes ...) + (for-each (cut sxml->html <> port) nodes)) + ((? string? text) + (string->escaped-html text port)) + ;; Render arbitrary Scheme objects, too. + (obj (object->escaped-html obj port)))) +``` + +In addition to being aware of void elements and escape characters, it +can also render `'(doctype "html")` as ``, or render an +unescaped HTML string using `'(raw "frog & toad")`. If we replace +`sxml->xml` with `sxml->html` in the failing example above we can see +that it does the right thing. + +```scheme +(sxml->html + '((script (@ (src "foo.js"))) + "Copyright © 2015 David Thompson ")) +``` + +```html + +Copyright © 2015 David Thompson <davet@gnu.org> +``` + +Here’s the full version of my `(sxml html)` module. It’s quite brief, +if you don’t count the ~250 lines of escape codes! This code requires +Guile 2.0.11 or greater. + +Happy hacking! + +```scheme +;; Copyright © 2015 David Thompson +;; +;; This library is free software; you can redistribute it and/or +;; modify it under the terms of the GNU Lesser General Public License +;; as published by the Free Software Foundation; either version 3 of +;; the License, or (at your option) any later version. +;; +;; This library is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; Lesser General Public License for more details. +;; +;; You should have received a copy of the GNU Lesser General Public +;; License along with this library. If not, see +;; . + +define-module (sxml html) + #:use-module (sxml simple) + #:use-module (srfi srfi-26) + #:use-module (ice-9 match) + #:use-module (ice-9 format) + #:use-module (ice-9 hash-table) + #:export (sxml->html)) + +define %void-elements + '(area + base + br + col + command + embed + hr + img + input + keygen + link + meta + param + source + track + wbr)) + +define (void-element? tag) + "Return #t if TAG is a void element." + (pair? (memq tag %void-elements))) + +define %escape-chars + (alist->hash-table + '((#\" . "quot") + (#\& . "amp") + (#\' . "apos") + (#\< . "lt") + (#\> . "gt") + (#\¡ . "iexcl") + (#\¢ . "cent") + (#\£ . "pound") + (#\¤ . "curren") + (#\¥ . "yen") + (#\¦ . "brvbar") + (#\§ . "sect") + (#\¨ . "uml") + (#\© . "copy") + (#\ª . "ordf") + (#\« . "laquo") + (#\¬ . "not") + (#\® . "reg") + (#\¯ . "macr") + (#\° . "deg") + (#\± . "plusmn") + (#\² . "sup2") + (#\³ . "sup3") + (#\´ . "acute") + (#\µ . "micro") + (#\¶ . "para") + (#\· . "middot") + (#\¸ . "cedil") + (#\¹ . "sup1") + (#\º . "ordm") + (#\» . "raquo") + (#\¼ . "frac14") + (#\½ . "frac12") + (#\¾ . "frac34") + (#\¿ . "iquest") + (#\À . "Agrave") + (#\Á . "Aacute") + (#\ . "Acirc") + (#\à . "Atilde") + (#\Ä . "Auml") + (#\Å . "Aring") + (#\Æ . "AElig") + (#\Ç . "Ccedil") + (#\È . "Egrave") + (#\É . "Eacute") + (#\Ê . "Ecirc") + (#\Ë . "Euml") + (#\Ì . "Igrave") + (#\Í . "Iacute") + (#\Î . "Icirc") + (#\Ï . "Iuml") + (#\Ð . "ETH") + (#\Ñ . "Ntilde") + (#\Ò . "Ograve") + (#\Ó . "Oacute") + (#\Ô . "Ocirc") + (#\Õ . "Otilde") + (#\Ö . "Ouml") + (#\× . "times") + (#\Ø . "Oslash") + (#\Ù . "Ugrave") + (#\Ú . "Uacute") + (#\Û . "Ucirc") + (#\Ü . "Uuml") + (#\Ý . "Yacute") + (#\Þ . "THORN") + (#\ß . "szlig") + (#\à . "agrave") + (#\á . "aacute") + (#\â . "acirc") + (#\ã . "atilde") + (#\ä . "auml") + (#\å . "aring") + (#\æ . "aelig") + (#\ç . "ccedil") + (#\è . "egrave") + (#\é . "eacute") + (#\ê . "ecirc") + (#\ë . "euml") + (#\ì . "igrave") + (#\í . "iacute") + (#\î . "icirc") + (#\ï . "iuml") + (#\ð . "eth") + (#\ñ . "ntilde") + (#\ò . "ograve") + (#\ó . "oacute") + (#\ô . "ocirc") + (#\õ . "otilde") + (#\ö . "ouml") + (#\÷ . "divide") + (#\ø . "oslash") + (#\ù . "ugrave") + (#\ú . "uacute") + (#\û . "ucirc") + (#\ü . "uuml") + (#\ý . "yacute") + (#\þ . "thorn") + (#\ÿ . "yuml") + (#\Œ . "OElig") + (#\œ . "oelig") + (#\Š . "Scaron") + (#\š . "scaron") + (#\Ÿ . "Yuml") + (#\ƒ . "fnof") + (#\ˆ . "circ") + (#\˜ . "tilde") + (#\Α . "Alpha") + (#\Β . "Beta") + (#\Γ . "Gamma") + (#\Δ . "Delta") + (#\Ε . "Epsilon") + (#\Ζ . "Zeta") + (#\Η . "Eta") + (#\Θ . "Theta") + (#\Ι . "Iota") + (#\Κ . "Kappa") + (#\Λ . "Lambda") + (#\Μ . "Mu") + (#\Ν . "Nu") + (#\Ξ . "Xi") + (#\Ο . "Omicron") + (#\Π . "Pi") + (#\Ρ . "Rho") + (#\Σ . "Sigma") + (#\Τ . "Tau") + (#\Υ . "Upsilon") + (#\Φ . "Phi") + (#\Χ . "Chi") + (#\Ψ . "Psi") + (#\Ω . "Omega") + (#\α . "alpha") + (#\β . "beta") + (#\γ . "gamma") + (#\δ . "delta") + (#\ε . "epsilon") + (#\ζ . "zeta") + (#\η . "eta") + (#\θ . "theta") + (#\ι . "iota") + (#\κ . "kappa") + (#\λ . "lambda") + (#\μ . "mu") + (#\ν . "nu") + (#\ξ . "xi") + (#\ο . "omicron") + (#\π . "pi") + (#\ρ . "rho") + (#\ς . "sigmaf") + (#\σ . "sigma") + (#\τ . "tau") + (#\υ . "upsilon") + (#\φ . "phi") + (#\χ . "chi") + (#\ψ . "psi") + (#\ω . "omega") + (#\ϑ . "thetasym") + (#\ϒ . "upsih") + (#\ϖ . "piv") + (#\  . "ensp") + (#\  . "emsp") + (#\  . "thinsp") + (#\– . "ndash") + (#\— . "mdash") + (#\‘ . "lsquo") + (#\’ . "rsquo") + (#\‚ . "sbquo") + (#\“ . "ldquo") + (#\” . "rdquo") + (#\„ . "bdquo") + (#\† . "dagger") + (#\‡ . "Dagger") + (#\• . "bull") + (#\… . "hellip") + (#\‰ . "permil") + (#\′ . "prime") + (#\″ . "Prime") + (#\‹ . "lsaquo") + (#\› . "rsaquo") + (#\‾ . "oline") + (#\⁄ . "frasl") + (#\€ . "euro") + (#\ℑ . "image") + (#\℘ . "weierp") + (#\ℜ . "real") + (#\™ . "trade") + (#\ℵ . "alefsym") + (#\← . "larr") + (#\↑ . "uarr") + (#\→ . "rarr") + (#\↓ . "darr") + (#\↔ . "harr") + (#\↵ . "crarr") + (#\⇐ . "lArr") + (#\⇑ . "uArr") + (#\⇒ . "rArr") + (#\⇓ . "dArr") + (#\⇔ . "hArr") + (#\∀ . "forall") + (#\∂ . "part") + (#\∃ . "exist") + (#\∅ . "empty") + (#\∇ . "nabla") + (#\∈ . "isin") + (#\∉ . "notin") + (#\∋ . "ni") + (#\∏ . "prod") + (#\∑ . "sum") + (#\− . "minus") + (#\∗ . "lowast") + (#\√ . "radic") + (#\∝ . "prop") + (#\∞ . "infin") + (#\∠ . "ang") + (#\∧ . "and") + (#\∨ . "or") + (#\∩ . "cap") + (#\∪ . "cup") + (#\∫ . "int") + (#\∴ . "there4") + (#\∼ . "sim") + (#\≅ . "cong") + (#\≈ . "asymp") + (#\≠ . "ne") + (#\≡ . "equiv") + (#\≤ . "le") + (#\≥ . "ge") + (#\⊂ . "sub") + (#\⊃ . "sup") + (#\⊄ . "nsub") + (#\⊆ . "sube") + (#\⊇ . "supe") + (#\⊕ . "oplus") + (#\⊗ . "otimes") + (#\⊥ . "perp") + (#\⋅ . "sdot") + (#\⋮ . "vellip") + (#\⌈ . "lceil") + (#\⌉ . "rceil") + (#\⌊ . "lfloor") + (#\⌋ . "rfloor") + (#\〈 . "lang") + (#\〉 . "rang") + (#\◊ . "loz") + (#\♠ . "spades") + (#\♣ . "clubs") + (#\♥ . "hearts") + (#\♦ . "diams")))) + +define (string->escaped-html s port) + "Write the HTML escaped form of S to PORT." + (define (escape c) + (let ((escaped (hash-ref %escape-chars c))) + (if escaped + (format port "&~a;" escaped) + (display c port)))) + (string-for-each escape s)) + +define (object->escaped-html obj port) + "Write the HTML escaped form of OBJ to PORT." + (string->escaped-html + (call-with-output-string (cut display obj <>)) + port)) + +define (attribute-value->html value port) + "Write the HTML escaped form of VALUE to PORT." + (if (string? value) + (string->escaped-html value port) + (object->escaped-html value port))) + +define (attribute->html attr value port) + "Write ATTR and VALUE to PORT." + (format port "~a=\"" attr) + (attribute-value->html value port) + (display #\" port)) + +define (element->html tag attrs body port) + "Write the HTML TAG to PORT, where TAG has the attributes in the +ist ATTRS and the child nodes in BODY." + (format port "<~a" tag) + (for-each (match-lambda + ((attr value) + (display #\space port) + (attribute->html attr value port))) + attrs) + (if (and (null? body) (void-element? tag)) + (display " />" port) + (begin + (display #\> port) + (for-each (cut sxml->html <> port) body) + (format port "" tag)))) + +define (doctype->html doctype port) + (format port "" doctype)) + +define* (sxml->html tree #:optional (port (current-output-port))) + "Write the serialized HTML form of TREE to PORT." + (match tree + (() *unspecified*) + (('doctype type) + (doctype->html type port)) + ;; Unescaped, raw HTML output + (('raw html) + (display html port)) + (((? symbol? tag) ('@ attrs ...) body ...) + (element->html tag attrs body port)) + (((? symbol? tag) body ...) + (element->html tag '() body port)) + ((nodes ...) + (for-each (cut sxml->html <> port) nodes)) + ((? string? text) + (string->escaped-html text port)) + ;; Render arbitrary Scheme objects, too. + (obj (object->escaped-html obj port)))) +``` diff --git a/posts/2015-08-08-haunt-0.1-release.md b/posts/2015-08-08-haunt-0.1-release.md new file mode 100644 index 0000000..836ebbd --- /dev/null +++ b/posts/2015-08-08-haunt-0.1-release.md @@ -0,0 +1,37 @@ +title: Introducing Haunt +date: 2015-08-08 10:00:00 +tags: news, releases +summary: Haunt 0.1 released +--- + +I am pleased to announce the first alpha release of Haunt, yet another +static site generator. Does the world really need another one of +those? No, but Haunt is special because it is written in Guile +Scheme, a clean and elegant Lisp dialect, which allows users to +compose their websites using functional programming techniques. Using +a general-purpose, extensible programming language to build websites +allows Haunt users to view their website as not just mere data, but a +program. Haunt empowers the user to build the abstractions they need +to make a great static website without getting in the way. + +At its core, Haunt is a very simple program. To build your site, +Haunt takes your posts and static assets as input, passes them to a +series of user-defined building procedures that return one or more +pages, and outputs all of the generated pages to the file system. +That's all there is to it. All of the "good stuff" is implemented in +the builder procedures. Haunt 0.1 comes with simple blog and Atom +feed generators. + +Haunt is built to be as hackable as possible, and patches to improve +it are very much welcome. In particular, new post readers for common +formats such as org-mode and Markdown are desired, along with a more +robust blog builder and theme engine. In lieu of a mailing list, +patches may be sent to `davet` at `gnu.org`. + +This blog is built with Haunt, by the way. :) + +Source tarball: [haunt-0.1.tar.gz](https://files.dthompson.us/haunt/haunt-0.1.tar.gz.sig) + +GPG signature [haunt-0.1.tar.gz.sig](https://files.dthompson.us/haunt/haunt-0.1.tar.gz.sig) + +Happy haunting! diff --git a/posts/2015-08-30-ruby-on-guix.md b/posts/2015-08-30-ruby-on-guix.md new file mode 100644 index 0000000..29804c6 --- /dev/null +++ b/posts/2015-08-30-ruby-on-guix.md @@ -0,0 +1,302 @@ +title: Ruby on Guix +date: 2015-08-30 15:00 +tags: gnu, guix, scheme, guile, ruby, wsu +summary: How to use Guix + some elbow grease to replace RVM and Bundler on GNU/Linux +--- + +I’ve been working with Ruby professionally for over 3 years now and +I’ve grown frustrated with two of its most popular development tools: +RVM and Bundler. For those that may not know, RVM is the Ruby version +manager and it allows unprivileged users to download, compile, +install, and manage many versions of Ruby instead of being stuck with +the one that is installed globally by your distro’s package manager. +Bundler is the tool that allows developers to keep a version +controlled “Gemfile” that specifies all of the project’s dependencies +and provides utilities to install and update those gems. These tools +are crucial because Ruby developers often work with many applications +that use different versions of Ruby and/or different versions of gems +such as Rails. Traditional GNU/Linux distributions install packages +to the global `/usr` directory, limiting users to a single version of +Ruby and associated gems, if they are packaged at all. Traditional +package management fails to meet the needs of a lot of users, so many +niche package managers have been developed to supplement them. + +Taking a step back, it becomes apparent that dependency isolation is a +general problem that isn’t confined to software written in Ruby: Node +has npm and nvm, Python has pip and virtualenv, and so on. A big +limitation of all these language-specific package managers is that +they cannot control what is outside of their language domain. In +order to use RVM to successfully compile a version of Ruby, you need +to make sure you have the GCC toolchain, OpenSSL, readline, libffi, +etc. installed using the system package manager (note: I’ve seen RVM +try to build prerequisites like OpenSSL before, which I then disabled +to avoid duplication and security issues and I recommend you do the +same.) In order to use Bundler to install Nokogiri, you need to make +sure libxml2 has been installed using the system package manager. If +you work with more than a single language, the number of different +package management tools needed to get work done is staggering. For +web applications, it’s not uncommon to use RVM, Bundler, NPM, Bower, +and the system package manager simultaneously to get all of the +necessary programs and libraries. Large web applications are +notoriously difficult to deploy, and companies hire a bunch of +operations folk like me to try to wrangle it all. + +Anyway, let’s forget about Node, Python, etc. and just focus on Ruby. +Have you or someone you work with encountered hard to debug issues and +Git merge conflicts due to a problem with `Gemfile.lock`? Bundler’s +fast and loose versioning in the `Gemfile` (e.g. `rails >= 4.0`) +causes headaches when different users update different gems at +different times and check the resulting auto-generated `Gemfile.lock` +into the repository. Have you ever been frustrated that it’s +difficult to deduplicate gems that are shared between multiple bundled +gem sets? Have you looked at the [RVM home page](https://rvm.io) and +been frustrated that they recommend you to `curl | bash` to install +their software? Have you been annoyed by RVM’s strange system of +overriding shell built-ins in order to work its magic? I’m not sure +how you feel, dear reader, but my Ruby environments feel like one +giant, brittle hack, and I’m often enough involved in fixing issues +with them on my own workstation, that of my colleagues, and on +production servers. + +So, if you’re still with me, what do we do about this? How can we +work to improve upon the status quo? Just use Docker? Docker is +helpful, and certainly much better than no isolation at all, but it +hides the flaws of package management inside an opaque disk image and +restricts the environments in which your application is built to +function. The general problem of dependency isolation is orthogonal +to the runtime environment, be it container, virtual machine, or “bare +metal.” Enter functional package management. What does it mean for a +package manager to be functional? GNU Guix, the functional package +manager that I contribute to and recommend, has this to say: + +> GNU Guix is a functional package management tool for the GNU +> system. Package management consists of all activities that relate +> to building packages from sources, honoring their build-time and +> run-time dependencies, installing packages in user environments, +> upgrading installed packages to new versions or rolling back to a +> previous set, removing unused software packages, etc. +> +> The term functional refers to a specific package management +> discipline. In Guix, the package build and installation process +> is seen as a function, in the mathematical sense. That function +> takes inputs, such as build scripts, a compiler, and libraries, +> and returns an installed package. + +Guix has a rich set of features, some of which you may find in other +package managers, but not all of them (unless you use another +functional package manager such as Nix.) Gem/Bundler can do +unprivileged gem installation, but it cannot do transactional upgrades +and rollbacks or install non-Ruby dependencies. Dpkg/yum/pacman can +install all build-time and runtime dependencies, but it cannot do +unprivileged package installation to isolated user environments. And +none of them can precisely describe the *full* dependency graph (all +the way down to the C compiler’s compiler) but *Guix can*. + +Guix is written in Guile, an implementation of the Scheme programming +language. The upcoming release of Guix will feature a Ruby build +system that captures the process of installing gems from `.gem` +archives and a RubyGems import utility to make it easier to write Guix +packages by using the metadata available on https://rubygems.org. +Ruby developers interested in functional package management are +encouraged to try packaging their gems (and dependencies) for Guix. + +Now, how exactly can Guix replace RVM and Bundler? Guix uses an +abstraction called a “profile” that represents a user-defined set of +packages that should work together. Think of it as having many `/usr` +file system trees that can be used in isolation from the others +(without invoking virtualization technologies such as virtual machines +or containers.) To install multiple versions of Ruby and various +gems, the user need only create a separate profile for them: + +```sh +guix package --profile=project-1 --install ruby-2.2 ruby-rspec-3 +# Hypothetical packages: +guix package --profile=project-2 --install ruby-1.9 ruby-rspec-2 +``` + +A profile is a “symlink forest” that is the union of all the packages +it includes, and files are deduplicated among all of them. To +actually use the profile, the relevant environment variables must be +configured. Guix is aware of such variables, and can tell you what to +set by running the following: + +```sh +guix package --search-paths --profile=project-1 +``` + +Additionally, you can also create ad-hoc development environments with +the `guix environment` tool. This tool will spawn a sub-shell (or +another program of your choice) in an environment in which a set of +specified packages are available. This is my preferred method as it +automagically sets all of the environment variables for me and Guix is +free to garbage collect the packages when I close the sub-shell: + +```sh +# Launch a Ruby REPL with ActiveSupport available. +guix environment --ad-hoc ruby ruby-activesupport -E irb +``` + +In order to make this environment reproducible for others, I recommend +keeping a `package.scm` file in version control that describes the +complete dependency graph for your project, as well as metadata such +as the license, version, and description: + +```scheme +(use-modules (guix packages) + (guix licenses) + (guix build-system ruby) + (gnu packages) + (gnu packages version-control) + (gnu packages ssh) + (gnu packages ruby)) + +(package + (name "cool-ruby-project") + (version "1.0") + (source #f) ; not needed just to create dev environment + (build-system ruby-build-system) + ;; These correspond roughly to "development" dependencies. + (native-inputs + `(("git" ,git) + ("openssh" ,openssh) + ("ruby-rspec" ,ruby-rspec))) + (propagated-inputs + `(("ruby-pg" ,ruby-pg) + ("ruby-nokogiri" ,ruby-nokogiri) + ("ruby-i18n" ,ruby-i18n) + ("ruby-rails" ,ruby-rails))) + (synopsis "A cool Ruby project") + (description "This software does some cool stuff, trust me.") + (home-page "https://example.com") + (license expat)) +``` + +With this package file, it is simple to an instantiate a development +environment: + +```sh +guix environment -l package.scm +``` + +I’m not covering it in this post, but properly filling out the blank +`source` field above would allow for building development snapshots, +including running the test suite, in an isolated build container using +the `guix build` utility. This is very useful when composed with a +continuous integration system. Guix itself uses +[Hydra](https://nixos.org/hydra/) as its CI system to perform all +package builds. + +As mentioned earlier, one of the big advantages of writing Guix +package recipes is that the full dependency graph can be captured, +including non-Ruby components. The pg gem provides a good example: + +```scheme +(define-public ruby-pg + (package + (name "ruby-pg") + (version "0.18.2") + (source + (origin + (method url-fetch) + (uri (rubygems-uri "pg" version)) + (sha256 + (base32 + "1axxbf6ij1iqi3i1r3asvjc80b0py5bz0m2wy5kdi5xkrpr82kpf")))) + (build-system ruby-build-system) + (arguments + '(#:test-target "spec")) + ;; Native inputs are used only at build and test time. + (native-inputs + `(("ruby-rake-compiler" ,ruby-rake-compiler) + ("ruby-hoe" ,ruby-hoe) + ("ruby-rspec" ,ruby-rspec))) + ;; Native extension links against PostgreSQL shared library. + (inputs + `(("postgresql" ,postgresql))) + (synopsis "Ruby interface to PostgreSQL") + (description "Pg is the Ruby interface to the PostgreSQL RDBMS. It works +with PostgreSQL 8.4 and later.") + (home-page "https://bitbucket.org/ged/ruby-pg") + (license license:ruby))) +``` + +Note how the recipe specifies the PostgreSQL dependency. Below is the +dependency graph for ruby-pg as produced by `guix graph`, excluding +the GCC compiler toolchain and other low-level tools for brevity. +Pretty neat, eh? + +![ruby-pg dependency graph](/images/guix/ruby-pg-graph.png) + +Given that Guix doesn’t yet have many gems packaged (help wanted), it +can still be advantageous to use it for getting more up-to-date +packages than many distros provide, but in conjuction with Bundler for +fetching Ruby gems. This gets RVM out of your hair whilst creating a +migration path away from Bundler at a later time once the required +gems have been packaged: + +```sh +cd my-project/ +guix environment --ad-hoc ruby bundler libxml2 libxslt # etc. +# A small bash script can be used to make these gem sets. +mkdir .gems +export GEM_HOME=$PWD/.gems +export GEM_PATH=$GEM_HOME:$GEM_PATH +export PATH=$GEM_HOME/bin:$PATH +bundle install +``` + +As you’ve seen in the above package snippets, Guix package definitions +are typically very short and rather easy to write yourself. The +`guix import gem` tool was made to lower the barrier even more by +generating most of the boilerplate code. For example: + +```sh +guix import gem pry +``` + +Produces this Scheme code: + +```scheme +(package + (name "ruby-pry") + (version "0.10.1") + (source + (origin + (method url-fetch) + (uri (rubygems-uri "pry" version)) + (sha256 + (base32 + "1j0r5fm0wvdwzbh6d6apnp7c0n150hpm9zxpm5xvcgfqr36jaj8z")))) + (build-system ruby-build-system) + (propagated-inputs + `(("ruby-coderay" ,ruby-coderay) + ("ruby-method-source" ,ruby-method-source) + ("ruby-slop" ,ruby-slop))) + (synopsis + "An IRB alternative and runtime developer console") + (description + "An IRB alternative and runtime developer console") + (home-page "http://pryrepl.org") + (license expat)) +``` + +One still has to package the propagated inputs if they aren’t yet +available, add the necessary inputs for building native extensions if +needed, and fiddle with the native inputs needed to run the test +suite, but for most pure Ruby gems this gets you close to a working +package quickly. + +In conclusion, while support for Ruby in Guix is still in its early +days, I hope that you have seen the benefits that using a +general-purpose, functional package manager can bring to your Ruby +environments (and all other environments, too.) For more information +about Guix concepts, installation instructions, programming interface, +and tools, please refer to the +[official manual](https://gnu.org/software/guix/manual/). Check out +the [help page](https://gnu.org/software/guix/help/) for ways to +contact the development team for help or to report bugs. If you are +interested in getting your hands dirty, please +[contribute](https://gnu.org/software/guix/contribute/). Besides +contributions of code, art, and docs, we also need +[hardware donations](https://gnu.org/software/guix/donate/) to grow +our build farm to meet the needs of all our users. Happy hacking! diff --git a/posts/2016-04-24-haunt-0.2-release.md b/posts/2016-04-24-haunt-0.2-release.md new file mode 100644 index 0000000..a100d92 --- /dev/null +++ b/posts/2016-04-24-haunt-0.2-release.md @@ -0,0 +1,32 @@ +title: Haunt 0.2 released +date: 2016-04-24 08:00:00 +tags: release +summary: Haunt 0.2 released +--- + +I am pleased to announce the release of Haunt version 0.2. This +release features new readers for the +[Texinfo](http://www.gnu.org/software/texinfo/) and +[Skribe](http://www.nongnu.org/skribilo/) markup formats, an Info +manual, small improvements to the Atom and blog builders, and bug +fixes. + +New reader modules: + +* `(haunt reader skribe)` +* `(haunt reader texinfo)` + +Bug fixes: +* [builder: atom: Allow atom feeds to correctly set the blog prefix.](https://git.dthompson.us/haunt.git/commit/ed1602dc36444bcf1023926854a2cb01b5199e7e) +* [build: Allow building with Guile 2.2.](https://git.dthompson.us/haunt.git/commit/5373877f989689209616109b13fd471b2d093b5f) +* [post: Allow dashes in slugs.](https://git.dthompson.us/haunt.git/commit/a37c3cd58525be76705ea930551f3935a232790e) +* [serve: Catch exceptions when rebuilding site.](https://git.dthompson.us/haunt.git/commit/0d67128c3da6413546015fa9092a0017f50d46b9) + +A special thank you to Christopher Webber and Mathieu Lirzin for +contributing to this release! + +Source tarball: [haunt-0.2.tar.gz](https://files.dthompson.us/haunt/haunt-0.2.tar.gz) + +GPG signature: [haunt-0.2.tar.gz.sig](https://files.dthompson.us/haunt/haunt-0.2.tar.gz.sig) + +Happy haunting! diff --git a/posts/2017-01-23-haunt-0.2.1-release.md b/posts/2017-01-23-haunt-0.2.1-release.md new file mode 100644 index 0000000..7752736 --- /dev/null +++ b/posts/2017-01-23-haunt-0.2.1-release.md @@ -0,0 +1,24 @@ +title: Haunt 0.2.1 released +date: 2017-01-23 08:00:00 +tags: release +summary: Haunt 0.2.1 released +--- + +I am pleased to announce the release of Haunt version 0.2.1. This +release features a new reader for the +[Markdown](http://commonmark.org/) format via +[guile-commonmark](https://github.com/OrangeShark/guile-commonmark) as +well as small improvements to the Atom feed generator and +documentation. + +The new Commonmark reader can be found in the `(haunt reader +commonmark)` module. + +Thank you to Erik Edrosa, Ludovic Courtès, and Vladimir Zhbanov for +contributing to this release. + +Source tarball: [haunt-0.2.1.tar.gz](https://files.dthompson.us/haunt/haunt-0.2.1.tar.gz) + +GPG signature [haunt-0.2.1.tar.gz.asc](https://files.dthompson.us/haunt/haunt-0.2.1.tar.gz.asc) + +Happy haunting! diff --git a/posts/2018-03-10-haunt-0.2.2-release.md b/posts/2018-03-10-haunt-0.2.2-release.md new file mode 100644 index 0000000..5ed22e7 --- /dev/null +++ b/posts/2018-03-10-haunt-0.2.2-release.md @@ -0,0 +1,23 @@ +title: Haunt 0.2.2 released +date: 2018-03-10 20:00:00 +tags: release +summary: Haunt 0.2.2 released +--- + +I am pleased to announce the release of Haunt version 0.2.2. This +release contains bug fixes accumulated over the past year. + +Fixes: +* [Don't ignore a specified non-default port in 'haunt serve'.](https://git.dthompson.us/haunt.git/commit/829ee49c2d5c13e3f8a6fc34bdd39f999efbaffe) +* [ui: serve: Fix deprecation warning.](https://git.dthompson.us/haunt.git/commit/be68ae741054986c3b8ff25cf69f3a08df94cce1) +* [html: Stop escaping apostrophes.](https://git.dthompson.us/haunt.git/commit/99181c71bc3ed7cf504374cb5d101e85ffaee1f5) +* [serve: Fix 'file-extension' so that the right MIME type is chosen.](https://git.dthompson.us/haunt.git/commit/e7b1b290b11c4f2c89999fc079278e917f65bca9) + +Thank you to Jelle Licht, Ludovic Courtès, Sudarshan S Chawathe, and +Urbain Vaes for contributing to this release. + +Source tarball: [haunt-0.2.2.tar.gz](https://files.dthompson.us/haunt/haunt-0.2.2.tar.gz) + +GPG signature [haunt-0.2.2.tar.gz.asc](https://files.dthompson.us/haunt/haunt-0.2.2.tar.gz.asc) + +Happy haunting! diff --git a/posts/angularjs.md b/posts/angularjs.md deleted file mode 100644 index 0ea8c19..0000000 --- a/posts/angularjs.md +++ /dev/null @@ -1,135 +0,0 @@ -title: AngularJS Post-mortem -date: 2013-08-08 11:00:00 -tags: web, javascript, angularjs, wsu -summary: AngularJS likes/dislikes and what went right/wrong ---- - -[AngularJS](http://angularjs.org/) is the new popular client-side -Javascript application framework developed by Google. We have recently -adopted it at Vista Higher Learning for building our latest features -that require a lot client-side logic. Now that I have a few -applications under my belt, it’s time to talk about my experience. - -If you want a quick TL;DR: I think AngularJS is good, but it has a -steep learning curve and there’s no well defined set of best -practices. - -Note: I will be using plenty of terms that will probably only make -sense for people that have used AngularJS. - -The Good Stuff --------------- - -These are the things that went well. The things that made me glad that -we chose to use AngularJS. - -### Easier Testing - -Our Javascript test suite uses -[Jasmine](http://pivotal.github.io/jasmine/). AngularJS is built with -test frameworks like Jasmine in mind. AngularJS could tends to be -easily testable due to dependency injection. When the components of an -application don’t rely on global state, it is easier to mock services -for unit tests. - -### Separation of Concerns - -AngularJS stresses separating the view from the data structures and -logic behind it. I think everyone that’s written a somewhat complex -JQuery application can relate to the mess of CSS selectors and click -callbacks that the code quickly degenerates into. - -AngularJS allows you to break up the DOM into logical chunks that are -handled by separate controllers. Treating the application as many -small pieces working together rather than one giant DOM blob keeps the -code clean. Message passing via `$emit` and `$broadcast` keeps -controllers loosely coupled to each other. - -### No More JQuery Spaghetti - -Directives, the strategy for encapsulating DOM manipulation, are -wonderful. It is an elegant solution to the mess that was JQuery -selectors and event callbacks. AngularJS comes with a lot of -directives out of the box to handle the most common stuff like -showing/hiding elements, handling clicks, dynamically setting CSS -classes. - -### More Maintainable Code - -AngularJS is feature-rich. It does a lot on your behalf, which greatly -reduces the amount of boilerplate code needed to get a prototype up -and running. I had the opportunity to essentially rewrite an existing -JQuery application using AngularJS. The results were clear: The -AngularJS version had fewer lines of code, was more readable, and -was easier to debug. - -Bumps in the Road ------------------ - -These are the things that didn’t go smoothly. They boil down to -AngularJS having a steep learning curve and ill-informed software -design decisions on my part. - -### Understanding the Magic - -A lot of things seem to happen by magic. For example, it is possible -to make a asynchronous request and get a promise object in -return. When the promise is assigned to a variable on `$scope`, -AngularJS not only knows to ignore it while the request hasn’t -finished, but it will re-assign to that variable the value of the -asynchronous call when it completes. It is a nice feature, but it -takes some digging to find out what is really going on. - -### Poor Documentation - -I know I’m not the only one that hates the official AngularJS -documentation. The documentation is getting more complete, but its -still not very useful. Functions frequently have a blurb describing -what they do, but no explanation of the parameter list. It’s hard to -use a function that doesn’t describe what it expects for input. - -When the documentation confused us, which it did frequently, we turned -to the AngularJS book from -[O’Reilly](http://shop.oreilly.com/product/0636920028055.do) for -help. I need to get around to reading more of it. - -### RESTful Resources and Rails - -AngularJS claims to be targeted at CRUD applications, but using the -HTTP backend and the `Resource` abstraction that sits on top of it was -particularly difficult. A good amount of time was spent on trying to -get the HTTP requests from resources to conform to what our Rails -backend expects, like root-wrapping. - -### Bloated Controllers - -I frequently made controllers that had too much state and logic that -should have been extracted into services/factories/etc. A controller -would start out slim but would quickly grow to several hundred lines -of code as features were added. Controllers should be the middle man -between the view and the model and that’s it. - -Some tell-tale signs that a controller is becoming bloated: - -* There are a lot of private functions (not defined on `$scope`) - -* Functions are defined on `$scope` just so you can unit-test them, - but are never used in the template - -I attribute controller bloat to a lack of knowing the appropriate uses -for other AngularJS components. It was easy to just keep adding to the -controller. - -Conclusion ----------- - -Overall, I think things went well, but I (and the rest of my team) -made a lot of beginner mistakes. But that’s the learning process, -isn’t it? - -Now that I know more about AngularJS, it will be easier to make better -design decisions moving forward. - -I believe that as AngularJS continues to mature, some concensus in the -community about best practices will emerge that will make things -easier for beginners. diff --git a/posts/diaspora.md b/posts/diaspora.md deleted file mode 100644 index 32a8991..0000000 --- a/posts/diaspora.md +++ /dev/null @@ -1,53 +0,0 @@ -title: Find Me on Diaspora -date: 2013-06-30 15:00 -tags: foss, diaspora, federated, decentralized, rails, wsu -summary: I have started using and contributing to Diaspora. ---- - -With all of the recent news about the NSA’s widespread spying, I have -decided to ween myself off of proprietary, centralized web -services. Facebook, Google, and other such corporations hold onto -massive amounts of our data that we’ve willingly given to them via -status messages, “like” buttons, searches, and emails. Using and -contributing to free (as in freedom), decentralized (like email) web -services is a really great way to re-establish control of our -data. These services rely on many small, interconnected nodes to -operate, rather than a monolithic website that is under the control of -one entity. If the distinction between centralized and decentralized -isn’t clear, consider how email functions. There are many email -providers to choose from. Users can communicate with others that -choose to use a different email provider. This is how web services -should work, but unfortunately very few work this way now. - -The centralized web application that I spend too much time using is -Facebook. I have knowingly given Facebook a “frontdoor” into my life -for years now and I’m ready to move on. I think that the concept of a -“social network” is fun, so I wanted a Facebook replacement. -Fortunately, there is one: [Diaspora](http://diasporaproject.org/). - -Diaspora is a [free](https://github.com/diaspora/diaspora), -distributed, social networking web application written in Ruby using -the Rails framework. Diaspora is a community-run project. Its success -depends upon users, developers, technical writers, user interface -designers, etc. investing their time and/or money into making it -better. The Diaspora network is broken up into many servers, known as -[pods](http://podupti.me). Users have the choice of which pod to store -their data on. Pods assert no ownership over their user’s data, unlike -Facebook, and do not use that data for targeted -advertisements. Diaspora is still a rather young project, but it does -everything that I need it to do. Goodbye, Facebook! - -Since I’m a programmer, I naturally wanted to hack on some code and -contribute. The main developers are very friendly and give great -guidance to newcomers that want to help out. Every Monday is a “Bug -Mash Monday”, where a list of open issues is presented to entice -contributors to resolve them. In the past few weeks, I have made two -contributions to the Diaspora project: a -[bug fix](https://github.com/diaspora/diaspora/issues/2948) and a -[small feature](https://github.com/diaspora/diaspora/issues/2948). Diaspora -is very hackable and I encourage other developers with Ruby/Rails and -Javascript knowledge to join in. - -TL;DR: Diaspora is great. Create an account. Check out my -[profile](https://joindiaspora.com/u/davexunit). Start sharing. Happy -hacking. :) diff --git a/posts/emacs-required-packages.md b/posts/emacs-required-packages.md deleted file mode 100644 index 58874f8..0000000 --- a/posts/emacs-required-packages.md +++ /dev/null @@ -1,62 +0,0 @@ -title: Syncing Required Packages in Emacs -date: 2013-12-30 11:30 -tags: emacs, wsu -summary: A simple way to keep Emacs packages synced across machines using package.el ---- - -I use Emacs on several different computers. To keep my configuration -consistent across all of them, I do what many people do and made the -`~/.emacs.d` directory a -[git repository](https://github.com/davexunit/.emacs.d). I don’t like -to keep copies of all of the Elisp extensions that I use, such as -paredit and geiser, in this repository. Instead, I prefer to use -package.el (introduced in Emacs 24) with the -[MELPA](https://melpa.org) repository. This saves me from having to -manually keep all of the extensions I use up-to-date, but requires -another method to keep useful packages in sync between computers. - -There’s a project called -[Pallet](https://github.com/rdallasgray/pallet) that solves this -problem, but it was too heavy for my liking. Instead, I wrote a short -function that simply iterates over a list of required packages and -installs those that are not currently installed. - -```elisp -;; Additional packages that I use. -(setq required-packages - '(better-defaults - elfeed - geiser - ido-ubiquitous - js2-mode - magit - paredit - rainbow-delimiters - smex)) - -(defun install-missing-packages () - "Install all required packages that haven’t been installed." - (interactive) - (mapc (lambda (package) - (unless (package-installed-p package) - (package-install package))) - required-packages) - (message "Installed all missing packages!")) -``` - -Now, it’s as easy as typing `M-x install-missing-packages RET` when -starting Emacs for the first time on a new computer to download all of -the extensions that I need. Note that before calling -`install-missing-packages` you must have already initialized the -package manager via the `package-initialize` function. This approach -does require some manual bookkeeping in order to keep the -`required-packages` list up-to-date with your workflow, but I haven’t -found it to be problematic. - -Update: If this solution is too simplistic for you, you should check -out [use-package](https://github.com/jwiegley/use-package), which -reddit user [lunayorn](http://www.reddit.com/user/lunaryorn) pointed -out to me. Thanks! - -Check out the comments on -[reddit](http://www.reddit.com/r/emacs/comments/1u0xr4/quick_hack_syncing_required_packages_in_emacs/). diff --git a/posts/first-foss-contribution.md b/posts/first-foss-contribution.md deleted file mode 100644 index 32aa752..0000000 --- a/posts/first-foss-contribution.md +++ /dev/null @@ -1,38 +0,0 @@ -title: My First Real FOSS Contribution -date: 2013-06-15 18:00 -tags: foss, mediagoblin, python, federated, decentralized, wsu -summary: I added a small feature! ---- - -I spend a lot of my free time writing code. I usually work on my own -personal projects that never really go anywhere. So, I decided to take -a detour from my normal hacking routine and contribute to an existing -free software project. My contribution was accepted awhile ago now, -but I wasn’t blogging then so I’m rambling about it now. - -It’s wise to find a project with a low barrier of entry. An active IRC -channel and/or mailing list with people willing to help newcomers is -ideal. I remembered hearing about -[GNU MediaGoblin](http://mediagoblin.org) at LibrePlanet 2012, so I -decided to check things out. MediaGoblin is a media sharing web -application written in Python. Their bug tracker marks tickets that -require little work and don’t require a deep understanding of -MediaGoblin as bitesized’. - -I chose to work on -[this ticket](http://issues.mediagoblin.org/ticket/453) because it -didn’t require any complicated database migrations or knowledge of the -media processing code. I added a new configuration option, -`allow_comments`, and a small amount of code to enforce the setting. - -Eventually, the ticket got reviewed and -[Christopher Webber](http://dustycloud.org) (MediaGoblin’s friendly -project leader) merged it: “Heya. Great branch, this works -perfectly. Merged!” - -It was a very small change, but I was happy to _finally_ have some -actual code of mine in a real free software project. I have a strong -passion for free software and the GNU philosophy, so it’s really great -to participate in the community. My job as a professional software -developer eats up a lot of my time these days, but I hope to find the -time to continue hacking and contributing. diff --git a/posts/first-guile-patch.md b/posts/first-guile-patch.md deleted file mode 100644 index d67c786..0000000 --- a/posts/first-guile-patch.md +++ /dev/null @@ -1,150 +0,0 @@ -title: First GNU Guile Patch and More Guix Packages -date: 2013-11-22 21:00 -tags: gnu, guix, scheme, guile, wsu -summary: I added alist->hash-table procedures for Guile’s native hash table implementation and packaged SDL libraries for Guix. ---- - -I have spent some of the last month working on contributing to GNU -Guile and now I can finally say that I have contributed code to the -project. Guile has several hash table implementations: a Guile native -one, SRFI-69, and R6RS. SRFI-69 contains a handy procedure, -`alist->hash-table`, which allows for a sort of hash literal-like -syntax: - -```scheme -(alist->hash-table - '((foo . 1) - (bar . 2) - (baz . 3))) -``` - -However, I prefer to use Guile’s native hash implementation, and -SRFI-69 is incompatible with it, so I decided to port -`alist->hash-table`. Unfortunately, it was not as simple as writing -just one procedure. The native hash tables actually have 4 different -ways of doing key comparisons: Using `equal?`, `eq?`, `eqv?`, and -user-defined procedures. This is a design flaw because the user must -make sure to use the right procedures at all times rather than simply -set the `hash` and `assoc` procedures when creating the hash table -instance. - -Below is the final implementation. Since 3 of the 4 variations were -essentially the same, I wrote a short macro to reduce code -duplication. - -```scheme -;;;; Copyright (C) 2013 Free Software Foundation, Inc. -;;;; -;;;; This library is free software; you can redistribute it and/or -;;;; modify it under the terms of the GNU Lesser General Public -;;;; License as published by the Free Software Foundation; either -;;;; version 3 of the License, or (at your option) any later version. -;;;; -;;;; This library is distributed in the hope that it will be useful, -;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -;;;; Lesser General Public License for more details. -;;;; -;;;; You should have received a copy of the GNU Lesser General Public -;;;; License along with this library; if not, write to the Free Software -;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -;;;; - -(define-syntax-rule (define-alist-converter name hash-set-proc) - (define (name alist) - "Convert ALIST into a hash table." - (let ((table (make-hash-table))) - (for-each (lambda (pair) - (hash-set-proc table (car pair) (cdr pair))) - (reverse alist)) - table))) - -(define-alist-converter alist->hash-table hash-set!) -(define-alist-converter alist->hashq-table hashq-set!) -(define-alist-converter alist->hashv-table hashv-set!) - -(define (alist->hashx-table hash assoc alist) - "Convert ALIST into a hash table with custom HASH and ASSOC -procedures." - (let ((table (make-hash-table))) - (for-each (lambda (pair) - (hashx-set! hash assoc table (car pair) (cdr pair))) - (reverse alist)) - table)) -``` - -Not only did I manage to get my code into Guile, I was given commit -access to GNU Guix by the lead developer, Ludovic Courtès. I have -never had commit access to any free software project repositories -besides my own. I feel quite honored, as cheesy as that may sound. - -Packages for SDL and SDL2 have been merged into master, and packages -for SDL_gfx, SDL_image, SDL_mixer, SDL_net, and SDL_ttf for SDL 1.2 -are pending review. - -I like to show off the elegance of Guix package recipes, so here’s -some code: - -```scheme -;;; Copyright © 2013 David Thompson -;;; -;;; This file is part of GNU Guix. -;;; -;;; GNU Guix is free software; you can redistribute it and/or modify it -;;; under the terms of the GNU General Public License as published by -;;; the Free Software Foundation; either version 3 of the License, or (at -;;; your option) any later version. -;;; -;;; GNU Guix is distributed in the hope that it will be useful, but -;;; WITHOUT ANY WARRANTY; without even the implied warranty of -;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;;; GNU General Public License for more details. -;;; -;;; You should have received a copy of the GNU General Public License -;;; along with GNU Guix. If not, see . - -(define sdl - (package - (name "sdl") - (version "1.2.15") - (source (origin - (method url-fetch) - (uri - (string-append "http://libsdl.org/release/SDL-" - version ".tar.gz")) - (sha256 - (base32 - "005d993xcac8236fpvd1iawkz4wqjybkpn8dbwaliqz5jfkidlyn")))) - (build-system gnu-build-system) - (arguments '(#:tests? #f)) ; no check target - (inputs `(("libx11" ,libx11) - ("libxrandr" ,libxrandr) - ("mesa" ,mesa) - ("alsa-lib" ,alsa-lib) - ("pkg-config" ,pkg-config) - ("pulseaudio" ,pulseaudio))) - (synopsis "Cross platform game development library") - (description "Simple DirectMedia Layer is a cross-platform development -library designed to provide low level access to audio, keyboard, mouse, -joystick, and graphics hardware.") - (home-page "http://libsdl.org/") - (license lgpl2.1))) - -(define sdl2 - (package (inherit sdl) - (name "sdl2") - (version "2.0.0") - (source (origin - (method url-fetch) - (uri - (string-append "http://libsdl.org/release/SDL2-" - version ".tar.gz")) - (sha256 - (base32 - "0y3in99brki7vc2mb4c0w39v70mf4h341mblhh8nmq4h7lawhskg")))) - (license bsd-3))) -``` - -Much help is needed to package the GNU system for Guix. If you are -interested in helping, please drop by `#guix` on freenode, we are -friendly. :) diff --git a/posts/first-guix-package.md b/posts/first-guix-package.md deleted file mode 100644 index 1b5c704..0000000 --- a/posts/first-guix-package.md +++ /dev/null @@ -1,43 +0,0 @@ -title: My First GNU Guix Patch -date: 2013-10-16 21:00 -tags: gnu, guix, scheme, guile, wsu -summary: I packaged libtheora for GNU Guix ---- - -Over the weekend, I decided to try out GNU Guix: A fully functional -package manager based on Nix and a distribution of the GNU system. I’m -a big proponent of GNU Guile, thus I was excited to see a DSL for -package management written with Guile. - -I was told that libtheora would be pretty easy to package, and it -was. Here’s what the package definition looks like: - -```scheme -(define libtheora - (package - (name "libtheora") - (version "1.1.1") - (source (origin - (method url-fetch) - (uri (string-append "http://downloads.xiph.org/releases/theora/libtheora-" - version ".tar.xz")) - (sha256 - (base32 - "0q8wark9ribij57dciym5vdikg2464p8q2mgqvfb78ksjh4s8vgk")))) - (build-system gnu-build-system) - (inputs `(("libvorbis" ,libvorbis))) - ;; The .pc files refer to libogg. - (propagated-inputs `(("libogg" ,libogg))) - (synopsis "Library implementing the Theora video format") - (description - "The libtheora library implements the ogg theora video format, -a fully open, non-proprietary, patent-and-royalty-free, general-purpose -compressed video format.") - (license license:bsd-3) - (home-page "http://xiph.org/theora/"))) -``` - -Pretty slick, eh? Now, I’m starting to work on packaging SDL (1.2 -and 2) and the SDL extensions (gfx, ttf, etc.), which are not quite as -easy. I hope to package all of the dependencies that guile-2d will -need to be available as a Guix package. diff --git a/posts/gnu30.md b/posts/gnu30.md deleted file mode 100644 index 3d42bf3..0000000 --- a/posts/gnu30.md +++ /dev/null @@ -1,62 +0,0 @@ -title: GNU 30th Anniversary Hackathon -date: 2013-09-30 22:00 -tags: gnu, fsf, free software -summary: Happy birthday, GNU! ---- - -I spent my weekend at MIT at the GNU 30th anniversary hackathon. I had -never participated in a hackathon before and was excited to see what -it was like. Developers from many GNU and non-GNU projects were there -to hack and help others get involved, and RMS was there to give the -keynote speech. - -On Saturday, I spent nearly the entire day in the GNU FM room. About a -year ago I wrote an -[installation guide](http://bugs.foocorp.net/projects/fm/wiki/How_to_install) -on the GNU FM wiki, and so Matt Lee asked me to walk some newcomers -through getting a development environment up and running. I was able -to help four people with this. They all had a functioning GNU FM -server and they were able to scrobble their music to it. Setting up -GNU FM can be quite a pain, and the guide I had written was missing -some information and gave some bad advice. I simplified and rewrote -some of it so that it’s easier to follow. Hopefully this will benefit -a future contributor to GNU FM. - -At 5PM on Saturday, RMS gave a talk about the future of free software -and the GNU project. He discussed the value of reverse engineering -proprietary applications and device drivers in order to write free -replacements. He also talked about the dangers of -software-as-a-service and the “iThings”. His -[new article](http://www.wired.com/opinion/2013/09/why-free-software-is-more-important-now-than-ever-before/) -on Wired covers much of the same subject matter. After his speech, he -raffled off a stuffed baby gnu and an “adorable” GNU 30th anniversary -mug. Chris, owner of ThinkPenguin, won both items! After the speech -came the reception in which I ate some delicious vegan cupcakes and -acquired two 3D printed gnu logos that were sitting atop each -cupcake. After the reception, I briefly went out to a pub with Matt -Lee and Matthew Garrett. Donald Robertson of the FSF joined in later. - -On Sunday, I spent the first couple of hours helping out more with GNU -FM because Matt Lee was sick. For lunch, I went with a large group to -a chinese restaurant. Included in the group was the John Eaton, the -GNU Octave author, and Zak Rogoff, Campaigns Manager at the FSF. It -was interesting to talk to John about the challenges that he faced and -continues to face when trying to keep up with Matlab and maintaining -compatibility even when the Matlab engineers make bad design decisions. - -After lunch, I met up with Mark Weaver, one of the GNU Guile -developers. He helped me write my first patch for Guile: a new REPL -option called “read-wrapper” that allows external code to hook into -the part of the REPL that waits for user input. Guile-2D needs this -functionality in order to create a REPL that plays nice with the game -event loop. Since the main thread is in an event loop, waiting for -user input at the REPL prompt would stop the game entirely. To get -around this, we used the “read-wrapper” option to pass the procedure -that reads user input into another thread so that Guile-2D’s event -loop can continue running. We achieved this functionality in less than -100 lines of code. This hack showed me how great it is to use a -language with first-class continuations. - -tl;dr: The hackathon was a great time. Happy birthday, GNU. - -![GNU 30th logo](/images/gnu30.jpg) diff --git a/posts/guile-2d-0.1.md b/posts/guile-2d-0.1.md deleted file mode 100644 index 150a760..0000000 --- a/posts/guile-2d-0.1.md +++ /dev/null @@ -1,28 +0,0 @@ -title: Guile-2D 0.1 Release -date: 2013-09-27 12:00:00 -tags: foss, gnu, guile, scheme, gamedev, wsu -summary: Official release of Guile-2D Version 0.1 ---- - -To celebrate the GNU Project’s 30th anniversary, I have decided to -make the very first release of my 2D game development framework for -[GNU Guile](https://gnu.org/s/guile). GNU Guile is a Scheme -implementation, and has the honor of being the official extension -language of the GNU project. Guile-2D is a layer above SDL, OpenGL, -FreeImage, and FTGL that provides abstractions for common 2D game -programming requirements such as sprites, tilesets, animations, -scripting, and collision detection. - -There is a lot of work to do in order to get Guile-2D up to snuff with -the game libraries for more popular languages like Python and Lua. I -am looking for contributors who share my vision of creating a fully -featured, easy to use game library in Scheme. - -Guile-2D currently supports GNU/Linux distributions. I am looking for -help to get it running on OS X and Windows. - -Please refer to the `INSTALL.org`, `README.org`, and texinfo files to -learn how to install Guile-2D, run example programs, and write your -own games. - -[Browse the source code on GitHub](https://github.com/davexunit/guile-2d) diff --git a/posts/guile-2d-devlog-1.md b/posts/guile-2d-devlog-1.md deleted file mode 100644 index 1b36772..0000000 --- a/posts/guile-2d-devlog-1.md +++ /dev/null @@ -1,44 +0,0 @@ -title: guile-2d - A 2D Game Development Framework for GNU Guile -date: 2013-08-07 23:00:00 -tags: foss, gnu, guile, scheme, gamedev, wsu -summary: I have started work on a 2D game framework ---- - -This is the very first devlog entry for my pet project, guile-2d. As -the title suggests, guile-2d is a 2D game development framework for -[GNU Guile](https://gnu.org/s/guile), a Scheme implementation that has -the honor of being the official extension language of the GNU -project. Guile is a language with a growing number of features, but it -still lacks a large assortment of libraries. I like to do 2D game -programming, and I saw a niche that needed to be filled. Python has -Pygame, Lua has Love, but there’s no fun and accessible game -programming library for Guile. Guile-2d is working to correct that. - -The goal of Guile-2d is to create an easy to use 2D game programming -framework. Libraries like SDL give the programmer a rather low-level -set of tools that they can use to build a game, guile-2d will provide -high-level tools over low-level SDL and OpenGL for commonly used -elements of 2D games: tile maps, sprite animation, particle systems, -collision detection, vector math, A* pathfinding, etc. Such features -will allow a game developer to very quickly produce a working -prototype with guile-2d. - -Guile-2d is a framework, which means that it has some opinion about -what the right way to do things is. The most apparent example of this -is the game loop. The game loop runs at 60 frames-per-second and uses -fixed timestep updates. Those that have read -[Fix Your Timestep](http://gafferongames.com/game-physics/fix-your-timestep/) -will know that this decision is a good thing. - -Perhaps the most important feature of guile-2d is the ability to do -“live coding”. When the game loop starts, a REPL -(read-eval-print-loop) server is started. Using the great -[Geiser](http://www.nongnu.org/geiser/) extension for Emacs to connect -to the REPL server, one can modify their game as it is running. This -gives users the power to evaluate some new code and see the changes -reflected immediately in the game window. No need to restart the game -unless you crash it! - -This has been a brief overview of some of the features and goals of -guile-2d. If this project interests you, you can check out the source -code on [Github](https://github.com/davexunit/guile-2d). diff --git a/posts/guile-2d-frp.md b/posts/guile-2d-frp.md deleted file mode 100644 index 1970e73..0000000 --- a/posts/guile-2d-frp.md +++ /dev/null @@ -1,62 +0,0 @@ -title: Functional Reactive Programming in Scheme with guile-2d -date: 2014-03-09 21:00:00 -tags: foss, gnu, guile, scheme, gamedev, wsu -summary: Introduction to FRP in Scheme with guile-2d ---- - -Last month, the [GNU Guile](https://gnu.org/s/guile) project -celebrated the 3rd anniversary of its 2.0 release with a hacker -[potluck](http://savannah.gnu.org/forum/forum.php?forum_id=7887). -Guilers were encouraged to bring a tasty hack to the mailing list to -share with everyone. My dish was a simple -[functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming) -library. - -Functional reactive programming (FRP) provides a way to describe -time-varying values and their relationships using a functional and -declarative programming style. To understand what this means, let’s -investigate a typical variable definition in Scheme. The expression -`(define c (+ a b))` defines the variable `c` to be the sum of -variables `a` and `b` at the time of evaluation. If `a` or `b` is -assigned a new value, `c` remains the same. However, for applications -that deal with state that transforms over time, it would be convenient -if we could define `c` to react to changes in `a` and `b` by -recomputing the sum. Contrast this approach with the more traditional -style of modeling dynamic state via events and callbacks. A lot of -programmers, myself included, have written code with so many callbacks -that the resulting program is unmaintainable spaghetti code. Callback -hell is real, but if you accept FRP into your heart then you will be -saved! - -By now you’re probably wondering: “What the hell does all this mean?” -So, here’s a real-world example of guile-2d’s FRP API: - -```scheme -(define-signal position - (signal-fold v+ (vector2 320 240) - (signal-map (lambda (v) - (vscale v 4)) - (signal-sample game-agenda 1 key-arrows)))) -``` - -In guile-2d, time-varying values are called “signals”. The above -signal describes a relationship between the arrow keys on the keyboard -and the position of the player. `signal-sample` is used to trigger a -signal update upon every game tick that provides the current state of -the arrow keys. `key-arrows` is a vector2 that maps to the current -state of the arrow keys, allowing for 8 direction movement. This -vector2 is then scaled 4x to make the player move faster. Finally, -the scaled vector is added to the previous player position via -`signal-fold`. The player’s position is at `(320, 240)` initially. -As you can see, there are no callbacks and explicit mutation needed. -Those details have been abstracted away, freeing the programmer to -focus on more important things. - -I think it’s helpful to see FRP in action to really appreciate the -magic. So, check out this screencast! - -![](/videos/guile-2d-frp-demo.webm) - -To see all of the juicy implementation details, check out the -[git repository](https://gitorious.org/sly/sly/source/sly/signal.scm). -Thanks for following along! diff --git a/posts/guile-2d-is-now-sly.md b/posts/guile-2d-is-now-sly.md deleted file mode 100644 index 4ec8255..0000000 --- a/posts/guile-2d-is-now-sly.md +++ /dev/null @@ -1,33 +0,0 @@ -title: Guile-2D is now named “Sly” -date: 2014-08-11 19:00 -tags: foss, gnu, guile, scheme, gamedev, wsu ---- - -Guile-2D has been the working title for my game engine written in -Guile Scheme for over a year now. The name has become limiting since -I realized that it wouldn’t be much extra work to support 3D graphics. -After much indecision, I’ve finally decided on an official name: Sly. -I think it’s a great name. It’s short, easy to type, and slyness is -one of the definitions of “guile”. - -In other news: - -* Sly has a new contributor! - [Jordan Russel](https://gitorious.org/~seconded) has written a - [new module](https://gitorious.org/sly/sly/source/9231dca37261de8269149ccad4517acad41aa015:sly/joystick.scm) - that provides joystick input support. - -* I have written a - [module for describing animations](https://gitorious.org/sly/sly/source/9231dca37261de8269149ccad4517acad41aa015:sly/transition.scm). - -* I have been slowly working on a scene graph implementation that - plays well with the functional reactive programming API. - -* As mentioned above, 3D graphics support is on the way! So far, I - have implemented a perspective projection matrix, a "look at" - matrix, and a cube primitive. - -![3D Scene Graph](/images/sly/scene-graph-prototype.png) - -Check out the Sly source code repository on -[Gitorious](https://gitorious.org/sly/sly)! diff --git a/posts/guile-2d-live-asset-reload.md b/posts/guile-2d-live-asset-reload.md deleted file mode 100644 index f1e67b5..0000000 --- a/posts/guile-2d-live-asset-reload.md +++ /dev/null @@ -1,49 +0,0 @@ -title: Live Asset Reloading with guile-2d -date: 2014-05-04 22:00:00 -tags: foss, gnu, guile, scheme, gamedev, wsu -summary: Automatically reload modified assets while developing guile-2d progams ---- - -Guile-2d provides a dynamic environment in which a developer can build -a game incrementally as it runs via the Guile REPL. It’s nice to be -able to hot-swap code and have the running game reflect the changes -made, but what about the game data files? If an image file or other -game asset is modified, it would be nice if the game engine took -notice and reloaded it automatically. This is what guile-2d’s live -asset reloading feature does. - -The new `(2d live-reload)` module provides the `live-reload` -procedure. `live-reload` takes a procedure like `load-texture` and -returns a new procedure that adds the live reload magic. The new -procedure returns assets wrapped in a signal, a time-varying value. A -coroutine is started that periodically checks if the asset file has -been modified, and if so, reloads the asset and propagates it via the -signal. Game objects that depend on the asset will be regenerated -immediately. - -Here’s some example code: - -```scheme -(define load-texture/live - (live-reload load-texture)) - -(define-signal texture - (load-texture/live "images/p1_front.png")) - -(define-signal sprite - (signal-map - (lambda (texture) - (make-sprite texture - #:position (vector2 320 240))) - texture)) -``` - -`load-texture/live` loads textures and reloads them when they change -on disk. Every time the texture is reloaded, the sprite is -regenerated using the new texture. - -Here’s a screencast to see live reloading in action: - -![](/videos/guile-2d-live-reload.webm) - -Guile-2d is ever-so-slowly approaching a 0.2 release. Stay tuned! diff --git a/posts/guile-cooperative-repl.md b/posts/guile-cooperative-repl.md deleted file mode 100644 index 50cf39e..0000000 --- a/posts/guile-cooperative-repl.md +++ /dev/null @@ -1,73 +0,0 @@ -title: A Cooperative REPL Server for Guile 2.0.10 -date: 2014-01-24 22:00 -tags: gnu, scheme, guile, wsu -summary: Guile 2.0.10 will include a new type of REPL server suited for single-threaded applications. ---- - -The next release of GNU Guile, 2.0.10, is to be released “real soon -now”. My contribution to this release is the new `(system repl -coop-server)` module. This module introduces a useful variant of the -REPL server that I’ve named the “cooperative” REPL server. It is -cooperative because it can be integrated with single-threaded programs -without the thread synchronization issues present in the ordinary REPL -server. - -Using delimited continuations and mvars (another new feature coming in -Guile 2.0.10), it was possible to create a REPL server whose clients -all ran in the context of a single thread. The cooperative server -puts the user in control of when it is safe to evaluate expressions -entered at the REPL prompt. - -Here’s an example of how to use it: - -```scheme -(use-modules (system repl coop-server)) - -(define server (spawn-coop-repl-server)) - -(while #t - (poll-coop-repl-server server) - (sleep 1)) -``` - -That’s all it takes. There are only 2 public procedures! -`spawn-coop-repl-server` creates a new cooperative REPL server -object and starts listening for clients. `poll-coop-repl-server` -applies pending operations for the server. - -Now that I’ve explained the API, onto the implementation details! -Although the REPLs run in the context of a single thread, there are -other threads needed to make it all work. To avoid thinking too much -about thread synchronization issues, I used the new `(ice-9 mvars)` -module to provide thread-safe objects for read/write operations. I -used delimited continuations in order to yield control back to the -user from inside the loop. - -When the server is spawned, a new thread is created to listen for -client connections. When a client connects, a request to create a new -REPL is put into the server’s "evaluation" mvar, the storage place for -pending server operations. The job of `poll-coop-repl-server` is to -attempt to take from the evaluation mvar and execute the operation -stored within, if any. In this case, an operation called `new-repl` -is hanging out along with the client socket. The contents of the mvar -are removed, and a new REPL is started in the main thread. - -This new REPL is run within a "prompt", Guile’s implementation of -delimited continuations. The prompt allows the REPL to be paused -temporarily while waiting for user input. Just before the prompt is -aborted, a thunk containing the logic to read user input is stored -within the client’s "read" mvar for yet another thread to use. -Without this additional thread and the use of a prompt, the main -thread would block while waiting for input, defeating the purpose of -the cooperative REPL. The reader thread waits for this thunk to -appear in the read mvar. When it appears, the thunk is applied and -the resulting expression is stored as an `eval` operation within the -evaluation mvar. When `poll-coop-repl-server` is called, the REPL -prompt is resumed. The input expression is evaluated in the context -of the main thread, the result is printed, and the process repeats -itself. - -That’s all, folks. Thanks for following along. I’m very excited to -use the cooperative REPL server in guile-2d and I hope that others -find it useful as well. Many thanks to Mark Weaver for helping me out -when I got stuck and for all of the helpful code review. diff --git a/posts/haunt-0.1-release.md b/posts/haunt-0.1-release.md deleted file mode 100644 index 836ebbd..0000000 --- a/posts/haunt-0.1-release.md +++ /dev/null @@ -1,37 +0,0 @@ -title: Introducing Haunt -date: 2015-08-08 10:00:00 -tags: news, releases -summary: Haunt 0.1 released ---- - -I am pleased to announce the first alpha release of Haunt, yet another -static site generator. Does the world really need another one of -those? No, but Haunt is special because it is written in Guile -Scheme, a clean and elegant Lisp dialect, which allows users to -compose their websites using functional programming techniques. Using -a general-purpose, extensible programming language to build websites -allows Haunt users to view their website as not just mere data, but a -program. Haunt empowers the user to build the abstractions they need -to make a great static website without getting in the way. - -At its core, Haunt is a very simple program. To build your site, -Haunt takes your posts and static assets as input, passes them to a -series of user-defined building procedures that return one or more -pages, and outputs all of the generated pages to the file system. -That's all there is to it. All of the "good stuff" is implemented in -the builder procedures. Haunt 0.1 comes with simple blog and Atom -feed generators. - -Haunt is built to be as hackable as possible, and patches to improve -it are very much welcome. In particular, new post readers for common -formats such as org-mode and Markdown are desired, along with a more -robust blog builder and theme engine. In lieu of a mailing list, -patches may be sent to `davet` at `gnu.org`. - -This blog is built with Haunt, by the way. :) - -Source tarball: [haunt-0.1.tar.gz](https://files.dthompson.us/haunt/haunt-0.1.tar.gz.sig) - -GPG signature [haunt-0.1.tar.gz.sig](https://files.dthompson.us/haunt/haunt-0.1.tar.gz.sig) - -Happy haunting! diff --git a/posts/haunt-0.2-release.md b/posts/haunt-0.2-release.md deleted file mode 100644 index a100d92..0000000 --- a/posts/haunt-0.2-release.md +++ /dev/null @@ -1,32 +0,0 @@ -title: Haunt 0.2 released -date: 2016-04-24 08:00:00 -tags: release -summary: Haunt 0.2 released ---- - -I am pleased to announce the release of Haunt version 0.2. This -release features new readers for the -[Texinfo](http://www.gnu.org/software/texinfo/) and -[Skribe](http://www.nongnu.org/skribilo/) markup formats, an Info -manual, small improvements to the Atom and blog builders, and bug -fixes. - -New reader modules: - -* `(haunt reader skribe)` -* `(haunt reader texinfo)` - -Bug fixes: -* [builder: atom: Allow atom feeds to correctly set the blog prefix.](https://git.dthompson.us/haunt.git/commit/ed1602dc36444bcf1023926854a2cb01b5199e7e) -* [build: Allow building with Guile 2.2.](https://git.dthompson.us/haunt.git/commit/5373877f989689209616109b13fd471b2d093b5f) -* [post: Allow dashes in slugs.](https://git.dthompson.us/haunt.git/commit/a37c3cd58525be76705ea930551f3935a232790e) -* [serve: Catch exceptions when rebuilding site.](https://git.dthompson.us/haunt.git/commit/0d67128c3da6413546015fa9092a0017f50d46b9) - -A special thank you to Christopher Webber and Mathieu Lirzin for -contributing to this release! - -Source tarball: [haunt-0.2.tar.gz](https://files.dthompson.us/haunt/haunt-0.2.tar.gz) - -GPG signature: [haunt-0.2.tar.gz.sig](https://files.dthompson.us/haunt/haunt-0.2.tar.gz.sig) - -Happy haunting! diff --git a/posts/haunt-0.2.1-release.md b/posts/haunt-0.2.1-release.md deleted file mode 100644 index 7752736..0000000 --- a/posts/haunt-0.2.1-release.md +++ /dev/null @@ -1,24 +0,0 @@ -title: Haunt 0.2.1 released -date: 2017-01-23 08:00:00 -tags: release -summary: Haunt 0.2.1 released ---- - -I am pleased to announce the release of Haunt version 0.2.1. This -release features a new reader for the -[Markdown](http://commonmark.org/) format via -[guile-commonmark](https://github.com/OrangeShark/guile-commonmark) as -well as small improvements to the Atom feed generator and -documentation. - -The new Commonmark reader can be found in the `(haunt reader -commonmark)` module. - -Thank you to Erik Edrosa, Ludovic Courtès, and Vladimir Zhbanov for -contributing to this release. - -Source tarball: [haunt-0.2.1.tar.gz](https://files.dthompson.us/haunt/haunt-0.2.1.tar.gz) - -GPG signature [haunt-0.2.1.tar.gz.asc](https://files.dthompson.us/haunt/haunt-0.2.1.tar.gz.asc) - -Happy haunting! diff --git a/posts/haunt-0.2.2-release.md b/posts/haunt-0.2.2-release.md deleted file mode 100644 index 5ed22e7..0000000 --- a/posts/haunt-0.2.2-release.md +++ /dev/null @@ -1,23 +0,0 @@ -title: Haunt 0.2.2 released -date: 2018-03-10 20:00:00 -tags: release -summary: Haunt 0.2.2 released ---- - -I am pleased to announce the release of Haunt version 0.2.2. This -release contains bug fixes accumulated over the past year. - -Fixes: -* [Don't ignore a specified non-default port in 'haunt serve'.](https://git.dthompson.us/haunt.git/commit/829ee49c2d5c13e3f8a6fc34bdd39f999efbaffe) -* [ui: serve: Fix deprecation warning.](https://git.dthompson.us/haunt.git/commit/be68ae741054986c3b8ff25cf69f3a08df94cce1) -* [html: Stop escaping apostrophes.](https://git.dthompson.us/haunt.git/commit/99181c71bc3ed7cf504374cb5d101e85ffaee1f5) -* [serve: Fix 'file-extension' so that the right MIME type is chosen.](https://git.dthompson.us/haunt.git/commit/e7b1b290b11c4f2c89999fc079278e917f65bca9) - -Thank you to Jelle Licht, Ludovic Courtès, Sudarshan S Chawathe, and -Urbain Vaes for contributing to this release. - -Source tarball: [haunt-0.2.2.tar.gz](https://files.dthompson.us/haunt/haunt-0.2.2.tar.gz) - -GPG signature [haunt-0.2.2.tar.gz.asc](https://files.dthompson.us/haunt/haunt-0.2.2.tar.gz.asc) - -Happy haunting! diff --git a/posts/hello-world.md b/posts/hello-world.md deleted file mode 100644 index 9a8be46..0000000 --- a/posts/hello-world.md +++ /dev/null @@ -1,29 +0,0 @@ -title: Hello, world! -date: 2013-06-08 17:18 -tags: hello -summary: I has a shiny new website ---- - -Hey! I have a shiny new website. I just purchased a cheap VPS from -[Digital Ocean](http://digitalocean.com) and registered the -[dthompson.us](https://dthompson.us) domain for this site. Instead of -the going the Wordpress route (or using similar blogging software), -I’ve decided to use a static site generator called -[Pelican](http://getpelican.com) after seeing that the -[Linux](http://kernel.org) kernel website was using it. I’m going to -use this site to host this blog, my resume, and information about my -software projects. - -A little about myself: I’m a software developer from Massachusetts. I -graduated from [Worcester State University](http://worcester.edu) in -2012 with a BS in Computer Science. I currently work as a junior -[Ruby on Rails](http://rubyonrails.org) developer at -[Vista Higher Learning](http://vistahigherlearning.com). I am an -advocate of free and open source software and I use GNU/Linux as my -operating system of choice. In my spare time, I like to write free -software (mostly my own never-to-be-finished projects). Oddly enough, -I’m still using a proprietary web service to host all of my software -projects, so check out my [Github](https://github.com/davexunit) page. - -Connect with me on [Diaspora](https://joindiaspora.com/u/davexunit) or -[Twitter](https://twitter.com/davexunit). diff --git a/posts/little-schemer.md b/posts/little-schemer.md deleted file mode 100644 index d58c96b..0000000 --- a/posts/little-schemer.md +++ /dev/null @@ -1,57 +0,0 @@ -title: The Little Schemer -date: 2013-08-11 15:00:00 -tags: scheme, books, wsu -summary: I bought "The Little Schemer" ---- - -Yesterday, I took a trip to the MIT Press Bookstore and picked up a -copy of -[The Little Schemer](http://mitpress.mit.edu/books/little-schemer). I’ve -only spent a few hours reading and coding along with it, but I’ve had -a lot of fun. The following is a mini-review based on my experience -thus far. - -“The Little Schemer” teaches you to think recursively using an -interesting and comedic writing style and the Scheme programming -language. While Scheme is the language of choice, the real goal is to -teach you problem solving rather than the details of a specific -language. The book starts off simple, explaining what atoms, lists, -and S-expressions are. Rather than providing the definition and then -showing examples, it first gives examples in the form of a question -and answer. - -Example: - -> Is it true that this an atom? -> -> **atom** -> -> Yes, because **atom** is a string of characters beginning with a -> letter. - -From the examples given, a definition is created. In later examples, a -Scheme procedure is written that produces the correct answers for all -of the questions stated before it. It’s fun to build the procedure, -verify that it works for all cases, and compare your implementation -with the book’s. - -“The Little Schemer” defines ten commandments that are essential to -correctly solving the problems in the book. Some commandments are -first given in an incomplete form, and expanded later when a greater -level of understanding has been achieved. The problems that you solve -reinforce the commandments. You might notice that you start writing -procedures without thinking much about it, much like the muscle memory -earned from using Emacs a lot. Gerald J. Sussman was right when he -said that this book “can perform the same service that Hanon’s finger -exercises or Czerny’s piano studies perform for the student of the -piano.” I have no idea who Hanon and Czerny are, but I get it. For the -drummers out there, you could liken this book to -[Stick Control](http://www.amazon.com/Stick-Control-For-Snare-Drummer/dp/1892764040). - -The writing style is very informal, comedic, and food themed. Page 13 -has a space reserved for jelly stains, and page 52 tells you to “go -cons a piece of cake onto your mouth.” I have laughed a number of -times while reading. Oh, and let’s not forget about the cute elephant -drawings. This is definitely not your average boring, dry computer -science book. If you are interested in a unique and enjoyable learning -experience, then I highly recommend reading “The Little Schemer”. diff --git a/posts/maine-2013.md b/posts/maine-2013.md deleted file mode 100644 index 2e979f9..0000000 --- a/posts/maine-2013.md +++ /dev/null @@ -1,41 +0,0 @@ -title: Maine! -date: 2013-07-15 22:00 -tags: maine, vacation -summary: My vacation in Maine ---- - -Every summer, my friends and I go to Maine for a week. We stay in the -Rockland area in an old house on a blueberry field. This year we hiked -a mountain in Camden, hiked Mt. Katahdin, and went to the Rockland -Blues Festival. - -Here are some pictures taken from my not-so-great cellphone camera: - -![View from the house](/images/maine-2013/view-from-house.jpg) - -The view from the driveway of our house. - -![Zeke's Lookout](/images/maine-2013/zekes-lookout.jpg) - -Sign at Zeke’s Lookout. Somewhere on a mountain near Camden. - -![Mt. Katahdin, foot of the Hunt trail](/images/maine-2013/katahdin-camp.jpg) - -Mt. Katahdin as seen from the foot of the Hunt trail. - -![Waterfall on Mt. Katahdin](/images/maine-2013/katahdin-waterfall.jpg) - -A beautiful waterfall that can be seen about a mile into the hike. - -![View from the mountain 1](/images/maine-2013/katahdin-view-1.jpg) -![View from the mountain 2](/images/maine-2013/katahdin-view-2.jpg) - -Views from the mountain. - -![Fog on the mountain](/images/maine-2013/katahdin-fog.jpg) - -Thick fog past the treeline. - -![Rockland Blues Festival](/images/maine-2013/blues-festival.jpg) - -Closed down street packed with people and bands in Rockland. diff --git a/posts/pangocairo.md b/posts/pangocairo.md deleted file mode 100644 index fd651b5..0000000 --- a/posts/pangocairo.md +++ /dev/null @@ -1,321 +0,0 @@ -title: Font Rendering in OpenGL with Pango and Cairo -date: 2013-08-17 16:00:00 -tags: opengl, pango, cairo, font, wsu -summary: A brief tutorial for rendering fonts in OpenGL with libpangocairo ---- - -I am working towards a 0.1 release of my game development framework -for GNU Guile, [guile-2d](https://github.com/davexunit/guile-2d). One -of the few remaining blockers on my to-do list is font rendering. A -reddit user, [Madsy9](http://www.reddit.com/user/Madsy9), pointed me -in the right direction with this -[comment](http://www.reddit.com/r/scheme/comments/1k739l/guile_2d_game_programming_lib_for_scheme/cbmnyuk). There -are two libraries needed to perform nice font rendering with proper -internationalization support: [Pango](http://www.pango.org/), “a -library for laying out and rendering of text, with an emphasis on -internationalization,” and [Cairo](http://cairographics.org/), “a 2D -graphics library with support for multiple output devices.” - -It took me awhile to put together all of the pieces and build a -working sample program. The goal of this post is to help others that -may be trying to accomplish a similar task that have no prior -knowledge of Pango and Cairo. I will assume basic knowledge of C, SDL, -and OpenGL throughout this post. - -Let’s get the basic SDL and OpenGL initialization out of the way: - -```c -#include -#include -#include - -#define WINDOW_WIDTH 800 -#define WINDOW_HEIGHT 600 -#define FONT "Sans Bold 18" -#define TEXT "The quick brown fox is so かわいい!" - -void -init_sdl () -{ - SDL_Init (SDL_INIT_EVERYTHING); - SDL_SetVideoMode (WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_OPENGL); -} - -void -init_gl () -{ - glClearColor (0.0f, 0.0f, 0.0f, 0.0f); - glDisable (GL_DEPTH_TEST); - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable (GL_TEXTURE_2D); - glViewport (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - glMatrixMode (GL_PROJECTION); - glLoadIdentity (); - glOrtho (0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, -1, 1); - glMatrixMode (GL_MODELVIEW); - glLoadIdentity (); -} -``` - -`create_texture` simply creates an OpenGL texture given an array of -pixel data and the texture dimensions. Our Cairo surface will use BGRA -color. - -```c -unsigned int -create_texture (unsigned int width, - unsigned int height, - unsigned char *pixels) -{ - unsigned int texture_id; - - glGenTextures (1, &texture_id); - glBindTexture (GL_TEXTURE_2D, texture_id); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D (GL_TEXTURE_2D, - 0, - GL_RGBA, - width, - height, - 0, - GL_BGRA, - GL_UNSIGNED_BYTE, - pixels); - - return texture_id; -} -``` - -`draw_texture` clears the screen, renders a simple textured quad using -OpenGL’s immediate mode, and then swaps buffers. - -```c -void -draw_texture (int width, - int height, - unsigned int texture_id) -{ - /* Render a texture in immediate mode. */ - glMatrixMode (GL_MODELVIEW); - glLoadIdentity (); - glClear (GL_COLOR_BUFFER_BIT); - glPushMatrix (); - glBindTexture (GL_TEXTURE_2D, texture_id); - glColor3f (1.f, 1.0f, 1.0f); - - glBegin (GL_QUADS); - glTexCoord2f (0.0f, 0.0f); - glVertex2f (0.0f, 0.0f); - glTexCoord2f (1.0f, 0.0f); - glVertex2f (width, 0.0f); - glTexCoord2f (1.0f, 1.0f); - glVertex2f (width , height); - glTexCoord2f (0.0f, 1.0f); - glVertex2f (0.0f, height); - glEnd (); - - glPopMatrix (); - SDL_GL_SwapBuffers(); -} -``` - -`create_cairo_context` is used to make a new Cairo context that draws -to a raw data surface. The return value, a `cairo_t`, is the main -object in Cairo. All drawing is done via a `cairo_t` object. A -context needs a surface to draw on. -`cairo_image_surface_create_for_data` creates a raw data surface for -us. We will be translating the surface into a texture later on. - -```c -cairo_t* -create_cairo_context (int width, - int height, - int channels, - cairo_surface_t** surf, - unsigned char** buffer) -{ - *buffer = calloc (channels * width * height, sizeof (unsigned char)); - *surf = cairo_image_surface_create_for_data (*buffer, - CAIRO_FORMAT_ARGB32, - width, - height, - channels * width); - return cairo_create (*surf); -} -``` - -`create_layout_context` also makes a new Cairo context, but this -context is for PangoLayout objects. In Pango, a layout describes the -style of a paragraph of text. The layout needs a context in order to -function. We use `cairo_image_surface_create` with dimensions of 0x0 -because we won’t actually be rendering to this surface. Instead, we -will layout our text and use `create_cairo_context` to build a context -with a surface that is the size of the rendered text. Cairo uses -reference counting for dynamically allocated objects, so we need to -call `cairo_surface_destroy` when we’re done with the temporary -surface. The context still maintains a reference to the surface, so -the memory for the surface will not be freed until the context is. - -```c -cairo_t* -create_layout_context () -{ - cairo_surface_t *temp_surface; - cairo_t *context; - - temp_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0); - context = cairo_create (temp_surface); - cairo_surface_destroy (temp_surface); - - return context; -} -``` - -`get_text_size` tells us the size of the text that’s in the layout, in -pixels. Pango’s units are not in pixels, so we must divide by -`PANGO_SCALE` in order to get pixel units. - -```c - -void -get_text_size (PangoLayout *layout, - unsigned int *width, - unsigned int *height) -{ - pango_layout_get_size (layout, width, height); - /* Divide by pango scale to get dimensions in pixels. */ - *width /= PANGO_SCALE; - *height /= PANGO_SCALE; -} -``` - -`render_text` is where all of the magic happens. First, we create a -layout with a layout context and set the text that we will render with -this layout. `TEXT` is defined earlier in the program as "The quick -brown fox is so かわいい!" - -Then we create a `PangoFontDescription` object. This object -represents the font that we want to render. Earlier in the program, -`FONT` is defined as "Sans Bold 18". Pango is able to figure out how -to load a font from a string in this format. Your system must be able -to recognize the font family and font face, though. I haven’t yet -figured out how to have Pango render an arbitrary font from a `*.ttf` -file. - -Next, we create a rendering context by getting the layout’s size and -creating a context with a surface big enough to show all of the -rendered text. - -Finally, we set the font color to white, render the text to the -surface with `pango_cairo_show_layout`, and create an OpenGL texture -from the surface. We also clean up all the objects that we no longer -need before returning. - -```c -unsigned int -render_text (const char *text, - unsigned int *text_width, - unsigned int *text_height, - unsigned int *texture_id) -{ - cairo_t *layout_context; - cairo_t *render_context; - cairo_surface_t *temp_surface; - cairo_surface_t *surface; - unsigned char* surface_data = NULL; - PangoFontDescription *desc; - PangoLayout *layout; - - layout_context = create_layout_context (); - - /* Create a PangoLayout, set the font and text */ - layout = pango_cairo_create_layout (layout_context); - pango_layout_set_text (layout, text, -1); - - /* Load the font */ - desc = pango_font_description_from_string (FONT); - pango_layout_set_font_description (layout, desc); - pango_font_description_free (desc); - - /* Get text dimensions and create a context to render to */ - get_text_size (layout, text_width, text_height); - render_context = create_cairo_context (*text_width, - *text_height, - 4, - &surface, - &surface_data); - - /* Render */ - cairo_set_source_rgba (render_context, 1, 1, 1, 1); - pango_cairo_show_layout (render_context, layout); - *texture_id = create_texture(*text_width, *text_height, surface_data); - - /* Clean up */ - free (surface_data); - g_object_unref (layout); - cairo_destroy (layout_context); - cairo_destroy (render_context); - cairo_surface_destroy (surface); -} -``` - -`main` is pretty simple. We initialize SDL and OpenGL, render text -to a texture, and enter the rendering loop. The program will run -until you click the close button, press "enter", or press "q". - -```c -int main (int argc, char **argv) -{ - SDL_Event event; - int keep_running = 1; - unsigned int texture_id; - unsigned int text_width = 0; - unsigned int text_height = 0; - - init_sdl (); - init_gl (); - render_text(TEXT, - &texture_id, - &text_width, - &text_height); - - /* Update/render loop */ - while (keep_running) { - SDL_PollEvent (&event); - - switch (event.type) { - case SDL_QUIT : - keep_running = 0; - break; - - case SDL_KEYDOWN : - if (event.key.keysym.sym == SDLK_ESCAPE) - keep_running = 0; - if (event.key.keysym.sym == SDLK_q) - keep_running = 0; - break; - } - - draw_texture (texture_id, text_width, text_height); - SDL_Delay (16); - } - - /* Clean up */ - glDeleteTextures (1, &texture_id); - - SDL_Quit(); - - return 0; -} -``` - -And we’re done! You should now be able to render some text in an -OpenGL context. I hope this brief tutorial was helpful. Font rendering -isn’t easy, and it’s not really my area of interest. I’m glad that -Pango exists to do all of the real work for me so that I can more -quickly move on to the parts of graphics programming that I actually -enjoy. - -You can download the full source code [here](/src/pangocairo.tar.gz). diff --git a/posts/reproducible-development-environments.md b/posts/reproducible-development-environments.md deleted file mode 100644 index 1672c78..0000000 --- a/posts/reproducible-development-environments.md +++ /dev/null @@ -1,120 +0,0 @@ -title: Reproducible Development Environments with GNU Guix -date: 2014-11-08 22:00 -tags: gnu, guix, scheme, guile, wsu -summary: Easily acquire the dependencies needed to hack on a project ---- - -If you’re a software developer, then you probably know very well that -setting up a project’s development environment for the first time can -be a real pain. Installing all of the necessary dependencies using -your system’s package manager can be very tedious. To "solve" this -problem, we have resorted to inventing new package managers and -dependency bundlers for pretty much every programming language. Ruby -has rubygems and bundler, Python has pip and virtualenv, PHP has -composer, node.js has npm, and so on. Wouldn’t it be nice to instead -have a single package manager that can handle it all? Enter -[GNU Guix](https://gnu.org/s/guix), a purely functional package -manager and GNU/Linux distribution. Using Guix, you can easily create -a development environment for any software project using the `guix -environment` tool. - -`guix environment` is a new utility added in Guix 0.8, which should be -released in a few weeks. It accepts one or more packages as input and -produces a new shell environment in which all of the dependencies for -those packages are made available. For example, `guix environment -emacs` will get you everything you need to build GNU Emacs from a -source release tarball. By default, your `$SHELL` is spawned, but -you may opt to use any arbitrary shell command instead. If you’ve -used Nix before, you may notice that this sounds exactly like -`nix-shell`, and that’s because it was what inspired me to write -`guix environment`. - -Now, let’s take a look at an example. One of my hobby projects is -[Sly](https://gitorious.org/sly/sly), a game engine written in Guile -Scheme. Here’s the relevant Guix code that will produce a complete -development environment: - -```scheme - - ;;; Copyright (C) 2014 David Thompson - ;;; - ;;; Sly is free software: you can redistribute it and/or modify it - ;;; under the terms of the GNU General Public License as published by - ;;; the Free Software Foundation, either version 3 of the License, or - ;;; (at your option) any later version. - - (use-modules (guix packages) - (guix licenses) - (guix build-system gnu) - (gnu packages) - (gnu packages autotools) - (gnu packages guile) - (gnu packages gl) - (gnu packages pkg-config) - (gnu packages sdl) - (gnu packages maths) - (gnu packages image)) - - ;; The development environment needs a tweaked LTDL_LIBRARY_PATH - ;; for finding libfreeimage. - (define freeimage - (package (inherit freeimage) - (native-search-paths - (list (search-path-specification - (variable "LTDL_LIBRARY_PATH") - (directories '("lib"))))))) - - (package - (name "sly") - (version "0.0") - (source #f) - (build-system gnu-build-system) - (inputs - `(("pkg-config" ,pkg-config) - ("autoconf" ,autoconf) - ("automake" ,automake) - ("guile" ,guile-2.0) - ("guile-sdl" ,guile-sdl) - ("guile-opengl" ,guile-opengl) - ("gsl" ,gsl) - ("freeimage" ,freeimage) - ("mesa" ,mesa))) - (synopsis "2D/3D game engine for GNU Guile") - (description "Sly is a 2D/3D game engine written in Guile Scheme. - Sly differs from most game engines in that it emphasizes functional - reactive programming and live coding.") - (home-page "https://gitorious.org/sly/sly") - (license gpl3+)) -``` - -You may have noticed that the source field has been set to false. -This is because the package is not for building and installing. It’s -sole purpose is to provide the necessary software needed to build Sly -from a fresh git checkout. - -Assuming this code is in a file called `package.scm`, you can simply -run `guix environment -l package.scm` to spawn a shell for hacking -on Sly. By default, the environment created is an augmented version -of your pre-existing shell’s environment. This is convenient when you -want to use your installed software in the new environment, such as -git. However, it’s important to make sure that the environment really -does have everything needed for development without relying on any -impurities introduced by your existing environment. Without verifying -this, new developers might be frustrated to find out that the -environment provided to them is incomplete. To verify, pass the -`--pure` flag and build from scratch. - -So, `guix environment` is a pretty nice way to acquire all the -dependencies you need to work on a project, but there is still a lot -of room for improvement. What if you were working on a web -application that required a running PostgreSQL database, Redis server, -and XMPP server? It would be really great if Guix could handle -setting this up for you, too, a la Vagrant. To do so, `guix -environment` could use the existing features of `guix system` to spawn -a new virtual machine that shares the host system’s package store and -the project’s source tree, and then spawn a shell with your -development environment. I hope to implement this in the -not-too-distant future. Until next time, happy hacking! - -Read the discussion about this post on -[Hacker News](https://news.ycombinator.com/item?id=8616918). diff --git a/posts/rinari-jasmine.md b/posts/rinari-jasmine.md deleted file mode 100644 index cb94b1f..0000000 --- a/posts/rinari-jasmine.md +++ /dev/null @@ -1,36 +0,0 @@ -title: Jump to Jasmine Specs with Rinari -date: 2013-12-17 13:00 -tags: emacs, javascript, wsu -summary: I hacked rinari mode to jump between JS sources and jasmine tests ---- - -I use the [rinari](https://github.com/eschulte/rinari) Emacs mode to -assist me when working on rails projects. One of rinari’s most useful -features is the ability to quickly jump from one file to another -related file. I use this feature almost exclusively for jumping -between a ruby class file and its associated rspec file, but lately -I’ve been spending most of my time writing javascript. At VHL, we use -[jasmine](http://pivotal.github.io/jasmine/) for our unit testing -framework and the -[jasmine ruby gem](https://github.com/pivotal/jasmine-gem) to -integrate it with our rails projects. Rinari doesn’t have any -built-in jump settings for jasmine test files, so I wrote this quick -hack to make it work: - -```elisp -;; Make rinari jump to/from javascript source files and specs. -(setcdr (assoc 'javascript rinari-jump-schema) - '("j" - (("app/assets/javascripts/\\1.js" . "spec/javascripts/\\1_spec.js") - ("spec/javascripts/\\1_spec.js" . "app/assets/javascripts/\\1.js") - (t . "spec/javascripts/.*") - (t . "app/javascripts/.*")) - t)) -(rinari-apply-jump-schema rinari-jump-schema) -``` - -Now I can press `C-c ; f j` to jump between a javascript file in -`app/assets/javascripts/` and its jasmine test file in -`spec/javascripts/`. Perhaps I shouldn’t be overwriting the -predefined (but not very useful) javascript jump settings, but I -really wanted to use the `j` key. diff --git a/posts/ruby-on-guix.md b/posts/ruby-on-guix.md deleted file mode 100644 index 29804c6..0000000 --- a/posts/ruby-on-guix.md +++ /dev/null @@ -1,302 +0,0 @@ -title: Ruby on Guix -date: 2015-08-30 15:00 -tags: gnu, guix, scheme, guile, ruby, wsu -summary: How to use Guix + some elbow grease to replace RVM and Bundler on GNU/Linux ---- - -I’ve been working with Ruby professionally for over 3 years now and -I’ve grown frustrated with two of its most popular development tools: -RVM and Bundler. For those that may not know, RVM is the Ruby version -manager and it allows unprivileged users to download, compile, -install, and manage many versions of Ruby instead of being stuck with -the one that is installed globally by your distro’s package manager. -Bundler is the tool that allows developers to keep a version -controlled “Gemfile” that specifies all of the project’s dependencies -and provides utilities to install and update those gems. These tools -are crucial because Ruby developers often work with many applications -that use different versions of Ruby and/or different versions of gems -such as Rails. Traditional GNU/Linux distributions install packages -to the global `/usr` directory, limiting users to a single version of -Ruby and associated gems, if they are packaged at all. Traditional -package management fails to meet the needs of a lot of users, so many -niche package managers have been developed to supplement them. - -Taking a step back, it becomes apparent that dependency isolation is a -general problem that isn’t confined to software written in Ruby: Node -has npm and nvm, Python has pip and virtualenv, and so on. A big -limitation of all these language-specific package managers is that -they cannot control what is outside of their language domain. In -order to use RVM to successfully compile a version of Ruby, you need -to make sure you have the GCC toolchain, OpenSSL, readline, libffi, -etc. installed using the system package manager (note: I’ve seen RVM -try to build prerequisites like OpenSSL before, which I then disabled -to avoid duplication and security issues and I recommend you do the -same.) In order to use Bundler to install Nokogiri, you need to make -sure libxml2 has been installed using the system package manager. If -you work with more than a single language, the number of different -package management tools needed to get work done is staggering. For -web applications, it’s not uncommon to use RVM, Bundler, NPM, Bower, -and the system package manager simultaneously to get all of the -necessary programs and libraries. Large web applications are -notoriously difficult to deploy, and companies hire a bunch of -operations folk like me to try to wrangle it all. - -Anyway, let’s forget about Node, Python, etc. and just focus on Ruby. -Have you or someone you work with encountered hard to debug issues and -Git merge conflicts due to a problem with `Gemfile.lock`? Bundler’s -fast and loose versioning in the `Gemfile` (e.g. `rails >= 4.0`) -causes headaches when different users update different gems at -different times and check the resulting auto-generated `Gemfile.lock` -into the repository. Have you ever been frustrated that it’s -difficult to deduplicate gems that are shared between multiple bundled -gem sets? Have you looked at the [RVM home page](https://rvm.io) and -been frustrated that they recommend you to `curl | bash` to install -their software? Have you been annoyed by RVM’s strange system of -overriding shell built-ins in order to work its magic? I’m not sure -how you feel, dear reader, but my Ruby environments feel like one -giant, brittle hack, and I’m often enough involved in fixing issues -with them on my own workstation, that of my colleagues, and on -production servers. - -So, if you’re still with me, what do we do about this? How can we -work to improve upon the status quo? Just use Docker? Docker is -helpful, and certainly much better than no isolation at all, but it -hides the flaws of package management inside an opaque disk image and -restricts the environments in which your application is built to -function. The general problem of dependency isolation is orthogonal -to the runtime environment, be it container, virtual machine, or “bare -metal.” Enter functional package management. What does it mean for a -package manager to be functional? GNU Guix, the functional package -manager that I contribute to and recommend, has this to say: - -> GNU Guix is a functional package management tool for the GNU -> system. Package management consists of all activities that relate -> to building packages from sources, honoring their build-time and -> run-time dependencies, installing packages in user environments, -> upgrading installed packages to new versions or rolling back to a -> previous set, removing unused software packages, etc. -> -> The term functional refers to a specific package management -> discipline. In Guix, the package build and installation process -> is seen as a function, in the mathematical sense. That function -> takes inputs, such as build scripts, a compiler, and libraries, -> and returns an installed package. - -Guix has a rich set of features, some of which you may find in other -package managers, but not all of them (unless you use another -functional package manager such as Nix.) Gem/Bundler can do -unprivileged gem installation, but it cannot do transactional upgrades -and rollbacks or install non-Ruby dependencies. Dpkg/yum/pacman can -install all build-time and runtime dependencies, but it cannot do -unprivileged package installation to isolated user environments. And -none of them can precisely describe the *full* dependency graph (all -the way down to the C compiler’s compiler) but *Guix can*. - -Guix is written in Guile, an implementation of the Scheme programming -language. The upcoming release of Guix will feature a Ruby build -system that captures the process of installing gems from `.gem` -archives and a RubyGems import utility to make it easier to write Guix -packages by using the metadata available on https://rubygems.org. -Ruby developers interested in functional package management are -encouraged to try packaging their gems (and dependencies) for Guix. - -Now, how exactly can Guix replace RVM and Bundler? Guix uses an -abstraction called a “profile” that represents a user-defined set of -packages that should work together. Think of it as having many `/usr` -file system trees that can be used in isolation from the others -(without invoking virtualization technologies such as virtual machines -or containers.) To install multiple versions of Ruby and various -gems, the user need only create a separate profile for them: - -```sh -guix package --profile=project-1 --install ruby-2.2 ruby-rspec-3 -# Hypothetical packages: -guix package --profile=project-2 --install ruby-1.9 ruby-rspec-2 -``` - -A profile is a “symlink forest” that is the union of all the packages -it includes, and files are deduplicated among all of them. To -actually use the profile, the relevant environment variables must be -configured. Guix is aware of such variables, and can tell you what to -set by running the following: - -```sh -guix package --search-paths --profile=project-1 -``` - -Additionally, you can also create ad-hoc development environments with -the `guix environment` tool. This tool will spawn a sub-shell (or -another program of your choice) in an environment in which a set of -specified packages are available. This is my preferred method as it -automagically sets all of the environment variables for me and Guix is -free to garbage collect the packages when I close the sub-shell: - -```sh -# Launch a Ruby REPL with ActiveSupport available. -guix environment --ad-hoc ruby ruby-activesupport -E irb -``` - -In order to make this environment reproducible for others, I recommend -keeping a `package.scm` file in version control that describes the -complete dependency graph for your project, as well as metadata such -as the license, version, and description: - -```scheme -(use-modules (guix packages) - (guix licenses) - (guix build-system ruby) - (gnu packages) - (gnu packages version-control) - (gnu packages ssh) - (gnu packages ruby)) - -(package - (name "cool-ruby-project") - (version "1.0") - (source #f) ; not needed just to create dev environment - (build-system ruby-build-system) - ;; These correspond roughly to "development" dependencies. - (native-inputs - `(("git" ,git) - ("openssh" ,openssh) - ("ruby-rspec" ,ruby-rspec))) - (propagated-inputs - `(("ruby-pg" ,ruby-pg) - ("ruby-nokogiri" ,ruby-nokogiri) - ("ruby-i18n" ,ruby-i18n) - ("ruby-rails" ,ruby-rails))) - (synopsis "A cool Ruby project") - (description "This software does some cool stuff, trust me.") - (home-page "https://example.com") - (license expat)) -``` - -With this package file, it is simple to an instantiate a development -environment: - -```sh -guix environment -l package.scm -``` - -I’m not covering it in this post, but properly filling out the blank -`source` field above would allow for building development snapshots, -including running the test suite, in an isolated build container using -the `guix build` utility. This is very useful when composed with a -continuous integration system. Guix itself uses -[Hydra](https://nixos.org/hydra/) as its CI system to perform all -package builds. - -As mentioned earlier, one of the big advantages of writing Guix -package recipes is that the full dependency graph can be captured, -including non-Ruby components. The pg gem provides a good example: - -```scheme -(define-public ruby-pg - (package - (name "ruby-pg") - (version "0.18.2") - (source - (origin - (method url-fetch) - (uri (rubygems-uri "pg" version)) - (sha256 - (base32 - "1axxbf6ij1iqi3i1r3asvjc80b0py5bz0m2wy5kdi5xkrpr82kpf")))) - (build-system ruby-build-system) - (arguments - '(#:test-target "spec")) - ;; Native inputs are used only at build and test time. - (native-inputs - `(("ruby-rake-compiler" ,ruby-rake-compiler) - ("ruby-hoe" ,ruby-hoe) - ("ruby-rspec" ,ruby-rspec))) - ;; Native extension links against PostgreSQL shared library. - (inputs - `(("postgresql" ,postgresql))) - (synopsis "Ruby interface to PostgreSQL") - (description "Pg is the Ruby interface to the PostgreSQL RDBMS. It works -with PostgreSQL 8.4 and later.") - (home-page "https://bitbucket.org/ged/ruby-pg") - (license license:ruby))) -``` - -Note how the recipe specifies the PostgreSQL dependency. Below is the -dependency graph for ruby-pg as produced by `guix graph`, excluding -the GCC compiler toolchain and other low-level tools for brevity. -Pretty neat, eh? - -![ruby-pg dependency graph](/images/guix/ruby-pg-graph.png) - -Given that Guix doesn’t yet have many gems packaged (help wanted), it -can still be advantageous to use it for getting more up-to-date -packages than many distros provide, but in conjuction with Bundler for -fetching Ruby gems. This gets RVM out of your hair whilst creating a -migration path away from Bundler at a later time once the required -gems have been packaged: - -```sh -cd my-project/ -guix environment --ad-hoc ruby bundler libxml2 libxslt # etc. -# A small bash script can be used to make these gem sets. -mkdir .gems -export GEM_HOME=$PWD/.gems -export GEM_PATH=$GEM_HOME:$GEM_PATH -export PATH=$GEM_HOME/bin:$PATH -bundle install -``` - -As you’ve seen in the above package snippets, Guix package definitions -are typically very short and rather easy to write yourself. The -`guix import gem` tool was made to lower the barrier even more by -generating most of the boilerplate code. For example: - -```sh -guix import gem pry -``` - -Produces this Scheme code: - -```scheme -(package - (name "ruby-pry") - (version "0.10.1") - (source - (origin - (method url-fetch) - (uri (rubygems-uri "pry" version)) - (sha256 - (base32 - "1j0r5fm0wvdwzbh6d6apnp7c0n150hpm9zxpm5xvcgfqr36jaj8z")))) - (build-system ruby-build-system) - (propagated-inputs - `(("ruby-coderay" ,ruby-coderay) - ("ruby-method-source" ,ruby-method-source) - ("ruby-slop" ,ruby-slop))) - (synopsis - "An IRB alternative and runtime developer console") - (description - "An IRB alternative and runtime developer console") - (home-page "http://pryrepl.org") - (license expat)) -``` - -One still has to package the propagated inputs if they aren’t yet -available, add the necessary inputs for building native extensions if -needed, and fiddle with the native inputs needed to run the test -suite, but for most pure Ruby gems this gets you close to a working -package quickly. - -In conclusion, while support for Ruby in Guix is still in its early -days, I hope that you have seen the benefits that using a -general-purpose, functional package manager can bring to your Ruby -environments (and all other environments, too.) For more information -about Guix concepts, installation instructions, programming interface, -and tools, please refer to the -[official manual](https://gnu.org/software/guix/manual/). Check out -the [help page](https://gnu.org/software/guix/help/) for ways to -contact the development team for help or to report bugs. If you are -interested in getting your hands dirty, please -[contribute](https://gnu.org/software/guix/contribute/). Besides -contributions of code, art, and docs, we also need -[hardware donations](https://gnu.org/software/guix/donate/) to grow -our build farm to meet the needs of all our users. Happy hacking! diff --git a/posts/stumpwm.md b/posts/stumpwm.md deleted file mode 100644 index e73412f..0000000 --- a/posts/stumpwm.md +++ /dev/null @@ -1,74 +0,0 @@ -title: StumpWM on Debian Wheezy -date: 2013-07-20 15:00 -tags: stumpwm, common lisp, debian, wheezy, wsu -summary: First steps with StumpWM on Debian Wheezy ---- - -Everyone that’s ever talked to me about software development knows -that I am in love with Emacs. Emacs has a wonderful keyboard driven -interface and is almost infinitely customizable via Emacs Lisp. I’ve -done a lot of programming in Emacs from my not-so-great laptop -lately. My laptop has a rather poor 1280x800 resolution and low -performing integrated graphics chip. Until today, I was running the -GNOME 3 desktop environment on it. Unlike most people (or perhaps just -a loud minority), I like GNOME 3. However, I wanted something that was -both less graphics intensive and more keyboard driven than GNOME -Shell and Mutter. - -Someone on IRC told me about -[StumpWM](http://www.nongnu.org/stumpwm/), a window manager written -entirely in Common Lisp. I had heard of StumpWM before, but back then -I wasn’t an Emacs user and I’ve never really stuck with any tiling -window manager that I’ve tried (DWM, Awesome). Now that I know the -power of a fully programmable environment thanks to Emacs, I decided -to give StumpWM a try. After some initial pains trying to get it to -run, I am now using it very happily. - -Here is what I had to do to get StumpWM running on Debian Wheezy. - -1) Install StumpWM - -```sh -sudo apt-get install stumpwm -``` - -2) Create an `.xinitrc` file in my home directory with the following - text - -```sh -exec stumpwm -``` - -3) Workaround clisp “component not found” errors - -I could not get StumpWM to start until I created the following -symbolic links: - -```sh -ln -s /usr/share/common-lisp/source/stumpwm/stumpwm.asd /usr/share/common-lisp/systems/stumpwm.asd -ln -s /usr/share/common-lisp/source/cl-ppcre/cl-ppcre.asd /usr/share/common-lisp/systems/cl-ppcre.asd -``` - -4) Start the X server - -```sh -startx -``` - -I use the GNOME Desktop Manager, so I also created a session file for -StumpWM in `/usr/share/xsessions/stumpwm.desktop`. - -``` -[Desktop Entry] -Encoding=UTF-8 -Name=StumpWM -Comment=This session logs you into StumpWM -Exec=stumpwm -TryExec=stumpwm -Icon= -Type=Application -``` - -I hope this brief installation guide can be of use to one of you out -there in Internet land. Perhaps in the future I will write an article -about customizing StumpWM with Common Lisp. diff --git a/posts/sxml-html-guile.md b/posts/sxml-html-guile.md deleted file mode 100644 index f600d7f..0000000 --- a/posts/sxml-html-guile.md +++ /dev/null @@ -1,501 +0,0 @@ -title: Rendering HTML with SXML and GNU Guile -date: 2015-04-10 18:00 -tags: gnu, guile, wsu -summary: With a little effort, SXML can be used for HTML templates ---- - -GNU Guile provides modules for working with XML documents called SXML. -SXML provides an elegant way of writing XML documents as s-expressions -that can be easily manipulated in Scheme. Here’s an example: - -```scheme -(sxml->xml '(foo (bar (@ (attr "something"))))) -``` - -```xml - -``` - -I don’t know about you, but I work with HTML documents much more often -than XML. Since HTML is very similar to XML, we should be able to -represent it with SXML, too! - -```scheme -(sxml->xml - '(html - (head - (title "Hello, world!") - (script (@ (src "foo.js")))) - (body - (h1 "Hello!")))) -``` - -```html - - - Hello, world! - - - -

Hello!

- - -``` - -Note the use of the empty string in `(script (@ (src "foo.js")) "")`. -The output looks correct now, great! But what about the other void -elements? We’ll have to remember to use the empty string hack each -time we use one. That doesn’t sound very elegant. - -Furthermore, text isn’t even escaped properly! - -```scheme -(sxml->xml "Copyright © 2015 David Thompson ") -``` - -```html -Copyright © 2015 David Thompson <davet@gnu.org> -``` - -The `<` and `>` braces were escaped, but `©` should’ve been rendered -as `©`. Why does this fail, too? Is there a bug in SXML? - -There’s no bug. The improper rendering happens because HTML, while -similar to XML, has some different syntax rules. Instead of using -`sxml->xml`, a new procedure that is tailored to the HTML syntax is -needed. Introducing `sxml->html`: - -```scheme -(define* (sxml->html tree #:optional (port (current-output-port))) - "Write the serialized HTML form of TREE to PORT." - (match tree - (() *unspecified*) - (('doctype type) - (doctype->html type port)) - ;; Unescaped, raw HTML output - (('raw html) - (display html port)) - (((? symbol? tag) ('@ attrs ...) body ...) - (element->html tag attrs body port)) - (((? symbol? tag) body ...) - (element->html tag '() body port)) - ((nodes ...) - (for-each (cut sxml->html <> port) nodes)) - ((? string? text) - (string->escaped-html text port)) - ;; Render arbitrary Scheme objects, too. - (obj (object->escaped-html obj port)))) -``` - -In addition to being aware of void elements and escape characters, it -can also render `'(doctype "html")` as ``, or render an -unescaped HTML string using `'(raw "frog & toad")`. If we replace -`sxml->xml` with `sxml->html` in the failing example above we can see -that it does the right thing. - -```scheme -(sxml->html - '((script (@ (src "foo.js"))) - "Copyright © 2015 David Thompson ")) -``` - -```html - -Copyright © 2015 David Thompson <davet@gnu.org> -``` - -Here’s the full version of my `(sxml html)` module. It’s quite brief, -if you don’t count the ~250 lines of escape codes! This code requires -Guile 2.0.11 or greater. - -Happy hacking! - -```scheme -;; Copyright © 2015 David Thompson -;; -;; This library is free software; you can redistribute it and/or -;; modify it under the terms of the GNU Lesser General Public License -;; as published by the Free Software Foundation; either version 3 of -;; the License, or (at your option) any later version. -;; -;; This library is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -;; Lesser General Public License for more details. -;; -;; You should have received a copy of the GNU Lesser General Public -;; License along with this library. If not, see -;; . - -define-module (sxml html) - #:use-module (sxml simple) - #:use-module (srfi srfi-26) - #:use-module (ice-9 match) - #:use-module (ice-9 format) - #:use-module (ice-9 hash-table) - #:export (sxml->html)) - -define %void-elements - '(area - base - br - col - command - embed - hr - img - input - keygen - link - meta - param - source - track - wbr)) - -define (void-element? tag) - "Return #t if TAG is a void element." - (pair? (memq tag %void-elements))) - -define %escape-chars - (alist->hash-table - '((#\" . "quot") - (#\& . "amp") - (#\' . "apos") - (#\< . "lt") - (#\> . "gt") - (#\¡ . "iexcl") - (#\¢ . "cent") - (#\£ . "pound") - (#\¤ . "curren") - (#\¥ . "yen") - (#\¦ . "brvbar") - (#\§ . "sect") - (#\¨ . "uml") - (#\© . "copy") - (#\ª . "ordf") - (#\« . "laquo") - (#\¬ . "not") - (#\® . "reg") - (#\¯ . "macr") - (#\° . "deg") - (#\± . "plusmn") - (#\² . "sup2") - (#\³ . "sup3") - (#\´ . "acute") - (#\µ . "micro") - (#\¶ . "para") - (#\· . "middot") - (#\¸ . "cedil") - (#\¹ . "sup1") - (#\º . "ordm") - (#\» . "raquo") - (#\¼ . "frac14") - (#\½ . "frac12") - (#\¾ . "frac34") - (#\¿ . "iquest") - (#\À . "Agrave") - (#\Á . "Aacute") - (#\ . "Acirc") - (#\à . "Atilde") - (#\Ä . "Auml") - (#\Å . "Aring") - (#\Æ . "AElig") - (#\Ç . "Ccedil") - (#\È . "Egrave") - (#\É . "Eacute") - (#\Ê . "Ecirc") - (#\Ë . "Euml") - (#\Ì . "Igrave") - (#\Í . "Iacute") - (#\Î . "Icirc") - (#\Ï . "Iuml") - (#\Ð . "ETH") - (#\Ñ . "Ntilde") - (#\Ò . "Ograve") - (#\Ó . "Oacute") - (#\Ô . "Ocirc") - (#\Õ . "Otilde") - (#\Ö . "Ouml") - (#\× . "times") - (#\Ø . "Oslash") - (#\Ù . "Ugrave") - (#\Ú . "Uacute") - (#\Û . "Ucirc") - (#\Ü . "Uuml") - (#\Ý . "Yacute") - (#\Þ . "THORN") - (#\ß . "szlig") - (#\à . "agrave") - (#\á . "aacute") - (#\â . "acirc") - (#\ã . "atilde") - (#\ä . "auml") - (#\å . "aring") - (#\æ . "aelig") - (#\ç . "ccedil") - (#\è . "egrave") - (#\é . "eacute") - (#\ê . "ecirc") - (#\ë . "euml") - (#\ì . "igrave") - (#\í . "iacute") - (#\î . "icirc") - (#\ï . "iuml") - (#\ð . "eth") - (#\ñ . "ntilde") - (#\ò . "ograve") - (#\ó . "oacute") - (#\ô . "ocirc") - (#\õ . "otilde") - (#\ö . "ouml") - (#\÷ . "divide") - (#\ø . "oslash") - (#\ù . "ugrave") - (#\ú . "uacute") - (#\û . "ucirc") - (#\ü . "uuml") - (#\ý . "yacute") - (#\þ . "thorn") - (#\ÿ . "yuml") - (#\Œ . "OElig") - (#\œ . "oelig") - (#\Š . "Scaron") - (#\š . "scaron") - (#\Ÿ . "Yuml") - (#\ƒ . "fnof") - (#\ˆ . "circ") - (#\˜ . "tilde") - (#\Α . "Alpha") - (#\Β . "Beta") - (#\Γ . "Gamma") - (#\Δ . "Delta") - (#\Ε . "Epsilon") - (#\Ζ . "Zeta") - (#\Η . "Eta") - (#\Θ . "Theta") - (#\Ι . "Iota") - (#\Κ . "Kappa") - (#\Λ . "Lambda") - (#\Μ . "Mu") - (#\Ν . "Nu") - (#\Ξ . "Xi") - (#\Ο . "Omicron") - (#\Π . "Pi") - (#\Ρ . "Rho") - (#\Σ . "Sigma") - (#\Τ . "Tau") - (#\Υ . "Upsilon") - (#\Φ . "Phi") - (#\Χ . "Chi") - (#\Ψ . "Psi") - (#\Ω . "Omega") - (#\α . "alpha") - (#\β . "beta") - (#\γ . "gamma") - (#\δ . "delta") - (#\ε . "epsilon") - (#\ζ . "zeta") - (#\η . "eta") - (#\θ . "theta") - (#\ι . "iota") - (#\κ . "kappa") - (#\λ . "lambda") - (#\μ . "mu") - (#\ν . "nu") - (#\ξ . "xi") - (#\ο . "omicron") - (#\π . "pi") - (#\ρ . "rho") - (#\ς . "sigmaf") - (#\σ . "sigma") - (#\τ . "tau") - (#\υ . "upsilon") - (#\φ . "phi") - (#\χ . "chi") - (#\ψ . "psi") - (#\ω . "omega") - (#\ϑ . "thetasym") - (#\ϒ . "upsih") - (#\ϖ . "piv") - (#\  . "ensp") - (#\  . "emsp") - (#\  . "thinsp") - (#\– . "ndash") - (#\— . "mdash") - (#\‘ . "lsquo") - (#\’ . "rsquo") - (#\‚ . "sbquo") - (#\“ . "ldquo") - (#\” . "rdquo") - (#\„ . "bdquo") - (#\† . "dagger") - (#\‡ . "Dagger") - (#\• . "bull") - (#\… . "hellip") - (#\‰ . "permil") - (#\′ . "prime") - (#\″ . "Prime") - (#\‹ . "lsaquo") - (#\› . "rsaquo") - (#\‾ . "oline") - (#\⁄ . "frasl") - (#\€ . "euro") - (#\ℑ . "image") - (#\℘ . "weierp") - (#\ℜ . "real") - (#\™ . "trade") - (#\ℵ . "alefsym") - (#\← . "larr") - (#\↑ . "uarr") - (#\→ . "rarr") - (#\↓ . "darr") - (#\↔ . "harr") - (#\↵ . "crarr") - (#\⇐ . "lArr") - (#\⇑ . "uArr") - (#\⇒ . "rArr") - (#\⇓ . "dArr") - (#\⇔ . "hArr") - (#\∀ . "forall") - (#\∂ . "part") - (#\∃ . "exist") - (#\∅ . "empty") - (#\∇ . "nabla") - (#\∈ . "isin") - (#\∉ . "notin") - (#\∋ . "ni") - (#\∏ . "prod") - (#\∑ . "sum") - (#\− . "minus") - (#\∗ . "lowast") - (#\√ . "radic") - (#\∝ . "prop") - (#\∞ . "infin") - (#\∠ . "ang") - (#\∧ . "and") - (#\∨ . "or") - (#\∩ . "cap") - (#\∪ . "cup") - (#\∫ . "int") - (#\∴ . "there4") - (#\∼ . "sim") - (#\≅ . "cong") - (#\≈ . "asymp") - (#\≠ . "ne") - (#\≡ . "equiv") - (#\≤ . "le") - (#\≥ . "ge") - (#\⊂ . "sub") - (#\⊃ . "sup") - (#\⊄ . "nsub") - (#\⊆ . "sube") - (#\⊇ . "supe") - (#\⊕ . "oplus") - (#\⊗ . "otimes") - (#\⊥ . "perp") - (#\⋅ . "sdot") - (#\⋮ . "vellip") - (#\⌈ . "lceil") - (#\⌉ . "rceil") - (#\⌊ . "lfloor") - (#\⌋ . "rfloor") - (#\〈 . "lang") - (#\〉 . "rang") - (#\◊ . "loz") - (#\♠ . "spades") - (#\♣ . "clubs") - (#\♥ . "hearts") - (#\♦ . "diams")))) - -define (string->escaped-html s port) - "Write the HTML escaped form of S to PORT." - (define (escape c) - (let ((escaped (hash-ref %escape-chars c))) - (if escaped - (format port "&~a;" escaped) - (display c port)))) - (string-for-each escape s)) - -define (object->escaped-html obj port) - "Write the HTML escaped form of OBJ to PORT." - (string->escaped-html - (call-with-output-string (cut display obj <>)) - port)) - -define (attribute-value->html value port) - "Write the HTML escaped form of VALUE to PORT." - (if (string? value) - (string->escaped-html value port) - (object->escaped-html value port))) - -define (attribute->html attr value port) - "Write ATTR and VALUE to PORT." - (format port "~a=\"" attr) - (attribute-value->html value port) - (display #\" port)) - -define (element->html tag attrs body port) - "Write the HTML TAG to PORT, where TAG has the attributes in the -ist ATTRS and the child nodes in BODY." - (format port "<~a" tag) - (for-each (match-lambda - ((attr value) - (display #\space port) - (attribute->html attr value port))) - attrs) - (if (and (null? body) (void-element? tag)) - (display " />" port) - (begin - (display #\> port) - (for-each (cut sxml->html <> port) body) - (format port "" tag)))) - -define (doctype->html doctype port) - (format port "" doctype)) - -define* (sxml->html tree #:optional (port (current-output-port))) - "Write the serialized HTML form of TREE to PORT." - (match tree - (() *unspecified*) - (('doctype type) - (doctype->html type port)) - ;; Unescaped, raw HTML output - (('raw html) - (display html port)) - (((? symbol? tag) ('@ attrs ...) body ...) - (element->html tag attrs body port)) - (((? symbol? tag) body ...) - (element->html tag '() body port)) - ((nodes ...) - (for-each (cut sxml->html <> port) nodes)) - ((? string? text) - (string->escaped-html text port)) - ;; Render arbitrary Scheme objects, too. - (obj (object->escaped-html obj port)))) -``` diff --git a/posts/thinkpad-x220.md b/posts/thinkpad-x220.md deleted file mode 100644 index fa69b98..0000000 --- a/posts/thinkpad-x220.md +++ /dev/null @@ -1,63 +0,0 @@ -title: Liberating a Thinkpad X220 -date: 2013-09-22 21:00 -tags: thinkpad, free software, wsu -summary: I bought a used Thinkpad X220 ---- - -I had been looking for a suitable replacement to my old, slow Compaq -laptop that I purchased during my freshman year of college when I had -very little money. What I liked about my old laptop was that it played -well with free software. I had no trouble getting all of my hardware -to work out-of-the-box with fully free GNU/Linux distributions such as -Trisquel, and I wanted any future laptops of mine to play nicely, too. - -I have heard much praise for Thinkpads over the years. Solid build -quality, utilitarian design, and cheap to buy used. However, upon -further reading, I realized that most newer Thinkpads require nonfree -software in order to the drive the Intel wireless chip. Furthermore, -there was DRM present in the BIOS that would prevent the installation -of PCIe wireless chips that weren't in the whitelist. - -This really bummed me out, but I bought a Thinkpad anyway. I found a -great deal on a used X220 on ebay for $400. In order to liberate it, I -had to make a small deal with the devil: Use the pre-installed Windows -7 to flash a hacked BIOS that removes the whitelist. I could only find -the needed BIOS as a Windows executable, so I didn't have much -choice. This process left me hoping that coreboot gains wider -adoption. - -Once I had verified that I didn't brick my Thinkpad, I installed the -new wireless card. I purchased a Wireless N, half-height, mini PCIe -card from -[Thinkpenguin](https://www.thinkpenguin.com/gnu-linux/penguin-wireless-n-half-height-mini-pcie-card). It -uses an Atheros chipset and is free software compatible. I met Chris, -the owner of Thinkpenguin, at this year's Northeast GNU/Linux Fest at -Harvard. He is doing some great work and I wanted to support his -business. It was nice to buy from someone who could assure me that the -hardware I purchased is fully supported on a libre GNU/Linux -distribution. - -Now that my Thinkpad was free (sans BIOS, of course), it was time for -the final touch. I replaced the hard drive with a 128GB SSD and -installed Debian testing. It takes roughly 9 seconds to get from GRUB -to the GDM login screen. It feels very nice to have a device that -boots so quickly. - -Now that everything had been installed and configured, I was able to -start hacking and get a feel for things. The keyboard is the nicest -I've ever used on a laptop. The [TrackPoint](http://xkcd.com/243/) is -quite a nice way to move around once you get used to it. The -ThinkLight is pretty neat when you're in a dark area. The battery life -is extremely impressive. I don't know exactly how long it lasts yet -but I never have to charge it while I am using it. I was lucky if I -got 2 hours of battery life out of my old laptop, which caused me to -be constantly tethered to an AC adapter. The screen is matte, which is -awesome because it's very difficult to use a laptop outdoors when the -screen is glossy. 1366x768 is not an ideal resolution (16:9 be -damned), but I can live with it on a 12.5" screen. Last but not least, -I honestly like the aesthetics. A lot of people are enamored with the -brushed aluminum designs by that fruit company, but I love the flat -black, functional design of the Thinkpad. - -I hope to really break this thing in over the weekend at the -[GNU 30th Anniversary](https://www.gnu.org/gnu30/) hackathon. -- cgit v1.2.3