PROBLEM:
I'm on a fresh install of Ubuntu 20.04 (xorg 1.20.11), with 2 connected 
keyboards (built-in and USB).
When I hit keys on each keyboard simultaneously in a program like firefox, 
gnome-terminal etc. stalling/lag occurs.
It only happens for keyboards it seems as moving an external mouse and trackpad 
together causes no issues.

TO REPRODUCE:
To investigate the problem I wrote a simple C file (see attached 'xorg-lag.c', 
compile with $(gcc xorg-lag.c -o xorg-lag))
The program finds keyboard devices under /dev/input/event and uses epoll to 
poll them for input via the evdev interface.

If I start entering keys on one keyboard and then switch to the other, the 
program stalls briefly.
If I simultaneously enter keys on each keyboard the program stalls indefinitely 
until I stop entering keys.
I have tested with different keyboards and experience the same result.
If I switch to a virtual terminal and run I experience no lag.
This makes me think it is a bug with xorg.

--
Ryan McClue, Sydney
// SPDX-License-Identifier: zlib-acknowledgement
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#include <sys/epoll.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/ioctl.h>
#include <time.h>

#define EVDEV_BITFIELD_QUANTA \
  (sizeof(unsigned long) * 8)
#define EVDEV_BITFIELD_LEN(bit_count) \
  ((bit_count) / EVDEV_BITFIELD_QUANTA + 1)

#define BILLION 1000000000L

long timespec_diff(struct timespec *start, struct timespec *end)
{
  return (BILLION * (end->tv_sec - start->tv_sec)) +
         (end->tv_nsec - start->tv_nsec);
}


int main(int argc, char *argv[])
{
  int epoll_fd = epoll_create1(0);

  char dev_path[64] = {};
  for (int ev_id = 0; ev_id < 64; ++ev_id)
  {
    sprintf(dev_path, "/dev/input/event%d", ev_id);
    if (access(dev_path, F_OK) == 0)
    {
      int dev_fd = open(dev_path, O_RDWR | O_NONBLOCK);

      unsigned long dev_key_capabilities[EVDEV_BITFIELD_LEN(KEY_CNT)] = {};
      ioctl(dev_fd, EVIOCGBIT(EV_KEY, KEY_CNT), dev_key_capabilities);

      unsigned long esc_keys_letters_mask = 0xfffffffe;
      if ((dev_key_capabilities[0] & esc_keys_letters_mask))
      {
        struct epoll_event event = {};
        event.events = EPOLLIN;
        event.data.fd = dev_fd;
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dev_fd, &event);
      }
    }
  }

  bool want_to_run = true;
  struct timespec prev_timespec = {};
  clock_gettime(CLOCK_MONOTONIC_RAW, &prev_timespec);
  while (want_to_run)
  {
    struct epoll_event epoll_events[5] = {0};
    int num_epoll_events = epoll_wait(epoll_fd, epoll_events, 5, 0);
    for (int epoll_event_i = 0; epoll_event_i < num_epoll_events; ++epoll_event_i)
    {
      int dev_fd = epoll_events[epoll_event_i].data.fd;

      struct input_event dev_events[4] = {0};
      int dev_event_bytes_read = read(dev_fd, dev_events, sizeof(dev_events));

      int num_dev_events = dev_event_bytes_read / sizeof(dev_events[0]); 
      for (int dev_event_i = 0; dev_event_i < num_dev_events; ++dev_event_i)
      {
        if (dev_events[dev_event_i].code == KEY_Q) want_to_run = false;
      }
    }

    struct timespec end_timespec = {};
    clock_gettime(CLOCK_MONOTONIC_RAW, &end_timespec);
    printf("ns per frame: %lu\n", timespec_diff(&prev_timespec, &end_timespec)); 
    prev_timespec = end_timespec;
  }

  return 0;
}

Reply via email to