Rename GP_Context -> GP_Pixmap
[gfxprim.git] / libs / backends / GP_LinuxFB.c
blobe358dae38d55175add7c28649036eafa1388b448
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-2013 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 "backends/GP_LinuxFB.h"
42 struct fb_priv {
43 GP_Pixmap pixmap;
44 uint32_t bsize;
45 void *fb_mem;
47 int flags;
49 /* console fd, nr and saved data */
50 int con_fd;
51 int con_nr;
52 int last_con_nr;
53 int saved_kb_mode;
54 struct termios ts;
55 int restore_termios;
57 int fb_fd;
58 char path[];
62 * Restore console and keyboard mode to whatever was there before.
64 static void exit_kbd(struct fb_priv *fb)
66 if (ioctl(fb->con_fd, KDSKBMODE, fb->saved_kb_mode) < 0) {
67 GP_DEBUG(1, "Failed to ioctl KDSKBMODE (restore KBMODE)"
68 " /dev/tty%i: %s", fb->con_nr,
69 strerror(errno));
72 if (fb->restore_termios) {
73 if (tcsetattr(fb->con_fd, TCSANOW, &fb->ts) < 0) {
74 GP_WARN("Failed to tcsetattr() (restore termios): %s",
75 strerror(errno));
81 * Save console mode and set the mode to raw.
83 static int init_kbd(struct fb_priv *fb)
85 struct termios t;
86 int fd = fb->con_fd;
88 if (tcgetattr(fd, &fb->ts)) {
89 GP_WARN("Failed to tcgetattr(): %s", strerror(errno));
90 fb->restore_termios = 0;
91 } else {
92 fb->restore_termios = 1;
95 cfmakeraw(&t);
97 if (tcsetattr(fd, TCSANOW, &t) < 0) {
98 GP_DEBUG(1, "Failed to tcsetattr(): %s",
99 strerror(errno));
100 close(fd);
101 return -1;
104 if (ioctl(fd, KDGKBMODE, &fb->saved_kb_mode)) {
105 GP_DEBUG(1, "Failed to ioctl KDGKBMODE tty%i: %s",
106 fb->con_nr, strerror(errno));
107 close(fd);
108 return -1;
111 GP_DEBUG(2, "Previous keyboard mode was '%i'",
112 fb->saved_kb_mode);
114 if (ioctl(fd, KDSKBMODE, K_MEDIUMRAW) < 0) {
115 GP_DEBUG(1, "Failed to ioctl KDSKBMODE tty%i: %s",
116 fb->con_nr, strerror(errno));
117 close(fd);
118 return -1;
121 return 0;
125 * Allocates and switches to newly allocated console.
127 static int allocate_console(struct fb_priv *fb, int flags)
129 struct vt_stat vts;
130 int fd, nr = -1;
131 char buf[255];
132 const char *tty = "/dev/tty";
134 if (flags & GP_FB_ALLOC_CON) {
135 GP_DEBUG(1, "Allocating new console");
137 fd = open("/dev/tty1", O_WRONLY);
139 if (fd < 0) {
140 GP_DEBUG(1, "Opening console /dev/tty1 failed: %s",
141 strerror(errno));
142 return -1;
145 if (ioctl(fd, VT_OPENQRY, &nr) < 0) {
146 GP_DEBUG(1, "Failed to ioctl VT_OPENQRY /dev/tty1: %s",
147 strerror(errno));
148 close(fd);
149 return -1;
152 GP_DEBUG(1, "Has been granted tty%i", nr);
154 close(fd);
156 snprintf(buf, sizeof(buf), "/dev/tty%i", nr);
157 tty = buf;
160 fd = open(tty, O_RDWR | O_NONBLOCK);
162 if (fd < 0) {
163 GP_DEBUG(1, "Opening console %s failed: %s",
164 tty, strerror(errno));
165 return -1;
168 if (ioctl(fd, VT_GETSTATE, &vts) == 0)
169 fb->last_con_nr = vts.v_active;
170 else
171 fb->last_con_nr = -1;
173 if (flags & GP_FB_ALLOC_CON) {
174 if (ioctl(fd, VT_ACTIVATE, nr) < 0) {
175 GP_DEBUG(1, "Failed to ioctl VT_ACTIVATE %s: %s",
176 tty, strerror(errno));
177 close(fd);
178 return -1;
181 GP_DEBUG(1, "Waiting for %s to activate", tty);
183 if (ioctl(fd, VT_WAITACTIVE, nr) < 0) {
184 GP_DEBUG(1, "Failed to ioctl VT_WAITACTIVE %s: %s",
185 tty, strerror(errno));
186 close(fd);
187 return -1;
191 fb->con_nr = nr;
192 fb->con_fd = fd;
194 /* turn off blinking cursor */
195 if (ioctl(fd, KDSETMODE, KD_GRAPHICS) < 0) {
196 GP_DEBUG(1, "Failed to ioctl KDSETMODE %s: %s",
197 tty, strerror(errno));
198 close(fd);
199 return -1;
202 return 0;
205 static void free_console(struct fb_priv *fb)
207 /* restore blinking cursor */
208 if (ioctl(fb->con_fd, KDSETMODE, KD_TEXT))
209 GP_WARN("Failed to ioctl KDSETMODE (restore KDMODE)");
211 /* switch back console */
212 if (fb->last_con_nr != -1)
213 ioctl(fb->con_fd, VT_ACTIVATE, fb->last_con_nr);
215 close(fb->con_fd);
218 /* Backend API callbacks */
220 static void fb_poll(GP_Backend *self)
222 struct fb_priv *fb = GP_BACKEND_PRIV(self);
223 unsigned char buf[16];
224 int i, res;
226 res = read(fb->con_fd, buf, sizeof(buf));
228 for (i = 0; i < res; i++)
229 GP_InputDriverKBDEventPut(&self->event_queue, buf[i]);
232 static void fb_wait(GP_Backend *self)
234 struct fb_priv *fb = GP_BACKEND_PRIV(self);
236 struct pollfd fd = {.fd = fb->con_fd, .events = POLLIN, .revents = 0};
238 if (poll(&fd, 1, -1) > 0)
239 fb_poll(self);
240 else
241 GP_WARN("poll(): %s", strerror(errno));
244 static void fb_exit(GP_Backend *self)
246 struct fb_priv *fb = GP_BACKEND_PRIV(self);
248 if (fb->flags & GP_FB_SHADOW)
249 free(fb->pixmap.pixels);
251 /* unmap framebuffer */
252 munmap(fb->fb_mem, fb->bsize);
253 close(fb->fb_fd);
255 if (fb->flags & GP_FB_INPUT_KBD)
256 exit_kbd(fb);
258 free_console(fb);
260 free(self);
263 static void fb_flip_shadow(GP_Backend *self)
265 struct fb_priv *fb = GP_BACKEND_PRIV(self);
267 GP_DEBUG(2, "Flipping buffer");
269 memcpy(fb->fb_mem, fb->pixmap.pixels, fb->bsize);
272 static void fb_update_rect_shadow(GP_Backend *self, GP_Coord x0, GP_Coord y0,
273 GP_Coord x1, GP_Coord y1)
275 struct fb_priv *fb = GP_BACKEND_PRIV(self);
277 GP_DEBUG(2, "Flipping buffer");
279 size_t size = ((x1 - x0) * fb->pixmap.bpp) / 8;
281 for (;y0 <= y1; y0++) {
282 void *src = GP_PIXEL_ADDR(&fb->pixmap, x0, y0);
283 void *dst = (char*)fb->fb_mem +
284 y0 * fb->pixmap.bytes_per_row +
285 (x0 * fb->pixmap.bpp)/8;
286 memcpy(dst, src, size);
290 GP_Backend *GP_BackendLinuxFBInit(const char *path, int flags)
292 GP_Backend *backend;
293 struct fb_priv *fb;
294 struct fb_fix_screeninfo fscri;
295 struct fb_var_screeninfo vscri;
296 int fd;
298 backend = malloc(sizeof(GP_Backend) +
299 sizeof(struct fb_priv) + strlen(path) + 1);
301 if (backend == NULL)
302 return NULL;
304 fb = GP_BACKEND_PRIV(backend);
306 if (allocate_console(fb, flags))
307 goto err0;
309 if (flags & GP_FB_INPUT_KBD) {
310 if (init_kbd(fb))
311 goto err1;
314 /* open and mmap framebuffer */
315 GP_DEBUG(1, "Opening framebuffer '%s'", path);
317 fd = open(path, O_RDWR);
319 if (fd < 0) {
320 GP_DEBUG(1, "Opening framebuffer failed: %s", strerror(errno));
321 goto err2;
324 if (ioctl(fd, FBIOGET_FSCREENINFO, &fscri) < 0) {
325 GP_DEBUG(1, "Failed to ioctl FBIOGET_FSCREENINFO: %s",
326 strerror(errno));
327 goto err3;
330 if (ioctl(fd, FBIOGET_VSCREENINFO, &vscri) < 0) {
331 GP_DEBUG(1, "Failed to ioctl FBIOGET_VSCREENINFO: %s",
332 strerror(errno));
333 goto err3;
336 GP_DEBUG(1, "Have framebufer %ix%i %s %ibpp", vscri.xres, vscri.yres,
337 vscri.grayscale ? "Gray" : "RGB", vscri.bits_per_pixel);
340 * Framebuffer is grayscale.
342 if (vscri.grayscale) {
343 GP_WARN("Grayscale not implemented!");
344 goto err3;
347 enum GP_PixelType pixel_type;
348 pixel_type = GP_PixelRGBLookup(vscri.red.length, vscri.red.offset,
349 vscri.green.length, vscri.green.offset,
350 vscri.blue.length, vscri.blue.offset,
351 vscri.transp.length, vscri.transp.offset,
352 vscri.bits_per_pixel);
354 if (pixel_type == GP_PIXEL_UNKNOWN) {
355 GP_DEBUG(1, "Unknown pixel type\n");
356 goto err3;
359 if (flags & GP_FB_SHADOW) {
360 fb->pixmap.pixels = malloc(fscri.smem_len);
362 if (!fb->pixmap.pixels) {
363 GP_DEBUG(1, "Malloc failed :(");
364 goto err3;
368 fb->fb_mem = mmap(NULL, fscri.smem_len,
369 PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
371 if (fb->fb_mem == MAP_FAILED) {
372 GP_DEBUG(1, "mmaping framebuffer failed: %s", strerror(errno));
373 goto err4;
376 fb->fb_fd = fd;
377 fb->bsize = fscri.smem_len;
378 strcpy(fb->path, path);
379 fb->flags = flags;
381 if (!(flags & GP_FB_SHADOW))
382 fb->pixmap.pixels = fb->fb_mem;
384 fb->pixmap.w = vscri.xres;
385 fb->pixmap.h = vscri.yres;
387 fb->pixmap.axes_swap = 0;
388 fb->pixmap.x_swap = 0;
389 fb->pixmap.y_swap = 0;
391 fb->pixmap.bpp = vscri.bits_per_pixel;
392 fb->pixmap.bytes_per_row = fscri.line_length;
393 fb->pixmap.pixel_type = pixel_type;
395 int shadow = flags & GP_FB_SHADOW;
396 int kbd = flags & GP_FB_INPUT_KBD;
398 /* update API */
399 backend->name = "Linux FB";
400 backend->pixmap = &fb->pixmap;
401 backend->Flip = shadow ? fb_flip_shadow : NULL;
402 backend->UpdateRect = shadow ? fb_update_rect_shadow : NULL;
403 backend->Exit = fb_exit;
404 backend->SetAttributes = NULL;
405 backend->ResizeAck = NULL;
406 backend->Poll = kbd ? fb_poll : NULL;
407 backend->Wait = kbd ? fb_wait : NULL;
408 backend->fd = fb->con_fd;
409 backend->timers = NULL;
411 GP_EventQueueInit(&backend->event_queue, vscri.xres, vscri.yres, 0);
413 return backend;
414 err4:
415 if (flags & GP_FB_SHADOW)
416 free(fb->pixmap.pixels);
417 err3:
418 close(fd);
419 err2:
420 if (flags & GP_FB_INPUT_KBD)
421 exit_kbd(fb);
422 err1:
423 free_console(fb);
424 err0:
425 free(backend);
426 return NULL;