teaching machines

CS 352 Lecture 31 – Framebuffer

November 21, 2016 by . Filed under cs352, fall 2016, lectures.

Dear students,

Today we continue our discussion of working more directly with I/O devices by reading and writing to their device files in Linux. Last time we reverse engineered the PS/2 mouse protocol. This time we use the Linux input_event API to read from keyboard.

With two input devices to read from, we’ll encounter an issue: how do we know which one to read from? If we try to read from the mouse, our program will block until a mouse event occurs, ignoring any keyboard events. Likewise if we try to read from the keyboard. The select function will come to the rescue. It takes in a set of file descriptors and tells us when one or more have data to read.

To write to the framebuffer, we’ll need to open up /dev/fb0. Since it’s a bit more natural to treat our computer’s display as a 2D array than a file, we’ll use mmap to get a pointer to the video memory. This lets us treat the screen as a 1D array, which is just a 2D array whose rows have all been laid end to end.

See you next class!

Sincerely,

P.S. Here’s the code we wrote together…

drawer.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <linux/input.h>
#include <sys/mman.h>
#include <sys/select.h>

int main(int argc, char **argv) {
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;

  int framebuffer_fd = open("/dev/fb0", O_RDWR);
  ioctl(framebuffer_fd, FBIOGET_VSCREENINFO, &vinfo);
  ioctl(framebuffer_fd, FBIOGET_FSCREENINFO, &finfo);

  int nbytes_in_framebuffer = finfo.smem_len;
  unsigned short *framebuffer = mmap(NULL, nbytes_in_framebuffer, PROT_READ | PROT_WRITE, MAP_SHARED, framebuffer_fd, 0);

  int mouse_fd = open("/dev/input/mouse0", O_RDWR);   
  int keyboard_fd = open("/dev/input/event1", O_RDONLY);   
  struct input_event event;

  const unsigned short RED = 63488;
  const unsigned short GREEN = 2016;
  const unsigned short BLUE = 31; 
  const unsigned short YELLOW = RED + GREEN;
  const unsigned short CYAN = GREEN + BLUE;
  const unsigned short MAGENTA = RED + BLUE;
  const unsigned short WHITE = CYAN + RED;
  unsigned short color = GREEN;
  unsigned char bytes[3];

  fd_set read_set;
  FD_ZERO(&read_set);
  FD_SET(mouse_fd, &read_set);
  FD_SET(keyboard_fd, &read_set);
  int max_fd = keyboard_fd + 1;
  int x = vinfo.xres / 2;
  int y = vinfo.yres / 2;
  int foo = read(mouse_fd, bytes, sizeof(unsigned char) * 3);

  while (select(max_fd, &read_set, NULL, NULL, NULL) >= 0) {
    if (FD_ISSET(mouse_fd, &read_set)) {
      read(mouse_fd, bytes, sizeof(unsigned char) * 3);
      
      int offset_x = (signed char) bytes[1];
      int offset_y = (signed char) bytes[2];
      int is_left_down = bytes[0] & 1;

      x += offset_x;
      y -= offset_y;

      if (x < 0) {
        x = 0;
      } else if (x >= vinfo.xres) {
        x = vinfo.xres - 1;
      }

      if (y < 0) {
        y = 0;
      } else if (y >= vinfo.yres) {
        y = vinfo.yres - 1;
      }

      if (is_left_down) {
        for (int r = y - 2; r <= y + 2; ++r) {
          for (int c = x - 2; c <= x + 2; ++c) {
            if (c >= 0 && c < vinfo.xres && r >= 0 && r < vinfo.yres) {
              framebuffer[r * vinfo.xres + c] = color;
            }
          }
        }
      }
    }

    if (FD_ISSET(keyboard_fd, &read_set)) {
      read(keyboard_fd, &event, sizeof(struct input_event));
      if (event.type == EV_KEY) {
        if (event.code == KEY_Q) {
          break;
        } else if (event.code == KEY_G) {
          color = GREEN;
        } else if (event.code == KEY_R) {
          color = RED;
        } else if (event.code == KEY_B) {
          color = BLUE;
        } else if (event.code == KEY_M) {
          color = MAGENTA;
        } else if (event.code == KEY_Y) {
          color = YELLOW;
        } else if (event.code == KEY_C) {
          color = CYAN;
        } else if (event.code == KEY_W) {
          color = WHITE;
        }
      }
    }

    FD_ZERO(&read_set);
    FD_SET(mouse_fd, &read_set);
    FD_SET(keyboard_fd, &read_set);
  }

  munmap(framebuffer, nbytes_in_framebuffer);
  close(framebuffer_fd);
  close(mouse_fd);
  close(keyboard_fd);
  return 0;
}