1 /*****************************************************************************
2 * This file is part of gfxprim library. *
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. *
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. *
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 *
19 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
23 #include <sys/types.h>
25 #include <sys/ioctl.h>
38 #include "core/GP_Debug.h"
39 #include "input/GP_InputDriverKBD.h"
40 #include "backends/GP_LinuxFB.h"
49 /* console fd, nr and saved data */
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
,
72 if (fb
->restore_termios
) {
73 if (tcsetattr(fb
->con_fd
, TCSANOW
, &fb
->ts
) < 0) {
74 GP_WARN("Failed to tcsetattr() (restore termios): %s",
81 * Save console mode and set the mode to raw.
83 static int init_kbd(struct fb_priv
*fb
)
88 if (tcgetattr(fd
, &fb
->ts
)) {
89 GP_WARN("Failed to tcgetattr(): %s", strerror(errno
));
90 fb
->restore_termios
= 0;
92 fb
->restore_termios
= 1;
97 if (tcsetattr(fd
, TCSANOW
, &t
) < 0) {
98 GP_DEBUG(1, "Failed to tcsetattr(): %s",
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
));
111 GP_DEBUG(2, "Previous keyboard mode was '%i'",
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
));
125 * Allocates and switches to newly allocated console.
127 static int allocate_console(struct fb_priv
*fb
, int flags
)
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
);
140 GP_DEBUG(1, "Opening console /dev/tty1 failed: %s",
145 if (ioctl(fd
, VT_OPENQRY
, &nr
) < 0) {
146 GP_DEBUG(1, "Failed to ioctl VT_OPENQRY /dev/tty1: %s",
152 GP_DEBUG(1, "Has been granted tty%i", nr
);
156 snprintf(buf
, sizeof(buf
), "/dev/tty%i", nr
);
160 fd
= open(tty
, O_RDWR
| O_NONBLOCK
);
163 GP_DEBUG(1, "Opening console %s failed: %s",
164 tty
, strerror(errno
));
168 if (ioctl(fd
, VT_GETSTATE
, &vts
) == 0)
169 fb
->last_con_nr
= vts
.v_active
;
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
));
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
));
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
));
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
);
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];
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)
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
);
255 if (fb
->flags
& GP_FB_INPUT_KBD
)
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
)
294 struct fb_fix_screeninfo fscri
;
295 struct fb_var_screeninfo vscri
;
298 backend
= malloc(sizeof(GP_Backend
) +
299 sizeof(struct fb_priv
) + strlen(path
) + 1);
304 fb
= GP_BACKEND_PRIV(backend
);
306 if (allocate_console(fb
, flags
))
309 if (flags
& GP_FB_INPUT_KBD
) {
314 /* open and mmap framebuffer */
315 GP_DEBUG(1, "Opening framebuffer '%s'", path
);
317 fd
= open(path
, O_RDWR
);
320 GP_DEBUG(1, "Opening framebuffer failed: %s", strerror(errno
));
324 if (ioctl(fd
, FBIOGET_FSCREENINFO
, &fscri
) < 0) {
325 GP_DEBUG(1, "Failed to ioctl FBIOGET_FSCREENINFO: %s",
330 if (ioctl(fd
, FBIOGET_VSCREENINFO
, &vscri
) < 0) {
331 GP_DEBUG(1, "Failed to ioctl FBIOGET_VSCREENINFO: %s",
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!");
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");
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 :(");
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
));
377 fb
->bsize
= fscri
.smem_len
;
378 strcpy(fb
->path
, path
);
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
;
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);
415 if (flags
& GP_FB_SHADOW
)
416 free(fb
->pixmap
.pixels
);
420 if (flags
& GP_FB_INPUT_KBD
)