Created
February 4, 2024 17:13
-
-
Save odiroot/95376623050bc6f34b5512520c078a58 to your computer and use it in GitHub Desktop.
Example of using Cairo over XCB in V to show a minimal GUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pkgconfig --cflags --libs xcb | |
#pkgconfig --libs cairo | |
#include <xcb/xcb.h> | |
#include <cairo/cairo-xcb.h> | |
#include <cairo/cairo.h> | |
type C.xcb_window_t = u32 | |
type C.xcb_visualid_t = u32 | |
type C.xcb_gcontext_t = u32 | |
type C.xcb_drawable_t = u32 | |
type C.xcb_keycode_t = u8 | |
@[typedef] | |
struct C.xcb_connection_t {} | |
@[typedef] | |
struct C.xcb_screen_t { | |
root C.xcb_window_t | |
white_pixel u32 | |
black_pixel u32 | |
root_visual C.xcb_visualid_t | |
} | |
@[typedef] | |
struct C.xcb_screen_iterator_t { | |
data &C.xcb_screen_t | |
} | |
@[typedef] | |
struct C.xcb_setup_t {} | |
@[typedef] | |
struct C.xcb_generic_event_t { | |
response_type u8 | |
} | |
@[typedef] | |
struct C.xcb_key_press_event_t { | |
detail C.xcb_keycode_t | |
} | |
@[typedef] | |
struct C.xcb_visualtype_t { | |
visual_id C.xcb_visualid_t | |
} | |
@[typedef] | |
struct C.xcb_rectangle_t { | |
x i16 | |
y i16 | |
width u16 | |
height u16 | |
} | |
@[typedef] | |
struct C.xcb_depth_t {} | |
@[typedef] | |
struct C.xcb_depth_iterator_t { | |
data &C.xcb_depth_t | |
rem int | |
index int | |
} | |
@[typedef] | |
struct C.xcb_visualtype_t {} | |
@[typedef] | |
struct C.xcb_visualtype_iterator_t { | |
data &C.xcb_visualtype_t | |
rem int | |
index int | |
} | |
@[typedef] | |
struct C.cairo_surface_t {} | |
@[typedef] | |
struct C.cairo_t {} | |
fn C.xcb_connect(&char, &int) &C.xcb_connection_t | |
fn C.xcb_disconnect(&C.xcb_connection_t) | |
fn C.xcb_get_setup(&C.xcb_connection_t) &C.xcb_setup_t | |
fn C.xcb_setup_roots_iterator(&C.xcb_setup_t) C.xcb_screen_iterator_t | |
fn C.xcb_generate_id(&C.xcb_connection_t) u32 | |
// vfmt off | |
fn C.xcb_create_window( | |
&C.xcb_connection_t, // connection | |
u8, // depth | |
C.xcb_window_t, // target window id | |
C.xcb_window_t, // parent window id | |
i16, i16, // x / y | |
u16, u16, // width / height | |
u16, // border width | |
u16, // class | |
&C.xcb_visualid_t, // visual id | |
u32, // value mask | |
&u32 // value list | |
) | |
// vfmt on | |
fn C.xcb_map_window(&C.xcb_connection_t, C.xcb_window_t) | |
fn C.xcb_flush(&C.xcb_connection_t) | |
fn C.xcb_wait_for_event(&C.xcb_connection_t) &C.xcb_generic_event_t | |
fn C.xcb_create_gc(&C.xcb_connection_t, C.xcb_gcontext_t, C.xcb_window_t, u32, &u32) | |
fn C.xcb_poly_fill_rectangle(&C.xcb_connection_t, C.xcb_drawable_t, C.xcb_gcontext_t, u32, &C.xcb_rectangle_t) | |
fn C.xcb_screen_allowed_depths_iterator(&C.xcb_screen_t) C.xcb_depth_iterator_t | |
fn C.xcb_depth_visuals_iterator(&C.xcb_depth_t) C.xcb_visualtype_iterator_t | |
fn C.xcb_depth_next(&C.xcb_depth_iterator_t) | |
fn C.xcb_visualtype_next(&C.xcb_visualtype_iterator_t) | |
fn C.cairo_xcb_surface_create(&C.xcb_connection_t, C.xcb_window_t, &C.xcb_visualtype_t, int, int) &C.cairo_surface_t | |
fn C.cairo_create(&C.cairo_surface_t) &C.cairo_t | |
fn C.cairo_set_source_rgb(&C.cairo_t, f64, f64, f64) | |
fn C.cairo_paint(&C.cairo_t) | |
fn C.cairo_surface_flush(&C.cairo_surface_t) | |
fn C.cairo_select_font_face(&C.cairo_t, &char, int, int) | |
fn C.cairo_set_font_size(&C.cairo_t, f64) | |
fn C.cairo_move_to(&C.cairo_t, f64, f64) | |
fn C.cairo_show_text(&C.cairo_t, &char) | |
fn C.cairo_destroy(&C.cairo_t) | |
fn C.cairo_surface_destroy(&C.cairo_surface_t) | |
fn main() { | |
connection := C.xcb_connect(unsafe { nil }, unsafe { nil }) | |
screen := C.xcb_setup_roots_iterator(C.xcb_get_setup(connection)).data | |
window := C.xcb_generate_id(connection) | |
window_value_list := [ | |
screen.black_pixel, | |
u32(C.XCB_EVENT_MASK_KEY_PRESS | C.XCB_EVENT_MASK_BUTTON_PRESS | C.XCB_EVENT_MASK_EXPOSURE), | |
]! | |
// vfmt off | |
C.xcb_create_window( | |
connection, | |
C.XCB_COPY_FROM_PARENT, | |
window, | |
screen.root, | |
0, 0, | |
640, 480, | |
2, | |
C.XCB_WINDOW_CLASS_INPUT_OUTPUT, | |
screen.root_visual, | |
C.XCB_CW_BACK_PIXEL | C.XCB_CW_EVENT_MASK, | |
&window_value_list[0] | |
) | |
// vfmt on | |
C.xcb_map_window(connection, window) | |
C.xcb_flush(connection) | |
visual := lookup_visual(screen) | |
surface := C.cairo_xcb_surface_create(connection, window, visual, 640, 480) | |
if isnil(surface) { | |
panic('Failed to create Cairo surface in window') | |
} | |
context := C.cairo_create(surface) | |
if isnil(context) { | |
panic('Failed to create Cairo context') | |
} | |
C.xcb_flush(connection) | |
for { | |
event := C.xcb_wait_for_event(connection) | |
if isnil(event) { | |
// NULL means closing window. | |
break | |
} | |
match event.response_type & ~0x80 { | |
u8(C.XCB_EXPOSE) { | |
println('Expose') | |
// draw_rectangles(connection, window, gc) | |
draw_cairo(connection, context, surface) | |
} | |
u8(C.XCB_KEY_PRESS) { | |
key_event := &C.xcb_key_press_event_t(event) | |
println('Key press: ${key_event.detail}') | |
if key_event.detail == 9 { | |
break // Manual exit. | |
} | |
} | |
u8(C.XCB_BUTTON_PRESS) { | |
println('Mouse button press') | |
} | |
else { | |
println('Unknown event: ${event.response_type}') | |
} | |
} | |
unsafe { free(event) } | |
} | |
C.cairo_destroy(context) | |
C.cairo_surface_destroy(surface) | |
C.xcb_disconnect(connection) | |
} | |
@[inline] | |
fn lookup_visual(screen &C.xcb_screen_t) &C.xcb_visualtype_t { | |
depth_iter := C.xcb_screen_allowed_depths_iterator(screen) | |
for depth_iter.rem != 0 { | |
visual_iter := C.xcb_depth_visuals_iterator(depth_iter.data) | |
for visual_iter.rem != 0 { | |
current_visual := &C.xcb_visualtype_t(visual_iter.data) | |
if current_visual.visual_id == screen.root_visual { | |
return current_visual | |
} | |
C.xcb_visualtype_next(&visual_iter) | |
} | |
C.xcb_depth_next(&depth_iter) | |
} | |
return unsafe { nil } | |
} | |
@[inline] | |
fn draw_cairo(connection &C.xcb_connection_t, context &C.cairo_t, surface &C.cairo_surface_t) { | |
C.cairo_set_source_rgb(context, 0.3, 0.3, 0.3) | |
C.cairo_paint(context) | |
C.cairo_select_font_face(context, c'serif', C.CAIRO_FONT_SLANT_NORMAL, C.CAIRO_FONT_WEIGHT_BOLD) | |
C.cairo_set_font_size(context, 32.0) | |
C.cairo_set_source_rgb(context, 0.5, 0.7, 1.0) | |
C.cairo_move_to(context, 10.0, 50.0) | |
C.cairo_show_text(context, c'Hello, world') | |
C.cairo_surface_flush(surface) | |
C.xcb_flush(connection) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment