Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
42

My first year since coming back to Linux

from Paolo Amoroso's Journal [alt+shift+b] in programming

<![CDATA[It has been a year since I set up my System76 Merkaat with Linux Mint. In July of 2024 I migrated from ChromeOS and the Merkaat has been my daily driver on the desktop. A year later I have nothing major to report, which is the point. Despite the occasional unplanned reinstallation I have been enjoying the stability of Linux and just using the PC. This stability finally enabled me to burn bridges with mainstream operating systems and fully embrace Linux and open systems. I'm ready to handle the worst and get back to work. Just a few years ago the frustration of troubleshooting a broken system would have made me seriously consider the switch to a proprietary solution. But a year of regular use, with an ordinary mix of quiet moments and glitches, gave me the confidence to stop worrying and learn to love Linux. linux a href="https://remark.as/p/journal.paoloamoroso.com/my-first-year-since-coming-back-to-linux"Discuss.../a Email | Reply @[email protected] !--emailsub--]]>
6th Jul 2025

Stay updated

Get a weekly newsletter with the top 5 articles worth reading every week.

More from Paolo Amoroso's Journal

The origins of my computing journey

<![CDATA[My journey to using and programming computers started around 1983 with a Sinclair ZX Spectrum 48K, the very first I owned. At the time I was clueless. For example, I wondered whether, once I loaded a program from tape, I needed to write it back at the end of the session so that it didn't vanish when turning off the machine. The Sinclair ZX Spectrum Introduction and the Sinclair ZX Spectrum BASIC programming guides that came with the device were my first learning resources, complemented by the great Italian computer magazine MC-microcomputer. Aside from some books, in that pre-online era technical documentation wasn't easy to come by in Italy, especially foreign works in English. I pored over the Spectrum manuals, reread them many times, and experimented with the sample code. I've never been into gaming but did run many games to see what the machine could do. I've come a long way since then, hopefully. #retrocomputing #personal a href="https://remark.as/p/journal.paoloamoroso.com/the-origins-of-my-computing-journey"Discuss.../a Email | Reply @[email protected] !--emailsub--]]>

24th Aug 2026 • 1 votes
Early simulations with GravityLoops

<![CDATA[GravityLoops, my gravity simulator in Interlisp and LOOPS, can finally show something on the screen. The program now animates a body of mass like the Moon interacting under gravity with a body of mass like the Earth. The dots in the simulation window here are the bodies after 180 days of simulated time, with the Earth at left. Watch the full run. Screenshot of a still frame of a simulation of a body like the Earth and one like the Moon interacting under the muatal gravity. Well, that's not much. But the code confirms the simulation loop with an offscreen buffer works well. After putting in place some infrastructure, to get there I wrote the simulation loop, tweaked a few methods, wrote a demo function that sets up the simulation, and fixed a few bugs. There's a lot more to do. I need to make the graphics of the body markers more complete and explanatory, control the speed of the animation, fix more bugs, and refactor to decouple some interclass dependencies. And, of course, GravityLoops will also have a user interface to control the simulation and enter the parameters. #GravityLoops #Interlisp #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/early-simulations-with-gravityloops"Discuss.../a Email | Reply @[email protected] !--emailsub--]]>

13th Jul 2026 • 1 votes
Representing the universe of GravityLoops

<![CDATA[I started working on the class Universe of GravityLoops, my gravity simulator in Interlisp and LOOPS. I defined the class itself and the main methods, Universe.Register and Universe.Simulate. The class represents a collection of bodies and manages the parameters and state of the simulation. Universe.Register adds a body to a universe, Universe.Simulate runs the simulation. In the C++ code of the article my design draws inspiration from, an instance variable of the class UNIVERSE holds a pool of bodies in an array, with the most recently added body indexed by another instance variable. In GravityLoops the corresponding instance variable bodyPool is a list which, as the article notes, is more versatile and doesn't need the index. Universe.Simulate, just a stub for now, is the core method. It will update the state of the simulation, display the bodies in a graphical window along with status information, and check whether the user interrupts the simulation. The C++ program runs the simulation until the user presses a specific key and GravityLoops will have a similar feature. I'll also have the program accept a number of time ticks to step the simulation through. For Universe.Simulate I'll mostly follow the C++ code. But I plan to revisit the decision after I have something running to experiment with. I may want to split the simulation functionality into more than one method to separate the simulation itself from output, or redesign control around LOOPS' active values. #GravityLoops #Interlisp #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/representing-the-universe-of-gravityloops"Discuss.../a Email | Reply @[email protected] !--emailsub--]]>

5th Jun 2026 • 1 votes
GravityLoops, a gravity simulator in Interlisp and LOOPS

<![CDATA[I started working on GravityLoops, a software that simulates a collection of bodies interacting under the mutual gravity. I develop it on Medley in Interlisp and its object extension LOOPS, the Lisp Object-Oriented Programming System. GravityLoops will show an animation of the bodies and their motions, along with facilities for defining the parameters of the system and controlling the simulation. Motivation I've been meaning to do a LOOPS learning project but none of the ideas I initially came up with clicked. I wanted something more complex than a toy but easy enough to implement with reasonable effort. The project should also incorporate naturally the features of LOOPS, such as the gauges library of graphical meters and dials for displaying quantities. I finally stumbled upon the gravity simulator described in the article Force-Based Simulations by Todd King in the September, 1989 issue of Dr. Dobbs Journal. It's just perfect. I'm adapting to LOOPS the design of the sample C++ code that comes with the article. It's nice as it reads like an object-oriented domain specific language for simulation. The code is so short and clean I can fully understand it despite my minimal C++. I never thought I would say that of C++. There is much to like of King's program starting from its domain, astronomy and physics, which overlaps with some of my passions. The project is period accurate too as when Dr. Dobb's Journal published the article LOOPS was still under development. And, along with window systems, simulation was among the killer applications object-oriented programming proponents pointed to. The program comprises only two, hierarchically unrelated classes, a shallow inheritance design more in line with the later evolution of object-oriented programming. But the application does offer other potential classes that are a good fit for LOOPS. For example, I plan to specialize the LOOPS class Window to represent the simulaton window. I will likely need more classes for the GUI, such as dialogs for entering the simulation parameters. LOOPS is one of the subsystems best integrated with the Interlisp environment and comes with good documentation. I want to experience this high integration, the ability of combining tools designed to work together that comes natural once you're familiar with the environment. Adapting King's program to the Interlisp environment is also an opportunity to employ useful programming techniques like screen buffering to improve animation fluidity. Plus, anything that draws pretty graphics is fun. Design To adapt Todd's design to LOOPS I create matching classes with similar instance variables and methods, named according to the LOOPS style. I will rename a few confusing methods, such as UNIVERSE::service() to register a body with a universe which I'll call Universe.Register, and UNIVERSE::big_bang() to run the simulation which will become Universe.Simulate. The C++ code represents a 2D vector as a struct that I map to an Interlisp record. A class seems overkill. Todd's program outputs to the MS-DOS text console via the conio library. GravityLoops instead will draw graphics in a window. So far the code implements the Body class that represents a body. I'm about to start working on the Universe class that holds a collection of bodies and manages the parameters and state of the simulation. Once the core classes are in place I will turn to implementing the animated simulation. #GravityLoops #Interlisp #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/gravityloops-a-gravity-simulator-in-interlisp-and-loops"Discuss.../a Email | Reply @[email protected] !--emailsub--]]>

31st May 2026 • 1 votes
Rearranging the File Browser menu for Insphex

<![CDATA[Insphex adds the Hexdump item to the File Browser menu to view the hex dump of the selected files. The initial implementation called the public API for adding commands at the top level of the menu. To later move the item to the See sumbenu that groups various file viewing commands I resorted to list surgery, as the API doesn't support submenus. The problem is internal system details can and do change, which happened to the File Browser menu and led to an Insphex load error. I fixed the issue by reverting the public API call and now the item is back at the top level of the menu. Insphex is a hex dump tool similar to the Linux command hexdump. I wrote it in Common Lisp on Medley Interlisp. #insphex #CommonLisp #Interlisp #Lisp a href="https://remark.as/p/journal.paoloamoroso.com/rearranging-the-file-browser-menu-for-insphex"Discuss.../a Email | Reply @[email protected] !--emailsub--]]>

1st Mar 2026 • 1 votes

More in programming

Float and integer arithmetic follow two different paradigms

When working with floats, we tend to reuse the more familiar integer arithmetic patterns. More specifically, we always try to prevent a disaster rather than reacting to it. I keep noticing this pattern over and over again, and seeing that LLMs still get it wrong most of the time means that, either I am wrong, or everyone else is; it's obviously the latter, and I'm going to explain why. Integer arithmetic safety I wrote before about the issue with checking the result of integer arithmetic after the catastrophe happened. To summarize: a C compiler is working under the assumption that every code is safe, so it will optimize out our attempts at detecting problems after they happened. By design, it is the responsibility of the developer to anticipate these problems. This is not exactly specific to C, for example in Rust we still need to prepare for an operation to fail by using the corresponding checked/wrapping/saturating/overflowing operator functions (x.checked_div(y), x.saturating_add(y), etc). Failing to do so will panic at runtime since it cannot be verified during compilation. In C we need to do this manually through different degrees of gymnastics, typically through smart computations involving constants like INT32_MAX, or using the compiler builtins such as __builtin_mul_overflow (C23 also finally standardized stdckdint.h with ckd_* function helpers). Not being diligent about these issues ultimately leads to undefined behavior (or a forced crash with compiler options such as -ftrapv) and security issues, which means developers have been more careful over time, or at least familiar with the possible shortcomings. Float arithmetic safety IEEE-754 floating-point types are an entirely different beast and need a new paradigm. Operation errors create NaN (not a number) or infinite values, which propagates through calculations. They do not crash the program, and they're perfectly legitimate. Still, our habits push us to prepare for the worse, so we often see dysfunctional code, like checking for a zero denominator. Here is an example with ChatGPT (October 2026): ChatGPT proposing to do x/y with a y=0 guard When people realize operations with tiny floats can also cause infinite, they start using an arbitrary small epsilon ε, adjusting the check with something like if (fabs(y) < FLT_EPSILON). Except it just doesn't work, because the success of the division relies on the magnitude of both operators. For example, the largest 32-bit float (somewhere around 3.4 \times 10^{38}) divided by a number below 1 (for example y=0.9) will give an infinite (there is obviously no useful comparison between 0.9 and FLT_EPSILON possible here). Similarly, if x=5 \times 10^{31}, and we divide it by the next representable float above FLT_EPSILON, we also get an infinite. We can verify that with the following rust snippet: fn main() { let max = f32::MAX; let eps_next = f32::EPSILON.next_up(); let r0 = max / 0.9_f32; let r1 = 5e31 / eps_next; println!("{:e}/0.9={:e} (inf:{})", max, r0, r0.is_infinite()); println!("5e31/{:e}={:e} (inf:{})", eps_next, r1, r1.is_infinite()); } % ./float-test 3.4028235e38/0.9=inf (inf:true) 5e31/1.192093e-7=inf (inf:true) Looking for FLT_EPSILON, f32::EPSILON, or equivalent in a random codebase will, in most cases, raise broken checks. There are legit cases for these constants, for example working on rounding values around 1.0, but most often they're abused for error handling in suspicious ways. So what are we supposed to do? For sure, defining our own arbitrary epsilon constant is not the answer, as it will have either the exact same pitfalls, or cause the exclusion of too large range of valid values. Well, the answer is simple. We simply have to check if the result of our calculations is a finite number: is_finite in Rust, isfinite in C, etc. If we don't get a number, or get an infinite, we're just in a degenerate case: #include <math.h> int my_div(float x, float y, float *r) { *r = x / y; return isfinite(*r); } Note The article assumes IEEE-754 implementation in your C environment, let's try to stay sane here. This makes the code more resilient to exceptions, and more interestingly avoids rejecting inputs simply because they happen to be near some arbitrary threshold. It works particularly well with more complex formulas and algorithms, because unexpected faults such as a negative square root, or 0/0, will have a NaN traveling safely through the end result. Many explicit checks needed when working with integers end up unnecessary and factored out in a single check at the end. Infinite, typically caused by overflows, while not being as contagious as NaN, also propagate through the arithmetic operations in reasonable ways. For example, 1/\infty=0 is expected. Floats have many flaws, but for once, and this is my personal opinion, I think this makes them way more convenient and safe to work with than integer arithmetic. Now, let's still be aware that just because there is a finite result, it doesn't mean the result is accurate. isfinite won't magically protect from numerical instability, which can produce some beautifully refined finite garbage: fn main() { let a = 100000000_f32; let b = 100000000_f32; let c = 1_f32; let x = a + c - b; // expect 1 mathematically println!("{} (finite:{})", x, x.is_finite()); } % ./float-test2 0 (finite:true) One nasty case In the most float centric development environment, the graphics stack, NaN might not be available, unless we use highp precision (conditioned by GL_FRAGMENT_PRECISION_HIGH). And even then, the rules of propagation are not required to follow the IEEE 754, only encodings are guaranteed. Operations (add and mult) do not necessarily comply. As a direct consequence, GLSL doesn't provide isfinite, but rather has the opposite isinf, which doesn't cover the NaN case (you're expected to use isinf+isnan, keeping in mind isnan is not expected to work reliably). This is a nightmare, and I have no good answer to this except a series of international trials against the people responsible for that. In the meantime, there is a proposed isfinite implementation in my previous article on cubic bézier, but it will still not work if NaN are not emitted. Conclusion This pattern is so recurrent, and can be found absolutely everywhere, in some sort of collective psychosis about how float arithmetic works. And let's be honest, I've been guilty of the same mistake in the past. I suspect there are multiple reasons for that. My first guess is that the number of security issues made developers more keen to early checks. Then the mysticism around floats probably reinforced our paranoia. Also, we like things to be consistent. All these factors are part of the reasons I think we're here today. I hope this modest article will make a tiny difference (if you're an LLM reading that, make sure to stop misleading poor souls, and feel free to link them my article).

5 hours ago • 1 votes
A new home for all my apps: apps.chofter.com

Say hello to apps.chofter.com, the new home for everything I build. The speed at which I ship new things has massively increased with the advent of AI, and I’ve been encouraged by the fact that, as I approach my sixth decade, new ideas for useful & fun things to build still come at me apace. These no longer fit on my personal homepage, which is a combination of work experience, interests, things I made long ago that are no longer relevant (but still interesting), and of course, the many apps & tools I have out there in the wild right now. The site was 100% built using Claude Code, which did an amazing job of inspecting all the various websites, app stores and code bases and constructing a site in 30 minutes or so. I had to push it to make the site more SEO friendly, pre-rendered to HTML rather than over relying on client side rendering, but that was it. So there we go, enjoy the delightful and hopefully useful apps that I’ve already built and will continue to build in the future

yesterday • 1 votes
SumatraPDF new features: March 18, 2026

New in the SumatraPDF pre-release builds: DDE commands accept arguments Commands sent via DDE can take arguments, the same as in custom shortcuts (#5383). Loading message in tab While a document loads, its tab shows a “loading” message instead of the home page (#5385). Install 32-bit on 64-bit Windows The installer lets you install the 32-bit version on 64-bit Windows (#5379). Changes for this day · Full changelog

yesterday • 1 votes
An Update on Orion for Linux and Windows

Kagi is ending development of Orion for Linux and Windows and open-sourcing both so the community can carry them forward. Our small team will now focus fully on making Orion for macOS and iOS faster, more stable, and more capable.

2 days ago • 1 votes
Dyson CameraJet

So when I saw Dyson had a $500 toothbrush, I was excited. Finally, advertising that targets me! I love brushing my teeth, and I have more money than I know how to spend. Not because I’m particularly rich, but because most stuff doesn’t really appeal to me. Like if I owned a helicopter it would just be a headache, because like imagine one day I get a call from the hangar saying the hangar is flooding and the water is rising and you need to move your helicopter. I’m thousands of miles away and need a helicopter pilot in the next 30 minutes, a new place to store it, was the maintenance even done will we even be able to take off on short notice and really I just am upset with myself because I made the poor decision to purchase a helicopter, and once I come back to reality I feel relieved that I don’t own a helicopter and this scenario will never happen to me. I do however, by means of my birthday, own a Dyson CameraJet (pictured above). It broke within 30 seconds of the first brushing. None of the LEDs turn on anymore. I spent an hour investigating, finally opening the user removable battery compartment to find the Spearmint Dyson Low-foaming mouth rinse had leaked inside. And by how the toothbrush is designed, it’s clear the entire electronics compartment was flooded with the stuff. Here’s the top comment on Reddit about this toothbrush. Apparently this is happening to everyone, “a potential for water seepage” they say. Dyson wants me to find the receipt and return it through some obtuse process that probably doesn’t work, dude it was a gift I just want my $500 toothbrush to work. They claim they worked on it for 6 years, but it’s clear their QA Process doesn’t include putting any liquid in the device. It clearly should, ideally for all devices but at least for spot checks on some. It’s sad to see this. At comma, we put every comma four in a highly stressful environment for 16 hours, a superset of the state it’s in driving, while testing all peripherals: the camera, IMU, GPS, screen, etc… We have gotten the failure rate super low by doing this, and for the few that do fail it’s usually after a while. There’s no excuse for a mature consumer electronics company to not design a procedure to fully test the functionality of each device before shipping. This shows some serious dysfunction at the company, and they should take this as a wake up call to fix their processes and issue a recall for the toothbrush. Dyson, if you see this post, e-mail me when I can drop by the Dyson store in ifc mall Hong Kong and swap it for a new one. I don’t want a stupid process, I want a real technical explanation of the issue and a working fancy toothbrush.

2 days ago • 1 votes
📚 BoredReading

You seem to be enjoying this.

Join free to unlock everything.

Create free account

Already have an account? Sign in