fbdev backend works
[retrobench] / src / fbdev / fbgfx.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8 #include <sys/mman.h>
9 #include <linux/fb.h>
10 #include "fbgfx.h"
11
12 static void cleanup(void);
13
14 static int fd = -1;
15 static void *vmem;
16 static int vmem_size;
17 static struct fb_fix_screeninfo finfo;
18 static struct fb_var_screeninfo vinfo;
19 static struct fb_var_screeninfo saved_vinfo;
20 static int saved_vinfo_valid;
21 static struct fbgfx_vmode curvm;
22
23 static int init(void)
24 {
25         if(fd >= 0) return 0;
26
27         if((fd = open("/dev/fb0", O_RDWR)) == -1) {
28                 fprintf(stderr, "failed to open framebuffer device\n");
29                 return -1;
30         }
31
32         ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
33
34         curvm.pitch = finfo.line_length;
35
36         atexit(cleanup);
37         return 0;
38 }
39
40 static void cleanup(void)
41 {
42         if(vmem) {
43                 munmap(vmem, vmem_size);
44         }
45         if(fd != -1) {
46                 close(fd);
47         }
48 }
49
50 void *fbgfx_set_video_mode(int x, int y, int depth)
51 {
52         struct fb_var_screeninfo new_vinfo;
53
54         if(init() == -1) {
55                 return 0;
56         }
57
58         ioctl(fd, FBIOGET_VSCREENINFO, &new_vinfo);
59         new_vinfo.xres = x;
60         new_vinfo.yres = y;
61         new_vinfo.bits_per_pixel = depth;
62
63         if(ioctl(fd, FBIOPUT_VSCREENINFO, &new_vinfo) == -1) {
64                 fprintf(stderr, "failed to set video mode %dx%d %dbpp: %s\n", x, y, depth, strerror(errno));
65                 return 0;
66         }
67
68         if(vmem) {
69                 munmap(vmem, vmem_size);
70                 vmem = 0;
71                 vmem_size = 0;
72         }
73
74         return fbgfx_get_video_mode(0, 0, 0);
75 }
76
77 void *fbgfx_get_video_mode(int *xptr, int *yptr, int *depthptr)
78 {
79         if(init() == -1) {
80                 return 0;
81         }
82
83         ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
84         /*vmem_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;*/
85         vmem_size = finfo.smem_len;
86
87         curvm.width = vinfo.xres;
88         curvm.height = vinfo.yres;
89         curvm.bpp = vinfo.bits_per_pixel;
90         curvm.fbsize = vmem_size;
91         curvm.rshift = vinfo.red.offset;
92         curvm.gshift = vinfo.green.offset;
93         curvm.bshift = vinfo.blue.offset;
94         curvm.rmask = (0xffffffff >> (32 - vinfo.red.length)) << curvm.rshift;
95         curvm.gmask = (0xffffffff >> (32 - vinfo.green.length)) << curvm.gshift;
96         curvm.bmask = (0xffffffff >> (32 - vinfo.blue.length)) << curvm.bshift;
97
98         if(!vmem) {
99                 if((vmem = mmap(0, vmem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == (void*)-1) {
100                         fprintf(stderr, "failed to map video memory\n");
101                         vmem = 0;
102                         return 0;
103                 }
104         }
105         if(xptr) *xptr = vinfo.xres;
106         if(yptr) *yptr = vinfo.yres;
107         if(depthptr) *depthptr = vinfo.bits_per_pixel;
108         return vmem;
109 }
110
111 void fbgfx_save_video_mode(void)
112 {
113         if(init() == -1) {
114                 return;
115         }
116
117         if(ioctl(fd, FBIOGET_VSCREENINFO, &saved_vinfo) == -1) {
118                 return;
119         }
120         saved_vinfo_valid = 1;
121 }
122
123 void fbgfx_restore_video_mode(void)
124 {
125         if(init() == -1 || !saved_vinfo_valid) {
126                 return;
127         }
128         ioctl(fd, FBIOPUT_VSCREENINFO, &saved_vinfo);
129 }
130
131 struct fbgfx_vmode *fbgfx_video_mode_info(void)
132 {
133         return &curvm;
134 }