Line |
Branch |
Exec |
Source |
1 |
|
|
/* GATE PROJECT LICENSE: |
2 |
|
|
+----------------------------------------------------------------------------+ |
3 |
|
|
| Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> | |
4 |
|
|
| All rights reserved. | |
5 |
|
|
| | |
6 |
|
|
| Redistribution and use in source and binary forms, with or without | |
7 |
|
|
| modification, are permitted provided that the following conditions are met:| |
8 |
|
|
| | |
9 |
|
|
| 1. Redistributions of source code must retain the above copyright notice, | |
10 |
|
|
| this list of conditions and the following disclaimer. | |
11 |
|
|
| 2. Redistributions in binary form must reproduce the above copyright | |
12 |
|
|
| notice, this list of conditions and the following disclaimer in the | |
13 |
|
|
| documentation and/or other materials provided with the distribution. | |
14 |
|
|
| | |
15 |
|
|
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"| |
16 |
|
|
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
17 |
|
|
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
18 |
|
|
| ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
19 |
|
|
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
20 |
|
|
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
21 |
|
|
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
22 |
|
|
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
23 |
|
|
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
24 |
|
|
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
25 |
|
|
| THE POSSIBILITY OF SUCH DAMAGE. | |
26 |
|
|
+----------------------------------------------------------------------------+ |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include "gate/graphics/framebuffers.h" |
30 |
|
|
#include "gate/platform/posix/xlib.h" |
31 |
|
|
#include "gate/inputs.h" |
32 |
|
|
|
33 |
|
|
typedef struct xlibfb_impl |
34 |
|
|
{ |
35 |
|
|
GATE_INTERFACE_VTBL(gate_framebuffer) const* vtbl; |
36 |
|
|
|
37 |
|
|
gate_atomic_int_t ref_counter; |
38 |
|
|
|
39 |
|
|
gate_uint32_t width; |
40 |
|
|
gate_uint32_t height; |
41 |
|
|
|
42 |
|
|
Display* display; |
43 |
|
|
int screen; |
44 |
|
|
Window root_window; |
45 |
|
|
Atom protocols_atom; |
46 |
|
|
Atom close_atom; |
47 |
|
|
|
48 |
|
|
Window fb_window; |
49 |
|
|
XImage* image; |
50 |
|
|
gate_size_t image_size; |
51 |
|
|
void* image_data; |
52 |
|
|
|
53 |
|
|
} xlibfb_t; |
54 |
|
|
|
55 |
|
|
|
56 |
|
✗ |
static void xlibfb_destroy(xlibfb_t* fb) |
57 |
|
|
{ |
58 |
|
✗ |
if (fb->display) |
59 |
|
|
{ |
60 |
|
✗ |
if (fb->image) |
61 |
|
|
{ |
62 |
|
✗ |
xlib.XDestroyImage(fb->image); |
63 |
|
|
/* XDestroyImage also deletes the embedded image_data */ |
64 |
|
|
} |
65 |
|
✗ |
else if (fb->image_data) |
66 |
|
|
{ |
67 |
|
✗ |
gate_mem_dealloc(fb->image_data); |
68 |
|
|
} |
69 |
|
|
|
70 |
|
✗ |
if (fb->fb_window) |
71 |
|
|
{ |
72 |
|
✗ |
xlib.XDestroyWindow(fb->display, fb->fb_window); |
73 |
|
|
} |
74 |
|
✗ |
xlib.XCloseDisplay(fb->display); |
75 |
|
|
} |
76 |
|
✗ |
gate_mem_dealloc(fb); |
77 |
|
✗ |
} |
78 |
|
|
|
79 |
|
|
|
80 |
|
✗ |
static char const* xlibfb_get_interface_name(void* obj) |
81 |
|
|
{ |
82 |
|
✗ |
return GATE_INTERFACE_NAME_FRAMEBUFFER; |
83 |
|
|
} |
84 |
|
✗ |
static void xlibfb_release(void* obj) |
85 |
|
|
{ |
86 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
87 |
|
✗ |
if (0 == gate_atomic_int_dec(&impl->ref_counter)) |
88 |
|
|
{ |
89 |
|
✗ |
xlibfb_destroy(impl); |
90 |
|
|
} |
91 |
|
✗ |
} |
92 |
|
✗ |
static int xlibfb_retain(void* obj) |
93 |
|
|
{ |
94 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
95 |
|
✗ |
return gate_atomic_int_inc(&impl->ref_counter); |
96 |
|
|
} |
97 |
|
|
|
98 |
|
✗ |
static gate_result_t xlibfb_get_info(void* obj, gate_framebuffer_info_t* ptr_info) |
99 |
|
|
{ |
100 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
101 |
|
✗ |
gate_result_t ret = GATE_RESULT_INVALIDARG; |
102 |
|
✗ |
if (impl && ptr_info) |
103 |
|
|
{ |
104 |
|
✗ |
ptr_info->width = impl->width; |
105 |
|
✗ |
ptr_info->height = impl->height; |
106 |
|
✗ |
ptr_info->bits_per_pixel = 32; |
107 |
|
✗ |
ptr_info->format_id = 0; |
108 |
|
✗ |
ret = GATE_RESULT_OK; |
109 |
|
|
} |
110 |
|
✗ |
return ret; |
111 |
|
|
} |
112 |
|
✗ |
static gate_result_t xlibfb_get_property(void* obj, gate_string_t const* name, gate_property_t* prop) |
113 |
|
|
{ |
114 |
|
✗ |
return GATE_RESULT_NOTSUPPORTED; |
115 |
|
|
} |
116 |
|
✗ |
static gate_result_t xlibfb_set_property(void* obj, gate_string_t const* name, gate_property_t const* prop) |
117 |
|
|
{ |
118 |
|
✗ |
return GATE_RESULT_NOTSUPPORTED; |
119 |
|
|
} |
120 |
|
|
|
121 |
|
✗ |
static void xlibfb_draw_image(xlibfb_t* fb) |
122 |
|
|
{ |
123 |
|
✗ |
GC gc = xlib.XCreateGC(fb->display, fb->fb_window, 0, 0); |
124 |
|
✗ |
xlib.XSetGraphicsExposures(fb->display, gc, 0); |
125 |
|
✗ |
xlib.XPutImage(fb->display, fb->fb_window, gc, fb->image, 0, 0, 0, 0, fb->width, fb->height); |
126 |
|
✗ |
xlib.XFreeGC(fb->display, gc); |
127 |
|
✗ |
} |
128 |
|
|
|
129 |
|
✗ |
static gate_result_t xlibfb_update(void* obj) |
130 |
|
|
{ |
131 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
132 |
|
✗ |
xlibfb_draw_image(impl); |
133 |
|
✗ |
return GATE_RESULT_OK; |
134 |
|
|
} |
135 |
|
|
|
136 |
|
✗ |
static unsigned long XGetRgbPixel(Display* display, Colormap colmap, gate_uint8_t r, gate_uint8_t g, gate_uint8_t b) |
137 |
|
|
{ |
138 |
|
|
XColor col; |
139 |
|
✗ |
col.pixel = 0; |
140 |
|
✗ |
col.red = ((gate_uint16_t)r << 8); |
141 |
|
✗ |
col.green = ((gate_uint16_t)g << 8); |
142 |
|
✗ |
col.blue = ((gate_uint16_t)b << 8); |
143 |
|
✗ |
col.flags = DoRed | DoGreen | DoBlue; |
144 |
|
|
|
145 |
|
✗ |
xlib.XAllocColor(display, colmap, &col); |
146 |
|
✗ |
return col.pixel; |
147 |
|
|
} |
148 |
|
|
|
149 |
|
✗ |
static void XResolveRgbPixel(Display* display, Colormap colmap, unsigned long pixel, gate_color_t* ptr_color) |
150 |
|
|
{ |
151 |
|
|
XColor col; |
152 |
|
✗ |
col.pixel = pixel; |
153 |
|
✗ |
col.flags = DoRed | DoGreen | DoBlue; |
154 |
|
|
|
155 |
|
✗ |
xlib.XQueryColor(display, colmap, &col); |
156 |
|
✗ |
ptr_color->r = (gate_uint8_t)(col.red >> 8); |
157 |
|
✗ |
ptr_color->g = (gate_uint8_t)(col.green >> 8); |
158 |
|
✗ |
ptr_color->b = (gate_uint8_t)(col.blue >> 8); |
159 |
|
✗ |
ptr_color->a = 255; |
160 |
|
✗ |
} |
161 |
|
|
|
162 |
|
✗ |
static gate_result_t xlibfb_get_pixel(void* obj, gate_uint32_t x, gate_uint32_t y, gate_color_t* ptr_col) |
163 |
|
|
{ |
164 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
165 |
|
|
unsigned long pixel; |
166 |
|
|
Colormap colmap; |
167 |
|
✗ |
if ((x >= impl->width) || (y >= impl->height)) |
168 |
|
|
{ |
169 |
|
✗ |
return GATE_RESULT_INVALIDARG; |
170 |
|
|
} |
171 |
|
✗ |
colmap = xlib.XDefaultColormap(impl->display, impl->screen); |
172 |
|
✗ |
pixel = xlib.XGetPixel(impl->image, x, y); |
173 |
|
✗ |
XResolveRgbPixel(impl->display, colmap, pixel, ptr_col); |
174 |
|
✗ |
return GATE_RESULT_OK; |
175 |
|
|
} |
176 |
|
✗ |
static gate_result_t xlibfb_set_pixel(void* obj, gate_uint32_t x, gate_uint32_t y, gate_color_t col) |
177 |
|
|
{ |
178 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
179 |
|
|
unsigned long pixel; |
180 |
|
|
Colormap colmap; |
181 |
|
|
|
182 |
|
✗ |
if ((x >= impl->width) || (y >= impl->height)) |
183 |
|
|
{ |
184 |
|
✗ |
return GATE_RESULT_INVALIDARG; |
185 |
|
|
} |
186 |
|
|
|
187 |
|
✗ |
colmap = xlib.XDefaultColormap(impl->display, impl->screen); |
188 |
|
✗ |
pixel = XGetRgbPixel(impl->display, impl->screen, col.r, col.g, col.b); |
189 |
|
✗ |
xlib.XPutPixel(impl->image, x, y, pixel); |
190 |
|
✗ |
return GATE_RESULT_OK; |
191 |
|
|
} |
192 |
|
|
|
193 |
|
✗ |
static gate_result_t xlibfb_get_image(void* obj, gate_uint32_t src_x, gate_uint32_t src_y, gate_uint32_t width, gate_uint32_t height, gate_rasterimage_t* ptr_image) |
194 |
|
|
{ |
195 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
196 |
|
|
gate_uint32_t x, y; |
197 |
|
|
gate_color_t* ptr_color; |
198 |
|
|
|
199 |
|
|
unsigned long pixel; |
200 |
|
|
Colormap colmap; |
201 |
|
✗ |
if ((src_x >= impl->width) || (src_y >= impl->height) || (width == 0) || (height == 0)) |
202 |
|
|
{ |
203 |
|
✗ |
return GATE_RESULT_INVALIDARG; |
204 |
|
|
} |
205 |
|
|
|
206 |
|
✗ |
if (!gate_rasterimage_create(ptr_image, GATE_IMAGE_PIXELFORMAT_DEFAULT, width, height, NULL)) |
207 |
|
|
{ |
208 |
|
✗ |
return GATE_RESULT_OUTOFMEMORY; |
209 |
|
|
} |
210 |
|
|
|
211 |
|
✗ |
if (src_y + height > impl->height) |
212 |
|
|
{ |
213 |
|
✗ |
height = impl->height - src_y; |
214 |
|
|
} |
215 |
|
✗ |
if (src_x + width > impl->width) |
216 |
|
|
{ |
217 |
|
✗ |
width = impl->width - src_x; |
218 |
|
|
} |
219 |
|
|
|
220 |
|
✗ |
colmap = xlib.XDefaultColormap(impl->display, impl->screen); |
221 |
|
|
|
222 |
|
✗ |
for (y = 0; y != width; ++y) |
223 |
|
|
{ |
224 |
|
✗ |
ptr_color = (gate_color_t*)gate_rasterimage_get_line_ptr(ptr_image, y); |
225 |
|
✗ |
for (x = 0; x != width; ++x) |
226 |
|
|
{ |
227 |
|
✗ |
pixel = xlib.XGetPixel(impl->image, x + src_x, y + src_y); |
228 |
|
✗ |
XResolveRgbPixel(impl->display, colmap, pixel, ptr_color); |
229 |
|
✗ |
++ptr_color; |
230 |
|
|
} |
231 |
|
|
} |
232 |
|
|
|
233 |
|
✗ |
return GATE_RESULT_OK; |
234 |
|
|
} |
235 |
|
|
|
236 |
|
✗ |
static gate_result_t xlibfb_set_image32(xlibfb_t* fb, gate_rasterimage_t const* ptr_image, |
237 |
|
|
gate_uint32_t dest_x, gate_uint32_t dest_y, |
238 |
|
|
gate_uint32_t width, gate_uint32_t height) |
239 |
|
|
{ |
240 |
|
|
gate_uint32_t x, y; |
241 |
|
✗ |
gate_uint32_t* ptr_data = (gate_uint32_t*)fb->image_data; |
242 |
|
|
gate_uint32_t* ptr_dst; |
243 |
|
|
gate_color_t const* ptr_src; |
244 |
|
|
gate_uint32_t pixel; |
245 |
|
|
|
246 |
|
✗ |
for (y = 0; y != height; ++y) |
247 |
|
|
{ |
248 |
|
✗ |
ptr_dst = &ptr_data[fb->width * (dest_y + y) + dest_x]; |
249 |
|
✗ |
ptr_src = (gate_color_t*)gate_rasterimage_get_line_ptr(ptr_image, y); |
250 |
|
|
|
251 |
|
✗ |
for (x = 0; x != width; ++x) |
252 |
|
|
{ |
253 |
|
✗ |
pixel = (((gate_uint32_t)ptr_src->r) << 16) |
254 |
|
✗ |
| (((gate_uint32_t)ptr_src->g) << 8) |
255 |
|
✗ |
| (((gate_uint32_t)ptr_src->b)) |
256 |
|
|
; |
257 |
|
✗ |
*ptr_dst = pixel; |
258 |
|
✗ |
++ptr_src; |
259 |
|
✗ |
++ptr_dst; |
260 |
|
|
} |
261 |
|
|
} |
262 |
|
✗ |
return GATE_RESULT_OK; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
✗ |
static gate_result_t xlibfb_set_image_generic(xlibfb_t* fb, gate_rasterimage_t const* ptr_image, |
266 |
|
|
gate_uint32_t dest_x, gate_uint32_t dest_y, |
267 |
|
|
gate_uint32_t width, gate_uint32_t height) |
268 |
|
|
{ |
269 |
|
|
gate_color_t col; |
270 |
|
|
unsigned x, y; |
271 |
|
|
unsigned long pixel; |
272 |
|
|
Colormap colmap; |
273 |
|
|
|
274 |
|
✗ |
if ((fb->image->bits_per_pixel == 32) && ((fb->image->depth == 24) || (fb->image->depth == 32))) |
275 |
|
✗ |
{ |
276 |
|
|
// frame-buffer is 24/32 bit -> optimize color access |
277 |
|
✗ |
gate_uint32_t* ptr_data = (gate_uint32_t*)fb->image_data; |
278 |
|
|
gate_uint32_t* ptr_dst; |
279 |
|
|
|
280 |
|
✗ |
for (y = 0; y != height; ++y) |
281 |
|
|
{ |
282 |
|
✗ |
ptr_dst = &ptr_data[fb->width * (dest_y + y) + dest_x]; |
283 |
|
|
|
284 |
|
✗ |
for (x = 0; x != width; ++x) |
285 |
|
|
{ |
286 |
|
✗ |
gate_rasterimage_get_pixel(ptr_image, x, y, &col); |
287 |
|
✗ |
pixel = (((gate_uint32_t)col.r) << 16) |
288 |
|
✗ |
| (((gate_uint32_t)col.g) << 8) |
289 |
|
✗ |
| (((gate_uint32_t)col.b)) |
290 |
|
|
; |
291 |
|
✗ |
*ptr_dst = pixel; |
292 |
|
✗ |
++ptr_dst; |
293 |
|
|
} |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
} |
297 |
|
|
else |
298 |
|
|
{ |
299 |
|
✗ |
colmap = xlib.XDefaultColormap(fb->display, fb->screen); |
300 |
|
|
|
301 |
|
✗ |
for (y = 0; y != height; ++y) |
302 |
|
|
{ |
303 |
|
✗ |
for (x = 0; x != width; ++x) |
304 |
|
|
{ |
305 |
|
✗ |
gate_rasterimage_get_pixel(ptr_image, x, y, &col); |
306 |
|
✗ |
pixel = XGetRgbPixel(fb->display, colmap, col.r, col.g, col.b); |
307 |
|
✗ |
xlib.XPutPixel(fb->image, (int)(dest_x + x), (int)(dest_y + y), pixel); |
308 |
|
|
} |
309 |
|
|
} |
310 |
|
|
} |
311 |
|
|
|
312 |
|
✗ |
return GATE_RESULT_OK; |
313 |
|
|
} |
314 |
|
|
|
315 |
|
✗ |
static gate_result_t xlibfb_set_image(void* obj, gate_rasterimage_t const* ptr_image, gate_uint32_t dest_x, gate_uint32_t dest_y) |
316 |
|
|
{ |
317 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
318 |
|
|
|
319 |
|
|
gate_uint32_t width, height; |
320 |
|
|
|
321 |
|
✗ |
if ((dest_x >= impl->width) || (dest_y >= impl->height)) |
322 |
|
|
{ |
323 |
|
✗ |
return GATE_RESULT_INVALIDARG; |
324 |
|
|
} |
325 |
|
|
|
326 |
|
✗ |
width = gate_rasterimage_width(ptr_image); |
327 |
|
✗ |
height = gate_rasterimage_height(ptr_image); |
328 |
|
|
|
329 |
|
✗ |
if (dest_y + height > impl->height) |
330 |
|
|
{ |
331 |
|
✗ |
height = impl->height - dest_y; |
332 |
|
|
} |
333 |
|
✗ |
if (dest_x + width > impl->width) |
334 |
|
|
{ |
335 |
|
✗ |
width = impl->width - dest_x; |
336 |
|
|
} |
337 |
|
|
|
338 |
|
✗ |
if ((impl->image->bits_per_pixel == 32) && ((impl->image->depth == 24) || (impl->image->depth == 32)) |
339 |
|
✗ |
&& ((ptr_image->pixel_format == GATE_IMAGE_PIXELFORMAT_RGBA) || (ptr_image->pixel_format == GATE_IMAGE_PIXELFORMAT_RGB32))) |
340 |
|
|
{ |
341 |
|
✗ |
return xlibfb_set_image32(impl, ptr_image, dest_x, dest_y, width, height); |
342 |
|
|
} |
343 |
|
|
|
344 |
|
✗ |
return xlibfb_set_image_generic(impl, ptr_image, dest_x, dest_y, width, height); |
345 |
|
|
} |
346 |
|
|
|
347 |
|
✗ |
static gate_bool_t xlibfb_decode_event(XEvent* ptr_event, xlibfb_t* impl, gate_framebuffer_event_t* ptr_received_evt) |
348 |
|
|
{ |
349 |
|
|
KeySym keysym; |
350 |
|
|
gate_input_keycode_t key_code; |
351 |
|
|
gate_uint32_t key_states; |
352 |
|
|
|
353 |
|
✗ |
switch (ptr_event->type) |
354 |
|
|
{ |
355 |
|
✗ |
case KeyPress: |
356 |
|
|
{ |
357 |
|
✗ |
keysym = xlib.XLookupKeysym(&ptr_event->xkey, 0); |
358 |
|
✗ |
key_states = 0; |
359 |
|
✗ |
if (ptr_event->xkey.state & ShiftMask) |
360 |
|
|
{ |
361 |
|
✗ |
key_states |= GATE_KBD_KEYSTATE_SHIFT; |
362 |
|
|
} |
363 |
|
✗ |
if (ptr_event->xkey.state & LockMask) |
364 |
|
|
{ |
365 |
|
✗ |
key_states |= GATE_KBD_KEYSTATE_MENU; |
366 |
|
|
} |
367 |
|
✗ |
if (ptr_event->xkey.state & ControlMask) |
368 |
|
|
{ |
369 |
|
✗ |
key_states |= GATE_KBD_KEYSTATE_CTRL; |
370 |
|
|
} |
371 |
|
✗ |
key_code = 0; |
372 |
|
✗ |
gate_xlib_convert_keysym(keysym, &key_code); |
373 |
|
✗ |
ptr_received_evt->event_type = GATE_FRAMEBUFFER_EVENT_KEY_DOWN; |
374 |
|
✗ |
ptr_received_evt->event_data.key.key_code = key_code; |
375 |
|
✗ |
ptr_received_evt->event_data.key.char_code = key_code; |
376 |
|
✗ |
return true; |
377 |
|
|
} |
378 |
|
✗ |
case KeyRelease: |
379 |
|
|
{ |
380 |
|
✗ |
keysym = xlib.XLookupKeysym(&ptr_event->xkey, 0); |
381 |
|
✗ |
key_states = 0; |
382 |
|
✗ |
if (ptr_event->xkey.state & ShiftMask) |
383 |
|
|
{ |
384 |
|
✗ |
key_states |= GATE_KBD_KEYSTATE_SHIFT; |
385 |
|
|
} |
386 |
|
✗ |
if (ptr_event->xkey.state & LockMask) |
387 |
|
|
{ |
388 |
|
✗ |
key_states |= GATE_KBD_KEYSTATE_MENU; |
389 |
|
|
} |
390 |
|
✗ |
if (ptr_event->xkey.state & ControlMask) |
391 |
|
|
{ |
392 |
|
✗ |
key_states |= GATE_KBD_KEYSTATE_CTRL; |
393 |
|
|
} |
394 |
|
✗ |
key_code = 0; |
395 |
|
✗ |
gate_xlib_convert_keysym(keysym, &key_code); |
396 |
|
✗ |
ptr_received_evt->event_type = GATE_FRAMEBUFFER_EVENT_KEY_UP; |
397 |
|
✗ |
ptr_received_evt->event_data.key.key_code = key_code; |
398 |
|
✗ |
ptr_received_evt->event_data.key.char_code = key_code; |
399 |
|
✗ |
return true; |
400 |
|
|
} |
401 |
|
✗ |
case ButtonPress: |
402 |
|
|
{ |
403 |
|
✗ |
ptr_received_evt->event_type = GATE_FRAMEBUFFER_EVENT_POINTER_BUTTON_DOWN; |
404 |
|
✗ |
ptr_received_evt->event_data.pointer.x = ptr_event->xbutton.x; |
405 |
|
✗ |
ptr_received_evt->event_data.pointer.y = ptr_event->xbutton.y; |
406 |
|
✗ |
switch (ptr_event->xbutton.button) |
407 |
|
|
{ |
408 |
|
✗ |
case Button1: ptr_received_evt->event_data.pointer.button = GATE_FRAMEBUFFER_BUTTON_1; break; |
409 |
|
✗ |
case Button2: ptr_received_evt->event_data.pointer.button = GATE_FRAMEBUFFER_BUTTON_2; break; |
410 |
|
✗ |
case Button3: ptr_received_evt->event_data.pointer.button = GATE_FRAMEBUFFER_BUTTON_3; break; |
411 |
|
|
} |
412 |
|
✗ |
return true; |
413 |
|
|
} |
414 |
|
✗ |
case ButtonRelease: |
415 |
|
|
{ |
416 |
|
✗ |
ptr_received_evt->event_type = GATE_FRAMEBUFFER_EVENT_POINTER_BUTTON_UP; |
417 |
|
✗ |
ptr_received_evt->event_data.pointer.x = ptr_event->xbutton.x; |
418 |
|
✗ |
ptr_received_evt->event_data.pointer.y = ptr_event->xbutton.y; |
419 |
|
✗ |
switch (ptr_event->xbutton.button) |
420 |
|
|
{ |
421 |
|
✗ |
case Button1: ptr_received_evt->event_data.pointer.button = GATE_FRAMEBUFFER_BUTTON_1; break; |
422 |
|
✗ |
case Button2: ptr_received_evt->event_data.pointer.button = GATE_FRAMEBUFFER_BUTTON_2; break; |
423 |
|
✗ |
case Button3: ptr_received_evt->event_data.pointer.button = GATE_FRAMEBUFFER_BUTTON_3; break; |
424 |
|
|
} |
425 |
|
✗ |
return true; |
426 |
|
|
} |
427 |
|
✗ |
case ClientMessage: |
428 |
|
|
{ |
429 |
|
✗ |
if (ptr_event->xclient.message_type == impl->protocols_atom) |
430 |
|
|
{ |
431 |
|
✗ |
if (ptr_event->xclient.data.l[0] == (long)impl->close_atom) |
432 |
|
|
{ |
433 |
|
✗ |
ptr_received_evt->event_type = GATE_FRAMEBUFFER_EVENT_QUIT; |
434 |
|
✗ |
return true; |
435 |
|
|
} |
436 |
|
|
} |
437 |
|
✗ |
break; |
438 |
|
|
} |
439 |
|
|
} |
440 |
|
✗ |
return false; |
441 |
|
|
} |
442 |
|
|
|
443 |
|
✗ |
static gate_result_t xlibfb_await_event(void* obj, gate_uint32_t timeout_ms, gate_framebuffer_event_t* ptr_received_evt) |
444 |
|
|
{ |
445 |
|
✗ |
xlibfb_t* impl = (xlibfb_t*)obj; |
446 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
447 |
|
|
|
448 |
|
|
gate_timecounter_t start_time; |
449 |
|
|
gate_timecounter_t current_time; |
450 |
|
|
gate_int64_t time_diff; |
451 |
|
✗ |
const gate_int64_t timeout_us = (gate_int64_t)timeout_ms * (gate_int64_t)1000; |
452 |
|
|
|
453 |
|
|
XEvent xevent; |
454 |
|
|
|
455 |
|
✗ |
long input_event_mask = FocusChangeMask | PropertyChangeMask |
456 |
|
|
| KeyPressMask | KeyReleaseMask | KeymapStateMask |
457 |
|
|
| ButtonPressMask | ButtonReleaseMask | PointerMotionMask | PointerMotionHintMask |
458 |
|
|
| ExposureMask | VisibilityChangeMask |
459 |
|
|
| StructureNotifyMask | SubstructureNotifyMask |
460 |
|
|
; |
461 |
|
|
|
462 |
|
|
do |
463 |
|
|
{ |
464 |
|
✗ |
gate_timecounter_now(&start_time); |
465 |
|
|
|
466 |
|
✗ |
xlib.XSelectInput(impl->display, impl->fb_window, input_event_mask); |
467 |
|
|
|
468 |
|
|
for (;;) |
469 |
|
|
{ |
470 |
|
✗ |
if (xlib.XPending(impl->display) > 0) |
471 |
|
|
{ |
472 |
|
✗ |
gate_mem_clear(&xevent, sizeof(xevent)); |
473 |
|
✗ |
xlib.XNextEvent(impl->display, &xevent); |
474 |
|
|
|
475 |
|
✗ |
if (xlibfb_decode_event(&xevent, impl, ptr_received_evt)) |
476 |
|
|
{ |
477 |
|
✗ |
ret = GATE_RESULT_OK; |
478 |
|
✗ |
break; |
479 |
|
|
} |
480 |
|
|
} |
481 |
|
|
else |
482 |
|
|
{ |
483 |
|
✗ |
gate_timecounter_now(¤t_time); |
484 |
|
✗ |
time_diff = gate_timecounter_diff(current_time, start_time); |
485 |
|
|
|
486 |
|
✗ |
if (time_diff >= timeout_us) |
487 |
|
|
{ |
488 |
|
✗ |
ret = GATE_RESULT_TIMEOUT; |
489 |
|
✗ |
break; |
490 |
|
|
} |
491 |
|
|
} |
492 |
|
|
} |
493 |
|
|
} while (0); |
494 |
|
|
|
495 |
|
✗ |
return ret; |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
static GATE_INTERFACE_VTBL(gate_framebuffer) gate_xlibfb_vtbl; |
499 |
|
✗ |
static void gate_init_xlibfb_vtbl() |
500 |
|
|
{ |
501 |
|
✗ |
if (!gate_xlibfb_vtbl.get_interface_name) |
502 |
|
|
{ |
503 |
|
|
GATE_INTERFACE_VTBL(gate_framebuffer) const local_vtbl = |
504 |
|
|
{ |
505 |
|
|
&xlibfb_get_interface_name, |
506 |
|
|
&xlibfb_release, |
507 |
|
|
&xlibfb_retain, |
508 |
|
|
|
509 |
|
|
&xlibfb_get_info, |
510 |
|
|
&xlibfb_get_property, |
511 |
|
|
&xlibfb_set_property, |
512 |
|
|
|
513 |
|
|
&xlibfb_update, |
514 |
|
|
|
515 |
|
|
&xlibfb_get_pixel, |
516 |
|
|
&xlibfb_set_pixel, |
517 |
|
|
|
518 |
|
|
&xlibfb_get_image, |
519 |
|
|
&xlibfb_set_image, |
520 |
|
|
|
521 |
|
|
&xlibfb_await_event |
522 |
|
|
}; |
523 |
|
|
|
524 |
|
✗ |
gate_xlibfb_vtbl = local_vtbl; |
525 |
|
|
} |
526 |
|
✗ |
} |
527 |
|
|
|
528 |
|
|
|
529 |
|
|
|
530 |
|
✗ |
gate_size_t gate_framebuffer_enum_displays(gate_framebuffer_info_t* ptr_infos, gate_size_t infos_capacity) |
531 |
|
|
{ |
532 |
|
✗ |
gate_size_t ret = infos_capacity; |
533 |
|
|
|
534 |
|
✗ |
if (ret > 2) |
535 |
|
|
{ |
536 |
|
✗ |
ret = 2; |
537 |
|
|
} |
538 |
|
|
|
539 |
|
✗ |
if (ret >= 1) |
540 |
|
|
{ |
541 |
|
✗ |
ptr_infos[0].width = 640; |
542 |
|
✗ |
ptr_infos[0].height = 480; |
543 |
|
✗ |
ptr_infos[0].bits_per_pixel = 32; |
544 |
|
✗ |
ptr_infos[0].format_id = 0; |
545 |
|
|
} |
546 |
|
|
|
547 |
|
✗ |
if (ret >= 2) |
548 |
|
|
{ |
549 |
|
✗ |
ptr_infos[1].width = 800; |
550 |
|
✗ |
ptr_infos[1].height = 600; |
551 |
|
✗ |
ptr_infos[1].bits_per_pixel = 32; |
552 |
|
✗ |
ptr_infos[1].format_id = 0; |
553 |
|
|
} |
554 |
|
|
|
555 |
|
✗ |
return ret; |
556 |
|
|
} |
557 |
|
|
|
558 |
|
✗ |
static gate_result_t xlibfb_create_image(xlibfb_t* fb) |
559 |
|
|
{ |
560 |
|
✗ |
Visual* visual = xlib.XDefaultVisual(fb->display, fb->screen); |
561 |
|
✗ |
unsigned int depth = (unsigned int)xlib.XDefaultDepth(fb->display, fb->screen); |
562 |
|
✗ |
unsigned int padding = xlib.XBitmapPad(fb->display); |
563 |
|
✗ |
unsigned int bytes_per_line = fb->width * 4; |
564 |
|
|
|
565 |
|
✗ |
if (bytes_per_line % padding != 0) |
566 |
|
|
{ |
567 |
|
✗ |
bytes_per_line += (padding - (bytes_per_line % padding)); |
568 |
|
|
} |
569 |
|
|
|
570 |
|
✗ |
fb->image_size = bytes_per_line * fb->height; |
571 |
|
✗ |
fb->image_data = gate_mem_alloc(fb->image_size); |
572 |
|
|
|
573 |
|
✗ |
if (!fb->image_data) |
574 |
|
|
{ |
575 |
|
✗ |
return GATE_RESULT_OUTOFMEMORY; |
576 |
|
|
} |
577 |
|
|
|
578 |
|
✗ |
fb->image = xlib.XCreateImage(fb->display, visual, depth, ZPixmap, 0, fb->image_data, fb->width, fb->height, padding, 0); |
579 |
|
✗ |
if (!fb->image) |
580 |
|
|
{ |
581 |
|
✗ |
return GATE_RESULT_OUTOFRESOURCES; |
582 |
|
|
} |
583 |
|
|
|
584 |
|
✗ |
return GATE_RESULT_OK; |
585 |
|
|
} |
586 |
|
|
|
587 |
|
✗ |
static gate_result_t xlibfb_create_window(xlibfb_t* fb) |
588 |
|
|
{ |
589 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
590 |
|
|
|
591 |
|
|
int depth; |
592 |
|
|
Visual* visual; |
593 |
|
|
Colormap colmap; |
594 |
|
|
XSetWindowAttributes win_attribs; |
595 |
|
|
int attr_mask; |
596 |
|
|
|
597 |
|
|
do |
598 |
|
|
{ |
599 |
|
✗ |
ret = gate_xlib_load_functions(); |
600 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
601 |
|
|
|
602 |
|
✗ |
fb->display = xlib.XOpenDisplay(NULL); |
603 |
|
✗ |
if (fb->display == NULL) |
604 |
|
|
{ |
605 |
|
✗ |
fb->display = xlib.XOpenDisplay(":0"); |
606 |
|
|
} |
607 |
|
✗ |
if (fb->display == NULL) |
608 |
|
|
{ |
609 |
|
✗ |
ret = GATE_RESULT_OUTOFRESOURCES; |
610 |
|
✗ |
break; |
611 |
|
|
} |
612 |
|
|
|
613 |
|
✗ |
fb->screen = xlib.XDefaultScreen(fb->display); |
614 |
|
✗ |
fb->root_window = xlib.XDefaultRootWindow(fb->display); |
615 |
|
✗ |
fb->protocols_atom = xlib.XInternAtom(fb->display, "WM_PROTOCOLS", False); |
616 |
|
✗ |
fb->close_atom = xlib.XInternAtom(fb->display, "WM_DELETE_WINDOW", False); |
617 |
|
|
|
618 |
|
✗ |
depth = xlib.XDefaultDepth(fb->display, fb->screen); |
619 |
|
✗ |
visual = xlib.XDefaultVisual(fb->display, fb->screen); |
620 |
|
✗ |
colmap = xlib.XDefaultColormap(fb->display, fb->screen); |
621 |
|
|
|
622 |
|
✗ |
gate_mem_clear(&win_attribs, sizeof(win_attribs)); |
623 |
|
✗ |
win_attribs.colormap = colmap; |
624 |
|
✗ |
win_attribs.event_mask = StructureNotifyMask; |
625 |
|
✗ |
win_attribs.border_pixel = xlib.XBlackPixel(fb->display, fb->screen); |
626 |
|
✗ |
win_attribs.background_pixel = xlib.XWhitePixel(fb->display, fb->screen); |
627 |
|
|
|
628 |
|
✗ |
attr_mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap; |
629 |
|
|
|
630 |
|
✗ |
fb->fb_window = xlib.XCreateWindow(fb->display, fb->root_window, 0, 0, fb->width, fb->height, |
631 |
|
|
0, depth, InputOutput, visual, attr_mask, &win_attribs); |
632 |
|
✗ |
if (!fb->fb_window) |
633 |
|
|
{ |
634 |
|
✗ |
ret = GATE_RESULT_FAILED; |
635 |
|
✗ |
break; |
636 |
|
|
} |
637 |
|
|
|
638 |
|
✗ |
ret = xlibfb_create_image(fb); |
639 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
640 |
|
|
|
641 |
|
✗ |
xlib.XSetWMProtocols(fb->display, fb->fb_window, &fb->close_atom, 1); |
642 |
|
|
|
643 |
|
✗ |
xlib.XMapWindow(fb->display, fb->fb_window); |
644 |
|
|
|
645 |
|
✗ |
xlib.XFlush(fb->display); |
646 |
|
|
|
647 |
|
|
/* successfully initialized: */ |
648 |
|
✗ |
ret = GATE_RESULT_OK; |
649 |
|
|
|
650 |
|
|
} while (0); |
651 |
|
|
|
652 |
|
✗ |
return ret; |
653 |
|
|
} |
654 |
|
|
|
655 |
|
✗ |
gate_result_t gate_framebuffer_find_display(gate_uint32_t required_width, gate_uint32_t required_height, gate_framebuffer_info_t* ptr_found_display_info) |
656 |
|
|
{ |
657 |
|
|
/* TODO */ |
658 |
|
✗ |
ptr_found_display_info->bits_per_pixel = 32; |
659 |
|
✗ |
ptr_found_display_info->format_id = 0; |
660 |
|
✗ |
ptr_found_display_info->width = required_width; |
661 |
|
✗ |
ptr_found_display_info->height = required_height; |
662 |
|
✗ |
return GATE_RESULT_OK; |
663 |
|
|
} |
664 |
|
|
|
665 |
|
✗ |
gate_result_t gate_framebuffer_open_display(gate_framebuffer_info_t const* ptr_infos, gate_framebuffer_t** ptr_framebuffer) |
666 |
|
|
{ |
667 |
|
|
gate_result_t ret; |
668 |
|
✗ |
xlibfb_t* fb = NULL; |
669 |
|
|
|
670 |
|
|
do |
671 |
|
|
{ |
672 |
|
✗ |
if (!ptr_infos) |
673 |
|
|
{ |
674 |
|
✗ |
ret = GATE_RESULT_INVALIDARG; |
675 |
|
✗ |
break; |
676 |
|
|
} |
677 |
|
|
|
678 |
|
✗ |
fb = (xlibfb_t*)gate_mem_alloc(sizeof(xlibfb_t)); |
679 |
|
✗ |
if (!fb) |
680 |
|
|
{ |
681 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
682 |
|
✗ |
break; |
683 |
|
|
} |
684 |
|
|
|
685 |
|
✗ |
gate_mem_clear(fb, sizeof(xlibfb_t)); |
686 |
|
✗ |
gate_init_xlibfb_vtbl(); |
687 |
|
✗ |
fb->vtbl = &gate_xlibfb_vtbl; |
688 |
|
✗ |
gate_atomic_int_init(&fb->ref_counter, 1); |
689 |
|
✗ |
fb->width = ptr_infos->width; |
690 |
|
✗ |
fb->height = ptr_infos->height; |
691 |
|
|
|
692 |
|
✗ |
ret = xlibfb_create_window(fb); |
693 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
694 |
|
|
|
695 |
|
|
/* initialization successfully completed: */ |
696 |
|
✗ |
ret = GATE_RESULT_OK; |
697 |
|
✗ |
if (ptr_framebuffer) |
698 |
|
|
{ |
699 |
|
✗ |
*ptr_framebuffer = (gate_framebuffer_t*)fb; |
700 |
|
✗ |
fb = NULL; |
701 |
|
|
} |
702 |
|
|
} while (0); |
703 |
|
|
|
704 |
|
✗ |
if (fb != NULL) |
705 |
|
|
{ |
706 |
|
|
/* destroy partially initialized framebuffer */ |
707 |
|
✗ |
xlibfb_destroy(fb); |
708 |
|
|
} |
709 |
|
✗ |
return ret; |
710 |
|
|
} |
711 |
|
|
|