10

Goal: I'm writing a very simple image viewer for framebuffer /dev/fb0 (something like fbi).

Current state:

  1. My software takes the pixel resolution from /sys/class/graphics/fb0/virtual_size (such as 1920,1080).
  2. And then (for each row) it will write 4 bytes (BGRA) for each 1920 row-pixels (total 4x1920=7680 bytes) to /dev/fb0. This works perfectly fine on my one laptop with a 1920x1080 resolution.
  3. More precisely: setting a pixel at y-row x-col => arr[y * 1920 * 4 + x * 4 + channel] where the value channel is 0,1,2,3 (for B, G, R, and A, respectively).

Problem:

When I try the same software on my old laptop with (/sys/.../virtual_size -> 1366,768 resolution), the image is not shown correctly (bit skewed). So I played around the pixel-width value and found out the value was 1376 (not 1366).

Questions:

  1. Where do these 10 extra bytes come from?
  2. And, how can I get this value of 10 extra bytes on different machines (automatically, not manually tuning it)?
  3. Why do some machines need these extra 10 bytes, when some machines don't need them?
0

2 Answers 2

13

Programmatically, to retrieve information about a framebuffer you should use the FBIOGET_FSCREENINFO and FBIOGET_VSCREENINFO ioctls:

#include <fcntl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>

int main(int argc, char **argv) {
  struct fb_fix_screeninfo fix;
  struct fb_var_screeninfo var;

  int fb = open("/dev/fb0", O_RDWR);
  if (fb < 0) {
    perror("Opening fb0");
    exit(1);
  }

  if (ioctl(fb, FBIOGET_FSCREENINFO, &fix) != 0) {
    perror("FSCREENINFO");
    exit(1);
  }

  if (ioctl(fb, FBIOGET_VSCREENINFO, &var) != 0) {
    perror("VSCREENINFO");
    exit(1);
  }

  printf("Line length: %ld\n", fix.line_length);
  printf("Visible resolution: %ldx%ld\n", var.xres, var.yres);
  printf("Virtual resolution: %ldx%ld\n", var.xres_virtual, var.yres_virtual);
}

line_length gives you the line stride.

5
  • 1
    I'm amazed there's a specific ioctl for that! Commented Jan 2, 2023 at 18:27
  • 4
    @MarcusMüller ioctls are specific to the type of device. Almost every device has a bunch of very specific ioctls you've never heard of. Every device feature that isn't a read or write or mmap is an ioctl.
    – user20574
    Commented Jan 2, 2023 at 21:47
  • @user253751 I'm somewhat painfully aware of the wealth of ioctls in Linux, it's just that these are properties I would have expected to be well enough represented through the sysfs API to the kernel Commented Jan 2, 2023 at 21:54
  • 2
    @MarcusMüller sysfs tells you generally about the things in the system; ioctl tells you about the thing you have open right now. Imagine needing to reverse-engineer the name of your framebuffer then go and open it in sysfs that may or may not be mounted. (Some APIs are like that and that's terrible)
    – user20574
    Commented Jan 2, 2023 at 21:58
  • 1
    I see that point, especially since fb might be useful quite early in userland booting Commented Jan 2, 2023 at 22:01
9

The row stride does not have to be the same as the width; on your 1920-wide screen your software just worked "by accident" (not much of an accident, strides are typically chosen for hardware alignment requirements, and 1920 is already divisible by 16, which is a common alignment requirement).

Not being familiar with the good ole Linux framebuffer infrastructure, I can't directly tell you how to do it, but you will need to figure out the stride.

Either Linux just documents that rows are aligned by multiples of 16, do that you always need to round up your width to the next multiple of that, or /sys/class/graphics/fb0/ contains a different virtual file that defined the stride, or the row alignment.

2
  • Thanks for the reply, it seems the stride value can be extracted from /sys/class/graphics/fb0/stride. Do you know why it's need to be multiples of 16 (just out of curiosity)?
    – Garid Z.
    Commented Jan 2, 2023 at 9:12
  • 2
    It boils down to various hardware elements (the graphics card's memory units, the DMA controller used to transfer data between RAM and GPU, and the CPU SIMD units used to calculate pixels) having needs for addresses to be "simple"; when something is 16 bit aligned, you can just worked with the fifth bit upwards. Commented Jan 2, 2023 at 10:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .