Losing keyboard input under user 'spamming' input

40 views
Skip to first unread message

rs

unread,
Sep 23, 2021, 10:20:24ā€ÆAM9/23/21
to fltk.general

Hi,

I have a modal window that I want to be toggled shown/hidden using some keyboard shortcut (lets say alt+S).

The way I am doing this is setting the shortcut to show the window in a menu item and then, since the shown window grabs focus away from the main window, using aĀ  subclassedĀ  FLTK window for the toggled window such that its handle causes it to hide itself when the user uses the same shortcut (i.e. Alt+s).

Under sensible user input and on certain OS's this all works fine. However on Linux, and unlike on Mac (I haven't checked Windows yet), holding buttons down seems to generate multiple key FL_KEYBOARD events (as opposed to just a single one when it is depressed/released).

As such the user can simply hold Alt + S, effectively spamming the shortcut. After, say, a second of this something breaks and the whole FLTK program simply doesn't appear to receive keyboard events anymore (other events like mouse presses are fine). I thought it might have been that focus got "lost" somewhere and so tried refocusing on the main window, but this didn't work.

I have other instances where holding down a button can be dealt with just fine (where it is just updating some data, not hiding/showing/setting FLTK objects), so it is something to do with the hiding and showing of the window - maybe some increasing queue of window hide/show calls are overflowing some buffer due to them accumulating faster than the OS can actually hide and show them, I don't know.

Anyway here's a (bodged) minimal example that replicates the issue on Linux (FLTK v.1.4.x)

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <iostream>


int shown_v;


class my_win : public Fl_Double_Window{
Ā Ā  Ā 
Ā Ā Ā  int handle(int);
public:
Ā Ā Ā  my_win(int x, int y,int w,int h,const char* e):Fl_Double_Window(x,y,w,h,e){};
};

