DrGrizz's blog

Jul 21, 2024

Adding support for open-color in rainbow-mode

Like many fellow software engineers, I'm bad at picking colors, so I was quite excited when I discovered open-color, less thinking to do ! Moreover, I use rainbow-mode once in a while, which allows to set the background of a string representing a color in a buffer to the corresponding color (i.e. "#ff0000" is displayed with a red background).

When I saw open-color had in its TODO list an item to add support for rainbow-mode, I wanted to give it a try.

Implementation

My original plan was to add support directly into rainbow-mode. But then I saw the package was offering a hook rainbow-keywords-hook for adding new keywords, my original plan changed to something less intrusive.

The work can be broken into 3 chunks.

  • The hook function itself, which be called by rainbow-mode: Its job is to add / remove keywords with font-lock-{add-keywords/remove-keywords} when the mode is enabled / disabled.
  • A list of keywords of things to highlight
  • Something to specify how to colorize what was matched

The hook is fairly straightforward. To check if rainbow-mode is enabled, we can simply check with (if rainbow-mode ...), such that it boils down to

(defun add-open-color-hook()
   (if rainbow-mode
         (font-lock-add-keywords nil open-color-rainbow-font-lock-keywords 'end)
     (font-lock-remove-keywords nil open-color-rainbow-font-lock-keywords)
     )
   )

The only gotcha here is to put our keywords at the end, otherwise when coloring something like open-color-red-1, the sub expression red would be colored in red, which is not what we want.

Each element in the list of keywords open-color-rainbow-font-lock-keywords can have one of 6 forms, as described in font-lock-keywords documentation. I took direct inspiration from rainbow-html-rgb-colors-font-lock-keywords to come with the following function

(defvar open-color-rainbow-font-lock-keywords
       '(
         ("\\<\\(open-color-[a-z]*-[0-9]\\)\\>" 1 (rainbow-colorize-open-color) )
         ("\\<\\(open-color-[a-z]*\\)\\>" 1 (rainbow-colorize-open-color) ) ;; handle open-color-black
         )
       "Font-lock keywords to add for open-color colors.")

In broad terms, if something matches the regex, then first capture group is getting colorized by rainbow-colorize-open-color (which we have to define).

The hardest part (in my opinion) is that last function. its goal to retrieve the string that was matched by font-lock and return a face for it.

  • For that, the first option is simply build a map, simple but unsatisfactory.
  • A more satisfactory solution would be from the string "X" to read the value of the variable X, such that for instance "open-color-red-1" background will be defined by the variable open-color-red-1 !

By looking on google, I found this StackOverflow question, where I learn about intern. That function returns the canonical symbol whose name is given, or creating one if any. From there, we can use symbol-value to read the content of the symbol (i.e. the actual value as defined by the open-color package). All that is left is to colorize the string using ainbow-colorize-match.

At the end, the colorization function looks like this

(defun rainbow-colorize-open-color()
  (let* (
         (color-name (match-string-no-properties 1))
         (color-object (intern color-name))
         )
    (if (boundp color-object)
        (let ((color-value (symbol-value color-object)))
          (rainbow-colorize-match color-value)
          )
      ))
  )

For some reasons, the regex doesn't always match the number at the end (such that color-name can be open-color-red), so we have to check if if the object is actually bound. If someone knows why, drop me a mail.

The result

two columns of texts, whose background is highlighted according to their name

The whole demo has been proposed in open-color README, but there has not been any activity on the PR since.

During the development, I found very useful to focus on quick (visual) feedback, and build gradually: - The first version of the hook was simply printing something in the minibuffer - The first version of open-color-rainbow-font-lock-keywords was highlighting the keyword "TEST" with with font-lock-warning-face. - A second iteration changed "TEST" to the regex, using re-builder.

May 19, 2024

Integers footgun

Recently, a colleague of mine proposed the following change in our code base

Change:

    memcpy(network_buffer,
           original_message,
           MIN(original_message_size, OUT_BUFFER_MAX_SIZE));

