Unbuffered reading of keystrokes for better latency

This commit is contained in:
Markus Gans 2020-03-05 21:30:54 +01:00
parent 3102fa6e59
commit 925f106846
13 changed files with 107 additions and 40 deletions

View File

@ -1,3 +1,7 @@
2020-03-05 Markus Gans <guru.mail@muenster.de>
* Unbuffered reading of keystrokes for better latency
* Mouse adjustments when resizing an rxvt terminal
2020-02-25 Markus Gans <guru.mail@muenster.de> 2020-02-25 Markus Gans <guru.mail@muenster.de>
* New command line switch "--no-terminal-data-request" to disable * New command line switch "--no-terminal-data-request" to disable
font and title determination font and title determination

View File

@ -274,6 +274,7 @@ int main (int argc, char* argv[])
{ {
// Create the application object // Create the application object
finalcut::FApplication app (argc, argv); finalcut::FApplication app (argc, argv);
app.setNonBlockingRead();
// Create main dialog object // Create main dialog object
MainWindow main_dlg (&app); MainWindow main_dlg (&app);

View File

@ -1004,6 +1004,7 @@ int main (int argc, char* argv[])
// Create the application object app // Create the application object app
finalcut::FApplication app(argc, argv); finalcut::FApplication app(argc, argv);
app.setNonBlockingRead();
app.redefineDefaultColors(true); app.redefineDefaultColors(true);
app.setTermTitle (title); app.setTermTitle (title);

View File

@ -684,13 +684,11 @@ void FApplication::processKeyboardEvent()
findKeyboardWidget(); findKeyboardWidget();
flush(); flush();
keyboard->escapeKeyHandling(); // special case: Esc key
keyboard->clearKeyBufferOnTimeout(); keyboard->clearKeyBufferOnTimeout();
if ( isKeyPressed() ) if ( isKeyPressed() )
keyboard->fetchKeyCode(); keyboard->fetchKeyCode();
// special case: Esc key
keyboard->escapeKeyHandling();
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -1121,6 +1119,12 @@ void FApplication::processResizeEvent()
if ( ! hasChangedTermSize() ) if ( ! hasChangedTermSize() )
return; return;
if ( mouse )
{
mouse->setMaxWidth (uInt16(getDesktopWidth()));
mouse->setMaxHeight (uInt16(getDesktopHeight()));
}
FResizeEvent r_ev(fc::Resize_Event); FResizeEvent r_ev(fc::Resize_Event);
sendEvent(app_object, &r_ev); sendEvent(app_object, &r_ev);

View File

@ -42,7 +42,8 @@ namespace finalcut
{ {
// static class attributes // static class attributes
uInt64 FKeyboard::key_timeout{100000}; // 100 ms (default timeout for keypress) uInt64 FKeyboard::read_blocking_time{100000}; // preset to 100 ms
uInt64 FKeyboard::key_timeout{100000}; // preset to 100 ms
struct timeval FKeyboard::time_keypressed{}; struct timeval FKeyboard::time_keypressed{};
#if defined(__linux__) #if defined(__linux__)
@ -123,7 +124,7 @@ bool FKeyboard::isKeyPressed()
FD_ZERO(&ifds); FD_ZERO(&ifds);
FD_SET(stdin_no, &ifds); FD_SET(stdin_no, &ifds);
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 100000; // 100 ms tv.tv_usec = FKeyboard::read_blocking_time; // preset to 100 ms
const int result = select (stdin_no + 1, &ifds, nullptr, nullptr, &tv); const int result = select (stdin_no + 1, &ifds, nullptr, nullptr, &tv);
if ( result > 0 && FD_ISSET(stdin_no, &ifds) ) if ( result > 0 && FD_ISSET(stdin_no, &ifds) )
@ -290,6 +291,7 @@ inline FKey FKeyboard::getSingleKey()
if ( utf8_input && (firstchar & 0xc0) == 0xc0 ) if ( utf8_input && (firstchar & 0xc0) == 0xc0 )
{ {
char utf8char[5]{}; // Init array with '\0' char utf8char[5]{}; // Init array with '\0'
const std::size_t buf_len = std::strlen(fifo_buf);
if ( (firstchar & 0xe0) == 0xc0 ) if ( (firstchar & 0xe0) == 0xc0 )
len = 2; len = 2;
@ -298,6 +300,9 @@ inline FKey FKeyboard::getSingleKey()
else if ( (firstchar & 0xf8) == 0xf0 ) else if ( (firstchar & 0xf8) == 0xf0 )
len = 4; len = 4;
if ( buf_len < len && ! isKeypressTimeout() )
return fc::need_more_data;
for (std::size_t i{0}; i < len ; i++) for (std::size_t i{0}; i < len ; i++)
utf8char[i] = char(fifo_buf[i] & 0xff); utf8char[i] = char(fifo_buf[i] & 0xff);
@ -403,7 +408,7 @@ FKey FKeyboard::UTF8decode (const char utf8[])
inline ssize_t FKeyboard::readKey() inline ssize_t FKeyboard::readKey()
{ {
setNonBlockingInput(); setNonBlockingInput();
const ssize_t bytes = read(FTermios::getStdIn(), &read_buf, READ_BUF_SIZE - 1); const ssize_t bytes = read(FTermios::getStdIn(), &read_character, 1);
unsetNonBlockingInput(); unsetNonBlockingInput();
return bytes; return bytes;
} }
@ -418,12 +423,8 @@ void FKeyboard::parseKeyBuffer()
{ {
if ( bytesread + fifo_offset <= int(FIFO_BUF_SIZE) ) if ( bytesread + fifo_offset <= int(FIFO_BUF_SIZE) )
{ {
for (std::size_t i{0}; i < std::size_t(bytesread); i++) fifo_buf[fifo_offset] = char(read_character);
{ fifo_offset++;
fifo_buf[fifo_offset] = read_buf[i];
fifo_offset++;
}
fifo_in_use = true; fifo_in_use = true;
} }
@ -453,7 +454,7 @@ void FKeyboard::parseKeyBuffer()
key = 0; key = 0;
} }
std::fill_n (read_buf, READ_BUF_SIZE, '\0'); read_character = 0;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------

View File

@ -527,7 +527,7 @@ int FMouseGPM::gpmEvent (bool clear)
FD_SET(stdin_no, &ifds); FD_SET(stdin_no, &ifds);
FD_SET(gpm_fd, &ifds); FD_SET(gpm_fd, &ifds);
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 100000; // 100 ms tv.tv_usec = FKeyboard::getReadBlockingTime(); // preset to 100 ms
const int result = select (max + 1, &ifds, nullptr, nullptr, &tv); const int result = select (max + 1, &ifds, nullptr, nullptr, &tv);
if ( result > 0 && FD_ISSET(stdin_no, &ifds) ) if ( result > 0 && FD_ISSET(stdin_no, &ifds) )

View File

@ -182,6 +182,13 @@ FColor FVTerm::rgb2ColorIndex (uInt8 r, uInt8 g, uInt8 b)
return 16 + ri + gi + bi; return 16 + ri + gi + bi;
} }
//----------------------------------------------------------------------
void FVTerm::setNonBlockingRead (bool enable)
{
uInt64 blocking_time = (enable) ? 0 : 100000; // 0 or 100 ms
FKeyboard::setReadBlockingTime (blocking_time);
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void FVTerm::clearArea (int fillchar) void FVTerm::clearArea (int fillchar)
{ {

View File

@ -36,7 +36,7 @@ namespace finalcut
{ {
// global FWidget object // global FWidget object
static FWidget* rootObject{nullptr}; static FWidget* root_widget{nullptr};
// static class attributes // static class attributes
FStatusBar* FWidget::statusbar{nullptr}; FStatusBar* FWidget::statusbar{nullptr};
@ -59,7 +59,7 @@ uInt FWidget::modal_dialog_counter{};
// constructors and destructor // constructors and destructor
//---------------------------------------------------------------------- //----------------------------------------------------------------------
FWidget::FWidget (FWidget* parent, bool disable_alt_screen) FWidget::FWidget (FWidget* parent, bool disable_alt_screen)
: FVTerm( ! (bool(parent) || rootObject), disable_alt_screen) : FVTerm( ! (bool(parent) || root_widget), disable_alt_screen)
, FObject(parent) , FObject(parent)
{ {
// init bit field with 0 // init bit field with 0
@ -73,7 +73,7 @@ FWidget::FWidget (FWidget* parent, bool disable_alt_screen)
if ( ! parent ) if ( ! parent )
{ {
if ( rootObject ) if ( root_widget )
{ {
auto ftermdata = getFTerm().getFTermData(); auto ftermdata = getFTerm().getFTermData();
ftermdata->setExitMessage("FWidget: No parent defined! " ftermdata->setExitMessage("FWidget: No parent defined! "
@ -82,12 +82,12 @@ FWidget::FWidget (FWidget* parent, bool disable_alt_screen)
return; return;
} }
rootObject = this; root_widget = this;
show_root_widget = nullptr; show_root_widget = nullptr;
redraw_root_widget = nullptr; redraw_root_widget = nullptr;
modal_dialog_counter = 0; modal_dialog_counter = 0;
statusbar = nullptr; statusbar = nullptr;
init(); initRootWidget();
} }
else else
{ {
@ -133,7 +133,7 @@ FWidget::~FWidget() // destructor
accelerator_list.clear(); accelerator_list.clear();
// finish the program // finish the program
if ( rootObject == this ) if ( root_widget == this )
finish(); finish();
} }
@ -454,7 +454,7 @@ void FWidget::setTopPadding (int top, bool adjust)
{ {
if ( isRootWidget() ) if ( isRootWidget() )
{ {
auto r = rootObject; auto r = root_widget;
r->wclient_offset.setY1 (r->padding.top); r->wclient_offset.setY1 (r->padding.top);
adjustSizeGlobal(); adjustSizeGlobal();
} }
@ -475,7 +475,7 @@ void FWidget::setLeftPadding (int left, bool adjust)
{ {
if ( isRootWidget() ) if ( isRootWidget() )
{ {
auto r = rootObject; auto r = root_widget;
r->wclient_offset.setX1 (r->padding.left); r->wclient_offset.setX1 (r->padding.left);
adjustSizeGlobal(); adjustSizeGlobal();
} }
@ -496,7 +496,7 @@ void FWidget::setBottomPadding (int bottom, bool adjust)
{ {
if ( isRootWidget() ) if ( isRootWidget() )
{ {
auto r = rootObject; auto r = root_widget;
r->wclient_offset.setY2 (int(r->getHeight()) - 1 - r->padding.bottom); r->wclient_offset.setY2 (int(r->getHeight()) - 1 - r->padding.bottom);
adjustSizeGlobal(); adjustSizeGlobal();
} }
@ -517,7 +517,7 @@ void FWidget::setRightPadding (int right, bool adjust)
{ {
if ( isRootWidget() ) if ( isRootWidget() )
{ {
auto r = rootObject; auto r = root_widget;
r->wclient_offset.setX2 (int(r->getWidth()) - 1 - r->padding.right); r->wclient_offset.setX2 (int(r->getWidth()) - 1 - r->padding.right);
adjustSizeGlobal(); adjustSizeGlobal();
} }
@ -533,8 +533,8 @@ void FWidget::setTermSize (const FSize& size)
if ( isXTerminal() ) if ( isXTerminal() )
{ {
rootObject->wsize.setRect(FPoint(1, 1), size); root_widget->wsize.setRect(FPoint(1, 1), size);
rootObject->adjust_wsize = rootObject->wsize; root_widget->adjust_wsize = root_widget->wsize;
FTerm::setTermSize(size); // width = columns / height = lines FTerm::setTermSize(size); // width = columns / height = lines
detectTermSize(); detectTermSize();
} }
@ -995,7 +995,7 @@ void FWidget::show()
{ {
// Sets the initial screen settings // Sets the initial screen settings
initScreenSettings(); initScreenSettings();
// Draw the vdesktop // Initializing vdesktop
const auto& r = getRootWidget(); const auto& r = getRootWidget();
setColor(r->getForegroundColor(), r->getBackgroundColor()); setColor(r->getForegroundColor(), r->getBackgroundColor());
clearArea (getVirtualDesktop()); clearArea (getVirtualDesktop());
@ -1292,7 +1292,7 @@ void FWidget::adjustSize()
if ( ignore_padding && ! isDialogWidget() ) if ( ignore_padding && ! isDialogWidget() )
setTermOffset(); setTermOffset();
else else
woffset = rootObject->wclient_offset; woffset = root_widget->wclient_offset;
} }
else if ( ignore_padding && p ) else if ( ignore_padding && p )
{ {
@ -1654,8 +1654,8 @@ void FWidget::onAccel (FAccelEvent*)
void FWidget::onResize (FResizeEvent* ev) void FWidget::onResize (FResizeEvent* ev)
{ {
// The terminal was resized // The terminal was resized
rootObject->resize(); root_widget->resize();
rootObject->redraw(); root_widget->redraw();
ev->accept(); ev->accept();
} }
@ -1676,7 +1676,7 @@ void FWidget::onClose (FCloseEvent* ev)
// private methods of FWidget // private methods of FWidget
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void FWidget::init() void FWidget::initRootWidget()
{ {
try try
{ {
@ -2012,7 +2012,7 @@ void FWidget::setStatusbarText (bool enable)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void detectTermSize() void detectTermSize()
{ {
const auto& r = rootObject; const auto& r = root_widget;
FTerm::detectTermSize(); FTerm::detectTermSize();
r->adjust_wsize.setRect (1, 1, r->getDesktopWidth(), r->getDesktopHeight()); r->adjust_wsize.setRect (1, 1, r->getDesktopWidth(), r->getDesktopHeight());
r->woffset.setRect (0, 0, r->getDesktopWidth(), r->getDesktopHeight()); r->woffset.setRect (0, 0, r->getDesktopWidth(), r->getDesktopHeight());

View File

@ -166,7 +166,7 @@ void drawTransparentShadow (FWidget* w)
w->print() << FPoint(int(width) + 1, int(y) + 1) << " "; w->print() << FPoint(int(width) + 1, int(y) + 1) << " ";
} }
w->print() << FStyle (fc::Transparent) w->print() << FStyle (fc::Reset) << FStyle (fc::Transparent)
<< FPoint (1, int(height) + 1) << FPoint (1, int(height) + 1)
<< " " << " "
<< FStyle (fc::Reset) << FStyle (fc::Reset)

View File

@ -104,10 +104,13 @@ class FKeyboard final
const FString getKeyName (const FKey); const FString getKeyName (const FKey);
keybuffer& getKeyBuffer(); keybuffer& getKeyBuffer();
timeval* getKeyPressedTime(); timeval* getKeyPressedTime();
static uInt64 getKeypressTimeout();
static uInt64 getReadBlockingTime();
// Mutators // Mutators
void setTermcapMap (fc::FKeyMap*); void setTermcapMap (fc::FKeyMap*);
void setKeypressTimeout (const uInt64); static void setKeypressTimeout (const uInt64);
static void setReadBlockingTime (const uInt64);
void enableUTF8(); void enableUTF8();
void disableUTF8(); void disableUTF8();
void enableMouseSequences(); void enableMouseSequences();
@ -130,7 +133,6 @@ class FKeyboard final
private: private:
// Constants // Constants
static constexpr std::size_t READ_BUF_SIZE{1024};
static constexpr FKey NOT_SET = static_cast<FKey>(-1); static constexpr FKey NOT_SET = static_cast<FKey>(-1);
// Accessors // Accessors
@ -169,10 +171,11 @@ class FKeyboard final
#endif #endif
static timeval time_keypressed; static timeval time_keypressed;
static uInt64 read_blocking_time;
static uInt64 key_timeout; static uInt64 key_timeout;
fc::FKeyMap* key_map{nullptr}; fc::FKeyMap* key_map{nullptr};
FKey key{0}; FKey key{0};
char read_buf[READ_BUF_SIZE]{'\0'}; uChar read_character{};
char fifo_buf[FIFO_BUF_SIZE]{'\0'}; char fifo_buf[FIFO_BUF_SIZE]{'\0'};
int fifo_offset{0}; int fifo_offset{0};
int stdin_status_flags{0}; int stdin_status_flags{0};
@ -200,10 +203,22 @@ inline FKeyboard::keybuffer& FKeyboard::getKeyBuffer()
inline timeval* FKeyboard::getKeyPressedTime() inline timeval* FKeyboard::getKeyPressedTime()
{ return &time_keypressed; } { return &time_keypressed; }
//----------------------------------------------------------------------
inline uInt64 FKeyboard::getKeypressTimeout()
{ return key_timeout; }
//----------------------------------------------------------------------
inline uInt64 FKeyboard::getReadBlockingTime()
{ return read_blocking_time; }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
inline void FKeyboard::setKeypressTimeout (const uInt64 timeout) inline void FKeyboard::setKeypressTimeout (const uInt64 timeout)
{ key_timeout = timeout; } { key_timeout = timeout; }
//----------------------------------------------------------------------
inline void FKeyboard::setReadBlockingTime (const uInt64 blocking_time)
{ read_blocking_time = blocking_time; }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
inline void FKeyboard::enableUTF8() inline void FKeyboard::enableUTF8()
{ utf8_input = true; } { utf8_input = true; }

View File

@ -236,6 +236,10 @@ class FVTerm
static bool setInheritBackground(); static bool setInheritBackground();
static bool unsetInheritBackground(); static bool unsetInheritBackground();
static void setNonBlockingRead (bool);
static void setNonBlockingRead();
static void unsetNonBlockingRead();
static void setTermTitle (const FString&); static void setTermTitle (const FString&);
static void setEncoding (fc::encoding); static void setEncoding (fc::encoding);
static bool setVGAFont(); static bool setVGAFont();
@ -871,6 +875,14 @@ inline bool FVTerm::setInheritBackground()
inline bool FVTerm::unsetInheritBackground() inline bool FVTerm::unsetInheritBackground()
{ return setInheritBackground(false); } { return setInheritBackground(false); }
//----------------------------------------------------------------------
inline void FVTerm::setNonBlockingRead()
{ setNonBlockingRead(true); }
//----------------------------------------------------------------------
inline void FVTerm::unsetNonBlockingRead()
{ setNonBlockingRead(false); }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
inline void FVTerm::setTermTitle (const FString& title) inline void FVTerm::setTermTitle (const FString& title)
{ FTerm::setTermTitle(title); } { FTerm::setTermTitle(title); }

View File

@ -401,7 +401,7 @@ class FWidget : public FVTerm, public FObject
private: private:
// Methods // Methods
void init(); void initRootWidget();
void finish(); void finish();
void insufficientSpaceAdjust(); void insufficientSpaceAdjust();
void KeyPressEvent (FKeyEvent*); void KeyPressEvent (FKeyEvent*);

View File

@ -330,6 +330,30 @@ void FKeyboardTest::noArgumentTest()
keyboard->escapeKeyHandling(); keyboard->escapeKeyHandling();
CPPUNIT_ASSERT ( keyboard->getKey() == 0 ); CPPUNIT_ASSERT ( keyboard->getKey() == 0 );
// Keypress timeout
CPPUNIT_ASSERT ( keyboard->getKeypressTimeout() == 100 * 1000 );
keyboard->setKeypressTimeout(0); // 0 ms
CPPUNIT_ASSERT ( keyboard->getKeypressTimeout() == 0 );
keyboard->setKeypressTimeout(100000); // 100 ms
CPPUNIT_ASSERT ( keyboard->getKeypressTimeout() == 100 * 1000 );
// Read blocking time
CPPUNIT_ASSERT ( keyboard->getReadBlockingTime() == 100 * 1000 );
keyboard->setReadBlockingTime(1000000); // 1000 ms
CPPUNIT_ASSERT ( keyboard->getReadBlockingTime() == 1000 * 1000 );
keyboard->setReadBlockingTime(0); // 0 ms
CPPUNIT_ASSERT ( keyboard->getReadBlockingTime() == 0 );
keyboard->setReadBlockingTime(50000); // 50 ms
CPPUNIT_ASSERT ( keyboard->getReadBlockingTime() == 50 * 1000 );
keyboard->setReadBlockingTime(100000); // 100 ms
CPPUNIT_ASSERT ( keyboard->getReadBlockingTime() == 100 * 1000 );
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -2823,13 +2847,11 @@ void FKeyboardTest::input (std::string s)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void FKeyboardTest::processInput() void FKeyboardTest::processInput()
{ {
keyboard->escapeKeyHandling(); // special case: Esc key
keyboard->clearKeyBufferOnTimeout(); keyboard->clearKeyBufferOnTimeout();
if ( keyboard->isKeyPressed() ) if ( keyboard->isKeyPressed() )
keyboard->fetchKeyCode(); keyboard->fetchKeyCode();
// special case: Esc key
keyboard->escapeKeyHandling();
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------