Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
15
<![CDATA[I upgraded my Raspberry Pi 400 to 64-bit Raspberry Pi OS 2024-11-19 based on Debian Bookworm 12.9: The desktop of 64-bit Raspberry Pi OS 2024-11-19 on a Raspberry Pi 400. Since I had no files to preserve the process was surprisingly easy as I went with a full installation. And this time I finally used the Raspberry Pi Imager. When I first set up the Pi 400 my only other desktop computer was a Chromebox that couldn't run the Imager on Crostini Linux. This imposed a less convenient network installation which, combined with a subtle bug, made me waste a couple of hours over three installation attempts. Now I have a real Linux PC that runs the Imager just fine. Downloading Raspberry Pi OS, configuring it, and flashing the microSD card went smoothly. When I booted the Pi 400 from the card I was greeted by a ready to run system. On the newly upgraded system, building Medley Interlisp from source for X11 took an hour or so. The environment still runs well with the labwc Wayland...
3 weeks ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Paolo Amoroso's Journal

Bitsnap, a screenshot capture tool for Medley Interlisp

<![CDATA[I wrote Bitsnap, a tool in Interlisp for capturing screenshots on the Medley environment. It can capture and optionally save to a file the full screen, a window with or without title bar and borders, or an arbitrary area. This project helped me learn the internals of Medley, such as extending the background menu, and produced a tool I wanted. For example, with Bitsnap I can capture some areas like specific windows without manually framing them; or the full screen of Medley excluding the title bar and borders of the operating systems that hosts Medley, Linux in my case. Medley can natively capture various portions of the screen. These facilities produce 1-bit images as instances of BITMAP, an image data structure Medley uses for everything from bit patterns, to icons, to actual images. Some Lisp functions manipulate bitmaps. Bitsnap glues together these facilities and packages them in an interactive interface accessible as a submenu of the background menu as well as a programmatic interface, the Interlisp function SNAP. To provide feedback after a capture Bitsnap displays in a window the area just captured, as shown here along with the Bitsnap menu. A bitmap captured with the Bitsnap screenshot tool and its menu on Medley Interlisp. The tool works by copying to a new bitmap the system bitmap that holds the designated area of the screen. Which is straighforward as there are Interlisp functions for accessing the source bitmaps. These functions return a BITMAP and capture: SCREENBITMAP: the full screen WINDOW.BITMAP: a window including the title bar and border BITMAPCOPY: the interior of a window with no title bar and border SNAPW: an arbitrary area The slightly more involved part is bringing captured bitmaps out of Medley in a format today's systems and tools understand. Some Interlisp functions can save a BITMAP to disk in text and binary encodings, none of which are modern standards. The only Medley tool to export to a modern — or less ancient — format less bound to Lisp is the xerox-to-xbm module which converts a BITMAP to the Unix XBM (X BitMap) format. However, xerox-to-xbm can't process large bitmaps. To work around the issue I wrote the function BMTOPBM that saves a BITMAP to a file in a slightly more modern and popular format, PBM (Portable BitMap). I can't think of anything simpler and, indeed, it took me just half a dozen minutes to write the function. Linux and other modern operating systems can natively display PBM files and Netpbm converts PBM to PNG and other widely used standards. For example, this Netpbm pipeline converts to PNG: $ pbmtopnm screenshot.pbm | pnmtopng screenshot.png BMTOPBM can handle bitmaps of any size but its simple algorithm is inefficient. However, on my PC the function takes about 5 seconds to save a 1920x1080 bitmap, which is the worst case as this is the maximum screen size Medley allows. Good enough for the time being. Bitsnap does pretty much all I want and doesn't need major new features. Still, I may optimize BMTOPBM or save directly to PNG. #Interlisp #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/bitsnap-a-screenshot-capture-tool-for-medley-interlisp"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

a month ago 22 votes
Enjoying the stability of Linux

<![CDATA[I initially used Linux from the mid 1990s to 2015, first as dual boot with Windows 95 and then as my only desktop operating system. Back then my PC had an Nvidia graphics card and system updates frequently broke X11 on Linux, leaving me at the text console with no idea what to do. At some point I stopped applying the updates as I dreaded change. In 2015 I had enough and switched to ChromeOS. Although I still used Crostini Linux on ChromeOS, over the years I increasingly bumped into the limitations of this containerized approach. The rumors of Google considering for ChromeOS a feature like Windows Recall eventually made the cloud operating system a deal breaker. So I decided to migrate back to Linux for good, bought a System76 Merkaat mini PC with no Nvidia hardware, and installed Linux Mint Cinnamon. It's been seven months since my switch back to Linux in July of 2024 and, despite some early issues, my experience with Mint has been smooth and uneventful. Linux supports all my hardware, system updates install seamlessly, and everything works. The system fades into the background and I can focus on running the programs I need. In my early Linux years I often upgraded to every major and minor version of my distro. There were good reasons as Linux evolved rapidly, significant features came out regularly, online updates weren't a thing, and getting online was costly and impractical. It helped that I was younger and eager to play with Linux. In January of 2025 Linux Mint 22.1 was released, the first minor version since my current Mint 22. But this time I'll defer upgrading until at least the next major release, or possibly for a year or two. I feel no pressure as system updates flow regularly over the support period of Mint 22 that ends in 2029. Besides, upgrading involves some preparation and work I don't look forard to doing. If some features I really want do come out I may consider upgrading. But, for now, I want to savor this newfound Linux stability. Linux has really come a long way. Linux a href="https://remark.as/p/journal.paoloamoroso.com/enjoying-the-stability-of-linux"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

a month ago 12 votes
Making uLisp more usable on the Cardputer

<![CDATA[It's a joy to use the Cardputer uLisp Machine, a nice little microcontroller system that runs uLisp. But after a short experience I had to put aside my Cardputer due to a showstopper issue that made it impractical to program the device. Since then a good workaround emerged and I learned how to improve the experience with the device. The showstopper is a buffer overflow when sending Lisp code from Emacs to the Cardputer over a serial USB line. If the receive buffer fills up too fast the device will crash and disconnect. Sending more than a few hundred bytes triggers the issue and makes it impractical to evaluate medium or large code blocks. This acknowledged Arduino issue reported in February of 2022 has not been addressed yet. Meanwhile, Dennis Draheim devised a workaround. He wrote some Emacs Lisp code to open a serial connection to the Cardputer and send an expressions or region for evaluation. The trick is to split the input into lines and send one line at a time, with a delay in between to keep the Cardputer's serial buffer from overflowing. Dennis' code works well and makes uLisp usable on the Cardputer. The only downside is the echoed input clutters the Emacs serial buffer. Our attempts at turning off echo failed as we don't know where Emacs handles this. The workaround enables running more substantial and interesting uLisp programs such as this nice surface of rotation graphics demo: 3D function plot on the display of a Cardputer uLisp Machine device. The Cardputer has a tiny built-in keyboard that is handy for short interactions. But it's prone to overtyping when entering a character that requires pressing two keys, such as shifted characters or the parentheses. I originally attempted to press at the same time the Aa shift key and the key with the desired symbol. But this often results in typing more than one character as hitting such tiny targets simultaneously is difficult. I later stumbled upon a way to consistently avoid overtyping: I press and hold Aa, then press the key with the desired character. #Cardputer #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/making-ulisp-more-usable-on-the-cardputer"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

a month ago 14 votes
Switching Blot sync from Google Drive to Git

<![CDATA[In 2023 I migrated my personal website from Squarespace to Blot. Blot is a static site generator that publishes as a website a folder stored on Google Drive, Dropbox, or a Git repository. Back then I set up Blot to publish a Google Drive folder but I recently switched the synchronization to a Git repository. It was necessary as Blot has been unable to access Google Drive since Google eventually imposed onerous and expensive auditing processes to use their API. This forced Blot and other small developers to drop support for Google Drive. Blot's developer is implementing a workaround that will allow to continue using Google Drive. But I decided to go with Git anyway as it's a better fit for my current tools and workflows. Switching to Git instantaneously and seamlessly created a repository I can clone and update to change the site. #blogging #Google a href="https://remark.as/p/journal.paoloamoroso.com/switching-blot-sync-from-google-drive-to-git"Discuss.../a Email | Reply @amoroso@fosstodon.org !--emailsub--]]>

2 months ago 12 votes

More in programming

Solving a "Layton Puzzle" with Prolog

I have a lot in the works for the this month's Logic for Programmers release. Among other things, I'm completely rewriting the chapter on Logic Programming Languages. I originally showcased the paradigm with puzzle solvers, like eight queens or four-coloring. Lots of other demos do this too! It takes creativity and insight for humans to solve them, so a program doing it feels magical. But I'm trying to write a book about practical techniques and I want everything I talk about to be useful. So in v0.9 I'll be replacing these examples with a couple of new programs that might get people thinking that Prolog could help them in their day-to-day work. On the other hand, for a newsletter, showcasing a puzzle solver is pretty cool. And recently I stumbled into this post by my friend Pablo Meier, where he solves a videogame puzzle with Prolog:1 Summary for the text-only readers: We have a test with 10 true/false questions (denoted a/b) and four student attempts. Given the scores of the first three students, we have to figure out the fourth student's score. bbababbabb = 7 baaababaaa = 5 baaabbbaba = 3 bbaaabbaaa = ??? You can see Pablo's solution here, and try it in SWI-prolog here. Pretty cool! But after way too long studying Prolog just to write this dang book chapter, I wanted to see if I could do it more elegantly than him. Code and puzzle spoilers to follow. (Normally here's where I'd link to a gentler introduction I wrote but I think this is my first time writing about Prolog online? Uh here's a Picat intro instead) The Program You can try this all online at SWISH or just jump to my final version here. :- use_module(library(dif)). % Sound inequality :- use_module(library(clpfd)). % Finite domain constraints First some imports. dif lets us write dif(A, B), which is true if A and B are not equal. clpfd lets us write A #= B + 1 to say "A is 1 more than B".2 We'll say both the student submission and the key will be lists, where each value is a or b. In Prolog, lowercase identifiers are atoms (like symbols in other languages) and identifiers that start with a capital are variables. Prolog finds values for variables that match equations (unification). The pattern matching is real real good. % ?- means query ?- L = [a,B,c], [Y|X] = [1,2|L], B + 1 #= 7. B = 6, L = [a, 6, c], X = [2, a, 6, c], Y = 1 Next, we define score/33 recursively. % The student's test score % score(student answers, answer key, score) score([], [], 0). score([A|As], [A|Ks], N) :- N #= M + 1, score(As, Ks, M). score([A|As], [K|Ks], N) :- dif(A, K), score(As, Ks, N). First key is the student's answers, second is the answer key, third is the final score. The base case is the empty test, which has score 0. Otherwise, we take the head values of each list and compare them. If they're the same, we add one to the score, otherwise we keep the same score. Notice we couldn't write if x then y else z, we instead used pattern matching to effectively express (x && y) || (!x && z). Prolog does have a conditional operator, but it prevents backtracking so what's the point??? A quick break about bidirectionality One of the coolest things about Prolog: all purely logical predicates are bidirectional. We can use score to check if our expected score is correct: ?- score([a, b, b], [b, b, b], 2). true But we can also give it answers and a key and ask it for the score: ?- score([a, b, b], [b, b, b], X). X = 2 Or we could give it a key and a score and ask "what test answers would have this score?" ?- score(X, [b, b, b], 2). X = [b, b, _A], dif(_A,b) X = [b, _A, b], dif(_A,b) X = [_A, b, b], dif(_A,b) The different value is written _A because we never told Prolog that the array can only contain a and b. We'll fix this later. Okay back to the program Now that we have a way of computing scores, we want to find a possible answer key that matches all of our observations, ie gives everybody the correct scores. key(Key) :- % Figure it out score([b, b, a, b, a, b, b, a, b, b], Key, 7), score([b, a, a, a, b, a, b, a, a, a], Key, 5), score([b, a, a, a, b, b, b, a, b, a], Key, 3). So far we haven't explicitly said that the Key length matches the student answer lengths. This is implicitly verified by score (both lists need to be empty at the same time) but it's a good idea to explicitly add length(Key, 10) as a clause of key/1. We should also explicitly say that every element of Key is either a or b.4 Now we could write a second predicate saying Key had the right 'type': keytype([]). keytype([K|Ks]) :- member(K, [a, b]), keytype(Ks). But "generating lists that match a constraint" is a thing that comes up often enough that we don't want to write a separate predicate for each constraint! So after some digging, I found a more elegant solution: maplist. Let L=[l1, l2]. Then maplist(p, L) is equivalent to the clause p(l1), p(l2). It also accepts partial predicates: maplist(p(x), L) is equivalent to p(x, l1), p(x, l2). So we could write5 contains(L, X) :- member(X, L). key(Key) :- length(Key, 10), maplist(contains([a,b]), L), % the score stuff Now, let's query for the Key: ?- key(Key) Key = [a, b, a, b, a, a, b, a, a, b] Key = [b, b, a, b, a, a, a, a, a, b] Key = [b, b, a, b, a, a, b, b, a, b] Key = [b, b, b, b, a, a, b, a, a, b] So there are actually four different keys that all explain our data. Does this mean the puzzle is broken and has multiple different answers? Nope The puzzle wasn't to find out what the answer key was, the point was to find the fourth student's score. And if we query for it, we see all four solutions give him the same score: ?- key(Key), score([b, b, a, a, a, b, b, a, a, a], Key, X). X = 6 X = 6 X = 6 X = 6 Huh! I really like it when puzzles look like they're broken, but every "alternate" solution still gives the same puzzle answer. Total program length: 15 lines of code, compared to the original's 80 lines. Suck it, Pablo. (Incidentally, you can get all of the answer at once by writing findall(X, (key(Key), score($answer-array, Key, X)), L).) I still don't like puzzles for teaching The actual examples I'm using in the book are "analyzing a version control commit graph" and "planning a sequence of infrastructure changes", which are somewhat more likely to occur at work than needing to solve a puzzle. You'll see them in the next release! I found it because he wrote Gamer Games for Lite Gamers as a response to my Gamer Games for Non-Gamers. ↩ These are better versions of the core Prolog expressions \+ (A = B) and A is B + 1, because they can defer unification. ↩ Prolog-descendants have a convention of writing the arity of the function after its name, so score/3 means "score has three parameters". I think they do this because you can overload predicates with multiple different arities. Also Joe Armstrong used Prolog for prototyping, so Erlang and Elixir follow the same convention. ↩ It still gets the right answers without this type restriction, but I had no idea it did until I checked for myself. Probably better not to rely on this! ↩ We could make this even more compact by using a lambda function. First import module yall, then write maplist([X]>>member(X, [a,b]), Key). But (1) it's not a shorter program because you replace the extra definition with an extra module import, and (2) yall is SWI-Prolog specific and not an ISO-standard prolog module. Using contains is more portable. ↩

18 hours ago 3 votes
Market Ending Moves

Startup CEOs should ask themselves what crazy ideas can turn into a move that just ends a market's competitive dynamic

19 hours ago 3 votes
A Data Engineering Perspective of LLMs

Data engineering is a field I would categorize as a subspecialty of software engineering. It shares the same concerns as software engineering—scalability, maintainability, and other “-ilities”—but its primary focus is on data. It’s a unique discipline because data is inherently messy, and as a result, no standard enterprise framework has emerged to dominate the space—and […]

13 hours ago 2 votes
Background Image Opacity in CSS

The other day I was working on something where I needed to use CSS to apply multiple background images to an element, e.g. <div> My content with background images. </div> <style> div { background-image: url(image-one.jpg), url(image-two.jpg); background-position: top right, bottom left; /* etc. */ } </style> As I was tweaking the appearance of these images, I found myself wanting to control the opacity of each one. A voice in my head from circa 2012 chimed in, “Um, remember Jim, there is no background-opacity rule. Can’t be done.” Then that voice started rattling off the alternatives: You’ll have to use opacity but that will apply to the entire element, which you have text in, so that won’t work. You’ll have to create a new empty element, apply the background images there, then use opacity. Or: You can use pseudo elements (:before & :after), apply the background images to those, then use opacity. Then modern me interrupted this old guy. “I haven’t reached for background-opacity in a long time. Surely there’s a way to do this with more modern CSS?” So I started searching and found this StackOverflow answer which says you can use background-color in combination with background-blend-mode to achieve a similar effect, e.g. div { /* Use some images */ background-image: url(image-one.jpg), url(image-two.jpg); /* Turn down their 'opacity' by blending them into the background color */ background-color: rgba(255,255,255,0.6); background-blend-mode: lighten; } Worked like a charm! It probably won’t work in every scenario like a dedicated background-image-opacity might, but for my particular use case at that moment in time it was perfect! I love little moments like this where I reach to do something in CSS that was impossible back when I really cut my teeth on the language, and now there’s a one- or two-line modern solution! [Sits back and gets existential for a moment.] We all face moments like this where we have to balance leveraging hard-won expertise with seeking new knowledge and greater understanding, which requires giving up the lessons of previous experience in order to make room for incorporating new lessons of experiences. It’s hard to give up the old, but it’s the only way to make room for the new — death of the old is birth of the new. Email · Mastodon · Bluesky

18 hours ago 1 votes
1995 Was the Most Important Year for the Web

The world changed a lot in 1995. And for the web, it was a transformational year. The post 1995 Was the Most Important Year for the Web appeared first on The History of the Web.

yesterday 4 votes