From 2c04b05d548987c39c8c8ba29cd7dd9ab101f093 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 27 Sep 2012 22:29:36 +0200 Subject: xcbviewfs,colorlookupgen: implement XPM loading --- Makefile | 17 +++++ colorlookupgen.c | 51 +++++++++++++ xcbviewfs.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 Makefile create mode 100644 colorlookupgen.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca94baf --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +XCBV_CFLAGS += $(shell pkg-config --cflags xcb-image xpm) -Wall -g +XCBV_LDFLAGS += $(shell pkg-config --libs xcb-image xpm) + +RGB_TXT := /usr/share/vim/vim73/rgb.txt + +xcbviewfs: xcbviewfs.c colorlookup.c + $(CC) $(XCBV_CFLAGS) -o $@ $< colorlookup.c $(XCBV_LDFLAGS) + +colorlookupgen: colorlookupgen.c + $(CC) -Wall -Werror -o $@ $< + +colorlookup.c: $(RGB_TXT) colorlookupgen + ./colorlookupgen $(RGB_TXT) > $@ + +.PHONY: clean +clean: + rm -vf xcbviewfs colorlookupgen colorlookup.c diff --git a/colorlookupgen.c b/colorlookupgen.c new file mode 100644 index 0000000..6770703 --- /dev/null +++ b/colorlookupgen.c @@ -0,0 +1,51 @@ +#include +#include + +int main(int argc, char **argv) { + FILE *file = stdin; + char *funcname = "color_lookup"; + char line[256]; + char color[256]; + unsigned int total_colors = 0; + + if (argc >= 2) { + file = fopen(argv[1], "r"); + if (!file) { + perror("Error opening rgb.txt"); + return 1; + } + } + + if (argc >= 3) { + funcname = argv[1]; + } + + printf("#include \n"); + printf("int %s(char *name, unsigned char color[3]) {\n", funcname); + printf( + " static struct {\n" + " char *name;\n" + " unsigned char color[3];\n" + " } colors[] = {\n"); + while (fgets(line, sizeof(line), file) != NULL) { + uint8_t r, g, b; + int matches = sscanf(line, "%hhd %hhd %hhd %255[^\n]", + &r, &g, &b, color); + if (matches == 4) { + ++total_colors; + printf(" { \"%s\", { %d, %d, %d } },\n", color, r, g, b); + } + } + printf( + " };\n" + " int i = 0;\n" + " for (; i<%d; ++i)\n" + " if (!strcasecmp(colors[i].name, name)) {\n" + " memcpy(color, colors[i].color, 3);\n" + " return 1;\n" + " }\n" + " return 0;\n" + "}\n", total_colors); + + return 0; +} diff --git a/xcbviewfs.c b/xcbviewfs.c index 8c57822..e7ab1d6 100644 --- a/xcbviewfs.c +++ b/xcbviewfs.c @@ -7,11 +7,17 @@ */ #include +#include //#include #include #include #include #include +#include +#include + +/* defined in colorlookup.c which is generated by colorlookupgen.c */ +extern int color_lookup(char *name, uint8_t color[3]); #define die(msg) do { \ fprintf(stderr, msg); \ @@ -24,6 +30,8 @@ } while (0) static unsigned char empty_cursor_data[32]; +static int load_xpm_image(char *filename, unsigned char **data, + unsigned int *len, unsigned int *width, unsigned int *height); int main(int argc, char **argv) { xcb_connection_t *c; @@ -31,29 +39,44 @@ int main(int argc, char **argv) { xcb_window_t win; xcb_cursor_t cursor; + char *filename; + unsigned char *image_data; + unsigned int image_data_len; + unsigned int image_width, image_height; + xcb_pixmap_t pmap = 0; + xcb_gcontext_t gc = 0; + /* options */ int grab_pointer = 0; int hide_cursor = 0; int grab_kbd = 0; int fullscreen = 0; + int draw_picture = 1; //grab_kbd = !(fullscreen = grab_pointer = hide_cursor = 0 ); if (argc < 2) { fprintf(stderr, "Usage: %s filename\n", *argv); return 1; } + filename = argv[1]; + c = xcb_connect(NULL, NULL); if (!c) { fprintf(stderr, "Cannot open display\n"); return 1; } + + if (!load_xpm_image(filename, &image_data, &image_data_len, + &image_width, &image_height)) + bail(disconnect, "Failed to load XPM image\n"); + screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data; win = xcb_generate_id(c); /* For each mask, need to add valwin item in order as defined by the * enum, see See http://xcb.freedesktop.org/tutorial/events/ */ uint32_t mask = XCB_CW_EVENT_MASK; uint32_t valwin[] = { - XCB_EVENT_MASK_KEY_PRESS, + XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS, }; xcb_create_window(c, 0, win, /* conn, depth, WinID */ screen->root, /* parent */ @@ -132,10 +155,49 @@ int main(int argc, char **argv) { property, XCB_ATOM_ATOM, 32, 1, &data); xcb_flush(c); } + if (draw_picture) { + /** + * X-Bitmap, XY-Pixmap or Z-Pixmap? See + * http://www.linux.ie/old-list/23540.html + */ + xcb_image_t *img; + img = xcb_image_create_native(c, image_width, image_height, + XCB_IMAGE_FORMAT_Z_PIXMAP, + 24, /* depth */ + image_data, + image_data_len, + image_data); + + pmap = xcb_generate_id(c); + xcb_create_pixmap(c, 24, pmap, win, image_width, image_height); + + gc = xcb_generate_id(c); + uint32_t mask = XCB_GC_BACKGROUND; + uint32_t value_list[] = { + screen->black_pixel + }; + xcb_create_gc(c, gc, pmap, mask, value_list); + + xcb_image_put(c, pmap, gc, img, 0, 0, 0); + xcb_flush(c); + + xcb_image_destroy(img); + } + xcb_generic_event_t *e; int running = 1; while (running && (e = xcb_wait_for_event(c))) { switch (e->response_type) { + case XCB_EXPOSE: + if (!draw_picture) break; + xcb_expose_event_t *ee = (xcb_expose_event_t *)e; + xcb_copy_area(c, pmap, win, gc, + ee->x, ee->y, + ee->x, ee->y, + ee->width, + ee->height); + xcb_flush(c); + break; case XCB_KEY_PRESS: ; xcb_button_press_event_t *press = (xcb_button_press_event_t *)e; xcb_keycode_t key = press->detail; @@ -145,11 +207,167 @@ int main(int argc, char **argv) { } free(e); } + if (pmap) xcb_free_pixmap(c, pmap); ungrab_kbd: xcb_ungrab_keyboard(c, XCB_CURRENT_TIME); ungrab_pointer: xcb_ungrab_pointer(c, XCB_CURRENT_TIME); disconnect: + if (!draw_picture) free(image_data); /* TODO: check other error paths */ xcb_disconnect(c); return 0; } + +#define print_once(...) do { \ + static int printed = 0; \ + if (!printed) { \ + fprintf(stderr, __VA_ARGS__); \ + printed = 1; \ + } \ +} while(0) + +static void xpm_parse_color_line(char *line, uint8_t color[3]) { + int search_color = 0; + char c = *line; + while (c) { + char *next_ws; + while (c == ' ' || c == '\t') + c = *++line; + + next_ws = strchr(line, ' '); + if (!next_ws) next_ws = strchr(line, '\t'); + if (next_ws) *next_ws = 0; + + if (search_color) { + if (line[0] == '#') { + unsigned int hex = strtol(line, NULL, 16); + if (strlen(line) == 4) { /* #RGB */ + color[0] = ((hex >> 2) & 0xF) * 0x11; + color[1] = ((hex >> 1) & 0xF) * 0x11; + color[2] = (hex & 0xF) * 0x11; + } else { /* assume #RRGGBB */ + color[0] = (hex >> 8) & 0xFF; + color[1] = (hex >> 4) & 0xFF; + color[2] = hex & 0xFF; + } + return; + } else if (line[0] == '%') { + /* HSV not implemented */ + print_once("HSV not implemented\n"); + } else if (strcasecmp("None", line) == 0) { + /* transparant wut? */ + memset(color, 0, 3); + return; + } else if (color_lookup(line, color)) { + /* TODO: handle colors with whitespace */ + return; + } else { + print_once("Color %s unimplemented\n", line); + } + search_color = 0; + } else if (strcmp(line, "c") == 0) { + search_color = 1; + } else { + /* not implemented */ + print_once("Color type not implemented: %s\n", line); + } + /* continue searching if there are more tokens */ + if (next_ws) line = next_ws + 1; + else break; + } + color[0] = color[1] = color[2] = 0x42; + print_once("No color found\n"); +} + +/* better use a RB tree instead of a linked list... */ +typedef struct _ColorList ColorList; +struct _ColorList { + unsigned int key; /* assume that the key has less or equal bytes */ + uint8_t color[3]; /* RGB */ + ColorList *next; +}; + +static int load_xpm_image(char *filename, unsigned char **data, + unsigned int *len, unsigned int *width, unsigned int *height) { + unsigned int w, h; + char **xpm_data; + int error; + + if ((error = XpmReadFileToData(filename, &xpm_data))) { + fprintf(stderr, "Cannot load image: %s\n", XpmGetErrorString(error)); + return 0; + } + + /* 1: width height ncolors chars_per_pixel [opt_hotspot_x opt_hotspot_y] + * ncolors: char (of len chars_per_pixel) (key color)+ + * key m(ono), s(ymbolic name), g(rayscale)4(level), g(rayscale w/ + * more than 4 levels), c(color visual). "s None" is transparant + * height: width*chars_per_pixels + * (ignore Extensions), array size = 1 + ncolors + height + * XCB image: width * height * 4 bytes + * See also http://www.net.uom.gr/Books/Manuals/xpm-3-paper.pdf + */ + + unsigned int ncolors, cpp; + unsigned int i; + /* assume XPM library is not stupid */ + sscanf(xpm_data[0], "%u %u %u %u", &w, &h, &ncolors, &cpp); + if (cpp > sizeof(unsigned int)) { + bail(free_xpm, "CPP does not fit in integer data type\n"); + } + *width = w; *height = h; + *len = w * h * 4; + + *data = malloc(w * h * 4); + if (!data) { + bail(free_xpm, "Cannot allocate memory for image\n"); + } + + /* horrible performance, but ok... */ + ColorList dummy_color = {0}; + ColorList *colors = &dummy_color; + for (i=1; i<=ncolors; i++) { + char *line = xpm_data[i]; + ColorList *color = malloc(sizeof(ColorList)); + color->key = 0; + memcpy(&color->key, line, cpp); + /* skip color key */ + xpm_parse_color_line(line + cpp, color->color); + colors->next = color; + colors = color; + } + colors->next = NULL; + colors = dummy_color.next; + + /* fill data */ + unsigned int x, y; + char **all_pixels = &xpm_data[1 + ncolors]; + for (y=0; ykey != key) + color = color->next; + if (color) { + memcpy(data_line + 4 * x, color->color, cpp); + } else { + fprintf(stderr, "No color found\n"); + } + } + } + + error = 1; /* success */ + + /* clean linked list of colors */ + while (colors) { + ColorList *next = colors->next; + free(colors); + colors = next; + } +free_xpm: + XpmFree(xpm_data); + return error; +} -- cgit v1.2.1