To

    memcpy(network_buffer,
           &original_message[2],
           MIN(original_message_size - 2, OUT_BUFFER_MAX_SIZE));

The original intend was to skip the first two bytes (if any) because we didn't need them.

To have the full picture,

  • network_buffer is defined as char[OUT_BUFFER_MAX_SIZE], 0-initialized.
  • original_message is char[2*OUT_BUFFER_MAX_SIZE], , 0-initialized as well.
  • By design, original_message is either empty (original_message_size is then 0) or not , then has original_message_size 18 characters.
  • OUT_BUFFER_MAX_SIZE is 16
  • original_message_size is uint8_t

Let's do the review:

  • If cpp original_message is not empty, then original_message_size - 2 is equal to OUT_BUFFER_MAX_SIZE and we we fill network_buffer, skipping its first 2 characters.
  • If original_message is empty, then original_message_size - 2 underflows as original_message_size is unsigned and unsigned arithmetic underflow is defined, such that MIN(original_message_size - 2, OUT_BUFFER_MAX_SIZE) is 16, and we fill 16 0 into network_buffer in an array already filled with 0.

LGTM, right ?

And of course, it's not. Because original_message_size is a uint8_t, integer promotion kicks in. Integer types smaller than int are promoted when an operation is performed on them, either in a int if int is big enough to cover all values from the original type, or unsigned int otherwise. You can see at work on C++ insight

#include <cstdio>
#include <cstdint>

int main()
{
   uint8_t x = 0;
   auto y = x - 2;

   unsigned int u = 0;
   auto v = u - 2;
}

So in our case, original_message_size is promoted to an int, such that is original_message_size - 2 is -2 when original_message is empty. And from there, memory havoc follows, likely resulting in a segfault.

Jan 07, 2024

Dark Mode is not just about aesthetic

I discovered in 2011 that I had won the genetic lottery: I am suffering from Retinis Pigmentosa. Since, my vision overall has been declining (worsening eyesight, narrowing field of view, loss of night vision), and my photophobia increased by a lot. The condition has reached a level where a moderate amount of light is not just uncomfortable, but really painful. And that is true from any kind of light, which includes light from screens as well.

To dim the light coming from the screen, I have to use a dark theme in every application/website I use. The consequence of that is if your application/website does not have a dark theme, I cannot use it. Thus dark theme is not just about aesthetic, but a core accessibility feature.

As a software engineer, I spend a lot of time in Emacs and my terminal, and for them, the problem is handled. Emacs has very good dark themes such as ef-themes or the modus-vivendi theme. For PDF reader, I use inverted colors. With that mode, pictures are obviously very distorted, but overall it does a good enough job.

At the browser level, the situation is more nuanced. Many sites have a white theme and do no offer another theme, but dark reader can generate dark modes for them on the fly. This tool is for me a live-saver but unfortunately, the situation is not ideal:

  • I am relying on plugin to make the web usable
  • Altering the website's style on the fly consumes resources (especially on mobile), and sometimes result in some white flickering when loading
  • Some sites do not work well with dark reader. The usual culprits include
  • Pictures with a full white background
  • Pictures with an alpha channel, but which are not readable with dark reader enabled because the text is dark Wikipedia picture not readable with dark reader. Test is black on a gray background
  • Websites with unusual construct, such that dark reader will only turn dark a frame while the majority of the page will remain white Website with a frome correctly dimmed by dark reader. But the other frame is a map and is mostly white with a lot of brightness

Outside of this core set of applications, using a new desktop app is a gamble:

  • will the application follow my desktop theme ?
  • If not, does it have its home made dark theme ?

On mobile, things are not really better, but nor worse. I have set my phone to always use a dark theme. Most of the applications from Fdroid will pick it, or have their own theme to enable, p But there a few (mandatory) applications (think banking, credit card management) that lack such thing, and are only usable with screen brightness to a minimum.

So please fellow developers, help spread the darkness to make the world more inclusive.