added license GPL
[winnie] / src / fbdev / gfx.cc
1 /*
2 winnie - an experimental window system
3
4 Copyright (C) 2013 Eleni Maria Stea
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19 Author: Eleni Maria Stea <elene.mst@gmail.com>
20 */
21
22 #ifdef WINNIE_FBDEV
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <fcntl.h>
30 #include <sys/ioctl.h>
31 #include <sys/mman.h>
32 #include <sys/time.h>
33 #include <unistd.h>
34
35 #include <linux/fb.h>
36
37 #include "gfx.h"
38 #include "shalloc.h"
39
40 #define FRAMEBUFFER_SIZE(xsz, ysz, bpp) ((xsz) * (ysz) * (bpp) / CHAR_BIT)
41
42 static unsigned char *framebuffer;
43 static int dev_fd;
44 static int rgb_order[3];
45
46 struct Graphics {
47         Rect screen_rect;
48         Rect clipping_rect;
49         int color_depth;
50         Pixmap *pixmap;
51 };
52
53 static Graphics *gfx;
54
55 bool init_gfx()
56 {
57         if(!(gfx = (Graphics*)sh_malloc(sizeof *gfx))) {
58                 return false;
59         }
60
61         dev_fd = -1;
62
63         if((dev_fd = open("/dev/fb0", O_RDWR)) == -1) {
64                 fprintf(stderr, "Cannot open /dev/fb0 : %s\n", strerror(errno));
65                 return false;
66         }
67
68         fb_var_screeninfo sinfo;
69         if(ioctl(dev_fd, FBIOGET_VSCREENINFO, &sinfo) == -1) {
70                 close(dev_fd);
71                 dev_fd = -1;
72                 fprintf(stderr, "Unable to get screen info : %s\n", strerror(errno));
73                 return false;
74         }
75
76         printf("width : %d height : %d\n : bpp : %d\n", sinfo.xres, sinfo.yres, sinfo.bits_per_pixel);
77         printf("virtual w: %d virtual h: %d\n", sinfo.xres_virtual, sinfo.yres_virtual);
78
79         gfx->screen_rect.x = gfx->screen_rect.y = 0;
80         gfx->screen_rect.width = sinfo.xres_virtual;
81         gfx->screen_rect.height = sinfo.yres_virtual;
82         gfx->color_depth = sinfo.bits_per_pixel;
83
84         rgb_order[0] = sinfo.red.offset / 8;
85         rgb_order[1] = sinfo.green.offset / 8;
86         rgb_order[2] = sinfo.blue.offset / 8;
87
88         set_clipping_rect(gfx->screen_rect);
89
90         int sz = FRAMEBUFFER_SIZE(gfx->screen_rect.width, gfx->screen_rect.height, gfx->color_depth);
91         framebuffer = (unsigned char*)mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, 0);
92
93         if(framebuffer == (void*)-1) {
94                 close(dev_fd);
95                 dev_fd = -1;
96                 fprintf(stderr, "Cannot map the framebuffer to memory : %s\n", strerror(errno));
97                 return false;
98         }
99
100 // TODO: uncomment when I find how to use intelfb instead of i915 GRRRR.-       
101         fb_vblank vblank;
102         if(ioctl(dev_fd, FBIOGET_VBLANK, &vblank) == -1) {
103 //              fprintf(stderr, "FBIOGET_VBLANK error: %s\n", strerror(errno));
104         }
105 /*      
106         else {
107                 printf("flags: %x\n", vblank.flags);
108                 printf("count: %d\n", vblank.count);
109                 printf("beam position: %d, %d\n", vblank.hcount, vblank.vcount);
110         }
111 */
112
113         if(!(gfx->pixmap = (Pixmap*)sh_malloc(sizeof(Pixmap)))) {
114                 fprintf(stderr, "Failed to allocate pixmap.\n");
115                 return false;
116         }
117
118         gfx->pixmap->width = gfx->screen_rect.width;
119         gfx->pixmap->height = gfx->screen_rect.height;
120
121         int fbsize = gfx->pixmap->width * gfx->pixmap->height * gfx->color_depth / 8;
122         if(!(gfx->pixmap->pixels = (unsigned char*)sh_malloc(fbsize))) {
123                 fprintf(stderr, "failed to allocate the pixmap framebuffer.\n");
124                 return false;
125         }
126
127         return true;
128 }
129
130 void destroy_gfx()
131 {
132         clear_screen(0, 0, 0);
133         gfx_update(gfx->screen_rect);
134
135         if(dev_fd != -1) {
136                 close(dev_fd);
137         }
138
139         dev_fd = -1;
140
141         munmap(framebuffer, FRAMEBUFFER_SIZE(gfx->screen_rect.width, gfx->screen_rect.height, gfx->color_depth));
142         framebuffer = 0;
143
144         sh_free(gfx->pixmap->pixels);
145         gfx->pixmap->pixels = 0;
146         sh_free(gfx->pixmap);
147         sh_free(gfx);
148 }
149
150 unsigned char *get_framebuffer()
151 {
152         return gfx->pixmap->pixels;
153 }
154
155 Pixmap *get_framebuffer_pixmap()
156 {
157         return gfx->pixmap;
158 }
159
160 Rect get_screen_size()
161 {
162         return gfx->screen_rect;
163 }
164
165 int get_color_depth()
166 {
167         return gfx->color_depth;
168 }
169
170 void set_clipping_rect(const Rect &rect)
171 {
172         gfx->clipping_rect = rect_intersection(rect, get_screen_size());
173 }
174
175 const Rect &get_clipping_rect()
176 {
177         return gfx->clipping_rect;
178 }
179
180 void set_cursor_visibility(bool visible)
181 {
182         fb_cursor curs;
183         curs.enable = visible ? 1 : 0;
184
185         if(ioctl(dev_fd, FBIO_CURSOR, &curs) == -1) {
186                 fprintf(stderr, "Cannot toggle cursor visibility : %s\n", strerror(errno));
187         }
188 }
189
190 void gfx_update(const Rect &upd_rect)
191 {
192         Rect rect = rect_intersection(upd_rect, gfx->screen_rect);
193         unsigned char *sptr = gfx->pixmap->pixels + (rect.y * gfx->screen_rect.width + rect.x) * 4;
194         unsigned char *dptr = framebuffer + (rect.y * gfx->screen_rect.width + rect.x) * 4;
195
196         for(int i=0; i<rect.height; i++) {
197                 memcpy(dptr, sptr, rect.width * 4);
198                 sptr += gfx->screen_rect.width * 4;
199                 dptr += gfx->screen_rect.width * 4;
200         }
201 }
202
203 void wait_vsync()
204 {
205         unsigned long arg = 0;
206         if(ioctl(dev_fd, FBIO_WAITFORVSYNC, &arg) == -1) {
207 //              printf("ioctl error %s\n", strerror(errno));
208         }
209 }
210
211 void get_rgb_order(int *r, int *g, int *b)
212 {
213         *r = rgb_order[0];
214         *g = rgb_order[1];
215         *b = rgb_order[2];
216 }
217
218 #endif // WINNIE_FBDEV