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-2012 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 "GP_LinuxFB.h"
48 /* console fd, nr and saved data */
59 * Allocates and switches to newly allocated console.
61 static int allocate_console(struct fb_priv
*fb
, int flag
)
67 /* allocate and switch to new console */
68 GP_DEBUG(1, "Allocating new console");
70 fd
= open("/dev/tty1", O_WRONLY
);
73 GP_DEBUG(1, "Opening console /dev/tty1 failed: %s",
78 if (ioctl(fd
, VT_OPENQRY
, &nr
) < 0) {
79 GP_DEBUG(1, "Failed to ioctl VT_OPENQRY /dev/tty1: %s",
85 GP_DEBUG(1, "Has been granted tty%i", nr
);
89 snprintf(buf
, sizeof(buf
), "/dev/tty%i", nr
);
90 fd
= open(buf
, O_RDWR
| O_NONBLOCK
);
93 GP_DEBUG(1, "Opening console %s failed: %s",
94 buf
, strerror(errno
));
99 if (ioctl(fd
, VT_GETSTATE
, &vts
) == 0)
100 fb
->last_con_nr
= vts
.v_active
;
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
));
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
));
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
));
128 /* set keyboard to raw mode */
133 if (tcsetattr(fd
, TCSANOW
, &t
) < 0) {
134 GP_DEBUG(1, "Failed to tcsetattr(): %s",
140 if (ioctl(fd
, KDGKBMODE
, &fb
->saved_kb_mode
)) {
141 GP_DEBUG(1, "Failed to ioctl KDGKBMODE %s: %s",
142 buf
, strerror(errno
));
147 GP_DEBUG(2, "Previous keyboard mode was '%i'",
150 if (ioctl(fd
, KDSKBMODE
, K_MEDIUMRAW
) < 0) {
151 GP_DEBUG(1, "Failed to ioctl KDSKBMODE %s: %s",
152 buf
, strerror(errno
));
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];
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)
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
);
213 ioctl(fb
->con_fd
, KDSETMODE
, KD_TEXT
);
215 /* restore keyboard mode */
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
);
231 GP_Backend
*GP_BackendLinuxFBInit(const char *path
, int flag
)
235 struct fb_fix_screeninfo fscri
;
236 struct fb_var_screeninfo vscri
;
239 backend
= malloc(sizeof(GP_Backend
) +
240 sizeof(struct fb_priv
) + strlen(path
) + 1);
245 fb
= GP_BACKEND_PRIV(backend
);
247 if (allocate_console(fb
, flag
))
250 /* open and mmap framebuffer */
251 GP_DEBUG(1, "Opening framebuffer '%s'", path
);
253 fd
= open(path
, O_RDWR
);
256 GP_DEBUG(1, "Opening framebuffer failed: %s", strerror(errno
));
260 if (ioctl(fd
, FBIOGET_FSCREENINFO
, &fscri
) < 0) {
261 GP_DEBUG(1, "Failed to ioctl FBIOGET_FSCREENINFO: %s",
266 if (ioctl(fd
, FBIOGET_VSCREENINFO
, &vscri
) < 0) {
267 GP_DEBUG(1, "Failed to ioctl FBIOGET_VSCREENINFO: %s",
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
) {
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");
295 fb
->context
.pixels
= mmap(NULL
, fscri
.smem_len
,
296 PROT_READ
| PROT_WRITE
, MAP_FILE
| MAP_SHARED
,
299 if (fb
->context
.pixels
== MAP_FAILED
) {
300 GP_DEBUG(1, "mmaping framebuffer failed: %s", strerror(errno
));
305 fb
->bsize
= fscri
.smem_len
;
306 strcpy(fb
->path
, path
);
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
;
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
;
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
);