button stuck after continuous depression for a few seconds (??)

102 views
Skip to first unread message

xol odho

unread,
Aug 25, 2022, 6:35:25 PMAug 25
to
Hi everyone. I'm learning some Tcl and Tk basics, enjoying what I'm
learning it so far.

I just found out an oddity about the classic Tk buttons: if you focus a
button and hold down spacebar (so that it's depressed continuously) for a
few seconds and then release spacebar, then it seems to remain stuck in
the depressed state.

Try it like this: open `wish`, then `pack [button .b1 -text hello]`, now
on the toplevel with the button hit tab to focus the button, now hold down
spacebar for at least four seconds, and release. Is it stuck? If so, why
does this happen...

This tickles my curiosity. It doesn't seem to happen with ttk::button
(instead it hangs stuck for a while and then reverts back to normal).

I confirm I get this behavior on the following platforms:
Windows XP, Iron Tcl, Tk 8.6.7
Windows 7, BAWT Tcl, Tk 8.6.11
ArchLinux, distro's default Tcl/Tk on X11, Tk 8.6.12

saito...@gmail.com

unread,
Aug 25, 2022, 7:37:04 PMAug 25
to
On 8/25/22 6:35 PM, xol odho wrote:
> Hi everyone. I'm learning some Tcl and Tk basics, enjoying what I'm
> learning it so far.
>
> I just found out an oddity about the classic Tk buttons: if you focus a
> button and hold down spacebar (so that it's depressed continuously) for a
> few seconds and then release spacebar, then it seems to remain stuck in
> the depressed state.
>

It is not stuck. You can configure it with a -command option to print
something to the console to confirm. As to why it appears that way,
since no mouse movement/click was involved, it still has the focus and
it shows this by displaying it in the "depressed" state.



Ralf Fassel

unread,
Aug 26, 2022, 4:57:41 AMAug 26
to
* saito...@gmail.com
| On 8/25/22 6:35 PM, xol odho wrote:
| > Hi everyone. I'm learning some Tcl and Tk basics, enjoying what I'm
| > learning it so far.
| >
| > I just found out an oddity about the classic Tk buttons: if you focus a
| > button and hold down spacebar (so that it's depressed continuously) for a
| > few seconds and then release spacebar, then it seems to remain stuck in
| > the depressed state.

Can confirm this in Opensuse 15.3, Tk 8.6.12.

| It is not stuck. You can configure it with a -command option to print
| something to the console to confirm. As to why it appears that way,
| since no mouse movement/click was involved, it still has the focus and
| it shows this by displaying it in the "depressed" state.

Seems like the Button-release-event which reconfigures the 'raised'
relief gets lost somehow. The button is fully functional, reacts to
mouse and keyboard, just the relief is 'sunken' instead of 'raised'.

R'

Ralf Fassel

unread,
Aug 26, 2022, 5:06:18 AMAug 26
to
* Ralf Fassel <ral...@gmx.de>
| * saito...@gmail.com
| | It is not stuck. You can configure it with a -command option to print
| | something to the console to confirm. As to why it appears that way,
| | since no mouse movement/click was involved, it still has the focus and
| | it shows this by displaying it in the "depressed" state.
>
| Seems like the Button-release-event which reconfigures the 'raised'
| relief gets lost somehow. The button is fully functional, reacts to
| mouse and keyboard, just the relief is 'sunken' instead of 'raised'.

And looking at the code whichs gets called on pressing 'Space', it
becomes clear:

proc ::tk::ButtonInvoke w {
if {[winfo exists $w] && [$w cget -state] ne "disabled"} {
set oldRelief [$w cget -relief]
set oldState [$w cget -state]
$w configure -state active -relief sunken
after 100 [list ::tk::ButtonInvokeEnd $w $oldState $oldRelief]
}
}

proc ::tk::ButtonInvokeEnd {w oldState oldRelief} {
if {[winfo exists $w]} {
$w configure -state $oldState -relief $oldRelief
uplevel #0 [list $w invoke]
}
}

If you keep pressing Space, the next button press arrives while the
button is still in the 'sunken' state (< 100ms), thus it will never get
configured to 'raised' again.

When selling a motocycle, this would be "technically 100%,
optically... not so much" ;-)

R'

xol odho

unread,
Aug 26, 2022, 7:51:01 PMAug 26
to
Nice catch. It's a bad interleaving of events then.

Let me see if I get this right...

If a second ButtonInvoke runs before the first ButtonInvokeEnd restablishes the button back to normal/raised, then the oldState/oldRelief variables get overwritten wrongly with active/sunken values, so then ButtonInvokeEnd does not reset them correctly. Perhaps ButtonInvoke should be stopped from touching anything (or running at all) if the button is still depressed. Then these two procs would run in strict pairs without allowing a second ButtonInvoke messing things in between...

Erik Leunissen

unread,
Sep 3, 2022, 5:03:36 AMSep 3
to
I agree with your analysis of the misbehaviour, and the design foundation for a fix.

Implementation-wise that would be something like:

proc ::tk::ButtonInvoke w {
if {[winfo exists $w] && ([$w cget -state] ni {disabled active})} {
set oldRelief [$w cget -relief]
set oldState [$w cget -state]
$w configure -state active -relief sunken
after 100 [list ::tk::ButtonInvokeEnd $w $oldState $oldRelief]
}
}

I believe a Tk bug ticket including a fix proposal is appropriate.

Erik.
--
elns@ nl | Merge the left part of these two lines into one,
xs4all. | respecting a character's position in a line.
Message has been deleted

xol odho

unread,
Sep 8, 2022, 1:58:55 AMSep 8
to
Yes, something along those lines might do it: you check whether the button
is busy transitioning and stop right there, not more race condition.

But perhaps this could be fixed at a lower (and more general) level,
instead of hardcoding it using specific widget states or tracking state
with external variables. More precisely, the button binding script could
just ignore repeated keypresses, so tk::ButtonInvoke doesn't even get to
run until the next physical keypress. This way you don't need to manually
track state changes.

For comparison, with the mouse, it is impossible to get a "repeated" click
while the mousebutton is depressed, so the bad interleaving is impossible
for mouseclicks on Tk buttons, and so that obviates any need for extra
ad-hoc state tracking.

So, is it possible to (easily, reliably) detect an auto-repeated keypress
in Tk? Perhaps `bind` could provide a percent-substitution with a boolean
value? Or perhaps some trick using <Double-Keypress-*> bindings ? I'm
really envying SDL's SDL_KeyboardEvent and Winapi's WM_KEYDOWN message,
both carry an explicit boolean indication for repeated keypresses.

Also, I notice the mouse-click logic and the spacebar-down logic use
different code paths in button.tcl, perhaps they could share behavior?
i.e., holding down click vs holding down spacebar, does the difference
matter? In win32, a native button gets depressed while click/space is down
(repeats ignored) but the command fires only at last when click/space is
released (both cases produce a WM_COMMAND message). I've been wondering
about this...

Last but not least, the <KeyRelease> event does not work properly on my
X11 desktops with autorepeat on (Tk 8.6.12 on i3wm, xfce, etc), for
instance with this code :

bind . <KeyPress> {puts "%# KeyPress %K"}
bind . <KeyRelease> {puts "%# KeyRelease %K"}

On X11 while I hold down some key, that prints a stream of alternated
KeyPress and KeyRelease lines, but the proper behaviour should be a unique
<KeyRelease> after each <KeyPress> repeated sequence, for each physical
key down/up pair. There was a similar bug reported for MacOSX some years
ago. It works correctly on Win32.

Erik Leunissen

unread,
Sep 11, 2022, 4:01:49 AMSep 11
to
On 08/09/2022 07:58, xol odho wrote:

> But perhaps this could be fixed at a lower (and more general) level,

I think hat your suggestions below are worth pursuing in the context of the misbehaviour that you
reported previously. I'd be sorry if they weren't.

I believe, now even more, that a Tk bug ticket including your suggestions (or a fix proposal) is
appropriate. Personally, I'm not experienced enough to comment in detail on your suggestions, but Tk
maintainers may be).

By the way:

https://core.tcl-lang.org/tk/ticket

Sincerely,
Erik Leunissen
--
Reply all
Reply to author
Forward
0 new messages