A drawing library modeled after Windows GDI
- CreateDC/ReleaseDC
- DrawText
- DrawRect
clang -lxcb -o draggable{,.c} draw.c
For support, email [email protected] or DM me on discord (robert_#4066)
Add badges from somewhere like: shields.io
A drawing library modeled after Windows GDI
clang -lxcb -o draggable{,.c} draw.c
For support, email [email protected] or DM me on discord (robert_#4066)
Add badges from somewhere like: shields.io
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <xcb/xcb.h> | |
| #include <stdbool.h> | |
| #include "draw.h" | |
| xcb_connection_t *c; | |
| xcb_screen_t *screen; | |
| xcb_gcontext_t gc; | |
| xcb_drawable_t parent; | |
| xcb_drawable_t swin; | |
| int drag_state = 0; | |
| uint32_t offset[2]; | |
| uint32_t origin[2]; | |
| void draw_button(xcb_drawable_t d) { | |
| DC surface = CreateDC(c, screen, d); | |
| surface.DrawText(surface, "Drag me"); | |
| ReleaseDC(surface); | |
| //char string[] = "Drag me"; | |
| //uint8_t string_len = strlen(string); | |
| //xcb_image_text_8(c, string_len, d, gc, 6, 12, string); | |
| } | |
| xcb_drawable_t make_button(xcb_drawable_t parent, uint32_t width, uint32_t height) { | |
| xcb_drawable_t child = xcb_generate_id(c); | |
| uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK; | |
| uint32_t values[3] = { screen->white_pixel, screen->black_pixel, 0 }; | |
| values[2] = ( XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | | |
| XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | | |
| XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | | |
| XCB_EVENT_MASK_KEY_PRESS); | |
| xcb_create_window (c, XCB_COPY_FROM_PARENT, child, parent, 0, 0, width, height, | |
| 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); | |
| origin[0] = 0; | |
| origin[1] = 0; | |
| xcb_map_window(c, child); | |
| draw_button(child); | |
| return child; | |
| } | |
| xcb_drawable_t create_window() { | |
| uint32_t mask = 0; | |
| uint32_t values[3] = { 0 }; | |
| //gc = xcb_generate_id (c); | |
| mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_GRAPHICS_EXPOSURES; | |
| values[0] = screen->black_pixel; | |
| values[1] = screen->white_pixel; | |
| //xcb_create_gc (c, gc, screen->root, mask, values); | |
| /* create the window */ | |
| int handle = xcb_generate_id(c); | |
| mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; | |
| values[0] = screen->white_pixel; | |
| values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_STRUCTURE_NOTIFY | | |
| XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_MOTION; | |
| xcb_create_window( c, /* connection */ | |
| screen->root_depth, /* depth */ | |
| handle, /* window Id */ | |
| screen->root, /* parent window */ | |
| 0, 0, /* x, y */ | |
| 300,300, /* width, height */ | |
| 0, /* border_width */ | |
| XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */ | |
| screen->root_visual, /* visual */ | |
| mask, values); /* masks */ | |
| xcb_map_window(c, handle); | |
| return handle; | |
| } | |
| void event_loop(int window) { | |
| for ( xcb_generic_event_t *e = xcb_wait_for_event (c) | |
| ; e | |
| ; e = xcb_wait_for_event (c)) | |
| { | |
| switch (e->response_type & ~0x80) { | |
| case XCB_MOTION_NOTIFY: | |
| if (((xcb_motion_notify_event_t *)e)->event == swin && drag_state) { | |
| xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)e; | |
| origin[0] += motion->event_x - offset[0]; | |
| origin[1] += motion->event_y - offset[1]; | |
| xcb_configure_window(c, swin, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, origin); | |
| } | |
| xcb_flush(c); | |
| break; | |
| /* grab the offset from the subwindow's origin */ | |
| case XCB_BUTTON_PRESS: | |
| if (((xcb_button_press_event_t *) e)->event == swin) { | |
| xcb_button_press_event_t *ev = (xcb_button_press_event_t *) e; | |
| printf("%d, %d\n", ev->event_x, ev->event_y); | |
| offset[0] = ev->event_x; | |
| offset[1] = ev->event_y; | |
| drag_state = ev->event_y <= 20; | |
| } | |
| break; | |
| case XCB_BUTTON_RELEASE: | |
| drag_state = 0; | |
| break; | |
| case XCB_CONFIGURE_NOTIFY: | |
| if (((xcb_configure_notify_event_t *) e)->event == swin) { | |
| xcb_configure_notify_event_t *ev = (xcb_configure_notify_event_t *) e; | |
| puts("pz"); | |
| //origin[0] = ev->x; | |
| //origin[1] = ev->y; | |
| } | |
| break; | |
| case XCB_EXPOSE: | |
| draw_button(swin); | |
| xcb_flush(c); | |
| break; | |
| case XCB_KEY_PRESS: | |
| if (((xcb_key_press_event_t *) e)->detail == 9) { | |
| xcb_key_press_event_t *ev = (xcb_key_press_event_t *) e; | |
| return; | |
| } | |
| break; | |
| } | |
| free (e); | |
| } | |
| } | |
| int main(void) { | |
| c = xcb_connect (NULL, NULL); | |
| screen = xcb_setup_roots_iterator (xcb_get_setup (c)).data; | |
| parent = create_window(); | |
| swin = make_button(parent, 320, 240); | |
| xcb_flush (c); | |
| event_loop(parent); | |
| return EXIT_SUCCESS; | |
| } |
| #include <locale.h> | |
| #include <stdbool.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <xcb/xcb.h> | |
| #include "draw.h" | |
| #define MAX(a, b) ((a) > (b) ? (a) : (b)) | |
| #define MIN(a, b) ((a) < (b) ? (a) : (b)) | |
| #define DEFAULTFN "fixed" | |
| bool DrawRect(DC dc, int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t color) { | |
| xcb_rectangle_t rect = {x, y, w, h}; | |
| uint32_t val[2] = { color, 0 }; | |
| xcb_change_gc(dc.c, dc.gc, XCB_GC_FOREGROUND, val); | |
| xcb_poly_rectangle(dc.c, dc.canvas, dc.gc, 1, &rect); | |
| return true; | |
| } | |
| bool FillRect(DC dc, int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t color) { | |
| xcb_rectangle_t rect = {x, y, w, h}; | |
| uint32_t val[2] = { color, 0 }; | |
| xcb_change_gc(dc.c, dc.gc, XCB_GC_FOREGROUND, val); | |
| xcb_poly_fill_rectangle(dc.c, dc.canvas, dc.gc, 1, &rect); | |
| return true; | |
| } | |
| void ReleaseDC(DC dc) { | |
| // if (dc.font.xfont) { | |
| // XFreeFont(dc.dpy, dc.font.xfont); | |
| // } | |
| if(dc.canvas) { | |
| xcb_free_pixmap(dc.c, dc.canvas); | |
| } | |
| xcb_free_gc(dc.c, dc.gc); | |
| } | |
| uint32_t GetColor(DC dc, const char *colstr){ | |
| unsigned int r = 0, g = 0, b = 0; | |
| uint32_t pixel = 0; | |
| xcb_alloc_color_reply_t *reply; | |
| /* convert 24 bit color values to 48 bit color values | |
| * with assistance from bspwm by Baskerville | |
| * https://github.com/baskerville/bspwm */ | |
| fprintf(stderr, "color string: %s\n", colstr+1); | |
| if (sscanf(colstr+1, "%2x%2x%2x", &r, &g, &b) == 3) { | |
| // convert color to 48 bit 0x23 * 0x101 = 0x2323 | |
| fprintf(stderr, "r: %x, g: %x, b: %x\n", r, g, b); | |
| r *= 0x101; | |
| g *= 0x101; | |
| b *= 0x101; | |
| if((reply = xcb_alloc_color_reply(dc.c, xcb_alloc_color(dc.c, dc.screen->default_colormap, r, g, b), NULL))) { | |
| pixel = reply->pixel; | |
| fprintf(stderr, "GetColor success\n"); | |
| free(reply); | |
| } | |
| } | |
| return pixel; | |
| } | |
| static bool | |
| TextFontInit(DC dc, const char* fontstr) { | |
| bool response = false; | |
| xcb_font_t font; | |
| xcb_void_cookie_t cookie; | |
| xcb_generic_error_t *error; | |
| xcb_query_font_cookie_t fookie; | |
| xcb_query_font_reply_t *reply; | |
| font = xcb_generate_id(dc.c); | |
| cookie = xcb_open_font_checked(dc.c, font, strlen(fontstr), fontstr); | |
| if(!((error = xcb_request_check(dc.c, cookie)))) { | |
| fookie = xcb_query_font(dc.c, font); | |
| if ((reply = xcb_query_font_reply(dc.c, fookie, NULL))) { | |
| dc.font.ascent = reply->font_ascent; | |
| dc.font.descent = reply->font_descent; | |
| dc.font.width = reply->max_bounds.character_width; | |
| dc.font.xfont = font; | |
| response = true; | |
| } | |
| } | |
| fprintf(stderr, "cannot load font"); | |
| free(error); | |
| return response; | |
| } | |
| void TextFontLoad(DC dc, const char *fontstr) { | |
| if(!TextFontInit(dc, fontstr ? fontstr : DEFAULTFN)) { | |
| if(fontstr != NULL) { | |
| fprintf(stderr, "cannot load font '%s'\n", fontstr); | |
| } | |
| else if(fontstr == NULL || !TextFontInit(dc, DEFAULTFN)) { | |
| fprintf(stderr, "cannot load font '%s'\n", DEFAULTFN); | |
| } | |
| } | |
| else { | |
| dc.font.height = dc.font.ascent + dc.font.descent; | |
| } | |
| } | |
| bool Map(DC dc, uint16_t w, uint16_t h) { | |
| xcb_void_cookie_t cookie = | |
| xcb_copy_area_checked(dc.c, dc.canvas, dc.window, dc.gc, 0, 0, 0, 0, w, h); | |
| xcb_generic_error_t *error; | |
| bool result = false; | |
| if ((error = xcb_request_check(dc.c, cookie))) { | |
| fprintf(stderr, "Map failure\n"); | |
| free(error); | |
| } | |
| else { | |
| fprintf(stderr, "Map success\n"); | |
| result = true; | |
| } | |
| xcb_flush(dc.c); | |
| return result;; | |
| } | |
| void Resize(DC dc, uint16_t w, uint16_t h) { | |
| xcb_generic_error_t *error; | |
| xcb_void_cookie_t cookie; | |
| dc.w = w; | |
| dc.h = h; | |
| dc.canvas = xcb_generate_id(dc.c); | |
| cookie = xcb_create_pixmap_checked(dc.c, dc.screen->root_depth, dc.canvas, dc.screen->root, w, h); | |
| if ((error = xcb_request_check(dc.c, cookie))) { | |
| fprintf(stderr, "Resize failure\n"); | |
| free(error); | |
| } | |
| else | |
| fprintf(stderr, "Resize success\n"); | |
| } | |
| bool DrawTextCW(DC dc, const char *text, size_t n, color_t col) { | |
| xcb_void_cookie_t cookie; | |
| xcb_generic_error_t *error; | |
| bool result = false; | |
| int xp = dc.x + dc.font.height/2, | |
| yp = dc.y + dc.font.ascent+1; | |
| uint32_t val[3] = { FG(dc, col), BG(dc, col), dc.font.xfont }; | |
| uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; | |
| cookie = xcb_change_gc_checked(dc.c, dc.gc, mask, val); | |
| if(((error = xcb_request_check(dc.c, cookie)))) { | |
| fprintf(stderr, "errror could not complete DrawText request"); | |
| free(error); | |
| } | |
| else { | |
| cookie = xcb_image_text_8_checked(dc.c, n, dc.canvas, dc.gc, xp, yp, text); | |
| if(((error = xcb_request_check(dc.c, cookie)))) { | |
| fprintf(stderr, "errror could not complete DrawText request"); | |
| free(error); | |
| } | |
| else { | |
| result = true; | |
| } | |
| } | |
| return result; | |
| } | |
| uint32_t DrawTextW(DC dc, const char *text, size_t len) { | |
| //xcb_query_text_extents_cookie_t cookie; | |
| //xcb_query_text_extents_reply_t *reply; | |
| int32_t result = 0, width = len; | |
| const char* string = strndup(text, len); | |
| /* | |
| xcb_char2b_t *string; | |
| string = calloc(1, sizeof(xcb_char2b_t)); | |
| string->byte1 = 0; | |
| string->byte2 = 0; | |
| memcpy(&string->byte2, text, sizeof(char)); | |
| cookie = xcb_query_text_extents(dc.c, dc.font.xfont, len, string); | |
| if ((reply = xcb_query_text_extents_reply(dc.c, cookie, NULL))) { | |
| width = reply->overall_width; | |
| free(reply); | |
| result = width; | |
| } | |
| */ | |
| xcb_image_text_8(dc.c, len, dc.window, dc.gc, 12, 12, string); | |
| return 0; | |
| } | |
| bool DrawText(DC dc, const char *text) { | |
| return DrawTextW(dc, text, strlen(text)) + dc.font.height; | |
| } | |
| bool DrawTextColor(DC dc, const char *text, color_t col) { | |
| char buf[BUFSIZ]; | |
| size_t n = strlen(text), | |
| mn = MIN(n, sizeof buf); | |
| /** | |
| * shorten text if necessary | |
| */ | |
| for (mn = MIN(n, sizeof buf); mn > 0; mn--) { | |
| DrawTextW(dc, text, mn); // + dc.font.height / 2 > dc.w); | |
| } | |
| memcpy(buf, text, mn); | |
| if(mn < n) { | |
| for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.'); | |
| } | |
| // DrawRect(dc, 0, 0, dc.w, dc.h, true, BG(dc, col)); | |
| return DrawTextCW(dc, buf, mn, col); | |
| } | |
| DC CreateDC(xcb_connection_t *connection, xcb_screen_t* screen, xcb_window_t window) { | |
| DC dc = { | |
| .c = connection, | |
| .gc = xcb_generate_id(connection), | |
| .screen = screen, | |
| .mask = XCB_GC_LINE_STYLE | XCB_GC_CAP_STYLE | XCB_GC_JOIN_STYLE, | |
| .window = window, | |
| .DrawRect = DrawRect, | |
| .FillRect = FillRect, | |
| .GetColor = GetColor, | |
| .LoadFont = TextFontInit, | |
| .DrawText = DrawText, | |
| .DrawTextW = DrawTextW, | |
| .DrawTextCW = DrawTextCW, | |
| }; | |
| uint32_t values[3] = { screen->black_pixel, screen->white_pixel, 0 }; | |
| uint32_t rect[3] = { XCB_LINE_STYLE_SOLID, XCB_CAP_STYLE_BUTT, XCB_JOIN_STYLE_MITER }; | |
| uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK; | |
| xcb_create_gc(connection, dc.gc, dc.screen->root, mask, rect); | |
| return dc; | |
| } |
| #define FG(dc, col) ((col)[(dc).invert ? ColBG : ColFG]) | |
| #define BG(dc, col) ((col)[(dc).invert ? ColFG : ColBG]) | |
| enum { ColBG, ColFG, ColBorder, ColLast }; | |
| typedef uint32_t color_t [ColLast]; | |
| typedef struct DC { | |
| uint32_t x, y, w, h; | |
| uint8_t invert; | |
| xcb_connection_t *c; | |
| uint32_t mask; | |
| xcb_screen_t *screen; | |
| xcb_window_t window; | |
| xcb_gcontext_t gc; | |
| xcb_pixmap_t canvas; | |
| struct { | |
| uint16_t ascent; | |
| uint16_t descent; | |
| uint32_t height; | |
| uint32_t width; | |
| xcb_font_t xfont; | |
| } font; | |
| bool (*DrawRect)(struct DC dc, int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t color); | |
| bool (*FillRect)(struct DC dc, int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t color); | |
| bool (*Text)(struct DC dc, const char *text, color_t col); | |
| uint32_t (*GetColor)(struct DC dc, const char *colstr); | |
| bool (*InitFont)(struct DC dc, const char *fontstr); | |
| bool (*LoadFont)(struct DC dc, const char *fontstr); | |
| bool (*MapDC)(struct DC dc, uint16_t w, uint16_t h); | |
| bool (*Resize)(struct DC dc, uint16_t w, uint16_t h); | |
| bool (*DrawText)(struct DC dc, const char *text); | |
| uint32_t (*DrawTextW)(struct DC dc, const char *text, size_t len); | |
| bool (*DrawTextCW)(struct DC dc, const char *text, size_t len, color_t col); | |
| } DC, *HDC; /* draw context */ | |
| DC CreateDC(xcb_connection_t *connection, xcb_screen_t* screen, xcb_window_t window); | |
| void ReleaseDC(DC dc); |