Skip to content

Commit

Permalink
Linux: use an event queue in THardwareInfo
Browse files Browse the repository at this point in the history
It turns out that the slugginess experienced when dragging the mouse quickly and showing lots of data is not caused by performance issues in the I/O functions, but because events are being received at a faster rate than they are processed.

A possible solution is to have a small queue of events and read as many events as possible in a non-blocking way before going the blocking way. This will give results when there are lots of input events, e.g. because the user is moving the mouse aggresively or text has been pasted into the terminal. If we then avoid flushing the screen or reading more events until the queue is empty, these events are processed with a considerably lower latency.

This is probably the most important performance improvement in this project up to now.

Fixes cosmos72/twin/magiblot#61.
  • Loading branch information
magiblot committed Aug 25, 2020
1 parent d8d7701 commit 8d75437
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 29 deletions.
8 changes: 7 additions & 1 deletion include/tvision/hardware.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class THardwareInfo

THardwareInfo();
#ifndef __BORLANDC__
static TEvent pendingMouseEvent;
enum { eventQSize = 24 };
static TEvent eventQ[eventQSize];
static size_t eventCount;
static void flushScreen();
static bool isLinuxConsole(int fd);
#endif
Expand Down Expand Up @@ -105,6 +107,10 @@ class THardwareInfo
static BOOL getMouseEvent( MouseEventType& event );
static BOOL getKeyEvent( TEvent& event );
static void clearPendingEvent();
#ifndef __BORLANDC__
static BOOL getPendingEvent( TEvent &event, ushort mask );
static void readEvents();
#endif

// System functions.

Expand Down
75 changes: 47 additions & 28 deletions source/linux/hardware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::steady_clock;

TEvent THardwareInfo::pendingMouseEvent;
TEvent THardwareInfo::eventQ[];
size_t THardwareInfo::eventCount = 0;

static bool alwaysFlush;

Expand Down Expand Up @@ -98,50 +99,68 @@ void THardwareInfo::restoreConsole()
platf.reset();
}

BOOL THardwareInfo::getPendingEvent(TEvent &event, ushort mask)
{
for (size_t i = 0; i < eventCount; ++i)
if (eventQ[i].what & mask)
{
event = eventQ[i];
for (; i + 1 < eventCount; ++i)
eventQ[i] = eventQ[i + 1];
--eventCount;
return True;
}
return False;
}

BOOL THardwareInfo::getMouseEvent( MouseEventType& event )
{
if (pendingEvent) {
event = pendingMouseEvent.mouse;
pendingEvent = 0;
TEvent ev;
if (getPendingEvent(ev, evMouse))
{
event = ev.mouse;
return True;
}
return False;
}

BOOL THardwareInfo::getKeyEvent( TEvent& event )
{
/* This is a good place to refresh the display, since it guarantees a
* a refresh each time an event is processed or there's a wait timeout,
* and avoids unnecessary screen refreshes. */
THardwareInfo::flushScreen();
if (!pendingEvent)
readEvents();
if (getPendingEvent(event, ~evMouse))
{
if (platf->waitForEvent(eventTimeoutMs, event))
if (event.what & evKeyboard)
{
if (event.what & evKeyboard)
{
// Set/Reset insert flag.
if( event.keyDown.keyCode == kbIns )
insertState = !insertState;
if( insertState )
event.keyDown.controlKeyState |= kbInsState;
return True;
}
else if (event.what & evMouse)
{
/* Like in the original implementation, let mouse events
* to be treated on the following polling loop. */
pendingMouseEvent = event;
pendingEvent = 1;
return False;
}
return (Boolean) event.what != evNothing;
// Set/Reset insert flag.
if( event.keyDown.keyCode == kbIns )
insertState = !insertState;
if( insertState )
event.keyDown.controlKeyState |= kbInsState;
return True;
}
return event.what != evNothing;
}
return False;
}

void THardwareInfo::readEvents()
{
// Do not read any more events until the queue is empty.
if (!eventCount)
{
// Flush the screen once for every time all events have been processed.
THardwareInfo::flushScreen();
TEvent event;
// Non-blocking read.
while ( eventCount < eventQSize &&
platf->waitForEvent(0, event) )
eventQ[eventCount++] = event;
// Blocking read.
if (!eventCount && platf->waitForEvent(eventTimeoutMs, event))
eventQ[eventCount++] = event;
}
}

ulong THardwareInfo::getTickCount()
{
// This effectively gives a system time reference in milliseconds.
Expand Down

0 comments on commit 8d75437

Please sign in to comment.