Skip to content

Instantly share code, notes, and snippets.

@odiroot
Created February 4, 2024 17:13
Show Gist options
  • Save odiroot/95376623050bc6f34b5512520c078a58 to your computer and use it in GitHub Desktop.
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
#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