CS 352 Lecture 31 – Framebuffer
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!
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; }