On Thu, Sep 07, 2017 at 01:29:08AM -0400 I heard the voice of
Steve Litt, and lo! it spake thus:
>
> I'll give it some more thought after I've recovered from Hurricane
> Irma, which depending on exact course could either give me some
> great kite flying weather or rip down my house.
Hope it wound up with more the latter than the former for you. I've
got family in the path, and they got plenty wet and blown around, but
luckily avoided any major damage.
> I can't actually fix the root cause: I tried, but couldn't
> understand the code enough.
OK, I had some time today, so I went through HandleKeyPress() and gave
it the comment/minor-cleanup treatment some other parts of the
codebase have had, so it should be a little more readable (in current
trunk)[2].
Short version: I think this is not exactly a bug; it's a clash of
expectations vs [poorly documented but consistent] ctwm behavior. You
want the Right arrow key to actually descend into a submenu (or invoke
a leaf entry; rewiring fingers entirely to using up/down/left/right
instead of bkspc/space/esc/return would be an approach.
Now, is there a better way we should handle that clash? That's
another discussion we can have...
Long version:
To summarize the keypresses-in-menu bits here[0], 66% of the cases are
nice and simple.
- Down arrow and Spacebar both move down to the next entry, wrapping
up to the top when you fall off the bottom.
- Up arrow and Backspace both move up, with similar wrapping at the
top.
- Left arrow and Escape both back up a menu level, including falling
out of menus totally when you're already at the top.
- Keys not falling into those cases or the two more involved below
just quietly do nothing.
One of the cases is a little more involved. "Normal"ish keys (e.g.,
letters and numbers) do the thing of finding the first menu entry that
starts with the matching char, with some magic as described in the
manual (about the only bit of keys-in-menus that is) and hopping to
it.
And finally, the case in question: Right arrow or Return. These both
do an invoke-y thing on the menu entry we're currently pointing at.
If you look at the first set of if/else if/etc blocks in the
if(ActiveMenu != NULL) block, this is the only case that doesn't
complete its work there and falls on down to the stuff around
if(item).
If we're trying to "invoke" a f.nop/f.title entry, it just does
nothing. If we're invoking something other than a f.menu entry, it
calls ExecuteFunction() to do whatever the entry is supposed to do.
And so we're winnowed down to the case we're talking about here, where
a Right/Return is pressed in a menu, on a menu item that is itself a
f.menu. And so we wind up in the "case F_MENU" block.
Now, this is where the oddities occur. Right is treated different
from Return in this situation.
At least in the case of f.menu "TwmWorkspaces", that's precisely
intended. That menu has various magic in handling its items in it, in
both keyboard and mouse cases. "Invoking" the entries (e.g., hitting
Return, or releasing the mouse in the left 2/3 of the item) will
essentially f.gotoworkspace you over to that given workspace. Going
into the sub-menu entries (hitting Right, or moving the mouse over to
the right 1/3 of the entry) opens up a submenu level of a list of
windows in that workspace (a per-workspace f.menu "TwmWindows", I
believe).
Here we have to look at bit at the history. In 3.2 (well, 3.2p1), the
condition at the beginning of that "case F_MENU" was
if (!strcmp (keynam, "Return") && (ActiveMenu == Scr->Workspaces))
with no else'ing. So, that distinguished Return from Right
specifically and only on the case of being in the TwmWorkspaces menu.
Otherwise, both went down to do_key_menu() which pops out the submenu
and moves you into it.
3.3 changed it around so that Return was treated differently for all
menus, winding up with the same split that we have as of today. Now,
why is that? I believe that it's a result of
Default menu entry : If a menu entry name begins with a "\*"
(star), this star won't be displayed and the corresponding entry
will be the default entry for this menu. When a menu has a
default entry and is used as a pull-right in another menu, this
default entry action will be executed automatically when this
submenu is selected without being displayed.
(from CHANGES; also related to [0]). The "selected without being
displayed" bit means the "release in left-2/3 vs move to right-1/3"
with the mouse or "Return vs Right" with keyboard. So now hitting
Return tries to invoke the function handler for f.menu to fire off the
default entry, rather than opening the sub-menu.
This is part of the "magic" of f.menu; invoking it at the top-level
from a key or mouse button binding pulls out a given menu via
do_menu() (mouse) or do_key_menu() (key). However, when encountered
during traversing a menu to create a (sub-)menu, it gets called either
- via descending into it (moving mouse right, or pressing Right),
which opens out the sub-menu using those named funcs, or
- via invoking it itself (releasing mouse over the left part of the
entry, or pressing Return), which immediately fires off the
"default" entry[1].
So, in summary, hitting Return there is Expected(tm) to invoke the
menu's default entry, rather than showing it, and since most menus you
make presumably don't have one, it results in you falling out of the
menu and invoking nothing, just like releasing the mouse on the entry
would. You can visibly see the difference by making one of the items
in the menu default with a * at the start of the name, then watching
what happens when you Return on it.
[0] Which are mostly not apparently well documented anywhere in the
manual, at least not that I could find. Someone(tm) should fix
that...
[1] which is currently magic for TwmWorkspaces; this is probably
actually fixable, but is peripheral to the matter in question.
[2] In VCS head now, Or x-ref webwise on LP at
<https://bazaar.launchpad.net/~ctwm/ctwm/trunk/view/600/event_handlers.c#L533>
for the narration of the func.
--
Matthew Fuller (MF4839) | [email protected]
Systems/Network Administrator | http://www.over-yonder.net/~fullermd/
On the Internet, nobody can hear you scream.