The latest release of sgug-rse brought us launchers for RSE apps in the standard icon catalog. This was relatively easy to set up by wrapping the `update-desktop-database` scripts to copy stuff into the catalog directory for IRIX. With a placeholder icon set, it looks like this:
The next step is to get some actual icons in. Let's get started!
What are SGI icons?
`*.fti` files are SGI's vector graphics format. IRIX 5.1 (1993) was the debut of the Indigo Magic Desktop -- so if you're wondering why they didn't use SVGs, it's because this was a whole 5 years before the 1998 SVG standard was developed. Here are some docs on the FTI standard and use with the SGI desktop:
Let's crack one of these files open:
You may recognize that directives such as `bgnpolygon` [match IRIS GL functions](https://docs.microsoft.com/en-us/windows/win32/opengl/opengl-functions-and-their-iris-gl-equivalents)!
`color(7)` corresponds to plain white. According to the specs linked above, valid values for the colors range from 0-15 for primary colors and -16 to -255 for extended colors. Below is a screenshot showing IconSmith and FTI editor's palettes:
At first I wrote 0-15 in BIN on some paper and tried to see if there was some clever scheme to use only 8 bits (e.g. `rrrggbbb`) to encode the color, but was unsuccessful. I searched endlessly for a color palette. I looked at IRIS GL headers and did find consts for each one of the 15 primary colors. I saw some really old example code online where people built color tables, but still don't really understand how this works (can someone please explain?). Only after a considerable amount of time playing with the FTI editor I began to notice:
So: if we have the vertices for a given path we can choose to make a line/path or polygon, and specify a stroke or fill, now in colors we understand.
XDG Desktop Enties
`/usr/sgug/share/applications` includes [XDG Desktop Menu](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html) formatted application entries that ship with each package for inclusion into your DM's launcher. RSE ships with a script to parse these and generate small scripts in the catalog directory that exec `Exec`.
For example `ddd.desktop`:
Icons live in `/usr/sgug/share/icons`. We need vector icons. Inconveniently the majority are PNGs, but most projects have an SVG icon (either official or fanfic) that is easily found online. Some even ship with packages we already have:
Make FTI out of SVG?
Most of the heavy lifting is done by the `<path>` line commands. Quoting the docs:
Python has a great package for parsing SVG: svg.path:
Hey! That looks like a Pidgin! Let's fix the scale next: we can find the maximum extents of the SVG (x/y) and then scale it to the 100x100 spec'd FTI grid. We need to fix the orientation of the image too, so let's reflect across the x axis by subtracting 100 from each y coordinate (the maximum allowed extent). Coordinate geometry saves the day!
Unfortunately this only works if the SVG contains one figure (i.e. all the paths are related to one central 'thing'). Compare the Pidgin below with what happens with the vim logo next to it:
Still, this is quite the headstart towards getting a good SGI icon; we can always load up our generated icon in an editor to do last mile tasks. Looking at a few SVGs, color is proving to be a quite grueling task, because it can come from:
Now that we have RGB values for each path, the last challenge is going to be bucketing the color values. We have a palette of ~130 colors, which means that we won't be able to represent every single color that is encountered. Let's just choose the closest one:
Putting all of this together, we get a result:
Using the generated FTIs
XDG desktop files that want to theme icons of files matching a certain type usually include a MIME type directive:
SGI's analog is tag:
On flat shell scripts, it does this:
It also works on binary files (this tag is the same, `0x7398CD9` is its hex representation). Wasn't able to find it annotated in `readelf` output. Oh well, it does do something:
Not really being sure which tag values are appropriate for which files, I thought it was best to tag everything with `0x0`. To make sure the icon appears, we need to make an `*.tr` entry in `/usr/lib/filetype`.
Two cool things about this:
Afterwards, we have to run `make` after our changes:
Log in and out, and you should see your shiny new icons! Not too shabby!
You can find the repo with the `svg2fti` tool here: https://github.com/mach-kernel/svg2fti
Let's make some icons!
The next step is to get some actual icons in. Let's get started!
What are SGI icons?
`*.fti` files are SGI's vector graphics format. IRIX 5.1 (1993) was the debut of the Indigo Magic Desktop -- so if you're wondering why they didn't use SVGs, it's because this was a whole 5 years before the 1998 SVG standard was developed. Here are some docs on the FTI standard and use with the SGI desktop:
Let's crack one of these files open:
Code:
#Path 0
color(7);
bgnpolygon();
vertex(8.051858,44.963826);
vertex(8.140266,46.948369);
vertex(9.011796,49.985860);
vertex(10.550670,53.866927);
# ... more vertices
endoutlinepolygon(12);
You may recognize that directives such as `bgnpolygon` [match IRIS GL functions](https://docs.microsoft.com/en-us/windows/win32/opengl/opengl-functions-and-their-iris-gl-equivalents)!
Code:
$ grep -rni 'bgnpolygon' /usr/include/
/usr/include/gl/dlproto.h:313:extern void gl_i_bgnpolygon( void );
/usr/include/gl/dlproto.h:314:extern void gl_c_bgnpolygon( void );
/usr/include/gl/gl.h:1017:extern void bgnpolygon( void );
At first I wrote 0-15 in BIN on some paper and tried to see if there was some clever scheme to use only 8 bits (e.g. `rrrggbbb`) to encode the color, but was unsuccessful. I searched endlessly for a color palette. I looked at IRIS GL headers and did find consts for each one of the 15 primary colors. I saw some really old example code online where people built color tables, but still don't really understand how this works (can someone please explain?). Only after a considerable amount of time playing with the FTI editor I began to notice:
- The leftmost vertical row of extended colors causes the index to jump by `16`
- Moving right within a row is `(-y * 16) + x`
- Each column sort of looks like the color from the row of originals
- The white column specifically like watered down versions of the colors before the initial white tile
Python:
# Color
PRIMARY_COLORS = [
(0, 0, 0),
(255, 0, 0),
(0, 255, 0),
(255, 255, 0),
(0, 0, 255),
(255, 0, 255),
(0, 255, 255),
(255, 255, 255),
(85, 85, 85),
(198, 113, 113),
(113, 198, 113),
(142, 142, 56),
(113, 113, 198),
(142, 56, 142),
(56, 142, 142),
(170, 170, 170)
]
color_map = {}
for i, (r, g, b) in enumerate(PRIMARY_COLORS):
color_map[i] = (r, g, b)
base = -16 * i
# zip keeps going until one collection runs out
for j, (rm, gm, bm) in zip(range(0, i), PRIMARY_COLORS):
mixed_index = base - j
color_map[mixed_index] = (
(r + rm) / 2,
(g + gm) / 2,
(b + bm) / 2
)
XDG Desktop Enties
`/usr/sgug/share/applications` includes [XDG Desktop Menu](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html) formatted application entries that ship with each package for inclusion into your DM's launcher. RSE ships with a script to parse these and generate small scripts in the catalog directory that exec `Exec`.
For example `ddd.desktop`:
Code:
[Desktop Entry]
Version=1.0
Name=Data Display Debugger
Comment=Graphical debugger frontend
Comment[fr]=Interface graphique pour débogueur
Exec=ddd
Terminal=false
Type=Application
Icon=ddd
Categories=Development;
Code:
$ find /usr/sgug/share/icons -iname 'ddd*' -type f
/usr/sgug/share/icons/hicolor/48x48/apps/ddd.png
$ tree /usr/sgug/share/icons/hicolor/scalable/apps/
hicolor/scalable/apps/
|-- barrier.svg
|-- emacs.svg
|-- filled-xterm.svg
|-- mini.xterm.svg
|-- pidgin.svg
|-- xterm-color.svg
`-- xterm.svg
Most of the heavy lifting is done by the `<path>` line commands. Quoting the docs:
FTI has support for arcs, but trying to map a path from SVG would require an operator who understands this link better. I think we can fake it though: if you keep adding sides to a polygon it will slowly start to resemble a circle. Get 100 or so points from each SVG path and your curves will probably look fine (especially icon sized).SVG defines 6 types of path commands, for a total of 20 commands:
MoveTo: M, m
LineTo: L, l, H, h, V, v
Cubic Bézier Curve: C, c, S, s
Quadratic Bézier Curve: Q, q, T, t
Elliptical Arc Curve: A, a
ClosePath: Z, z
Python has a great package for parsing SVG: svg.path:
Using Python's XML parser and our shiny new tool, I was able to produce this FTI icon from this Pidgin svg! The first one shows 10 samples, then 50, 100, and 1000 (the last one is especially fun because it's all the overlapping yellow vertex circles from the editor!):All of these objects have a .point() function which will return the coordinates of a point on the path, where the point is given as a floating point value where 0.0 is the start of the path and 1.0 is end.
Hey! That looks like a Pidgin! Let's fix the scale next: we can find the maximum extents of the SVG (x/y) and then scale it to the 100x100 spec'd FTI grid. We need to fix the orientation of the image too, so let's reflect across the x axis by subtracting 100 from each y coordinate (the maximum allowed extent). Coordinate geometry saves the day!
Python:
def fix_scale(self):
max_real, max_imag, max_final = 0, 0, 0
for fti_path in self.fti_paths:
for point in fti_path.points:
max_real = max(max_real, point.real)
max_imag = max(max_imag, point.imag)
max_final = max(max_imag, max_real)
scale = float(100) / max_final
# ...apply scale to points
Still, this is quite the headstart towards getting a good SGI icon; we can always load up our generated icon in an editor to do last mile tasks. Looking at a few SVGs, color is proving to be a quite grueling task, because it can come from:
- `stroke` or `fill` DOM attributes
- CSS classes or inlined into `style` tags
- `linearGradient` DOM objects referenced by `url(#foo)` where `#foo` is the gradient ID
- CSS colors can be represented as
- `#rrggbb` or shorter variants like `#rrgg`
- `rgb(x,y,z)`
- rgba and others
- What makes something a polygon versus a path?
- From attributes (in order)
- `stroke` or `fill`
- Follow DOM IDs to gradient objects, and walk all attribute values until we reify a valid color3 object
- From CSS using regexes (as terrible as it sounds)
Now that we have RGB values for each path, the last challenge is going to be bucketing the color values. We have a palette of ~130 colors, which means that we won't be able to represent every single color that is encountered. Let's just choose the closest one:
Python:
def rgb2index(self, fr, fg, fb):
avg, color = 500, 0
for fti_index, (r, g, b) in self.color_map.items():
cur = abs(r - fr) + abs(g - fg) + abs(b - fb)
if cur < avg:
avg = cur
color = int(fti_index)
return color
Putting all of this together, we get a result:
Using the generated FTIs
XDG desktop files that want to theme icons of files matching a certain type usually include a MIME type directive:
Code:
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
`tag my_stuff.sh`tag is used to set, clear or query the tag number in a MIPS executable or
shell script that follows the convention of #!/bin/sh or #!/bin/csh on
the first line. The tag number is used by the IRIX Interactive Desktop
to determine the type of a file and thus display the appropriate icon and
have it exhibit the correct behavior when the user interacts with it.
On flat shell scripts, it does this:
Code:
#!/usr/sgug/bin/sh
#Tag 0x121212121
Code:
< 0000520 0000 0001 0739 8cd9 0000 0000 0000 0000
< \0 \0 \0 001 \a 9 214 331 \0 \0 \0 \0 \0 \0 \0 \0
---
> 0000520 0000 0000 0000 0000 0000 0000 0000 0000
> \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
Two cool things about this:
- The match syntax is pretty expressive
- `if (opened)` block shows how overlays are added to your icon to show them as if they were appearing on a little platform. So -- you can compose the icons!
Code:
TYPE Pidgin
MATCH glob("/usr/lib/desktop/iconcatalog/pages/C/RSE/pidgin") && tag == 0x00000000;
LEGEND :216:Unix command
SUPERTYPE Executable
CMD OPEN $LEADER $REST
CMD ALTOPEN launch -c "$LEADER $REST"
CMD DROP $TARGET $SELECTED
ICON {
if (opened) {
include("../iconlib/generic.exec.open.fti");
} else {
include("../iconlib/generic.exec.closed.fti");
}
include("/usr/lib/filetype/install/iconlib/pidgin.rse.fti");
}
Code:
$ cd /usr/lib/filetype
$ make -u
Creating /usr/lib/mime.types and /usr/lib/mailcap ...
Done building the .otr files.
You can find the repo with the `svg2fti` tool here: https://github.com/mach-kernel/svg2fti
Let's make some icons!
Last edited: