diff --git a/app.c b/app.c index aebaa04..6abd02b 100644 --- a/app.c +++ b/app.c @@ -6,11 +6,8 @@ int appInit(App *app) { LOGDBG("app: init"); - app->x = 0; - app->y = 0; - app->w = 100; - app->h = 50; app->run = 0; + app->x = app->y = app->w = app->h = 0; app->irx = app->iry = app->irw = app->irh = 0; LOGDBG("app: init: connect to xcb"); @@ -22,10 +19,22 @@ int appInit(App *app) { 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[] = { - 0, // override redirect + noborder, // override redirect XCB_EVENT_MASK_EXPOSURE // event mask | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS @@ -59,6 +68,7 @@ int appInit(App *app) { // init submodules if (graphInit(&app->graph, app)) { + graphResize(&app->graph); if (inputInit(&app->input, app)) { if (keyboardInit(&app->keyboard, app)) { return 1; @@ -121,10 +131,7 @@ int appRun(App *app) { app->y = e->y; app->w = e->width; app->h = e->height; - if (resized) { - graphResize(&app->graph); - keyboardResize(&app->keyboard); - } + if (resized) graphResize(&app->graph); } break; } @@ -159,6 +166,9 @@ int appRun(App *app) { if (app->run != 1) break; + inputUpdateModifiers(&app->input); + keyboardResize(&app->keyboard); + keyboardUpdateModifiers(&app->keyboard); if (app->irw > 0 && app->irh > 0) { LOGDBG("app: draw: x=%d, y=%d, w=%d, h=%d", app->irx, app->iry, app->irw, app->irh); graphDrawBackgound(&app->graph, app->irx, app->iry, app->irw, app->irh); diff --git a/common.c b/common.c index 0b44fa9..aaf6deb 100644 --- a/common.c +++ b/common.c @@ -317,23 +317,23 @@ int keysymString(unsigned int keySym, char *buf5bytes) { return 1; } if (uc <= 0x0007ff) { - buf5bytes[0] = 0x80 | (uc & 0x6f); - buf5bytes[1] = 0xc0 | (uc >> 6); + buf5bytes[0] = 0xc0 | (uc >> 6); + buf5bytes[1] = 0x80 | (uc & 0x1f); buf5bytes[2] = 0; return 2; } if (uc <= 0x00ffff) { - buf5bytes[0] = 0x80 | (uc & 0x6f); - buf5bytes[1] = 0x80 | ((uc >> 6) & 0x6f); - buf5bytes[2] = 0xe0 | (uc >> 12); + buf5bytes[0] = 0xe0 | (uc >> 12); + buf5bytes[1] = 0x80 | ((uc >> 6) & 0x3f); + buf5bytes[2] = 0x80 | (uc & 0x3f); buf5bytes[3] = 0; return 3; } if (uc <= 0x10ffff) { - buf5bytes[0] = 0x80 | (uc & 0x6f); - buf5bytes[1] = 0x80 | ((uc >> 6) & 0x6f); - buf5bytes[2] = 0x80 | ((uc >> 12) & 0x6f); - buf5bytes[3] = 0xf0 | (uc >> 18); + buf5bytes[0] = 0xf0 | (uc >> 18); + buf5bytes[1] = 0x80 | ((uc >> 12) & 0x3f); + buf5bytes[2] = 0x80 | ((uc >> 6) & 0x3f); + buf5bytes[3] = 0x80 | (uc & 0x3f); buf5bytes[4] = 0; return 4; } diff --git a/common.h b/common.h index d7f7f81..33d2537 100644 --- a/common.h +++ b/common.h @@ -10,6 +10,8 @@ #include +//#define NOBORDER + #define COUNTOF(x) (sizeof(x)/sizeof((x)[0])) diff --git a/graph.c b/graph.c index 76bdc48..8c89dfd 100644 --- a/graph.c +++ b/graph.c @@ -37,14 +37,56 @@ int graphInit(Graph *g, App *app) { // associate palette with the window xcb_change_window_attributes(g->app->xcb, g->app->win, XCB_CW_COLORMAP, &g->cm); + 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); + } + free(r); + } else { + LOGWRN("graph: init: cannot list fonts"); + } + } + // init font - const char *fontname = "sans"; + LOGDBG("graph: init: open font [%s]", fontname); g->font = xcb_generate_id(g->app->xcb); - xcb_open_font(g->app->xcb, g->font, strlen(fontname), fontname); + 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); + return 0; + } // create contexts uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT | XCB_GC_GRAPHICS_EXPOSURES; - uint32_t values[3] = {0, 0, g->font, 0}; + 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 @@ -86,12 +128,64 @@ void graphDrawButton(Graph *g, int x, int y, int w, int h, int active) { void textLayoutInit(TextLayout *tl, Graph *g, const char *text) { tl->g = g; - tl->text = text; - xcb_query_text_extents(g->app->xcb, g->font, strlen(text), text); + 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; + } + + 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; + } + + tl->x = -tx->overall_left - tx->overall_width/2; + tl->y = tx->font_ascent/2; + free(tx); } + void textLayoutDeinit(TextLayout *tl) { } -void textLayoutDraw(TextLayout *tl, int x, int y, int active) - { } + +void textLayoutDraw(TextLayout *tl, int x, int y, int active) { + if (!tl->len) return; + GraphStyle s = active ? GS_TEXT_ACTIVE : 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); +} + diff --git a/graph.h b/graph.h index fe5f084..bce1f64 100644 --- a/graph.h +++ b/graph.h @@ -5,6 +5,9 @@ #include "common.h" +#define TL_MAXLEN 16 + + typedef enum { GS_WINDOW, GS_BUTTON_INACTIVE, @@ -25,8 +28,8 @@ typedef struct Graph { typedef struct TextLayout { Graph *g; - int x, y; - const char *text; + int x, y, len; + xcb_char2b_t text[TL_MAXLEN]; } TextLayout; diff --git a/input.c b/input.c index 0eeea83..9783098 100644 --- a/input.c +++ b/input.c @@ -20,7 +20,8 @@ void inputPrepareKeysyms(unsigned int *ks0, unsigned int *ks1, int *isLetter, in if (*ks0) { KeySym kl = 0, ku = 0; XConvertCase(*ks0, &kl, &ku); - if (*ks0 == kl && kl != ku && ku) { + LOGDBG("XConvertCase %x %lx %lx", *ks0, kl, ku); + if (*ks0 == kl && ku && kl != ku) { if (!*ks1) *ks1 = ku; *isLetter = 1; } diff --git a/keyboard.c b/keyboard.c index 3a60592..11a3027 100644 --- a/keyboard.c +++ b/keyboard.c @@ -9,13 +9,14 @@ int keyInit(Key *k, Layout *l) { k->downKeycode = 0; k->mx = k->my = 0; k->isLetter = k->isKeypad = 0; + k->x = k->y = k->w = k->h = 0; inputPrepareKeysyms(&k->keySym, &k->keySym2, &k->isLetter, &k->isKeypad); if (!k->label[0]) keysymString(k->keySym, k->label); if (!k->label2[0]) keysymString(k->keySym2, k->label2); textLayoutInit(&k->tl, &k->l->kbd->app->graph, k->label); - textLayoutInit(&k->tl, &k->l->kbd->app->graph, k->label2); + textLayoutInit(&k->tl2, &k->l->kbd->app->graph, k->label2); return 1; } @@ -45,16 +46,21 @@ 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 = k->x + k->w/2; - int ly = k->y + k->h/2; - textLayoutDraw(tl, lx, ly, active); - - if (!k->isLetter) { - int d = k->h/4; - lx = k->x + k->w - d; - ly = k->y + d; - textLayoutDraw(tl2, lx, ly, active); + int lx, lx2, ly, ly2; + lx = lx2 = k->x + k->w/2; + ly = ly2 = k->y + k->h/2; + if (!k->isLetter && k->label2[0]) { + int dx = k->w/6; + int dy = k->h/6; + lx -= dx; + ly += dy; + lx2 += dx; + ly2 -= dy; } + + textLayoutDraw(tl, lx, ly, active); + if (!k->isLetter) + textLayoutDraw(tl2, lx2, ly2, active); } @@ -154,48 +160,50 @@ int layoutInit(Layout *l, Keyboard *kbd) { l->kbd = kbd; l->prev = NULL; l->downKey = NULL; + l->w = l->h = 0; if (!l->keysCount) return 1; // fix keys coords Key *k = &l->keys[0]; - int x0 = k->x; - int y0 = k->y; - int x1 = x0 + k->w; - int y1 = y0 + k->h; + int x0 = k->ox; + int y0 = k->oy; + int x1 = x0 + k->ow; + int y1 = y0 + k->oh; for(int i = 1; i < l->keysCount; ++i) { k = &l->keys[i]; Key *p = k - 1; // coord < 0 means relative offset from previos key -1 // zero coord of size means copy value from previous key - if (!k->x) k->x = p->x; else - if (k->x < 0) k->x = p->x + p->w - k->x - 1; - if (!k->y) k->y = p->y; else - if (k->y < 0) k->y = p->y + p->h - k->y - 1; - if (!k->w) k->w = p->w; - if (!k->h) k->h = p->h; + if (!k->ox) k->ox = p->ox; else + if (k->ox < 0) k->ox = p->ox + p->ow - k->ox - 1; + if (!k->oy) k->oy = p->oy; else + if (k->oy < 0) k->oy = p->oy + p->oh - k->oy - 1; + if (!k->ow) k->ow = p->ow; + if (!k->oh) k->oh = p->oh; // calc bounds - if (x0 > k->x) x0 = k->x; - if (y0 > k->y) y0 = k->y; - if (x1 < k->x + k->w) x1 = k->x + k->w; - if (y1 < k->y + k->h) y1 = k->y + k->h; + if (x0 > k->ox) x0 = k->ox; + if (y0 > k->oy) y0 = k->oy; + if (x1 < k->ox + k->ow) x1 = k->ox + k->ow; + if (y1 < k->oy + k->oh) y1 = k->oy + k->oh; } // fix layout size - if (!l->w) l->w = x1 + x0; - if (!l->h) l->h = y1 + y0; - LOGDBG("layout: init: size x0=%d y0=%d x1=%d y1=%d w=%d h=%d", x0, y0, x1, y1, l->w, l->h); + if (!l->ow) l->ow = x1 + x0; + if (!l->oh) l->oh = y1 + y0; + LOGDBG("layout: init: size x0=%d y0=%d x1=%d y1=%d w=%d h=%d", x0, y0, x1, y1, l->ow, l->oh); // init keys - for(int i = 0; i < l->keysCount; ++i) + for(int i = 0; i < l->keysCount; ++i) { if (!keyInit(&l->keys[i], l)) { for(--i; i >= 0; --i) keyDeinit(&l->keys[i]); return 0; } - + } + return 1; } @@ -208,6 +216,23 @@ void layoutDeinit(Layout *l) { } +void layoutResize(Layout *l) { + if (l->w == l->kbd->app->w && l->h == l->kbd->app->h) return; + LOGDBG("layout: resize w=%d h=%d", l->kbd->app->w, l->kbd->app->h); + l->w = l->kbd->app->w; + l->h = l->kbd->app->h; + float kx = l->w/(float)l->ow; + float ky = l->h/(float)l->oh; + for(int i = 0; i < l->keysCount; ++i) { + Key *k = &l->keys[i]; + k->x = (int)(k->ox*kx + 0.5f); + k->y = (int)(k->oy*ky + 0.5f); + k->w = (int)(k->ow*kx + 0.5f); + k->h = (int)(k->oh*ky + 0.5f); + } +} + + void layoutDraw(Layout *l, int cx, int cy, int cw, int ch) { for(int i = 0; i < l->keysCount; ++i) keyDraw(&l->keys[i], cx, cy, cw, ch); @@ -252,6 +277,7 @@ int keyboardInit(Keyboard *kbd, App *app) { LOGDBG("keyboard: init"); kbd->app = app; kbd->firstHeld = NULL; + kbd->lastModifiers = kbd->app->input.modifiers; kbd->current = kbd->layoutsCount > 0 ? kbd->layouts : NULL; for(int i = 0; i < kbd->layoutsCount; ++i) { @@ -263,12 +289,12 @@ int keyboardInit(Keyboard *kbd, App *app) { // fix size for(int i = 0; i < kbd->layoutsCount; ++i) { - if (kbd->w < kbd->layouts[i].w) kbd->w = kbd->layouts[i].w; - if (kbd->h < kbd->layouts[i].h) kbd->h = kbd->layouts[i].h; + if (kbd->ow < kbd->layouts[i].ow) kbd->ow = kbd->layouts[i].ow; + if (kbd->oh < kbd->layouts[i].oh) kbd->oh = kbd->layouts[i].oh; } - LOGDBG("keyboard: init: size w=%d h=%d", kbd->w, kbd->h); + LOGDBG("keyboard: init: size ow=%d oh=%d", kbd->ow, kbd->oh); - appMove(app, app->x, app->y, kbd->w, kbd->h); + //appMove(app, app->x, app->y, kbd->ow, kbd->oh); return 1; } @@ -282,9 +308,16 @@ void keyboardDeinit(Keyboard *kbd) { void keyboardResize(Keyboard *kbd) { - LOGDBG("keyboard: resize"); - LOGWRN("keyboard: resize: not implemented yet"); - // todo: + if (kbd->current) + layoutResize(kbd->current); +} + + +void keyboardUpdateModifiers(Keyboard* kbd) { + if (kbd->lastModifiers == kbd->app->input.modifiers) return; + LOGDBG("keyboard: update modifiers"); + kbd->lastModifiers = kbd->app->input.modifiers; + appInvalidateRect(kbd->app, 0, 0, kbd->app->w, kbd->app->h); } @@ -337,6 +370,8 @@ void keyboardSwitchLayout(Keyboard *kbd, int index) { if (index == LI_REVERT) { if (kbd->current && kbd->current->prev) kbd->current = kbd->current->prev; + keyboardResize(kbd); + appInvalidateRect(kbd->app, 0, 0, kbd->app->w, kbd->app->h); return; } @@ -347,5 +382,8 @@ void keyboardSwitchLayout(Keyboard *kbd, int index) { kbd->layouts[index].prev = kbd->current; kbd->current = &kbd->layouts[index]; + + keyboardResize(kbd); + appInvalidateRect(kbd->app, 0, 0, kbd->app->w, kbd->app->h); } diff --git a/keyboard.h b/keyboard.h index 79a0d79..074b9bc 100644 --- a/keyboard.h +++ b/keyboard.h @@ -36,7 +36,7 @@ typedef struct Keyboard Keyboard; struct Key { // keep these filds at the begining for easyest static initialization - int x, y, w, h; + int ox, oy, ow, oh; unsigned int keySym; unsigned int keySym2; char label[K_LBL_SIZE]; @@ -52,6 +52,7 @@ struct Key { TextLayout tl2; // dynamic fields + int x, y, w, h; int down; int downKeycode; int mx, my; // values for move and size buttons @@ -63,12 +64,13 @@ struct Layout { // keep these filds at the begining for easyest static initialization Key *keys; int keysCount; - int w, h; + int ow, oh; // these fields will be set while initialization Keyboard *kbd; // dynamic fields + int w, h; Layout *prev; // previously used layout Key *downKey; // key that received the last mouse down event }; @@ -78,12 +80,13 @@ struct Keyboard { // keep these filds at the begining for easyest static initialization Layout *layouts; int layoutsCount; - int w, h; + int ow, oh; // these fields will be set while initialization App *app; // dynamic fields + unsigned int lastModifiers; Layout *current; Key *firstHeld; }; @@ -98,6 +101,7 @@ void keyUp(Key *k, int force); int layoutInit(Layout *l, Keyboard *kbd); void layoutDeinit(Layout *l); +void layoutResize(Layout *l); void layoutDraw(Layout *l, int cx, int cy, int cw, int ch); void layoutMouseDown(Layout *l, int x, int y); void layoutMouseMotion(Layout *l, int x, int y); @@ -106,6 +110,7 @@ void layoutMouseUp(Layout *l); int keyboardInit(Keyboard *kbd, App *app); void keyboardDeinit(Keyboard *kbd); void keyboardResize(Keyboard *kbd); +void keyboardUpdateModifiers(Keyboard *kbd); void keyboardDraw(Keyboard *kbd, int cx, int cy, int cw, int ch); void keyboardMouseDown(Keyboard *kbd, int x, int y); void keyboardMouseMotion(Keyboard *kbd, int x, int y); diff --git a/main.c b/main.c index 1df85ec..c7dbf13 100644 --- a/main.c +++ b/main.c @@ -9,21 +9,38 @@ #define S 10 // spacing +#define W 50 // width +#define H 40 // height #define N (-S-1) // place key next to previos key +#ifdef NOBORDER + #define HEADER \ + { S, S, 200, 20, 0, 0, "::: coolkbd :::", "", KF_MOVE }, \ + { N, 0, 20, 20, 0, 0, "[X]", "", KF_CLOSE }, + #define FOOTER \ + { S, N, 220+S, 20, 0, 0, "resize", "", KF_SIZE }, + #define Y N +#else + #define Y S + #define HEADER + #define FOOTER +#endif + Key keys[] = { - { S, S, 30, 20, XK_Armenian_vyun, XK_Armenian_VYUN, "ւ", "Ւ" }, + HEADER + { S, Y, 50, 40, XK_Armenian_vyun, XK_Armenian_VYUN, "ւ", "Ւ" }, { N, 0, 0, 0, XK_b }, { N, 0, 0, 0, XK_c, XK_X, "", "Iks" }, { N, 0, 0, 0, XK_5, XK_percent, "5", "%" }, - { S, N, 0, 0, XK_Cyrillic_a }, + { S, N, 0, 0, XK_Cyrillic_a, 0, "а", "А" }, { N, 0, 0, 0, XK_Cyrillic_be }, { N, 0, 0, 0, XK_Cyrillic_ve }, { N, 0, 0, 0, XK_6, XK_exclam }, { S, N, 0, 0, 0, 0, "[x]", "", KF_CLOSE }, { N, 0, 0, 0, 0, 0, "[:::]", "", KF_MOVE }, { N, 0, 0, 0, 0, 0, "[<]", "", KF_LAYOUT, LI_REVERT }, - { N, 0, 0, 0, 0, 0, "[x]", "", KF_SIZE }, + { N, 0, 0, 0, 0, 0, "<:::>", "", KF_SIZE }, + FOOTER }; Layout layouts[] = {