From dbe1a198747ba925b730f006b0f682182b9222c3 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Aug 18 2023 11:57:02 +0000 Subject: replace xcb to xlib and xft --- diff --git a/app.c b/app.c index 7bf6c44..5106673 100644 --- a/app.c +++ b/app.c @@ -5,86 +5,105 @@ #include #include +#include +#include int appInit(App *app) { LOGDBG("app: init"); - app->run = 0; - app->x = app->y = app->w = app->h = 0; - app->irx = app->iry = app->irw = app->irh = 0; + + CLEARFROM(app, dpy); LOGDBG("app: init: connect to xcb"); - app->xcb = xcb_connect(NULL, NULL); - if (!app->xcb) + app->dpy = XOpenDisplay(NULL); + if (!app->dpy) return LOGERR("app: init: cannot connect to xcb"); LOGDBG("app: init: get screen"); - app->screen = xcb_setup_roots_iterator(xcb_get_setup(app->xcb)).data; - if (app->screen) { - LOGDBG("app: init: create window"); - - app->x = 0; - app->w = app->screen->width_in_pixels; - app->h = app->screen->height_in_pixels/4; - app->y = app->screen->height_in_pixels - app->h; - - #ifdef NOBORDER - int noborder = 1; - #else - int noborder = 0; - #endif - - app->win = xcb_generate_id(app->xcb); - uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; - uint32_t values[] = { - noborder, // override redirect - XCB_EVENT_MASK_EXPOSURE // event mask - | XCB_EVENT_MASK_STRUCTURE_NOTIFY - | XCB_EVENT_MASK_BUTTON_PRESS - | XCB_EVENT_MASK_BUTTON_1_MOTION - | XCB_EVENT_MASK_BUTTON_RELEASE }; - xcb_create_window( - app->xcb, // connection - XCB_COPY_FROM_PARENT, // depth (same as root) - app->win, // window id - app->screen->root, // parent window - app->x, app->y, // origin - app->w, app->h, // size - 10, // border_width - XCB_WINDOW_CLASS_INPUT_OUTPUT, // class - app->screen->root_visual, // visual - mask, // additional options mask - values ); // additional options - - uint32_t hints[9] = { - 1, // mask for input hints - 0, // value of input hint - }; - xcb_change_property_checked( - app->xcb, - XCB_PROP_MODE_REPLACE, - app->win, - XCB_ATOM_WM_HINTS, - XCB_ATOM_WM_HINTS, - 32, - sizeof(hints)/sizeof(hints[0]), hints ); + app->screen = DefaultScreen(app->dpy); + app->sw = DisplayWidth(app->dpy, app->screen); + app->sh = DisplayHeight(app->dpy, app->screen); + LOGDBG("app: init: screen=%d sw=%d sh=%d", app->screen, app->sw, app->sh); + + LOGDBG("app: init: get root window"); + app->root = RootWindow(app->dpy, app->screen); + + LOGDBG("app: init: create window"); + + app->x = 0; + app->w = app->sw; + app->h = app->sh/4; + app->y = app->sh - app->h; - // init submodules - if (graphInit(&app->graph, app)) { - graphResize(&app->graph); - if (inputInit(&app->input, app)) { - if (keyboardInit(&app->keyboard, app)) { - return 1; - } - inputDeinit(&app->input); + XSetWindowAttributes attr = {}; + #ifdef NOBORDER + attr.override_redirect = 1; + #endif + + app->win = XCreateWindow( + app->dpy, app->root, // display and parent window + app->x, app->y, app->w, app->h, 0, // position, size and border width + CopyFromParent, CopyFromParent, CopyFromParent, // depth, class and visual + CWOverrideRedirect, &attr); // mask with attributes + + XSelectInput( + app->dpy, app->win, + StructureNotifyMask + | ExposureMask + | ButtonPressMask + | ButtonReleaseMask + | Button1MotionMask ); + + LOGDBG("app: init: set window title"); + #ifndef NOBORDER + char *title = TITLE; + XTextProperty wtitle; + if (!XStringListToTextProperty(&title, 1, &wtitle)) { + LOGWRN("app: init: XStringListToTextProperty error for title: %s", title); + } else { + XSetWMName(app->dpy, app->win, &wtitle); + XFree(wtitle.value); + } + #endif + + LOGDBG("app: init: set window minimum size"); + XSizeHints *wsh = XAllocSizeHints(); + if (!wsh) { + LOGWRN("app: init: XAllocSizeHints error"); + } else { + wsh->flags = PMinSize; + wsh->min_width = MIN_WIDTH; + wsh->min_height = MIN_HEIGHT; + XSetSizeHints(app->dpy, app->win, wsh, XA_WM_NORMAL_HINTS); + XFree(wsh); + } + + // window must not grab the focus + LOGDBG("app: init: set window input hint"); + XWMHints *wmh = XAllocWMHints(); + if (!wmh) { + LOGWRN("app: init: XAllocWMHints error"); + } else { + wmh->flags = InputHint; + wmh->input = False; + XSetWMHints(app->dpy, app->win, wmh); + XFree(wmh); + } + + // init submodules + if (graphInit(&app->graph, app)) { + graphResize(&app->graph); + if (inputInit(&app->input, app)) { + if (keyboardInit(&app->keyboard, app)) { + return 1; } - graphDeinit(&app->graph); + inputDeinit(&app->input); } + graphDeinit(&app->graph); } else { - LOGERR("app: init: no screen found"); + XCloseDisplay(app->dpy); } - xcb_disconnect(app->xcb); return 0; } @@ -94,7 +113,7 @@ void appDeinit(App *app) { keyboardDeinit(&app->keyboard); inputDeinit(&app->input); graphDeinit(&app->graph); - xcb_disconnect(app->xcb); + XCloseDisplay(app->dpy); } @@ -106,23 +125,26 @@ int appRun(App *app) { app->run = 1; LOGDBG("app: run: show window"); - xcb_map_window(app->xcb, app->win); + XMapWindow(app->dpy, app->win); - LOGDBG("app: run: event loop"); - - int fd = xcb_get_file_descriptor(app->xcb); + LOGDBG("app: run: get connection file descriptor"); + int fd = ConnectionNumber(app->dpy); if (fd < 0) - LOGERR("app: run: cannot get xcb file descruptor"); + LOGERR("app: run: cannot get XLib file descruptor"); + LOGDBG("app: run: event loop"); int buttonDown = 0; unsigned int buttonDownMs = 0; while(1) { - xcb_generic_event_t *event = NULL; - xcb_flush(app->xcb); + int hasEvent = 0; + XEvent event = {}; + // wait for next event if (buttonDown == 1) { - event = xcb_poll_for_event(app->xcb); - if (!event) { + if (XPending(app->dpy)) { + XNextEvent(app->dpy, &event); + hasEvent = 1; + } else { // manually call select for the X-socket to use timeout unsigned int dt = monotonicMs() - buttonDownMs; if (dt < LONGPRESS_MS) { @@ -133,78 +155,76 @@ int appRun(App *app) { LOGDBG("app: run: use select fd=%d, timeout=%ums, sec=%ld, usec=%ld", fd, dt, t.tv_sec, t.tv_usec); - xcb_flush(app->xcb); - fd_set fds = {}; FD_ZERO(&fds); FD_SET(fd, &fds); + XFlush(app->dpy); select(fd + 1, &fds, NULL, NULL, &t); - event = xcb_poll_for_event(app->xcb); + if (XPending(app->dpy)) { + XNextEvent(app->dpy, &event); + hasEvent = 1; + } } } } else { - event = xcb_wait_for_event(app->xcb); - if (!event) break; + XNextEvent(app->dpy, &event); + hasEvent = 1; } - if (event) { - switch(event->response_type & ~0x80) { - case XCB_EXPOSE: { - xcb_expose_event_t *e = (xcb_expose_event_t*)event; - LOGDBG("app: expose: x=%d, y=%d, w=%d, h=%d", e->x, e->y, e->width, e->height); - appInvalidateRect(app, e->x, e->y, e->width, e->height); + if (hasEvent) { + switch(event.type) { + case Expose: { + LOGDBG( "app: expose: x=%d, y=%d, w=%d, h=%d", + event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height ); + appInvalidateRect(app, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); break; } - case XCB_CONFIGURE_NOTIFY: { - xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t*)event; - if ( e->width - && e->height - && ( app->x != e->x - || app->y != e->y - || app->w != e->width - || app->h != e->height )) + case ConfigureNotify: { + if ( event.xconfigure.width + && event.xconfigure.height + && ( app->x != event.xconfigure.x + || app->y != event.xconfigure.y + || app->w != event.xconfigure.width + || app->h != event.xconfigure.height )) { - LOGDBG("app: moved: x=%d y=%d w=%d, h=%d", e->x, e->y, e->width, e->height); - int resized = app->w != e->width || app->h != e->height; - app->x = e->x; - app->y = e->y; - app->w = e->width; - app->h = e->height; + LOGDBG( "app: moved: x=%d y=%d w=%d, h=%d", + event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height ); + int resized = app->w != event.xconfigure.width || app->h != event.xconfigure.height; + app->x = event.xconfigure.x; + app->y = event.xconfigure.y; + app->w = event.xconfigure.width; + app->h = event.xconfigure.height; if (resized) graphResize(&app->graph); } break; } - case XCB_BUTTON_PRESS: { - xcb_button_press_event_t *e = (xcb_button_press_event_t*)event; - if (e->detail == 1) { - LOGDBG("app: mouse down: x=%d, y=%d", e->event_x, e->event_y); + case ButtonPress: { + if (event.xbutton.button == 1) { + LOGDBG("app: mouse down: x=%d, y=%d", event.xbutton.x, event.xbutton.y); if (!buttonDown) { buttonDown = 1; buttonDownMs = monotonicMs(); } - keyboardMouseDown(&app->keyboard, e->event_x, e->event_y); + keyboardMouseDown(&app->keyboard, event.xbutton.x, event.xbutton.y); } break; } - case XCB_BUTTON_RELEASE: { - xcb_button_press_event_t *e = (xcb_button_press_event_t*)event; - if (e->detail == 1) { + case ButtonRelease: { + if (event.xbutton.button == 1) { LOGDBG("app: mouse up"); buttonDown = 0; keyboardMouseUp(&app->keyboard); } break; } - case XCB_MOTION_NOTIFY: { - xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*)event; - if (e->state & Button1Mask) { - LOGDBG("app: mouse motion: x=%d, y=%d", e->event_x, e->event_y); - keyboardMouseMotion(&app->keyboard, e->event_x, e->event_y); + case MotionNotify: { + if (event.xmotion.state & Button1Mask) { + LOGDBG("app: mouse motion: x=%d, y=%d", event.xmotion.x, event.xmotion.y); + keyboardMouseMotion(&app->keyboard, event.xmotion.x, event.xmotion.y); } break; } default: break; } - free(event); } if (buttonDown == 1 && monotonicMs() - buttonDownMs >= LONGPRESS_MS) { @@ -246,13 +266,17 @@ void appStop(App *app, int err) { void appMove(App *app, int x, int y, int w, int h) { LOGDBG("app: move: x=%d, y=%d, w=%d, h=%d", x, y, w, h); - if (w <= 0 || h <= 0) return; - if (app->x == x && app->y == y && app->w == w && app->h == h) - return; - uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - uint32_t values[] = { x, y, w, h }; - xcb_configure_window(app->xcb, app->win, mask, values); - xcb_flush(app->xcb); + if (w < MIN_WIDTH || h < MIN_HEIGHT) return; + + unsigned int mask = 0; + XWindowChanges ch = {}; + if (app->x != x) ch.x = x, mask |= CWX; + if (app->y != y) ch.y = y, mask |= CWY; + if (app->w != w) ch.width = w, mask |= CWWidth; + if (app->h != h) ch.height = h, mask |= CWHeight; + if (!mask) return; + + XConfigureWindow(app->dpy, app->win, mask, &ch); } diff --git a/app.h b/app.h index 4ccc3c4..8807c71 100644 --- a/app.h +++ b/app.h @@ -15,14 +15,16 @@ struct App { Graph graph; // these fields will be set while initialization - xcb_connection_t *xcb; - xcb_screen_t *screen; - xcb_window_t win; + Display *dpy; + int screen; + Window root; + Window win; + int sw, sh; // dynamic fields - int irx, iry, irw, irh; - int x, y, w, h; int run; + int x, y, w, h; + int irx, iry, irw, irh; }; diff --git a/build.sh b/build.sh index 6d2b5cc..2659160 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ set -e -FLAGS="$(pkg-config --cflags --libs xcb x11 xtst)" +FLAGS="$(pkg-config --cflags --libs x11 xft xtst)" FLAGS="$FLAGS -Wall" if [ "$1" = "debug" ]; then FLAGS="$FLAGS -g -O0" @@ -10,6 +10,6 @@ else FLAGS="$FLAGS -O3 -DNDEBUG" fi -cc $FLAGS main.c -o coolkbd +cc main.c $FLAGS -o coolkbd echo done diff --git a/common.h b/common.h index 89bb61f..87454f9 100644 --- a/common.h +++ b/common.h @@ -2,6 +2,9 @@ #define COMMON_H +#include "config.h" + + #define _XOPEN_SOURCE 600 #include @@ -10,16 +13,12 @@ #include #include -#include - - - -#define NOBORDER -#define LONGPRESS_MS 1500 +#include #define COUNTOF(x) (sizeof(x)/sizeof((x)[0])) +#define CLEARFROM(x, field) memset( &((x)->field), 0, sizeof(*(x)) - ((const char *)&((x)->field) - (const char *)(x)) ) #define LOG(stream, prefix, suffix, result, ...) \ ( fputs(prefix, stream), fprintf(stream, __VA_ARGS__), fputs(suffix, stream), fflush(stream), dummy(result) ) @@ -35,7 +34,6 @@ #define LOGERR(...) LOG(stderr, "ERROR: ", "\n", 0, __VA_ARGS__) - struct App; typedef struct App App; diff --git a/config.h b/config.h new file mode 100644 index 0000000..e02ea37 --- /dev/null +++ b/config.h @@ -0,0 +1,18 @@ +#ifndef CONFIG_H +#define CONFIG_H + + +//#define NOBORDER +#define TITLE "coolkbd" +#define MIN_WIDTH 200 +#define MIN_HEIGHT 100 +#define LONGPRESS_MS 1500 + +#define FONTS { "Canada Sans:bold", "Canada Sans", "Canada", "DejaVu Sans:bold", "DejaVu Sans", "DejaVu" } +#define FONT_SIZES { 2, 3, 4, 6, 8, 10, 12, 16, 18, 24, 32, 40, 48 } +#define FONT_MAX_SIZES 16 + +#define LABEL_MAXLEN 32 + + +#endif diff --git a/graph.c b/graph.c index ef5749e..05a7abd 100644 --- a/graph.c +++ b/graph.c @@ -6,94 +6,66 @@ int graphInit(Graph *g, App *app) { LOGDBG("graph: init"); - const uint16_t F = 65535; // full - const uint16_t H = 32768; // half - const uint16_t Q = 16384; // quart - const uint16_t colorsRgb[GS_COUNT*2][3] = { // background, foreground - { 0, 0, Q }, { 0, 0, Q }, // window - { 0, 0, H }, { 0, 0, H }, // inactive button - { 0, 0, F }, { 0, 0, F }, // active button - { H, 0, F }, { H, 0, F }, // active button 2 - { 0, 0, H }, { F, F, 0 }, // inactive text - { 0, 0, F }, { F, F, 0 }, // active text - { H, 0, F }, { F, F, 0 } }; // active text 2 - - // create palette + CLEARFROM(g, app); g->app = app; - g->cm = xcb_generate_id(g->app->xcb); - xcb_create_colormap(g->app->xcb, XCB_COLORMAP_ALLOC_NONE, g->cm, g->app->win, g->app->screen->root_visual); - - // encqueue colors allocarion requests - xcb_alloc_color_cookie_t cookies[GS_COUNT*2]; - for(int i = 0; i < GS_COUNT*2; ++i) - cookies[i] = xcb_alloc_color(g->app->xcb, g->cm, colorsRgb[i][0], colorsRgb[i][1], colorsRgb[i][2]); - - // take color allocation replies (take color indices) - uint32_t colors[GS_COUNT*2]; - for(int i = 0; i < GS_COUNT*2; ++i) { - xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(g->app->xcb, cookies[i], NULL); - colors[i] = reply->pixel; - free(reply); - } - // associate palette with the window - xcb_change_window_attributes(g->app->xcb, g->app->win, XCB_CW_COLORMAP, &g->cm); + enum { + F = 65535u, // full + H = 32768u, // half + Q = 16384u }; // quart + static const XRenderColor renderColors[GC_COUNT] = { + { 0, 0, Q, F }, // window + { 0, 0, H, F }, // inactive button + { 0, 0, F, F }, // active button + { H, 0, F, F }, // active button 2 + { F, F, 0, F }, // inactive text + { F, F, 0, F }, // active text + { F, F, 0, F } }; // active text 2 + + static const char *fonts[] = FONTS; + static const int sizes[FONT_MAX_SIZES] = FONT_SIZES; - char fontname[1024] = "fixed"; - { // scan fonts - int found = 0; - int maxcnt = 1024; - const char *pattern = "*misc*medium-r-normal*-c-0-ISO10646-1*"; - LOGDBG("graph: init: scan fonts by pattern [%s], maxcnt=%d", pattern, maxcnt); - xcb_list_fonts_cookie_t c = xcb_list_fonts(g->app->xcb, maxcnt, strlen(pattern), pattern); - xcb_list_fonts_reply_t *r = xcb_list_fonts_reply(g->app->xcb, c, NULL); - if (r) { - int cnt = xcb_list_fonts_names_length(r); - LOGDBG("graph: init: found %d fonts", cnt); - xcb_str_iterator_t it = xcb_list_fonts_names_iterator(r); - for(int i = 0; i < cnt; ++i) { - int l = xcb_str_name_length(it.data); - if (!found && l < sizeof(fontname)) { - memcpy(fontname, xcb_str_name(it.data), l); - fontname[l] = 0; - found = 1; - } - - #ifndef NDEBUG - char buf[1024] = {}; - if (l > 1023) l = 1023; - memcpy(buf, xcb_str_name(it.data), l); - buf[l] = 0; - LOGDBG("graph: init: found font: %s", buf); - #endif - - xcb_str_next(&it); + LOGDBG("graph: init: create XftDraw"); + g->draw = XftDrawCreate( + g->app->dpy, + g->app->win, + DefaultVisual(g->app->dpy, g->app->screen), + DefaultColormap(g->app->dpy, g->app->screen) ); + if (!g->draw) + return LOGERR("graph: init: cannot create XftDraw"); + + LOGDBG("graph: init: open fonts"); + char buf[1024] = {}; + for(int i = 0; i < FONT_MAX_SIZES; ++i) { + if (!sizes[i]) break; + for(int j = 0; j < COUNTOF(fonts); ++j) { + snprintf(buf, sizeof(buf), "%s:size=%d", fonts[j], sizes[i]); + LOGDBG("graph: init: try open font [%s]", buf); + g->fonts[g->fontsCnt] = XftFontOpenName(g->app->dpy, g->app->screen, buf); + if (g->fonts[g->fontsCnt]) { + ++g->fontsCnt; + break; + } else { + LOGDBG("graph: init: cannot open font [%s]", buf); } - free(r); - } else { - LOGWRN("graph: init: cannot list fonts"); } } - // init font - LOGDBG("graph: init: open font [%s]", fontname); - g->font = xcb_generate_id(g->app->xcb); - xcb_void_cookie_t c = xcb_open_font_checked(g->app->xcb, g->font, strlen(fontname), fontname); - if (xcb_request_check(g->app->xcb, c)) { - LOGERR("graph: init: cannot open font [%s]", fontname); - xcb_change_window_attributes(g->app->xcb, g->app->win, XCB_CW_COLORMAP, &g->app->screen->default_colormap); - xcb_free_colormap(g->app->xcb, g->cm); + if (!g->fontsCnt) { + LOGERR("graph: init: cannot open any font"); + XftDrawDestroy(g->draw); + CLEARFROM(g, app); return 0; } - - // create contexts - uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT | XCB_GC_GRAPHICS_EXPOSURES; - uint32_t values[4] = {0, 0, g->font, 0}; - for(int i = 0; i < GS_COUNT; ++i) { - values[0] = colors[i*2 + 1]; // foreground - values[1] = colors[i*2]; // background - g->gc[i] = xcb_generate_id(g->app->xcb); - xcb_create_gc(g->app->xcb, g->gc[i], g->app->win, mask, values); + + LOGDBG("graph: init: allocate colors"); + for(int i = 0; i < GC_COUNT; ++i) { + XftColorAllocValue( + g->app->dpy, + DefaultVisual(g->app->dpy, g->app->screen), + DefaultColormap(g->app->dpy, g->app->screen), + &renderColors[i], + &g->colors[i] ); } return 1; @@ -102,11 +74,15 @@ int graphInit(Graph *g, App *app) { void graphDeinit(Graph *g) { LOGDBG("graph: deinit"); - for(int i = 0; i < GS_COUNT; ++i) - xcb_free_gc(g->app->xcb, g->gc[i]); - xcb_close_font(g->app->xcb, g->font); - xcb_change_window_attributes(g->app->xcb, g->app->win, XCB_CW_COLORMAP, &g->app->screen->default_colormap); - xcb_free_colormap(g->app->xcb, g->cm); + for(int i = 0; i < GC_COUNT; ++i) + XftColorFree( + g->app->dpy, + DefaultVisual(g->app->dpy, g->app->screen), + DefaultColormap(g->app->dpy, g->app->screen), + &g->colors[i] ); + for(int i = 0; i < g->fontsCnt; ++i) + XftFontClose(g->app->dpy, g->fonts[i]); + XftDrawDestroy(g->draw); } @@ -115,83 +91,63 @@ void graphResize(Graph *g) void graphDrawBackgound(Graph *g, int x, int y, int w, int h) { - xcb_rectangle_t r = {x, y, w, h}; - xcb_poly_fill_rectangle(g->app->xcb, g->app->win, g->gc[GS_WINDOW], 1, &r); + XftDrawRect(g->draw, &g->colors[GC_WINDOW], x, y, w, h); } void graphDrawButton(Graph *g, int x, int y, int w, int h, int active) { - GraphStyle s = active == 1 ? GS_BUTTON_ACTIVE - : active == 2 ? GS_BUTTON_ACTIVE2 - : GS_BUTTON_INACTIVE; - xcb_rectangle_t r = {x, y, w, h}; - xcb_poly_fill_rectangle(g->app->xcb, g->app->win, g->gc[s], 1, &r); + GraphStyle s = active == 1 ? GC_BUTTON_ACTIVE + : active == 2 ? GC_BUTTON_ACTIVE2 + : GC_BUTTON_INACTIVE; + XftDrawRect(g->draw, &g->colors[s], x, y, w, h); } void textLayoutInit(TextLayout *tl, Graph *g, const char *text) { + CLEARFROM(tl, g); tl->g = g; - tl->x = tl->y = 0; - tl->len = 0; - - // convert utf8 to char2b - const unsigned char *s = (const unsigned char *)text; - while(*s && tl->len < TL_MAXLEN) { - unsigned int d = 0xfffd; - if (!(s[0] & 0x80)) { - d = s[0]; - } else - if ( (s[0] & 0xe0) == 0xc0 - && (s[1] & 0xc0) == 0x80 ) - { - d = ((s[0] & 0x1f) << 6) - | ((s[1] & 0x3f) << 0); - ++s; - } else - if ( (s[0] & 0xf0) == 0xe0 - && (s[1] & 0xc0) == 0x80 - && (s[2] & 0xc0) == 0x80 ) - { - d = ((s[0] & 0x0f) << 12) - | ((s[1] & 0x3f) << 6) - | ((s[2] & 0x3f) << 0); - s += 2; - } - tl->text[tl->len].byte1 = d >> 8; - tl->text[tl->len].byte2 = d & 0xff; - ++tl->len; - ++s; - } - + tl->len = text ? strlen(text) : 0; + tl->text = text; if (!tl->len) return; - // measure text - xcb_query_text_extents_cookie_t cookie = - xcb_query_text_extents(tl->g->app->xcb, tl->g->font, tl->len, tl->text); - xcb_query_text_extents_reply_t *tx = xcb_query_text_extents_reply(tl->g->app->xcb, cookie, NULL); - if (!tx) { - LOGWRN("text layout: cannot get text extents for string [%s] len=%d", text, tl->len); - //tl->len = 0; - return; + for(int i = 0; i < tl->g->fontsCnt; ++i) { + XGlyphInfo e = {}; + XftTextExtentsUtf8(g->app->dpy, g->fonts[i], (const FcChar8*)tl->text, tl->len, &e); + int w = e.width; + int h = tl->g->fonts[i]->ascent; + int minw = h + h/3; + tl->sizes[i].x = -e.x - w/2; + tl->sizes[i].y = h/2; + tl->sizes[i].w = w < minw ? minw : w; + tl->sizes[i].h = h; } - - tl->x = -tx->overall_left - tx->overall_width/2; - tl->y = tx->font_ascent/2; - free(tx); } void textLayoutDeinit(TextLayout *tl) - { } + { CLEARFROM(tl, g); } -void textLayoutDraw(TextLayout *tl, int x, int y, int active) { +void textLayoutDraw(TextLayout *tl, int x, int y, int w, int h, int active) { if (!tl->len) return; - GraphStyle s = active == 1 ? GS_TEXT_ACTIVE - : active == 2 ? GS_TEXT_ACTIVE2 - : GS_TEXT_INACTIVE; - xcb_image_text_16(tl->g->app->xcb, tl->len, tl->g->app->win, tl->g->gc[s], x + tl->x, y + tl->y, tl->text); + GraphStyle s = active == 1 ? GC_TEXT_ACTIVE + : active == 2 ? GC_TEXT_ACTIVE2 + : GC_TEXT_INACTIVE; + + // find font size + int i; + for(i = tl->g->fontsCnt - 1; i > 0; --i) + if (tl->sizes[i].w <= w && tl->sizes[i].h <= h) break; + + XftDrawStringUtf8( + tl->g->draw, + &tl->g->colors[s], + tl->g->fonts[i], + x + tl->sizes[i].x, + y + tl->sizes[i].y, + (const FcChar8*)tl->text, + tl->len ); } diff --git a/graph.h b/graph.h index 5605da6..e5f6202 100644 --- a/graph.h +++ b/graph.h @@ -4,34 +4,41 @@ #include "common.h" +#include "X11/Xft/Xft.h" -#define TL_MAXLEN 32 typedef enum { - GS_WINDOW, - GS_BUTTON_INACTIVE, - GS_BUTTON_ACTIVE, - GS_BUTTON_ACTIVE2, - GS_TEXT_INACTIVE, - GS_TEXT_ACTIVE, - GS_TEXT_ACTIVE2 } GraphStyle; + GC_WINDOW, + GC_BUTTON_INACTIVE, + GC_BUTTON_ACTIVE, + GC_BUTTON_ACTIVE2, + GC_TEXT_INACTIVE, + GC_TEXT_ACTIVE, + GC_TEXT_ACTIVE2 } GraphStyle; enum { - GS_COUNT = GS_TEXT_ACTIVE2 + 1 }; + GC_COUNT = GC_TEXT_ACTIVE2 + 1 }; typedef struct Graph { App *app; - xcb_colormap_t cm; - xcb_font_t font; - xcb_gcontext_t gc[GS_COUNT]; + XftDraw *draw; + XftFont *fonts[FONT_MAX_SIZES]; + int fontsCnt; + XftColor colors[GC_COUNT]; } Graph; +typedef struct TextLayoutSize { + int x, y, w, h; +} TextLayoutSize; + + typedef struct TextLayout { Graph *g; - int x, y, len; - xcb_char2b_t text[TL_MAXLEN]; + int len; + const char *text; + TextLayoutSize sizes[FONT_MAX_SIZES]; } TextLayout; @@ -45,7 +52,7 @@ void graphDrawButton(Graph *g, int x, int y, int w, int h, int active); void textLayoutInit(TextLayout *tl, Graph *g, const char *text); void textLayoutDeinit(TextLayout *tl); -void textLayoutDraw(TextLayout *tl, int x, int y, int active); +void textLayoutDraw(TextLayout *tl, int x, int y, int w, int h, int active); #endif diff --git a/keyboard.c b/keyboard.c index 2a268c6..4eb88a9 100644 --- a/keyboard.c +++ b/keyboard.c @@ -66,19 +66,26 @@ void keyDraw(Key *k, int cx, int cy, int cw, int ch) { if (inputChooseKeysym(app->input.modifiers, k->keySym, k->keySym2, k->isLetter, k->isKeypad)) tl = tl2, tl2 = &k->tl; // swap labels - int lx, lx2, ly, ly2; + int lx, lx2, ly, ly2, lw, lw2, lh, lh2; lx = lx2 = k->x + k->w/2; ly = ly2 = k->y + k->h/2; + lw = lw2 = k->w - k->w/8; + lh = lh2 = k->h - k->h/8; if (!k->isLetter && k->label2[0]) { lx -= k->w/8; ly += k->h/8; + lw -= k->w/4; + lh -= k->h/4; + lx2 += k->w/4; ly2 -= k->h/4; + lw2 -= k->w/2; + lh2 -= k->h/2; } - textLayoutDraw(tl, lx, ly, active); + textLayoutDraw(tl, lx, ly, lw, lh, active); if (!k->isLetter) - textLayoutDraw(tl2, lx2, ly2, active); + textLayoutDraw(tl2, lx2, ly2, lw2, lh2, active); } diff --git a/keyboard.h b/keyboard.h index 798dee6..de508b3 100644 --- a/keyboard.h +++ b/keyboard.h @@ -6,7 +6,7 @@ #include "input.h" -#define K_LBL_SIZE TL_MAXLEN +#define K_LBL_SIZE LABEL_MAXLEN // key flags diff --git a/main.c b/main.c index dd5a3c6..12e4cac 100644 --- a/main.c +++ b/main.c @@ -26,9 +26,8 @@ App app = { int main() { if (!appInit(&app)) return 1; - if (!appRun(&app)) - return 1; + int success = appRun(&app); appDeinit(&app); - return 0; + return !success; }