git: Add more gitignore files.
[gfxprim.git] / libs / backends / GP_LinuxFB.c
blob6d69ce43ca21db300000a5c4f669177ca1cfec44
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/ioctl.h>
26 #include <sys/mman.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <termios.h>
31 #include <unistd.h>
32 #include <poll.h>
34 #include <linux/fb.h>
35 #include <linux/kd.h>
36 #include <linux/vt.h>
38 #include "core/GP_Debug.h"
39 #include "input/GP_InputDriverKBD.h"
40 #include "GP_LinuxFB.h"
42 struct fb_priv {
43 GP_Context context;
44 uint32_t bsize;
46 int flag;
48 /* console fd, nr and saved data */
49 int con_fd;
50 int con_nr;
51 int last_con_nr;
52 int saved_kb_mode;
54 int fb_fd;
55 char path[];
59 * Allocates and switches to newly allocated console.
61 static int allocate_console(struct fb_priv *fb, int flag)
63 struct vt_stat vts;
64 int fd, nr;
65 char buf[255];
67 /* allocate and switch to new console */
68 GP_DEBUG(1, "Allocating new console");
70 fd = open("/dev/tty1", O_WRONLY);
72 if (fd < 0) {
73 GP_DEBUG(1, "Opening console /dev/tty1 failed: %s",
74 strerror(errno));
75 return -1;
78 if (ioctl(fd, VT_OPENQRY, &nr) < 0) {
79 GP_DEBUG(1, "Failed to ioctl VT_OPENQRY /dev/tty1: %s",
80 strerror(errno));
81 close(fd);
82 return -1;
85 GP_DEBUG(1, "Has been granted tty%i", nr);
87 close(fd);
89 snprintf(buf, sizeof(buf), "/dev/tty%i", nr);
90 fd = open(buf, O_RDWR | O_NONBLOCK);
92 if (fd < 0) {
93 GP_DEBUG(1, "Opening console %s failed: %s",
94 buf, strerror(errno));
95 close(fd);
96 return -1;
99 if (ioctl(fd, VT_GETSTATE, &vts) == 0)
100 fb->last_con_nr = vts.v_active;
101 else
102 fb->last_con_nr = -1;
104 if (ioctl(fd, VT_ACTIVATE, nr) < 0) {
105 GP_DEBUG(1, "Failed to ioctl VT_ACTIVATE %s: %s",
106 buf, strerror(errno));
107 close(fd);
108 return -1;
111 GP_DEBUG(1, "Waiting for tty%i to activate", nr);
113 if (ioctl(fd, VT_WAITACTIVE, nr) < 0) {
114 GP_DEBUG(1, "Failed to ioctl VT_WAITACTIVE %s: %s",
115 buf, strerror(errno));
116 close(fd);
117 return -1;
120 /* turn off blinking cursor */
121 if (ioctl(fd, KDSETMODE, KD_GRAPHICS) < 0) {
122 GP_DEBUG(1, "Failed to ioctl KDSETMODE %s: %s",
123 buf, strerror(errno));
124 close(fd);
125 return -1;
128 /* set keyboard to raw mode */
129 if (flag) {
130 struct termios t;
131 cfmakeraw(&t);
133 if (tcsetattr(fd, TCSANOW, &t) < 0) {
134 GP_DEBUG(1, "Failed to tcsetattr(): %s",
135 strerror(errno));
136 close(fd);
137 return -1;
140 if (ioctl(fd, KDGKBMODE, &fb->saved_kb_mode)) {
141 GP_DEBUG(1, "Failed to ioctl KDGKBMODE %s: %s",
142 buf, strerror(errno));
143 close(fd);
144 return -1;
147 GP_DEBUG(2, "Previous keyboard mode was '%i'",
148 fb->saved_kb_mode);
150 if (ioctl(fd, KDSKBMODE, K_MEDIUMRAW) < 0) {
151 GP_DEBUG(1, "Failed to ioctl KDSKBMODE %s: %s",
152 buf, strerror(errno));
153 close(fd);
154 return -1;
158 fb->con_nr = nr;
159 fb->con_fd = fd;
161 return 0;
164 /* Backend API callbacks */
166 static void fb_flip_noop(GP_Backend *self __attribute__((unused)))
171 static void fb_update_rect_noop(GP_Backend *self __attribute__((unused)),
172 GP_Coord x1 __attribute__((unused)),
173 GP_Coord y1 __attribute__((unused)),
174 GP_Coord x2 __attribute__((unused)),
175 GP_Coord y2 __attribute__((unused)))
180 static void fb_poll(GP_Backend *self)
182 struct fb_priv *fb = GP_BACKEND_PRIV(self);
183 unsigned char buf[16];
184 int i, res;
186 res = read(fb->con_fd, buf, sizeof(buf));
188 for (i = 0; i < res; i++)
189 GP_InputDriverKBDEventPut(buf[i]);
192 static void fb_wait(GP_Backend *self)
194 struct fb_priv *fb = GP_BACKEND_PRIV(self);
196 struct pollfd fd = {.fd = fb->con_fd, .events = POLLIN, .revents = 0};
198 if (poll(&fd, 1, -1) > 0)
199 fb_poll(self);
200 else
201 GP_WARN("poll(): %s", strerror(errno));
204 static void fb_exit(GP_Backend *self)
206 struct fb_priv *fb = GP_BACKEND_PRIV(self);
208 /* unmap framebuffer */
209 munmap(fb->context.pixels, fb->bsize);
210 close(fb->fb_fd);
212 /* reset keyboard */
213 ioctl(fb->con_fd, KDSETMODE, KD_TEXT);
215 /* restore keyboard mode */
216 if (fb->flag) {
217 if (ioctl(fb->con_fd, KDSKBMODE, fb->saved_kb_mode) < 0) {
218 GP_DEBUG(1, "Failed to ioctl KDSKBMODE (restore KBMODE)"
219 " /dev/tty%i: %s", fb->con_nr, strerror(errno));
223 /* switch back console */
224 if (fb->last_con_nr != -1)
225 ioctl(fb->con_fd, VT_ACTIVATE, fb->last_con_nr);
227 close(fb->con_fd);
228 free(self);
231 GP_Backend *GP_BackendLinuxFBInit(const char *path, int flag)
233 GP_Backend *backend;
234 struct fb_priv *fb;
235 struct fb_fix_screeninfo fscri;
236 struct fb_var_screeninfo vscri;
237 int fd;
239 backend = malloc(sizeof(GP_Backend) +
240 sizeof(struct fb_priv) + strlen(path) + 1);
242 if (backend == NULL)
243 return NULL;
245 fb = GP_BACKEND_PRIV(backend);
247 if (allocate_console(fb, flag))
248 goto err1;
250 /* open and mmap framebuffer */
251 GP_DEBUG(1, "Opening framebuffer '%s'", path);
253 fd = open(path, O_RDWR);
255 if (fd < 0) {
256 GP_DEBUG(1, "Opening framebuffer failed: %s", strerror(errno));
257 goto err2;
260 if (ioctl(fd, FBIOGET_FSCREENINFO, &fscri) < 0) {
261 GP_DEBUG(1, "Failed to ioctl FBIOGET_FSCREENINFO: %s",
262 strerror(errno));
263 goto err3;
266 if (ioctl(fd, FBIOGET_VSCREENINFO, &vscri) < 0) {
267 GP_DEBUG(1, "Failed to ioctl FBIOGET_VSCREENINFO: %s",
268 strerror(errno));
269 goto err3;
272 GP_DEBUG(1, "Have framebufer %ix%i %s %ibpp", vscri.xres, vscri.yres,
273 vscri.grayscale ? "Gray" : "RGB", vscri.bits_per_pixel);
276 * Framebuffer is grayscale.
278 if (vscri.grayscale) {
279 //TODO
280 goto err3;
283 enum GP_PixelType pixel_type;
284 pixel_type = GP_PixelRGBLookup(vscri.red.length, vscri.red.offset,
285 vscri.green.length, vscri.green.offset,
286 vscri.blue.length, vscri.blue.offset,
287 vscri.transp.length, vscri.transp.offset,
288 vscri.bits_per_pixel);
290 if (pixel_type == GP_PIXEL_UNKNOWN) {
291 GP_DEBUG(1, "Unknown pixel type\n");
292 goto err3;
295 fb->context.pixels = mmap(NULL, fscri.smem_len,
296 PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
297 fd, 0);
299 if (fb->context.pixels == MAP_FAILED) {
300 GP_DEBUG(1, "mmaping framebuffer failed: %s", strerror(errno));
301 goto err3;
304 fb->fb_fd = fd;
305 fb->bsize = fscri.smem_len;
306 strcpy(fb->path, path);
307 fb->flag = flag;
309 fb->context.w = vscri.xres;
310 fb->context.h = vscri.yres;
312 fb->context.axes_swap = 0;
313 fb->context.x_swap = 0;
314 fb->context.y_swap = 0;
316 fb->context.bpp = vscri.bits_per_pixel;
317 fb->context.bytes_per_row = fscri.line_length;
318 fb->context.pixel_type = pixel_type;
320 /* update API */
321 backend->name = "Linux FB";
322 backend->context = &fb->context;
323 backend->Flip = fb_flip_noop;
324 backend->UpdateRect = fb_update_rect_noop;
325 backend->Exit = fb_exit;
326 backend->SetAttributes = NULL;
327 backend->Poll = flag ? fb_poll : NULL;
328 backend->Wait = flag ? fb_wait : NULL;
329 backend->fd = fb->con_fd;
331 return backend;
332 err3:
333 close(fd);
334 err2:
335 close(fb->con_fd);
337 /* reset keyboard */
338 ioctl(fb->con_fd, KDSETMODE, KD_TEXT);
340 /* switch back console */
341 if (fb->last_con_nr != -1)
342 ioctl(fb->con_fd, VT_ACTIVATE, fb->last_con_nr);
343 err1:
344 free(backend);
345 return NULL;