Thread: Linux Framebuffer Transparency

  1. #1
    Registered User
    Join Date
    Apr 2019
    Posts
    121

    Linux Framebuffer Transparency

    Hi,

    I have been messing with the framebuffer on Linux with good results. But then I ran into a snag when trying to use the transparency field. I don't know if I need to enable an alpha channel for the framebuffer, I can't seem to find any references to this problem. But the following code doesn't seem to change the transparency at all.

    Code:
    // Declare includes.
    #include <errno.h>
    #include <fcntl.h>
    #include <libgen.h>
    #include <linux/fb.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ioctl.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    // Declare defines.
    #define FRAMEBUFFER            "/dev/fb0"
    #define PROGRAM_NAME_SIZE     50
    #define SIZE_OF_MARK           4
    
    // Declare stuctures.
    struct _colour
    {
        int blue;
        int green;
        int red;
        int transparency;
    };
    
    struct _info
    {
        int fbfd;
        char *fbp;
        long screensize;
        struct fb_fix_screeninfo finfo;
        struct fb_var_screeninfo vinfo;
    };
    
    // Declare global variables.
    char program_name[PROGRAM_NAME_SIZE + 1] = {0};
    
    // Declare function prototypes.
    void framebuffer_writing (void);
    void get_info (struct _info *info);
    void mark_row (int x, int y, int length, struct _info *info, struct _colour *colour);
    
    int main (int argc, char *argv[])
    {
    // Get program name for error reporting.
        strcpy(program_name, basename(argv[0]));
    
    // Check number of arguments.
        if(argc != 1)
        {
            fprintf(stderr, "Usage: %s\n", program_name);
            exit(EXIT_FAILURE);
        }
    
    // Run process.
        framebuffer_writing();
    
    // Exit cleanly.
        exit(EXIT_SUCCESS);
    }
    
    void framebuffer_writing (void)
    {
    // Declare variables.
        struct _info info = {0};
        struct _colour red = {0x00, 0x00, 0x87, 0x00};
    
    // Get the framebuffer file descriptor.
        get_info(&info);
    
    // Make gradient row.
        for(int a = 0; a < 255; a++, red.transparency++)
            mark_row(0 + (a * SIZE_OF_MARK), 300, 1, &info, &red);
    
    // Used to show the transparency value was changed.
        printf("Transparency = %d\n", red.transparency);
    
    // Unmap the framebuffer.
        munmap(info.fbp, info.screensize);
    // Close framebuffer file descriptor.
        close(info.fbfd);
    }
    
    void get_info (struct _info *info)
    {
    // Open the framebuffer for reading and writing.
        if(((*info).fbfd = open(FRAMEBUFFER, O_RDWR)) == -1)
        {
            fprintf(stderr, "%s: %s error: open failed (%s) (%s)\n", program_name, __func__, strerror(errno), FRAMEBUFFER);
            exit(EXIT_FAILURE);
        }
    
    // Get fixed screen information.
        if(ioctl((*info).fbfd, FBIOGET_FSCREENINFO, &(*info).finfo) == -1)
        {
            fprintf(stderr, "%s: %s error: ioctl failed (%s) (Error reading fixed information)\n", program_name, __func__, strerror(errno));
            exit(EXIT_FAILURE);
        }
    
    // Get variable screen information.
        if(ioctl((*info).fbfd, FBIOGET_VSCREENINFO, &(*info).vinfo) == -1)
        {
            fprintf(stderr, "%s: %s error: ioctl failed (%s) (Error reading variable information)\n", program_name, __func__, strerror(errno));
            exit(EXIT_FAILURE);
        }
    
    // Figure out the size of the screen in bytes.
        (*info).screensize = (*info).vinfo.xres * (*info).vinfo.yres * (*info).vinfo.bits_per_pixel / 8;
    
    // Map the device to memory.
        if (((*info).fbp = (char *)mmap(0, (*info).screensize, PROT_READ | PROT_WRITE, MAP_SHARED, (*info).fbfd, 0)) == MAP_FAILED)
        {
            fprintf(stderr, "%s: %s error: mmap failed (%s)\n", program_name, __func__, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
    
    void mark_row (int x, int y, int length, struct _info *info, struct _colour *colour)
    {
    // Declare variables.
        long location = {0};
    
    // Fill in a box on the framebuffer.
        for(int a = 0; a < SIZE_OF_MARK; a++)
            for(int b = 0; b < (SIZE_OF_MARK * length); b++)
            {
    // Determine location of the pixel placement.
                location = (b + x + (*info).vinfo.xoffset) * ((*info).vinfo.bits_per_pixel / 8) + (a + y + (*info).vinfo.yoffset) * (*info).finfo.line_length;
    // Set each colour for the pixel in question.
                *((*info).fbp + location) = (*colour).blue;
                *((*info).fbp + location + 1) = (*colour).green;
                *((*info).fbp + location + 2) = (*colour).red;
                *((*info).fbp + location + 3) = (*colour).transparency;
            }
    }
    Can someone please give a little insight into why the transparency might not be changing for me?

    Thanx.

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    The following says he's never seen transparency implemented.
    https://kevinboone.me/linuxfbc.html

    Also, don't say (*info).whatever. Instead say info->whatever.
    And don't say *(a + i) but instead say a[i].
    That's what those notations are for.

    An error routine would be useful.
    Code:
    #include <stdarg.h>
     
    const char *program_name; // don't need to copy the string
    // in main: program_name = argv[0];
     
    void error(const char *fmt, ...) {
        va_list args;
        va_start(args, fmt);
        fprintf(stderr, "%s: ", program_name);
        vfprintf(stderr, fmt, args);
        va_end(args);
        exit(EXIT_FAILURE);
    }
     
    // Example usage:
        if ((info->fbfd = open(FRAMEBUFFER, O_RDWR)) == -1)
            error("%s error: open failed (%s) (%s)\n",
                __func__, strerror(errno), FRAMEBUFFER);
    A little inaccuracy saves tons of explanation. - H.H. Munro

  3. #3
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    277
    If nothing else, transparency could be implemented at a higher level. At the expense of having to use a second buffer of course. Also seems a bit of a drawback, what with the need for sudo privileges to run such an application.

  4. #4
    Registered User
    Join Date
    Apr 2019
    Posts
    121
    Quote Originally Posted by john.c View Post
    The following says he's never seen transparency implemented.
    https://kevinboone.me/linuxfbc.html
    Very disappointing to hear. But ty for finding that.

    Quote Originally Posted by john.c View Post
    Also, don't say (*info).whatever. Instead say info->whatever.
    And don't say *(a + i) but instead say a[i].
    I will try to learn more on that. I don't have a lot of experience with '->' and '.'. It usually goes...

    1) Write a statement involving a structure.
    2) use '->' reference a struct variable.
    3) Get an error regarding the use of '->'.
    4) Change '->' to '.'.
    5) Error disappears.

    Basically if one doesn't work, the other one does. But you're right, I should learn to do it properly.

    Ty for your time in correcting me. I really like the error handler for sure.

  5. #5
    Registered User
    Join Date
    Apr 2019
    Posts
    121
    Quote Originally Posted by Sir Galahad View Post
    Also seems a bit of a drawback, what with the need for sudo privileges to run such an application.
    I don't require sudo privileges. Normally I start my project with a base template that includes all header files (commented out), a main routine (for checking argument and privileges), and a starting function (in case I just want to dump some code and quickly run it.

    Anyway, I noticed when running my program, that the following was still commented out, meaning I don't need elevated privileges.

    Code:
    /*
    // Check that user is root or that sudo is used.
        if(geteuid() != 0)
        {
            fprintf(stderr, "%s: must be run as root or with 'sudo'\n", program_name);
            exit(EXIT_FAILURE);
        }
    */
    Edit: I don't know if adding my username to the group 'video' made a difference or not though.
    Last edited by Yonut; 04-14-2023 at 01:10 PM. Reason: Afterthought

  6. #6
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    277
    Quote Originally Posted by Yonut View Post
    I don't require sudo privileges. Normally I start my project with a base template that includes all header files (commented out), a main routine (for checking argument and privileges), and a starting function (in case I just want to dump some code and quickly run it.

    Anyway, I noticed when running my program, that the following was still commented out, meaning I don't need elevated privileges.

    Code:
    /*
    // Check that user is root or that sudo is used.
        if(geteuid() != 0)
        {
            fprintf(stderr, "%s: must be run as root or with 'sudo'\n", program_name);
            exit(EXIT_FAILURE);
        }
    */
    Edit: I don't know if adding my username to the group 'video' made a difference or not though.

    Well sure if you're running things in "developer mode" (so to speak), why not? I was thinking more in terms of whether or not framebuffers might be a good route for "general application development".

    And yes the "video" group is naturally granted special privileges not available to the average user.

    I did notice that the program you've posted doesn't seem to write anything to the screen. Perchance do you have a more complete working example?

  7. #7
    Registered User
    Join Date
    Apr 2019
    Posts
    121
    Quote Originally Posted by Sir Galahad View Post
    I did notice that the program you've posted doesn't seem to write anything to the screen. Perchance do you have a more complete working example?
    This is a simple example of basic drawing, it should work from the console. When you use xwindows, it won't show the framebuffer I use in my program. Dunno why. But if you can exit xwindows or wayland and go back to the console (tty), it should display a red line. If you're on a tty and it isn't visible, maybe increase the following:
    Code:
    #define SIZE_OF_MARK           4
    Basically this all started from wanting to recreate the digital front cover of the "IT Crowd" dvd. I've got Moss all mapped out, but wanted to add the gradient background. Here is what I'm talking about:
    Linux Framebuffer Transparency-3y5807uroqcgexh8ryxgtquzwr4-jpg

  8. #8
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    277
    Quote Originally Posted by Yonut View Post
    This is a simple example of basic drawing, it should work from the console. When you use xwindows, it won't show the framebuffer I use in my program. Dunno why. But if you can exit xwindows or wayland and go back to the console (tty), it should display a red line.
    Ah right, sudo chvt 1 to switch to pure-tty. Program displays just fine. Then sudo chvt 7 to get back to Xorg system. (CTL-ALT-F1/CTL-ALT-F7 also works).


    Quote Originally Posted by Yonut View Post
    Basically this all started from wanting to recreate the digital front cover of the "IT Crowd" dvd. I've got Moss all mapped out, but wanted to add the gradient background. Here is what I'm talking about:
    Again, just do the transparency masking "by hand". Long ago I put together a simple graphics library that did just that, drawing basic shapes with anti-aliasing, alpha channel, etc, all done directly on a chunk of memory. Pretty straight-forward stuff actually.

  9. #9
    Registered User
    Join Date
    Mar 2023
    Posts
    33
    What is meant here when you folks discuss "transparency"? Aren't we already discussing open source?

  10. #10
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    Quote Originally Posted by C_me_run View Post
    What is meant here when you folks discuss "transparency"? Aren't we already discussing open source?
    It's this kind of transparency - Alpha compositing - Wikipedia
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  11. #11
    Registered User
    Join Date
    Apr 2019
    Posts
    121
    Quote Originally Posted by john.c View Post
    Code:
    const char *program_name; // don't need to copy the string
    // in main: program_name = argv[0];
    Sorry, I just noticed this. The reason why I use `basename` on the program name (or argv[0]) is because argv[0] depends on where and how the program is called.

    Code:
    ie:
    $ run_my_program (in $PATH)    -    argv[0] = "run_my_program"
    $ ./run_my_program             -    argv[0] = "./run_my_program"
    $ /home/user/run_my_program    -    argv[0] = "/home/user/run_my_program"
    I don't care where the program resides, when error reporting. So I strip it first thing. It's just cleaner to my eyes. And like I said, this is all in a template, I never have to type anything other than my desire to use `program_name` when I need to.

  12. #12
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    277
    Quote Originally Posted by Yonut View Post
    Sorry, I just noticed this. The reason why I use `basename` on the program name (or argv[0]) is because argv[0] depends on where and how the program is called.

    Code:
    ie:
    $ run_my_program (in $PATH)    -    argv[0] = "run_my_program"
    $ ./run_my_program             -    argv[0] = "./run_my_program"
    $ /home/user/run_my_program    -    argv[0] = "/home/user/run_my_program"
    I don't care where the program resides, when error reporting. So I strip it first thing. It's just cleaner to my eyes. And like I said, this is all in a template, I never have to type anything other than my desire to use `program_name` when I need to.
    FWIW, unless you intend to modify it at some point, the `program_name` buffer isn't even necessary. The pointer returned by `basename` can be stored in a variable. No allocation needed.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. framebuffer writing a new clut
    By carl mckenzie in forum C++ Programming
    Replies: 1
    Last Post: 06-13-2011, 10:59 AM
  2. Linux framebuffer
    By thinice16 in forum C Programming
    Replies: 4
    Last Post: 07-02-2008, 08:25 PM
  3. Display a string on Linux Framebuffer console
    By chandra80 in forum Linux Programming
    Replies: 2
    Last Post: 11-08-2005, 02:43 PM
  4. framebuffer implementation
    By v6sa in forum Linux Programming
    Replies: 1
    Last Post: 11-03-2005, 11:02 AM
  5. Linux framebuffer; display an image
    By bludstayne in forum Tech Board
    Replies: 1
    Last Post: 03-29-2004, 02:50 AM

Tags for this Thread