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!
*.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:
#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!
$ 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 );
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:
16
(-y * 16) + x
After what was days of struggling I finally got it: each color from the 15 initial ones is mixed with every other by doing avg(r, r'), avg(g, g'), avg(b, b')
. The x
offset previously mentioned corresponds to the iteration offset in the primary color table. The colormap can then be generated with this Python code:
# 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
)
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.
/usr/sgug/share/applications
includes XDG Desktop Menu 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
:
[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;
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:
$ 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:
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
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).
Python has a great package for parsing SVG: svg.path:
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.
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!):
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!
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
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:
stroke
or fill
DOM attributesstyle
tagslinearGradient
DOM objects referenced by url(#foo)
where #foo
is the gradient ID#rrggbb
or shorter variants like #rrgg
rgb(x,y,z)
The polygon question is solved by naively choosing to use bgnpolygon()
for everything that has a fill present. Using tinycss2’s color parser, I managed to abstract color resolution to two coarse categories:
stroke
or fill
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:
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:
XDG desktop files that want to theme icons of files matching a certain type usually include a MIME type directive:
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++;
SGI’s analog is tag:
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.
tag my_stuff.sh
On flat shell scripts, it does this:
#!/usr/sgug/bin/sh
#Tag 0x121212121
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:
< 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
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:
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!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");
}
Afterwards, we have to run make
after our changes:
$ cd /usr/lib/filetype
$ make -u
Creating /usr/lib/mime.types and /usr/lib/mailcap ...
Done building the .otr files.
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. Let’s make some icons!