-
Notifications
You must be signed in to change notification settings - Fork 1
/
cairofb.c
139 lines (121 loc) · 3.38 KB
/
cairofb.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
* cairofb.c
*
* a cairo context for drawing on a linux framebuffer
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include "cairofb.h"
/*
* create a cairo context from a framebuffer device
*/
struct cairofb *cairofb_init(char *devname, int doublebuffering) {
struct cairofb *cairofb;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
int res;
int stride;
cairo_format_t format;
cairo_status_t status;
cairofb = malloc(sizeof(struct cairofb));
/* open device, get info */
cairofb->dev = open(devname, O_RDWR);
if (cairofb->dev == -1) {
perror(devname);
free(cairofb);
return NULL;
}
res = ioctl(cairofb->dev, FBIOGET_FSCREENINFO, &finfo);
if (res == -1) {
perror("FBIOGET_FSCREENINFO");
free(cairofb);
return NULL;
}
res = ioctl(cairofb->dev, FBIOGET_VSCREENINFO, &vinfo);
if (res == -1) {
perror("FBIOGET_VSCREENINFO");
free(cairofb);
return NULL;
}
cairofb->width = vinfo.xres;
cairofb->height = vinfo.yres;
stride = finfo.line_length;
cairofb->length = finfo.smem_len;
format = CAIRO_FORMAT_INVALID;
if (finfo.type == FB_TYPE_PACKED_PIXELS &&
finfo.visual == FB_VISUAL_TRUECOLOR) {
if (vinfo.bits_per_pixel == 16)
format = CAIRO_FORMAT_RGB16_565;
if (vinfo.bits_per_pixel == 32)
format = CAIRO_FORMAT_RGB24;
/* TBD: also check grayscale, offset/length */
/* TBF: if bpp, grayscale or offset/length are not supported by
cairo, try changing vinfo by FBIOPUT_VSCREENINFO */
}
if (format == CAIRO_FORMAT_INVALID) {
printf("ERROR: unsupported type/visual\n");
free(cairofb);
return NULL;
}
/* from framebuffer to cairo */
cairofb->img = mmap(0, cairofb->length,
PROT_READ | PROT_WRITE, MAP_SHARED, cairofb->dev, 0);
if (cairofb->img == MAP_FAILED) {
perror("mmap");
free(cairofb);
return NULL;
}
cairofb->dbuf = doublebuffering ?
malloc(cairofb->length) : cairofb->img;
cairofb->surface = cairo_image_surface_create_for_data(cairofb->dbuf,
format, cairofb->width, cairofb->height, stride);
status = cairo_surface_status(cairofb->surface);
if (status != CAIRO_STATUS_SUCCESS)
printf("WARNING: cairo status=%d\n", status);
cairofb->cr = cairo_create(cairofb->surface);
/* set cairo clip */
cairo_rectangle(cairofb->cr, 0, 0, cairofb->width, cairofb->height);
cairo_clip(cairofb->cr);
return cairofb;
}
/*
* clear the cairo context
*/
void cairofb_clear(struct cairofb *cairofb,
double red, double green, double blue) {
cairo_identity_matrix(cairofb->cr);
cairo_set_source_rgb(cairofb->cr, red, green, blue);
cairo_rectangle(cairofb->cr, 0, 0, cairofb->width, cairofb->height);
cairo_fill(cairofb->cr);
}
/*
* return whether double buffering is used
*/
int cairofb_doublebuffering(struct cairofb *cairofb) {
return cairofb->img != cairofb->dbuf;
}
/*
* flush output, if double buffering
*/
void cairofb_flush(struct cairofb *cairofb) {
if (cairofb_doublebuffering(cairofb))
memcpy(cairofb->img, cairofb->dbuf, cairofb->length);
}
/*
* deallocate the cairo context and related data
*/
void cairofb_finish(struct cairofb *cairofb) {
cairo_destroy(cairofb->cr);
cairo_surface_destroy(cairofb->surface);
if (cairofb->img != cairofb->dbuf)
free(cairofb->dbuf);
munmap(cairofb->img, cairofb->length);
close(cairofb->dev);
free(cairofb);
}