I'm taking a MOOC that includes equations involving Greek letters
like epsilon. I'm taking notes online, in Emacs, using the
iimage
mode tricks for taking MOOC class notes in emacs
that I worked out a few years back.
Iimage mode works fine for taking screenshots of the blackboard in the
videos, but sometimes I'd prefer to just put the equations inline
in my file. At first I was typing out things like
E = epsilon * sigma * T^4
but that's silly, and of course the professor isn't spelling out the
Greek letters like that when he writes the equations on the blackboard.
There's got to be a way to type Greek letters on this US keyboard.
I know how to type things like accented characters using the
"Multi key" or
"Compose key". In /etc/default/keyboard I have
XKBOPTIONS="ctrl:nocaps,compose:menu,terminate:ctrl_alt_bksp"
which, among other things, sets the compose key to be my "Menu" key,
which I never used otherwise.
And there's a file, /usr/share/X11/locale/en_US.UTF-8/Compose,
that includes all the built-in compose key sequences.
I have a shell function in my .zshrc,
composekey() {
grep -i $1 /usr/share/X11/locale/en_US.UTF-8/Compose
}
so I can type something like
composekey epsilon
and find out how to type specific codes. But that didn't work so well
for Greek letters. It turns out this is how you type them:
<dead_greek> <A> : "Α" U0391 # GREEK CAPITAL LETTER ALPHA
<dead_greek> <a> : "α" U03B1 # GREEK SMALL LETTER ALPHA
<dead_greek> <B> : "Β" U0392 # GREEK CAPITAL LETTER BETA
<dead_greek> <b> : "β" U03B2 # GREEK SMALL LETTER BETA
<dead_greek> <D> : "Δ" U0394 # GREEK CAPITAL LETTER DELTA
<dead_greek> <d> : "δ" U03B4 # GREEK SMALL LETTER DELTA
<dead_greek> <E> : "Ε" U0395 # GREEK CAPITAL LETTER EPSILON
<dead_greek> <e> : "ε" U03B5 # GREEK SMALL LETTER EPSILON
... and so forth. And this
<dead_greek>
key isn't
actually defined in most US/English keyboard layouts: you can check
whether it's defined for you with:
xmodmap -pke | grep dead_greek
Of course you can use xmodmap to define a key to be <dead_greek>.
I stared at my keyboard for a bit, and decided that, considering how
seldom I actually need to type Greek characters, I didn't see the point
of losing a key for that purpose (though if you want to, here's a
thread on
how to map <dead_greek> with xmodmap).
I decided it would make much more sense to map it to the compose key
with a prefix, like 'g', that I don't need otherwise. I can do that in
~/.XCompose like this:
<Multi_key> <g> <A> : "Α" U0391 # GREEK CAPITAL LETTER ALPHA
<Multi_key> <g> <a> : "α" U03B1 # GREEK SMALL LETTER ALPHA
<Multi_key> <g> <B> : "Β" U0392 # GREEK CAPITAL LETTER BETA
<Multi_key> <g> <b> : "β" U03B2 # GREEK SMALL LETTER BETA
<Multi_key> <g> <D> : "Δ" U0394 # GREEK CAPITAL LETTER DELTA
<Multi_key> <g> <d> : "δ" U03B4 # GREEK SMALL LETTER DELTA
<Multi_key> <g> <E> : "Ε" U0395 # GREEK CAPITAL LETTER EPSILON
<Multi_key> <g> <e> : "ε" U03B5 # GREEK SMALL LETTER EPSILON
... and so forth.
And now I can type [MENU] g e
and a lovely ε appears,
at least in any app that supports Greek fonts, which is most of them
nowadays.
Tags: linux, X11
[
12:57 Apr 25, 2017
More linux |
permalink to this entry |
]
Last week, my hiking group had its annual trip, which this year
was Bluff, Utah, near Comb Ridge and Cedar Mesa, an area particular
known for its Anasazi ruins and petroglyphs.
(I'm aware that "Anasazi" is considered a politically incorrect term
these days, though it still seems to be in common use in Utah; it isn't
in New Mexico. My view is that I can understand why Pueblo people
dislike hearing their ancestors referred to by a term that means
something like "ancient enemies" in Navajo; but if they want everyone
to switch from using a mellifluous and easy to pronounce word like
"Anasazi", they ought to come up with a better, and shorter,
replacement than "Ancestral Puebloans." I mean, really.)
The photo at right is probably the most photogenic of the ruins I saw.
It's in Mule Canyon, on Cedar Mesa, and it's called "House on Fire"
because of the colors in the rock when the light is right.
The light was not right when we encountered it, in late morning around
10 am; but fortunately, we were doing an out-and-back hike. Someone in
our group had said that the best light came when sunlight reflected
off the red rock below the ruin up onto the rock above it, an effect
I've seen in other places, most notably Bryce Canyon, where the hoodoos
look positively radiant when seen backlit, because that's when
the most reflected light adds to the reds and oranges in the rock.
Sure enough, when we got back to House on Fire at 1:30 pm, the
light was much better. It wasn't completely obvious to the eye,
but comparing the photos afterward, the difference is impressive:
Changing
light on House on Fire Ruin.
The weather was almost perfect for our trip, except for one overly hot
afternoon on Wednesday.
And the hikes were fairly perfect, too -- fantastic ruins you can see
up close, huge petroglyph panels with hundreds of different creatures
and patterns (and some that could only have been science fiction,
like brain-man at left), sweeping views of canyons and slickrock,
and the geology of Comb Ridge and the Monument Upwarp.
And in case you read my last article, on translucent windows, and are
wondering how those generated waypoints worked: they were terrific,
and in some cases made the difference between finding a ruin and
wandering lost on the slickrock. I wish I'd had that years ago.
Most of what I have to say about the trip are already in the comments to
the photos, so I'll just link to the photo page:
Photos: Bluff trip, 2017.
Tags: travel, photography
[
19:28 Apr 20, 2017
More travel |
permalink to this entry |
]
Update 2022-06-24: Although the concepts described in this article
are still valid, the program I wrote depends on GTK2 and is therefore
obsolete. I discuss versions for more modern toolkits here:
Clicking through a Translucent Image Window.
It happened again: someone sent me a JPEG file with an image of a topo
map, with a hiking trail and interesting stopping points drawn on it.
Better than nothing. But what I really want on a hike is GPX waypoints
that I can load into OsmAnd, so I can see whether I'm still on the trail
and how to get to each point from where I am now.
My PyTopo program
lets you view the coordinates of any point, so you can make a waypoint
from that. But for adding lots of waypoints, that's too much work, so
I added an "Add Waypoint" context menu item -- that was easy,
took maybe twenty minutes.
PyTopo already had the ability to save its existing tracks and waypoints
as a GPX file, so no problem there.
But how do you locate the waypoints you want? You can do it the hard
way: show the JPEG in one window, PyTopo in the other, and
do the "let's see the road bends left then right, and the point is
off to the northwest just above the right bend and about two and a half
times as far away as the distance through both road bends". Ugh.
It takes forever and it's terribly inaccurate.
More than once, I've wished for a way to put up a translucent image
overlay that would let me click through it. So I could see the image,
line it up with the map in PyTopo (resizing as needed),
then click exactly where I wanted waypoints.
I needed two features beyond what normal image viewers offer:
translucency, and the ability to pass mouse clicks through to the
window underneath.
A translucent image viewer, in Python
The first part, translucency, turned out to be trivial.
In a class inheriting from my
Python
ImageViewerWindow, I just needed to add this line to the constructor:
self.set_opacity(.5)
Plus one more step.
The window was translucent now, but it didn't look translucent,
because I'm running a simple window manager (Openbox) that
doesn't have a compositor built in. Turns out you can run a compositor on top
of Openbox. There are lots of compositors; the first one I found,
which worked fine, was
xcompmgr -c -t-6 -l-6 -o.1
The -c specifies client-side compositing. -t and -l specify top and left
offsets for window shadows (negative so they go on the bottom right).
-o.1 sets the opacity of window shadows. In the long run, -o0 is
probably best (no shadows at all) since the shadow interferes
a bit with seeing the window under the translucent one. But having a
subtle .1 shadow was useful while I was debugging.
That's all I needed: voilà, translucent windows.
Now on to the (much) harder part.
A click-through window, in C
X11 has something called the SHAPE extension, which I experimented with
once before to make a silly program called
moonroot.
It's also used for the familiar "xeyes" program.
It's used to make windows that aren't square, by passing a shape mask
telling X what shape you want your window to be.
In theory, I knew I could do something like make a mask where every
other pixel was transparent, which would simulate a translucent image,
and I'd at least be able to pass clicks through on half the pixels.
But fortunately, first I asked the estimable Openbox guru Mikael
Magnusson, who tipped me off that the SHAPE extension also allows for
an "input shape" that does exactly what I wanted: lets you catch
events on only part of the window and pass them through on the rest,
regardless of which parts of the window are visible.
Knowing that was great. Making it work was another matter.
Input shapes turn out to be something hardly anyone uses, and
there's very little documentation.
In both C and Python, I struggled with drawing onto a pixmap
and using it to set the input shape. Finally I realized that there's a
call to set the input shape from an X region. It's much easier to build
a region out of rectangles than to draw onto a pixmap.
I got a C demo working first. The essence of it was this:
if (!XShapeQueryExtension(dpy, &shape_event_base, &shape_error_base)) {
printf("No SHAPE extension\n");
return;
}
/* Make a shaped window, a rectangle smaller than the total
* size of the window. The rest will be transparent.
*/
region = CreateRegion(outerBound, outerBound,
XWinSize-outerBound*2, YWinSize-outerBound*2);
XShapeCombineRegion(dpy, win, ShapeBounding, 0, 0, region, ShapeSet);
XDestroyRegion(region);
/* Make a frame region.
* So in the outer frame, we get input, but inside it, it passes through.
*/
region = CreateFrameRegion(innerBound);
XShapeCombineRegion(dpy, win, ShapeInput, 0, 0, region, ShapeSet);
XDestroyRegion(region);
CreateRegion sets up rectangle boundaries, then creates a region
from those boundaries:
Region CreateRegion(int x, int y, int w, int h) {
Region region = XCreateRegion();
XRectangle rectangle;
rectangle.x = x;
rectangle.y = y;
rectangle.width = w;
rectangle.height = h;
XUnionRectWithRegion(&rectangle, region, region);
return region;
}
CreateFrameRegion() is similar but a little longer. Rather than post it
all here, I've created a
GIST:
transregion.c, demonstrating X11 shaped input.
Next problem: once I had shaped input working, I could no longer move
or resize the window, because the window manager passed events through
the window's titlebar and decorations as well as through the rest of
the window.
That's why you'll see that CreateFrameRegion call in the gist:
-- I had a theory that if I omitted the outer part of the window from
the input shape, and handled input normally around the outside, maybe
that would extend to the window manager decorations. But the problem
turned out to be a minor Openbox bug, which Mikael quickly
tracked down (in openbox/frame.c, in the
XShapeCombineRectangles call on line 321,
change ShapeBounding to kind).
Openbox developers are the greatest!
Input Shapes in Python
Okay, now I had a proof of concept: X input shapes definitely can work,
at least in C. How about in Python?
There's a set of python-xlib bindings, and they even supports the SHAPE
extension, but they have no documentation and didn't seem to include
input shapes. I filed a GitHub issue and traded a few notes with
the maintainer of the project.
It turned out the newest version of python-xlib had been completely
rewritten, and supposedly does support input shapes. But the API is
completely different from the C API, and after wasting about half a day
tweaking the demo program trying to reverse engineer it, I gave up.
Fortunately, it turns out there's a much easier way. Python-gtk has
shape support, even including input shapes. And if you use regions
instead of pixmaps, it's this simple:
if self.is_composited():
region = gtk.gdk.region_rectangle(gtk.gdk.Rectangle(0, 0, 1, 1))
self.window.input_shape_combine_region(region, 0, 0)
My transimageviewer.py
came out nice and simple, inheriting from imageviewer.py and adding only
translucency and the input shape.
If you want to define an input shape based on pixmaps instead of regions,
it's a bit harder and you need to use the Cairo drawing API. I never got as
far as working code, but I believe it should go something like this:
# Warning: untested code!
bitmap = gtk.gdk.Pixmap(None, self.width, self.height, 1)
cr = bitmap.cairo_create()
# Draw a white circle in a black rect:
cr.rectangle(0, 0, self.width, self.height)
cr.set_operator(cairo.OPERATOR_CLEAR)
cr.fill();
# draw white filled circle
cr.arc(self.width / 2, self.height / 2, self.width / 4,
0, 2 * math.pi);
cr.set_operator(cairo.OPERATOR_OVER);
cr.fill();
self.window.input_shape_combine_mask(bitmap, 0, 0)
The translucent image viewer worked just as I'd hoped. I was able to
take a JPG of a trailmap, overlay it on top of a PyTopo window, scale
the JPG using the normal Openbox window manager handles, then
right-click on top of trail markers to set waypoints. When I was done,
a "Save as GPX" in PyTopo and I had a file ready to take with me on my
phone.
Tags: programming, X11, python, mapping, GIS, pytopo
[
17:08 Apr 06, 2017
More programming |
permalink to this entry |
]