int my_win::handle (int e){
Ā Ā Ā  int ret = Fl_Double_Window::handle(e);
Ā Ā Ā  if (e==FL_KEYBOARD){
Ā Ā Ā Ā Ā Ā Ā  if (Fl::event_state()&(FL_ALT))Ā  {
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  if (Fl::event_key()=='s'){
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  if (shown_v){
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  this->hide();
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  shown_v=0;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  else{
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  this->show();
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  shown_v=1;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  return 1;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā  }
Ā Ā Ā  return ret;
}

void toggle_win(Fl_Widget*,void* data){
Ā Ā Ā  my_win* w = static_cast<my_win*>(data);

Ā Ā Ā  if (shown_v){
Ā Ā Ā Ā Ā Ā Ā  w->hide();
Ā Ā Ā Ā Ā Ā Ā  shown_v=0;
Ā Ā Ā  }
Ā Ā Ā  else{
Ā Ā Ā Ā Ā Ā Ā  w->show();
Ā Ā Ā Ā Ā Ā Ā  shown_v=1;
Ā Ā Ā  }
}

int main() {

Ā Ā Ā  shown_v=0;

Ā Ā Ā  my_win *win2 = new my_win(0.5*Fl::w()-100,0.5*Fl::h()-100,200,200,"Second win");
Ā Ā Ā  win2->end();
Ā Ā  Ā 
Ā Ā  Ā Fl_Double_Window *win = new Fl_Double_Window(0.5*Fl::w()-200,0.5*Fl::h()-200,400,400,"Main Win");
Ā Ā Ā  Ā 

Ā Ā  Ā Fl_Menu_Item items[] = {
Ā Ā Ā Ā Ā Ā Ā  {"Menu1",0,0,0,FL_SUBMENU,0,0,FL_NORMAL_SIZE,FL_BLACK}, //0
Ā Ā Ā Ā Ā Ā Ā  {"Hide/show second window",FL_ALT+'s',toggle_win,win2,0,0,0,FL_NORMAL_SIZE,FL_BLACK},
Ā Ā Ā Ā Ā Ā Ā  {0,0,0,0,0,0,0,0,0},
Ā Ā Ā Ā Ā Ā Ā  {0,0,0,0,0,0,0,0,0}
Ā Ā Ā  };

Ā Ā Ā  Fl_Menu_Bar *menu = new Fl_Menu_Bar(0, 0, win->w(),25);
Ā Ā Ā  menu->menu(items);
Ā Ā  Ā win->end();
Ā Ā Ā  win->show();
Ā Ā  Ā 
Ā Ā  Ā 
Ā Ā Ā  return Fl::run();

}


Thanks,

R.

Ian MacArthur

unread,
Sep 23, 2021, 10:50:28ā€ÆAM9/23/21
to fltk.general
On Thursday, 23 September 2021 at 15:20:24 UTC+1 rs wrote:

Under sensible user input and on certain OS's this all works fine. However on Linux, and unlike on Mac (I haven't checked Windows yet), holding buttons down seems to generate multiple key FL_KEYBOARD events (as opposed to just a single one when it is depressed/released).

FWIW, tried your example on Win10, seems fine - in that win2 was repeatedly shown then hidden, and continued to flicker on and off continuously until I got bored...
Not sure if that's what macOS does for you, but it didn't crash, in any case.

I suspect X11 versions might need a client->server round trip for the window events, so may well be substantially slower than macOS or Win32.


As such the user can simply hold Alt + S, effectively spamming the shortcut. After, say, a second of this something breaks and the whole FLTK program simply doesn't appear to receive keyboard events anymore (other events like mouse presses are fine). I thought it might have been that focus got "lost" somewhere and so tried refocusing on the main window, but this didn't work.

I have other instances where holding down a button can be dealt with just fine (where it is just updating some data, not hiding/showing/setting FLTK objects), so it is something to do with the hiding and showing of the window - maybe some increasing queue of window hide/show calls are overflowing some buffer due to them accumulating faster than the OS can actually hide and show them, I don't know.

No good idea now - there are a few places in fltk where we queue events, but the queues are (IIRC) mostly pretty deep (thousands) and in any case I think they are protected against overflow... But you never know.
I suppose it might be event queues in X11 too, though I'd expect that to be able to handle key-repeat events without failing, so that seems unlikely too.

Ian MacArthur

unread,
Sep 23, 2021, 11:11:22ā€ÆAM9/23/21
to fltk.general
If it is a window show/hide issue, then breaking the link between the keyboard events and the window events might help?

If so, this might be better?

#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <iostream>

static int shown_v;

class my_win : public Fl_Double_Window {

Ā  Ā  int handle(int);

Ā  Ā  int please_change;
public:
Ā  Ā  my_win(int x, int y,int w,int h,const char* L):Fl_Double_Window(x,y,w,h,L), please_change(0) {};
}; // my_win

int my_win::handle (int e){
Ā  Ā  int ret = Fl_Double_Window::handle(e);
Ā  Ā  if (e == FL_KEYBOARD) {
Ā  Ā  Ā  Ā  if (Fl::event_state() & (FL_ALT)) {
Ā  Ā  Ā  Ā  Ā  Ā  if (Fl::event_key()=='s') {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  please_change = (-1);
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  return 1;
Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  }
Ā  Ā  }
Ā  Ā  else if (e == FL_KEYUP) {
Ā  Ā  Ā  Ā  if (please_change) {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  if (shown_v) {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  this->hide();
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  shown_v = 0;
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  else {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  this->show();
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  shown_v = 1;
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  Ā  Ā  please_change = 0;
Ā  Ā  Ā  Ā  Ā  Ā  return 1;
Ā  Ā  Ā  Ā  }
Ā  Ā  }
Ā  Ā  return ret;
} // my_win::handle

void toggle_win (Fl_Widget*,void* data) {
Ā  Ā  my_win* w = static_cast<my_win*>(data);

Ā  Ā  if (shown_v) {
Ā  Ā  Ā  Ā  w->hide();
Ā  Ā  Ā  Ā  shown_v = 0;
Ā  Ā  }
Ā  Ā  else {
Ā  Ā  Ā  Ā  w->show();
Ā  Ā  Ā  Ā  shown_v = 1;
Ā  Ā  }
} // toggle_win

int main() {
Ā  Ā  shown_v = 0;

Ā  Ā  my_win *win2 = new my_win(0.5*Fl::w()-100,0.5*Fl::h()-100,200,200,"Second win");
Ā  Ā  win2->end();

Ā  Ā  Fl_Double_Window *win = new Fl_Double_Window(0.5*Fl::w()-200,0.5*Fl::h()-200,400,400,"Main Win");
Ā  Ā  win->begin();

Ā  Ā  Fl_Menu_Item items[] = {
Ā  Ā  Ā  Ā  {"Menu1",0,0,0,FL_SUBMENU,0,0,FL_NORMAL_SIZE,FL_BLACK}, //0
Ā  Ā  Ā  Ā  {"Hide/show second window",FL_ALT+'s',toggle_win,win2,0,0,0,FL_NORMAL_SIZE,FL_BLACK},
Ā  Ā  Ā  Ā  {0,0,0,0,0,0,0,0,0},
Ā  Ā  Ā  Ā  {0,0,0,0,0,0,0,0,0}
Ā  Ā  };

Ā  Ā  Fl_Menu_Bar *menu = new Fl_Menu_Bar(0, 0, win->w(),25);
Ā  Ā  menu->menu(items);
Ā  Ā  win->end();
Ā  Ā  win->show();

Ā  Ā  return Fl::run();
} // main
// end of file

rs

unread,
Sep 23, 2021, 12:02:17ā€ÆPM9/23/21
to fltk.general
It's a nice idea, but I tested it and, whilst it sometime "survives" longer (sometimes it doesn't - indeed sometimes only managing one full "loop" see below) whilst flicking the window on/off, it does eventually lead to the same problem. Basically, its not *just* FL_KEYBOARD events but also FL_KEYUP events that are continuously being generated, so this doesn't sidestep the issue. In an attempt a debugging with std::couts in a few places I get the following:

The code:
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <iostream>

static int shown_v;

class my_win : public Fl_Double_Window {

Ā Ā Ā  int handle(int);

Ā Ā Ā  int please_change;
public:
Ā Ā Ā  my_win(int x, int y,int w,int h,const char* L):Fl_Double_Window(x,y,w,h,L), please_change(0) {};
}; // my_win

int my_win::handle (int e){
Ā Ā Ā  int ret = Fl_Double_Window::handle(e);

Ā Ā Ā  if (e == FL_KEYBOARD) {
Ā Ā Ā Ā Ā Ā Ā  std::cout<<"FL_KEYBOARD"<<std::endl;

Ā Ā Ā Ā Ā Ā Ā  if (Fl::event_state() & (FL_ALT)) {
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  if (Fl::event_key()=='s') {
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  std::cout<<"shortcut"<<std::endl;

Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  please_change = (-1);
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  return 1;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā  }
Ā Ā Ā  else if (e == FL_KEYUP) {
Ā Ā Ā Ā Ā Ā Ā  std::cout<<"FL_KEYUP"<<std::endl;
Ā Ā Ā Ā Ā Ā Ā  if (please_change) {
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  std::cout<<"please change"<<std::endl;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  if (shown_v) {
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  std::cout<<"hide"<<std::endl;

Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  this->hide();
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  shown_v = 0;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  else {
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  std::cout<<"show"<<std::endl;

Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  this->show();
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  shown_v = 1;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  please_change = 0;
Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  return 1;
Ā Ā Ā Ā Ā Ā Ā  }
Ā Ā Ā  }
Ā Ā Ā  return ret;
} // my_win::handle

void toggle_win (Fl_Widget*,void* data) {
Ā Ā Ā  my_win* w = static_cast<my_win*>(data);

Ā Ā Ā  if (shown_v) {
Ā Ā Ā Ā Ā Ā Ā  std::cout<<"hide from main"<<std::endl;

Ā Ā Ā Ā Ā Ā Ā  w->hide();
Ā Ā Ā Ā Ā Ā Ā  shown_v = 0;
Ā Ā Ā  }
Ā Ā Ā  else {
Ā Ā Ā Ā Ā Ā Ā  std::cout<<"show from main"<<std::endl;

Ā Ā Ā Ā Ā Ā Ā  w->show();
Ā Ā Ā Ā Ā Ā Ā  shown_v = 1;
Ā Ā Ā  }
} // toggle_win

int main() {
Ā Ā Ā  shown_v = 0;

Ā Ā Ā  my_win *win2 = new my_win(0.5*Fl::w()-100,0.5*Fl::h()-100,200,200,"Second win");
Ā Ā Ā  win2->end();

Ā Ā Ā  Fl_Double_Window *win = new Fl_Double_Window(0.5*Fl::w()-200,0.5*Fl::h()-200,400,400,"Main Win");
Ā Ā Ā  win->begin();

Ā Ā Ā  Fl_Menu_Item items[] = {
Ā Ā Ā Ā Ā Ā Ā  {"Menu1",0,0,0,FL_SUBMENU,0,0,FL_NORMAL_SIZE,FL_BLACK}, //0
Ā Ā Ā Ā Ā Ā Ā  {"Hide/show second window",FL_ALT+'s',toggle_win,win2,0,0,0,FL_NORMAL_SIZE,FL_BLACK},
Ā Ā Ā Ā Ā Ā Ā  {0,0,0,0,0,0,0,0,0},
Ā Ā Ā Ā Ā Ā Ā  {0,0,0,0,0,0,0,0,0}
Ā Ā Ā  };

Ā Ā Ā  Fl_Menu_Bar *menu = new Fl_Menu_Bar(0, 0, win->w(),25);
Ā Ā Ā  menu->menu(items);
Ā Ā Ā  win->end();
Ā Ā Ā  win->show();

Ā Ā Ā  return Fl::run();
} // main
// end of file




The output from the end of one of the "long" flickering sequences:
...
FL_KEYBOARD
shortcut
FL_KEYUP
please change
hide
show from main
FL_KEYUP
FL_KEYBOARD
shortcut
FL_KEYUP
please change
hide
show from main
FL_KEYUP
FL_KEYBOARD
shortcut
FL_KEYUP
please change
hide
show from main
<----No more output here when keyboard events are no longer delivered to program.

A particulary short output (all of it) can look like

show from main
FL_KEYUP
FL_KEYBOARD
shortcut
FL_KEYUP
please change
hide
show from main
hide from main <---- The fact that this happens might be due to the cout calls, but could vaguely indicate that the window manager simply can't keep up
show from main
<----No more output here when keyboard events are no longer delivered to program.

with the final "show from main" being the last std::cout to be called before it falls over in all cases. The state of the window when this happens is *shown* so I actually think it is the "hide" method that is actually failing. I wonder if the during the failure it fails to hand over the keyboard shortcuts back to the main window, leaving the main window without keyboard input.Ā  (I don't know how the internals actually work here)

Unless there are core FLTK things that can be learnt from this, I wouldn't worry too much about this. I.e. working out why it is doing this might be valuable, but generating workarounds for my use case may not be worth your time - I'll probably just disable the ability to toggle the modal window off/hidden on Linux builds.

My main thought, actually, is that this is rather a symptom of misbehaving event signals - repeated keyup and keydown is not what is actually happening, but those events are being generated.

Thanks,

R.

Ian MacArthur

unread,
Sep 23, 2021, 12:20:27ā€ÆPM9/23/21
to fltk.general
On Thursday, 23 September 2021 at 17:02:17 UTC+1 rs wrote:
It's a nice idea, but I tested it and, whilst it sometime "survives" longer (sometimes it doesn't - indeed sometimes only managing one full "loop" see below) whilst flicking the window on/off, it does eventually lead to the same problem. Basically, its not *just* FL_KEYBOARD events but also FL_KEYUP events that are continuously being generated, so this doesn't sidestep the issue. In an attempt a debugging with std::couts in a few places I get the following:

Ah... hmm, OK, that's a bit more of a nuisance...

FWIW, under WindowsĀ  (Win10, I only have my laptop today...) "my" version does not flicker at all, the win2 toggles on/off once per keyboard press, regardless how long I keep the "Alt+s" combo held for, suggesting (to me, at least) that the Win32 system is only delivering the KEY_UP when I actually let go the keys.

It's been a long time since I looked at raw X11 keyboard handling, so I can't remember what I'd even expect it to do, but it does look very much as if it is generating down/up pairs all the time when the key-combo is held down (where I think Win32 is generating streams of key_down events, but apparently fewer, or only one, key_up.)

(Actually, reading that back, that rang a very faint bell... there is something about handling repeat keys under X11, might be that fltk is generating synthetic events for repeated keys... but I can't remember any of the details...)

If it is a "feature" of the X11 client/server mechanism, it may be hard to dodge. Might be able to start a fl_timeout on key_up events and then only toggle the show/hide state if the key_up is not followed by a key_down within the timeout period... or something. (I'm just making this up as I go along now, can you tell?)

rs

unread,
Sep 23, 2021, 1:14:35ā€ÆPM9/23/21
to fltk.general
>If it is a "feature" of the X11 client/server mechanism, it may be hard to dodge.

Yeah more and more this is what it looks like.

Might be able to start a fl_timeout on key_up events and then only toggle the show/hide state if the key_up is not followed by a key_down within the timeout period... or something. (I'm just making this up as I go along now, can you tell?)

Heh, yeah, perhaps. I think I'll go with my "disable on linux" approach...

However, the plot thickens... I thought about just slowing it all down so put some

std::this_thread::sleep_for (std::chrono::milliseconds(1000));

afterĀ  (and also before, with no difference) all show() and hide() calls as just a bodge, and it actually made things *worse* - it survived at most *one* single alt+s event (i.e. failing to perform the second) -Ā  not event holding it down/spamming. Which is really weird (unless those timing calls are really messing up FLTK internals). That makes it seem like some kind of timing mismatch between showing the window and the handle/callback wrapping up... but I don't know enough about the internals.

I may give up here to be honest!

Bill Spitzak

unread,
Sep 23, 2021, 1:20:35ā€ÆPM9/23/21
to fltkg...@googlegroups.com
I believe this is some stupidity by the X11 developers, instead of making keys repeat as they used to, X reports many key down/up pairs. There is a workaround added, I believe, to the fltk2.0Ā code, this needs to be copied into fltk1. Basically in the event handling when it gets a key-up, it needs to check if there is a key-down for the same key queued and instead turn it into only a repeating key-down event.

--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/8de25866-c9f8-4e48-b8c3-317c058c5d65n%40googlegroups.com.

Ian MacArthur

unread,
Sep 24, 2021, 3:57:20ā€ÆAM9/24/21
to fltk.general
On Thursday, 23 September 2021 at 18:20:35 UTC+1 spitzak wrote:
I believe this is some stupidity by the X11 developers, instead of making keys repeat as they used to, X reports many key down/up pairs. There is a workaround added, I believe, to the fltk2.0Ā code, this needs to be copied into fltk1. Basically in the event handling when it gets a key-up, it needs to check if there is a key-down for the same key queued and instead turn it into only a repeating key-down event.

Yeah - what Bill said; peeking ahead in the queueĀ  and eliding any KEYUP that is immediately followed by the same KEYDOWN might be the only robust solution?
Dunno...

Interestingly, I actually looked at the docs for Keyboard Events, and it includes this exhortation:

Todo:Ā Add details on how to detect repeating keys, since on some X servers a repeating key will generate both FL_KEYUP and FL_KEYDOWN, such that to tell if a key is held, you needĀ Fl::event_key(int)Ā to detect if the key is being held down during FL_KEYUP or not.

I don't know who wrote that (I'm fairly sure it was not me, at any rate!) but that sounds like exactly the thing... Might be worth a try.

Meanwhile, in the interests of completeness, I added the timer check I proposed earlier. This appears to work just fine (On Windows) but today's another non-Linux day for me, so can't readily test under X11...

//////////////////////////////////
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>

static int shown_v = 0;
/*****************************************************************************/
static bool still_pressing = false;
static bool timeout_active = false;
static void check_timeout (void *pv)
{
Ā  Ā  if (still_pressing == true)
Ā  Ā  {
Ā  Ā  Ā  Ā  still_pressing = false;
Ā  Ā  Ā  Ā  Fl::add_timeout(0.2, check_timeout, pv);
Ā  Ā  }
Ā  Ā  else
Ā  Ā  {
Ā  Ā  Ā  Ā  timeout_active = false;
Ā  Ā  Ā  Ā  Fl_Double_Window *w = (Fl_Double_Window *)pv;
Ā  Ā  Ā  Ā  if (shown_v) {
Ā  Ā  Ā  Ā  Ā  Ā  w->hide();
Ā  Ā  Ā  Ā  Ā  Ā  shown_v = 0;
Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  else {
Ā  Ā  Ā  Ā  Ā  Ā  w->show();
Ā  Ā  Ā  Ā  Ā  Ā  shown_v = 1;
Ā  Ā  Ā  Ā  }
Ā  Ā  }
} // check_timeout

/*****************************************************************************/

class my_win : public Fl_Double_Window {

Ā  Ā  int handle (int);

public:
Ā  Ā  my_win(int x, int y,int w,int h,const char* L) : Fl_Double_Window(x,y,w,h,L) {};
}; // my_win

int my_win::handle (int e) {
Ā  Ā  int ret = Fl_Double_Window::handle(e);
Ā  Ā  if (e == FL_KEYDOWN) {
Ā  Ā  Ā  Ā  if (Fl::event_state() & (FL_ALT)) {
Ā  Ā  Ā  Ā  Ā  Ā  if (Fl::event_key()=='s') {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  still_pressing = true;
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  if (!timeout_active)
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Fl::add_timeout(0.2, check_timeout, (void *)this);
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  timeout_active = true;
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  return 1;
Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  }
Ā  Ā  }
Ā  Ā  else if (e == FL_KEYUP) {
Ā  Ā  Ā  Ā  if (still_pressing)
Ā  Ā  Ā  Ā  {
Ā  Ā  Ā  Ā  Ā  Ā  still_pressing = false;
Ā  Ā  Ā  Ā  Ā  Ā  return 1;
Ā  Ā  Ā  Ā  }
Ā  Ā  }
Ā  Ā  return ret;
} // my_win::handle

static void toggle_win (Fl_Widget*,void* data) {
Ā  Ā  my_win* w = static_cast<my_win*>(data);

Ā  Ā  if (shown_v) {
Ā  Ā  Ā  Ā  w->hide();
Ā  Ā  Ā  Ā  shown_v = 0;
Ā  Ā  }
Ā  Ā  else {
Ā  Ā  Ā  Ā  w->show();
Ā  Ā  Ā  Ā  shown_v = 1;
Ā  Ā  }
} // toggle_win

int main() {
Ā  Ā  shown_v = 0;

Ā  Ā  my_win *win2 = new my_win(0.5*Fl::w()-100,0.5*Fl::h()-100,200,200,"2nd Win");
Ā  Ā  win2->end();

Ā  Ā  Fl_Double_Window *win = new Fl_Double_Window(0.5*Fl::w()-200,0.5*Fl::h()-200,400,400,"Main Win");
Ā  Ā  win->begin();
Ā  Ā  win->box (FL_FLAT_BOX);

Ā  Ā  Fl_Menu_Item items[] = {
Ā  Ā  Ā  Ā  {"Menu1",0,0,0,FL_SUBMENU,0,0,FL_NORMAL_SIZE,FL_BLACK}, //0
Ā  Ā  Ā  Ā  {"Hide/show second window",FL_ALT+'s',toggle_win,win2,0,0,0,FL_NORMAL_SIZE,FL_BLACK},
Ā  Ā  Ā  Ā  {0,0,0,0,0,0,0,0,0},
Ā  Ā  Ā  Ā  {0,0,0,0,0,0,0,0,0}
Ā  Ā  };

Ā  Ā  Fl_Menu_Bar *menu = new Fl_Menu_Bar(0, 0, win->w(),25);
Ā  Ā  menu->box (FL_BORDER_BOX);

Ian MacArthur

unread,
Sep 24, 2021, 4:24:28ā€ÆAM9/24/21
to fltk.general
On Friday, 24 September 2021 at 08:57:20 UTC+1 Ian MacArthur wrote:

Interestingly, I actually looked at the docs for Keyboard Events, and it includes this exhortation:

Todo:Ā Add details on how to detect repeating keys, since on some X servers a repeating key will generate both FL_KEYUP and FL_KEYDOWN, such that to tell if a key is held, you needĀ Fl::event_key(int)Ā to detect if the key is being held down during FL_KEYUP or not.

I don't know who wrote that (I'm fairly sure it was not me, at any rate!) but that sounds like exactly the thing... Might be worth a try.

And for (even more!) completeness, here's what I think that might mean - though given that I'm not on X11 today I have no clue of this will work or not.
Works. OK on Win10, but that's not all that helpful in this instance...

////////////////////////////
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Menu_Bar.H>

static int shown_v = 0;

class my_win : public Fl_Double_Window {

Ā  Ā  int handle (int);

Ā  Ā  int please_change;
public:
Ā  Ā  my_win(int x, int y,int w,int h,const char* L):Fl_Double_Window(x,y,w,h,L), please_change(0) {};
}; // my_win

int my_win::handle (int e) {
Ā  Ā  int ret = Fl_Double_Window::handle(e);
Ā  Ā  if (e == FL_KEYDOWN) {
Ā  Ā  Ā  Ā  if (Fl::event_state() & FL_ALT) {
Ā  Ā  Ā  Ā  Ā  Ā  if (Fl::event_key() == 's') {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  please_change = (-1);
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  return 1;
Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  }
Ā  Ā  }
Ā  Ā  else if (e == FL_KEYUP) {
Ā  Ā  Ā  Ā  if (please_change) {
Ā  Ā  Ā  Ā  Ā  Ā  if ((Fl::event_state() & FL_ALT) && (Fl::event_key() == 's')) {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  // Probably a synthetic repeat keyup on X11? Ignore...
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  return 1;
Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  Ā  Ā  else { // Assumed to be a real keyup
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  please_change = 0;
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  if (shown_v) {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  this->hide();
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  shown_v = 0;
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  }
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  else {
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  this->show();
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  shown_v = 1;
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  }

rs

unread,
Sep 24, 2021, 8:03:08ā€ÆAM9/24/21
to fltk.general
Thanks for the suggestions, Ian.

FYI, the first managed to avoid the issue entirely (albeit with the 0.2 second delay). The second still causes the keyboard events to fall over if the button is held down long enough.

Thanks,

R.

Albrecht Schlosser

unread,
Sep 24, 2021, 8:29:23ā€ÆAM9/24/21
to fltkg...@googlegroups.com
On 9/24/21 2:03 PM rs wrote:
> Thanks for the suggestions, Ian.
>
> FYI, the first managed to avoid the issue entirely (albeit with the
> 0.2 second delay). The second still causes the keyboard events to fall
> over if the button is held down long enough.

I'm late to the party, but anyway... I tried the original test program
on my Linux Mint 20 system and didn't see any issues. It seems to toggle
the 2nd window as expected.

My assumption was that the FL_ALT state might get lost when switching
windows (IIRC only the last key pressed and held - i.e. the 's' key -
gets the key repetition events) but this didn't seem to affect the test.

My next suspect was the shown_v variable. What if the variable and the
actual state of the window get out of sync due to the delay induced by
the X window server turnaround needed to show the window? So I used the
shown() method instead, but this didn't change anything.

Anyway, I attach my modified version of the original test program. Can
you please build this and run it on your system? Maybe this can give
some clues...

Here's a typical output on my system:

Ā  1 - toggle_win, shown() = 0
Ā  1 - FL_ALT is on , key = 's', shown = 1
Ā  2 - toggle_win, shown() = 0
Ā  3 - toggle_win, shown() = 1
Ā  4 - toggle_win, shown() = 0
Ā  5 - toggle_win, shown() = 1
Ā  6 - toggle_win, shown() = 0
Ā  2 - FL_ALT is on , key = 's', shown = 1
Ā  7 - toggle_win, shown() = 0
Ā  8 - toggle_win, shown() = 1
Ā  9 - toggle_win, shown() = 0
Ā  3 - FL_ALT is on , key = 's', shown = 1
Ā 10 - toggle_win, shown() = 0
...
511 - toggle_win, shown() = 1
512 - toggle_win, shown() = 0
513 - toggle_win, shown() = 1
514 - toggle_win, shown() = 0
183 - FL_ALT is on , key = 's', shown = 1
515 - toggle_win, shown() = 0
516 - toggle_win, shown() = 1
517 - toggle_win, shown() = 0
184 - FL_ALT is on , key = 's', shown = 1
518 - toggle_win, shown() = 0
185 - FL_ALT is on , key = 's', shown = 1
519 - toggle_win, shown() = 0

As you can clearly see 'toggle_win' (the menu callback) runs much more
often than the event handling of the derived window class, which is to
be expected because of the X server turnaround needed to effectively
show/hide the window.

However, in my test everything works as expected (as far as user input
and the show/hide toggling is concerned).

It would be interesting to see what happens on your system with this
program. Can you please test and report?

Albrecht Schlosser

unread,
Sep 24, 2021, 8:31:33ā€ÆAM9/24/21
to fltkg...@googlegroups.com

[Attaching forgotten demo program]
alt-s2.cxx

Ian MacArthur

unread,
Sep 24, 2021, 8:33:13ā€ÆAM9/24/21
to fltk.general
On Friday, 24 September 2021 at 13:03:08 UTC+1 rs wrote:
Thanks for the suggestions, Ian.

FYI, the first managed to avoid the issue entirely (albeit with the 0.2 second delay).


OK. well that sounds reasonably positive I guess - FWIW the 200ms delay I set was entirely arbitrary, for the purposes of testing. It may be (should be) feasible to shorten that and still get robust behaviour, but lacking a suitable X11 box to try it on today, I just picked something...

Ā 
The second still causes the keyboard events to fall over if the button is held down long enough.


OK, that's a pity: What that's really telling us is that I implemented it wrong, as that approach (or at least something very like it, only better!) should work!


Ian MacArthur

unread,
Sep 24, 2021, 8:37:53ā€ÆAM9/24/21
to fltk.general
On Friday, 24 September 2021 at 13:29:23 UTC+1 Albrecht Schlosser wrote:

I'm late to the party, but anyway... I tried the original test program
on my Linux Mint 20 system and didn't see any issues. It seems to toggle
the 2nd window as expected.

Hi Albrecht,
The behaviour is (at least rumoured to be) dependent on which specific X11 setup is used, and may vary from host to host, so it's quite likely that the same program will behave differently depending on which X / WM / whatever, is in use.

Which is a nuisance to work with...

Albrecht Schlosser

unread,
Sep 24, 2021, 8:51:34ā€ÆAM9/24/21
to fltkg...@googlegroups.com
Hi Ian,

that's why I reported my results and asked the OP to try my test program on their system and report their results. Maybe this can give us a clue what's happening on that system. Which reminds me that I forgot to ask:

@OP: which Linux flavor and version and which WM are you using?


Which is a nuisance to work with...

I agree.

rs

unread,
Sep 24, 2021, 11:53:25ā€ÆAM9/24/21
to fltk.general
Ok, So I'm using PopOS 20.04 LTS with Mate desktop environmentĀ  and Metacity (Marco) as the window manager (thats the output of wmctrl -m)

The output of cat /proc/version is

Linux version 5.11.0-7620-generic (buildd@lgw01-amd64-045) (gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #21~1626191760~20.04~55de9c3-Ubuntu SMP Wed Jul 21 20:31:55 UTC

@Albrecht

I ran your version and it runs into the same issue. Here are some sample outputs:

A longer one:

Ā  1 - toggle_win, shown() = 0
Ā  1 - FL_ALT is on , key = 's', shown = 1
Ā  2 - toggle_win, shown() = 0
Ā  2 - FL_ALT is on , key = 's', shown = 1
Ā  3 - toggle_win, shown() = 0

Ā  3 - FL_ALT is on , key = 's', shown = 1
Ā  4 - toggle_win, shown() = 0
Ā  4 - FL_ALT is on , key = 's', shown = 1
Ā  5 - toggle_win, shown() = 0
Ā  5 - FL_ALT is on , key = 's', shown = 1

Ā  6 - toggle_win, shown() = 0
Ā  6 - FL_ALT is on , key = 's', shown = 1

Ā  7 - toggle_win, shown() = 0
Ā  8 - toggle_win, shown() = 1
Ā  9 - toggle_win, shown() = 0
<--- fails here


A short one:

1 - toggle_win, shown() = 0
Ā  1 - FL_ALT is on , key = 's', shown = 1
Ā  2 - toggle_win, shown() = 0
Ā  3 - toggle_win, shown() = 1
Ā  4 - toggle_win, shown() = 0
<--- fails here

>As you can clearly see 'toggle_win' (the menu callback) runs much more
often than the event handling of the derived window class, which is to
be expected because of the X server turnaround needed to effectively
show/hide the window.

So I see this too, but it is what (appears to be) causing (or at least coinciding) with the problem. Note that when the output stops (because the keyboard input stops/breaks) the second window is always *shown* on the screen.

Thanks,

R.

Bill Spitzak

unread,
Sep 24, 2021, 1:52:15ā€ÆPM9/24/21
to fltkg...@googlegroups.com
All I remember is that it was fairly easy to fix this in fltk2.0. The bogus key-up/down messages are sent as the same block of events. So when the key-up message is received, the key-down is a pending event, immediately available and already in the Xlib incoming event buffer. There absolutely should be no need for a timeout.

Relevant code from fltk2.0 handlng of KeyRelease events:

Ā  Ā  // Bogus KeyUp events are generated by repeated KeyDown events. One
Ā  Ā  // neccessary condition is an identical key event pending right after
Ā  Ā  // the bogus KeyUp.
Ā  Ā  // The new code introduced Dec 2009 differs in that it only check the very
Ā  Ā  // next event in the queue, not the entire queue of events.
Ā  Ā  // This function wrongly detects a repeat key if a software keyboard
Ā  Ā  // sends a burst of events containing two consecutive equal keys. However,
Ā  Ā  // in every non-gaming situation, this is no problem because both KeyPress
Ā  Ā  // events will cause the expected behavior.
Ā  Ā  XEvent peekevent;
Ā  Ā  if (XPending(xdisplay)) {
Ā  Ā  Ā  XPeekEvent(xdisplay, &peekevent);
Ā  Ā  Ā  if ( Ā  (peekevent.type == KeyPress) // must be a KeyPress event
Ā  Ā  Ā  Ā  Ā  && (peekevent.xkey.keycode == xevent.xkey.keycode) // must be the same key
Ā  Ā  Ā  Ā  Ā  && (peekevent.xkey.time == xevent.xkey.time) // must be sent at the exact same time
Ā  Ā  Ā  Ā  Ā  ) {
Ā  Ā  Ā  Ā  XNextEvent(xdisplay, &xevent);
Ā  Ā  Ā  Ā  goto KEYPRESS;
Ā  Ā  Ā  }
Ā  Ā  }


--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.

Ian MacArthur

unread,
Sep 25, 2021, 5:11:07ā€ÆAM9/25/21
to Fltk General
On 24 Sep 2021, at 18:52, Bill Spitzak wrote:

> There absolutely should be no need for a timeout.


Sure - my suggestion of using a timeout, and the demo of same, were not intended as ā€œthe fixā€ but really just as a workaround to get the OP up and running with the existing implementation.

A better fix probably is to trap for this behaviour in the fltk X11 event handling and make the output more consistent and congruent across platforms, as you say. But thatā€™ll take a while to propagate out into the wild, so...

Harking back to the workaround: Given that the UP and DOWN events occur in the same event block, hence effectively ā€œsimultaneously", as it were, it is likely that the event timer could be *really short* (i.e. too short for a human to notice the delay, but long enough for the computer to notice!) and still work.
Worth a shot anyway...


Reply all
Reply to author
Forward
0 new messages