From 843e7a76b71dcec86f0375c5ac10bea9af7a37ff Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Aug 13 2023 15:35:42 +0000 Subject: second commit --- diff --git a/app.c b/app.c index e5b22f0..598ad02 100644 --- a/app.c +++ b/app.c @@ -6,6 +6,12 @@ int appInit(App *app) { LOGDBG("app: init"); + app->x = 0; + app->y = 0; + app->w = 100; + app->h = 50; + app->run = 0; + app->irx = app->iry = app->irw = app->irh = 0; LOGDBG("app: init: connect to xcb"); app->xcb = xcb_connect(NULL, NULL); @@ -16,12 +22,10 @@ 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->w = 300; - app->h = 150; app->win = xcb_generate_id(app->xcb); uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; uint32_t values[] = { - 1, // override redirect + 0, // override redirect XCB_EVENT_MASK_EXPOSURE // event mask | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS @@ -31,8 +35,8 @@ int appInit(App *app) { XCB_COPY_FROM_PARENT, // depth (same as root) app->win, // window id app->screen->root, // parent window - 0, 0, // x, y - app->w, app->h, // width, height + app->x, app->y, // origin + app->w, app->h, // size 10, // border_width XCB_WINDOW_CLASS_INPUT_OUTPUT, // class app->screen->root_visual, // visual @@ -83,13 +87,10 @@ void appDeinit(App *app) { int appRun(App *app) { LOGDBG("app: run"); - LOGDBG("app: run: resize window"); - app->w = app->keyboard.w; - app->h = app->keyboard.h; - uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; - uint32_t values[] = { app->w, app->h }; - xcb_configure_window(app->xcb, app->win, mask, values); - + if (app->run) + return LOGERR("app: run: seems already run"); + app->run = 1; + LOGDBG("app: run: show window"); xcb_map_window(app->xcb, app->win); xcb_flush(app->xcb); @@ -106,11 +107,23 @@ int appRun(App *app) { } case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t*)event; - if (e->width && e->height && app->w != e->width && app->h != e->height) { - LOGDBG("app: resize: w=%d, h=%d", e->width, e->height); + if ( e->width + && 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", 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; - graphResize(&app->graph); + if (resized) { + graphResize(&app->graph); + keyboardResize(&app->keyboard); + } } break; } @@ -135,6 +148,8 @@ int appRun(App *app) { } free(event); + if (app->run != 1) break; + 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); @@ -144,8 +159,32 @@ int appRun(App *app) { } } - LOGDBG("app: run: done"); - return 1; + int err = app->run == -2; + app->run = 0; + LOGDBG("app: run: done err=%d", err); + return !err; +} + + +void appStop(App *app, int err) { + LOGDBG("app: stop: err=%d", err); + if (!app->run) { + LOGERR("app: stop: seems not started"); + return; + } + if (app->run == 1) app->run = -1; + if (err) app->run = -2; +} + + +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); } diff --git a/app.h b/app.h index eb02b98..4ccc3c4 100644 --- a/app.h +++ b/app.h @@ -18,16 +18,19 @@ struct App { xcb_connection_t *xcb; xcb_screen_t *screen; xcb_window_t win; - int w, h; // dynamic fields int irx, iry, irw, irh; + int x, y, w, h; + int run; }; int appInit(App *app); void appDeinit(App *app); int appRun(App *app); +void appStop(App *app, int err); +void appMove(App *app, int x, int y, int w, int h); void appInvalidateRect(App *app, int x, int y, int w, int h); diff --git a/common.h b/common.h index 7359851..41992a2 100644 --- a/common.h +++ b/common.h @@ -4,13 +4,14 @@ #include #include +#include #include #include -#include +#define COUNTOF(x) (sizeof(x)/sizeof((x)[0])) #define LOG(stream, prefix, suffix, result, ...) \ ( fputs(prefix, stream), fprintf(stream, __VA_ARGS__), fputs(suffix, stream), fflush(stream), dummy(result) ) diff --git a/graph.c b/graph.c index 7a9244d..82f919a 100644 --- a/graph.c +++ b/graph.c @@ -78,14 +78,13 @@ void graphDrawButton(Graph *g, int x, int y, int w, int h, int active) { -int textLayoutInit(TextLayout *tl, Graph *g, int cx, int cy, const char *text) { +void textLayoutInit(TextLayout *tl, Graph *g, const char *text) { tl->g = g; tl->text = text; - return 1; } void textLayoutDeinit(TextLayout *tl) { } -void textLayoutDraw(TextLayout *tl, int active) +void textLayoutDraw(TextLayout *tl, int x, int y, int active) { } diff --git a/graph.h b/graph.h index 95fd7d7..fe46904 100644 --- a/graph.h +++ b/graph.h @@ -37,9 +37,9 @@ void graphResize(Graph *g); void graphDrawBackgound(Graph *g, int x, int y, int w, int h); void graphDrawButton(Graph *g, int x, int y, int w, int h, int active); -int textLayoutInit(TextLayout *tl, Graph *g, int cx, int cy, const char *text); +void textLayoutInit(TextLayout *tl, Graph *g, const char *text); void textLayoutDeinit(TextLayout *tl); -void textLayoutDraw(TextLayout *tl, int active); +void textLayoutDraw(TextLayout *tl, int x, int y, int active); #endif diff --git a/input.c b/input.c index 7db0763..5d142ba 100644 --- a/input.c +++ b/input.c @@ -1,7 +1,10 @@ #include "app.h" +#include + #include +#include #include @@ -9,31 +12,25 @@ int inputInit(Input *in, App *app) { LOGDBG("input: init"); in->app = app; - in->mappedKeyCode = 0; - in->mappedPressed = 0; + in->key0 = in->key1 = in->mapKey = 0; + in->mapDown = 0; + in->updated = 0; + memset(in->keys, 0, sizeof(in->keys)); + memset(in->masks, 0, sizeof(in->masks)); LOGDBG("input: init: connect to X11"); in->dpy = XOpenDisplay(NULL); if (!in->dpy) return LOGERR("input: init: cannot connect to X11"); - LOGDBG("input: init: search free keycode for temporary mappings"); - int kk = 0, kc0 = 0, kc1 = 0; - XDisplayKeycodes(in->dpy, &kc0, &kc1); - KeySym *ks = XGetKeyboardMapping(in->dpy, kc0, kc1 - kc0 + 1, &kk); - for(int kc = kc0; kc <= kc1; ++kc) { - int found = 1; - for(int i = 0; i < kk; ++i, ++ks) - if (ks[kc*kk + i]) { found = 0; break; } - if (found) - { in->mappedKeyCode = kc; break; } - } - - return 1; + LOGDBG("input: init: query keycode range"); + XDisplayKeycodes(in->dpy, &in->key0, &in->key1); + ++in->key1; + if (in->key0 < 0) in->key0 = 0; + if (in->key1 > IN_MAXKEYS) in->key1 = IN_MAXKEYS; + LOGDBG("input: init: keycode range [%d, %d)", in->key0, in->key1); - LOGDBG("input: init: found keycode %d", in->mappedKeyCode); - if (!in->mappedKeyCode) - LOGWRN("input: init: cannot found free keycode for temporary mappings, some keys may not work"); + inputUpdateLayout(in); return 1; } @@ -41,42 +38,200 @@ int inputInit(Input *in, App *app) { void inputDeinit(Input *in) { LOGDBG("input: deinit"); + if (in->mapKey) { + LOGDBG("input: deinit: unmap temporary key %d", in->mapKey); + if (in->mapDown) + inputEvent(in, in->mapKey, 0); + KeySym ks[4] = {}; + XChangeKeyboardMapping(in->dpy, in->mapKey, 4, ks, 1); + } XCloseDisplay(in->dpy); } -void inputEvent(Input *in, unsigned int keySym, int press) { - LOGDBG("input: event: keySym=%u press=%d", keySym, press); +void inputUpdateLayout(Input *in) { + LOGDBG("input: update layout"); - KeyCode keycode = XKeysymToKeycode(in->dpy, keySym); + static const KeySym imKeys[IM_COUNT] = { + XK_Shift_L, + XK_Caps_Lock, + XK_Shift_Lock, + XK_Num_Lock, + XK_Mode_switch }; - if (!keycode) { - if (!in->mappedKeyCode) { - LOGWRN("input: event: no keycode mapped to keySym[%u], and no free keycodes for temporary mapping", keySym); - return; + in->updated = 0; + memset(in->masks, 0, sizeof(in->masks)); + + LOGDBG("input: update layout: read modifiers"); + XModifierKeymap *mods = XGetModifierMapping(in->dpy); + + LOGDBG("input: update layout: read keyboard mapping"); + int mcnt = 0; + KeySym *ks = XGetKeyboardMapping(in->dpy, in->key0, in->key1 - in->key0, &mcnt); + for(int i = in->key0; i < in->key1; ++i, ks += mcnt) { + // search key for temporary maps + if (!in->mapKey) { + int found = 1; + for(int i = 0; i < mcnt; ++i) + if (ks[i]) { found = 0; break; } + if (found) { + in->mapKey = i; + LOGDBG("input: update layout: choose keycode for temporary map: %d", in->mapKey); + } } - keycode = in->mappedKeyCode; - LOGDBG("input: event: temporary remap: keySym[%u] -> keycode[%hhu]", keySym, keycode); + //if (i == 50 || i == 62 || i == 66) { + // printf("keysyms for key #%d (%d):\n", i, mcnt); + // for(int i = 0; i < mcnt; ++i) { + // printf(" %lu\n", ks[i]); + // } + // printf("\n"); + //} + + // update state masks + for(int imask = 0; imask < IM_COUNT; ++imask) + for(int iks = 0; iks < mcnt; ++iks) + if (ks[iks] == imKeys[imask]) { + LOGDBG("input: update layout: keycode for mask %d is %d", imask, i); + for(int imod = 0; imod < XkbNumModifiers; ++imod) + for(int ik = 0; ik < mods->max_keypermod; ++ik) + if (mods->modifiermap[imod*mods->max_keypermod + ik] == i) + in->masks[imask] |= 1 << imod; + } + + // get base keys + unsigned int k[] = { + mcnt > 0 ? ks[0] : 0, + mcnt > 1 ? ks[1] : 0, + mcnt > 2 ? ks[2] : 0, + mcnt > 3 ? ks[3] : 0 }; + + if (!k[2] && !k[3]) + k[2] = k[0], k[3] = k[1]; - if (in->mappedPressed) { - LOGWRN("input: event: temporary remap: mapped keys collision, release previously mapped key"); - XTestFakeKeyEvent(in->dpy, keycode, False, CurrentTime); - XSync(in->dpy, False); + int isKeypad[2] = { + (0xFF80 <= k[1] && k[1] <= 0xFFBD) || (0x11000000 <= k[1] && k[1] <= 0x1100FFFF), + (0xFF80 <= k[3] && k[3] <= 0xFFBD) || (0x11000000 <= k[3] && k[3] <= 0x1100FFFF) }; + + int isLetter[2] = {}; + for(int i = 0; i < 2; ++i) { + if (k[i*2]) { + KeySym kl = 0, ku = 0; + XConvertCase(k[i*2], &kl, &ku); + if (k[i*2] == kl && kl != ku && ku) + k[i*3+1] = ku, isLetter[i] = 1; + } } - KeySym ks[2]; - XConvertCase(keySym, &ks[0], &ks[1]); - LOGDBG("input: event: temporary remap: keySym[%u] -> keySyms[%lu, %lu]", keySym, ks[0], ks[1]); - XChangeKeyboardMapping(in->dpy, keycode, ks[0] == ks[1] ? 1 : 2, ks, 1); - XSync(in->dpy, False); + if (!k[1]) k[1] = k[0]; + if (!k[3]) k[3] = k[2]; + + // fill keys by algorithm from here: + // https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#Manipulating_the_Keyboard_Encoding + for(int j = 0; j < (1 << IM_COUNT); ++j) { + int shift = !!(j & (1 << IM_SHIFT )); + int capsLock = !!(j & (1 << IM_CAPSLOCK )); + int shiftLock = !!(j & (1 << IM_SHIFTLOCK)); + int numLock = !!(j & (1 << IM_NUMLOCK )); + int group = !!(j & (1 << IM_GROUP )); + + if (capsLock) shiftLock = 0; + + unsigned int k0 = k[group*2]; + unsigned int k1 = k[group*2 + 1]; + + if (numLock && isKeypad[group]) { + in->keys[i][j] = shift || shiftLock ? k1 : k0; + continue; + } + + if (!shift && !capsLock && !shiftLock) { + in->keys[i][j] = k0; + continue; + } + + if (!shift && capsLock) { + in->keys[i][j] = isLetter[group] ? k1 : k0; + continue; + } + + if (shift && capsLock) { + in->keys[i][j] = isLetter[group] ? k0 : k1; + continue; + } + + if (shift || shiftLock) { + in->keys[i][j] = k1; + continue; + } + + in->keys[i][j] = k0; + } } - if (keycode == in->mappedKeyCode) - in->mappedPressed = press; + // force shift mask to first modifier + in->masks[IM_SHIFT] = 1; + XFreeModifiermap(mods); + + for(int i = 0; i < IM_COUNT; ++i) + LOGDBG("input: update layout: masks[%d] = %08u", i, in->masks[i]); +} + + +int inputKeycode(Input *in, unsigned int keySym) { + LOGDBG("input: keycode: keySym=%u", keySym); + + if (!keySym) + return 0; - LOGDBG("input: event: keycode=%hhu press=%d", keycode, press); + XkbStateRec state = {}; + XkbGetState(in->dpy, XkbUseCoreKbd, &state); + int mod = 0; + for(int i = 0; i < IM_COUNT; ++i) + if (in->masks[i] & state.mods) + mod |= (1 << i); + + LOGDBG("input: keycode: current state %08x, chosen mod %04x", (unsigned int)state.mods, (unsigned int)mod); + + for(int i = in->key0; i < in->key1; ++i) { + if (in->keys[i][mod] == keySym) { + LOGDBG("input: keycode: found keycode %d", i); + return i; + } + } + + if (!in->mapKey) { + LOGWRN("input: keycode: no keycode mapped to keySym[%u], and no free keycodes for temporary mapping", keySym); + return 0; + } + + LOGDBG("input: keycode: temporary remap keySym[%u] to %d", keySym, in->mapKey); + + if (in->mapDown) { + LOGWRN("input: event: temporary remap: mapped keys collision, release previously mapped key"); + inputEvent(in, in->mapKey, 0); + } + + for(int i = 0; i < IM_COUNT; ++i) + in->keys[in->mapKey][i] = keySym; + KeySym ks[4] = { keySym, keySym, keySym, keySym }; + XChangeKeyboardMapping(in->dpy, in->mapKey, 4, ks, 1); + XSync(in->dpy, False); + in->updated = 1; + + return in->mapKey; +} + + +void inputEvent(Input *in, int keycode, int press) { + LOGDBG("input: event: keycode=%d press=%d", keycode, press); + press = !!press; + if (keycode < in->key0 || keycode >= in->key1) { + LOGERR("input: event: bad keycode=%d", keycode); + return; + } XTestFakeKeyEvent(in->dpy, keycode, press, CurrentTime); XSync(in->dpy, False); } + diff --git a/input.h b/input.h index c434efc..5f74c12 100644 --- a/input.h +++ b/input.h @@ -9,18 +9,36 @@ #include +#define IN_MAXKEYS 256 + +enum { + IM_SHIFT, + IM_CAPSLOCK, + IM_SHIFTLOCK, + IM_NUMLOCK, + IM_GROUP, + IM_COUNT }; + + typedef struct Input { App *app; Display *dpy; - int mappedKeyCode; - int mappedPressed; + int key0, key1; + unsigned int keys[IN_MAXKEYS][1 << IM_COUNT]; + unsigned int masks[IM_COUNT]; + + int mapKey; + int mapDown; + int updated; } Input; int inputInit(Input *in, App *app); void inputDeinit(Input *in); -void inputEvent(Input *in, unsigned int keySym, int press); +void inputUpdateLayout(Input *in); +int inputKeycode(Input *in, unsigned int keySym); +void inputEvent(Input *in, int keycode, int press); #endif diff --git a/keyboard.c b/keyboard.c index 3e2e1e4..ace60ad 100644 --- a/keyboard.c +++ b/keyboard.c @@ -3,52 +3,220 @@ -int keyInit(Key *k, Keyboard *kbd) { - k->kbd = kbd; +int keyInit(Key *k, Layout *l) { + k->l = l; k->down = 0; - - int lx = k->x + k->w/2; - int ly = k->y + k->h/2; - if (k->label && !textLayoutInit(&k->tl, &k->kbd->app->graph, lx, ly, k->label)) - return 0; - - int d = k->h/4; - lx = k->x + k->w - d; - ly = k->y + d; - if (k->label2 && !textLayoutInit(&k->tl2, &k->kbd->app->graph, lx, ly, k->label2)) { - if (k->label) textLayoutDeinit(&k->tl); - return 0; - } + k->downKeycode = 0; + k->mx = k->my = 0; + + textLayoutInit(&k->tl, &k->l->kbd->app->graph, k->label); + textLayoutInit(&k->tl, &k->l->kbd->app->graph, k->label2); return 1; } void keyDeinit(Key *k) { - if (k->label) textLayoutDeinit(&k->tl); - if (k->label2) textLayoutDeinit(&k->tl2); + keyUp(k, 1); + textLayoutDeinit(&k->tl); + textLayoutDeinit(&k->tl2); } void keyDraw(Key *k, int cx, int cy, int cw, int ch) { if ( k->x + k->w <= cx || k->x >= cx + cw || k->y + k->h <= cy || k->y >= cy + ch ) return; - graphDrawButton(&k->kbd->app->graph, k->x, k->y, k->w, k->h, k->down); - textLayoutDraw(&k->tl, k->down); + + int active = (k->flags & KF_CAPS) ? k->l->kbd->capslock : k->down; + + graphDrawButton(&k->l->kbd->app->graph, k->x, k->y, k->w, k->h, active); + + TextLayout *tl = &k->tl; + TextLayout *tl2 = &k->tl2; + if (k->label2) { + int sw = k->l->kbd->shiftsDown; + if (!(k->flags & KF_DOUBLE) && k->l->kbd->capslock) + sw = !sw; + if (sw) + tl = tl2, tl2 = &k->tl; + } + + int lx = k->x + k->w/2; + int ly = k->y + k->h/2; + textLayoutDraw(tl, lx, ly, active); + + if (k->flags & KF_DOUBLE) { + int d = k->h/4; + lx = k->x + k->w - d; + ly = k->y + d; + textLayoutDraw(tl2, lx, ly, active); + } } -void keyMouseDown(Key *k) { +void keyDown(Key *k, int x, int y) { + if (k->down == 2) k->down = 1; + if (k->down) return; + + Keyboard *kbd = k->l->kbd; k->down = 1; - inputEvent(&k->kbd->app->input, k->keySym, 1); - appInvalidateRect(k->kbd->app, k->x, k->y, k->w, k->h); + k->mx = x - k->x; + k->my = y - k->y; + + // handle special keys + if (k->flags & KF_SHIFT) { + if (!kbd->shiftsDown) appInvalidateRect(kbd->app, 0, 0, kbd->w, kbd->h); + ++kbd->shiftsDown; + } + if ((k->flags & KF_CAPS) && !kbd->capslock) { + kbd->capslock = 1; + appInvalidateRect(kbd->app, 0, 0, kbd->w, kbd->h); + } + if (k->flags & KF_HOLD) { + k->down = 2; + // insert to held list + if (!k->prevHeld && !k->nextHeld && kbd->firstHeld != k) { + k->nextHeld = kbd->firstHeld; + if (k->nextHeld) k->nextHeld->prevHeld = k; + kbd->firstHeld = k; + } + } + + // choose keySym + int shift = kbd->shiftsDown; + int caps = kbd->capslock; + unsigned int keySym = k->keySym; + if (k->keySym2) { + int sw = shift; + if (!(k->flags & KF_DOUBLE) && caps) + sw = !sw; + if (sw) + keySym = k->keySym2; + } + + // press key + if (keySym) { + k->downKeycode = inputKeycode(&kbd->app->input, keySym); + if (k->downKeycode) inputEvent(&kbd->app->input, k->downKeycode, 1); + } + + // redraw + appInvalidateRect(kbd->app, k->x, k->y, k->w, k->h); +} + + +void keyMotion(Key *k, int x, int y) { + App *app = k->l->kbd->app; + int dx = x - k->x - k->mx; + int dy = y - k->y - k->my; + if (k->flags & KF_MOVE) { + appMove(app, app->x + dx, app->y + dy, app->w, app->h); + } else + if (k->flags & KF_SIZE) { + appMove(app, app->x, app->y, app->w + dx, app->h + dy); + } } -void keyMouseUp(Key *k) { +void keyUp(Key *k, int force) { + if (!force && k->down != 1) return; + + Keyboard *kbd = k->l->kbd; + int wasDown = k->down; k->down = 0; - inputEvent(&k->kbd->app->input, k->keySym, 0); - appInvalidateRect(k->kbd->app, k->x, k->y, k->w, k->h); + + // handle special keys + if (wasDown && (k->flags & KF_SHIFT)) { + --kbd->shiftsDown; + if (!kbd->shiftsDown) appInvalidateRect(kbd->app, 0, 0, kbd->w, kbd->h); + } + if ((k->flags & KF_CAPS) && kbd->capslock) { + kbd->capslock = 0; + appInvalidateRect(kbd->app, 0, 0, kbd->w, kbd->h); + } + if (k->flags & KF_CLOSE) appStop(kbd->app, 0); + + // release key + if (k->downKeycode) { + inputEvent(&kbd->app->input, k->downKeycode, 0); + k->downKeycode = 0; + } + + // remove from held list + if (k->prevHeld || k->nextHeld || kbd->firstHeld == k) { + if (k->nextHeld) k->nextHeld->prevHeld = k->prevHeld; + if (k->prevHeld) k->prevHeld->nextHeld = k; else kbd->firstHeld = k->nextHeld; + k->nextHeld = k->prevHeld = NULL; + } + + // on releasing a regular key release all held special keys + if (!force && k->flags & ~KF_DOUBLE) + keyboardRelaseHeld(kbd); + + // redraw + if (wasDown) appInvalidateRect(kbd->app, k->x, k->y, k->w, k->h); +} + + + +int layoutInit(Layout *l, Keyboard *kbd) { + LOGDBG("layout: init"); + l->kbd = kbd; + l->prev = NULL; + l->downKey = NULL; + 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; +} + + +void layoutDeinit(Layout *l) { + LOGDBG("layout: deinit"); + layoutMouseUp(l); + for(int i = 0; i < l->keysCount; ++i) + keyDeinit(&l->keys[i]); +} + + +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); +} + + +void layoutMouseDown(Layout *l, int x, int y) { + LOGDBG("layout: mouse down: x=%d y=%d", x, y); + for(int i = l->keysCount - 1; i >= 0; --i) { + Key *k = &l->keys[i]; + if ( k->x <= x && x < k->x + k->w + && k->y <= y && y < k->y + k->h ) + { + if (l->downKey != k) { + layoutMouseUp(l); + l->downKey = k; + keyDown(k, x, y); + } + break; + } + } +} + + +void layoutMouseMotion(Layout *l, int x, int y) { + LOGDBG("layout: mouse motion: x=%d y=%d", x, y); + if (!l->downKey) return; + keyMotion(l->downKey, x, y); +} + + +void layoutMouseUp(Layout *l) { + LOGDBG("layout: mouse up"); + if (!l->downKey) return; + keyUp(l->downKey, 0); + l->downKey = NULL; } @@ -56,12 +224,17 @@ void keyMouseUp(Key *k) { int keyboardInit(Keyboard *kbd, App *app) { LOGDBG("keyboard: init"); kbd->app = app; - kbd->downKey = NULL; - for(int i = 0; i < kbd->keysCount; ++i) - if (!keyInit(&kbd->keys[i], kbd)) { - for(--i; i >= 0; --i) keyDeinit(&kbd->keys[i]); + kbd->firstHeld = NULL; + kbd->shiftsDown = 0; + kbd->capslock = 0; + kbd->current = kbd->layoutsCount > 0 ? kbd->layouts : NULL; + for(int i = 0; i < kbd->layoutsCount; ++i) { + if (!layoutInit(&kbd->layouts[i], kbd)) { + for(--i; i >= 0; --i) layoutDeinit(&kbd->layouts[i]); return 0; } + } + appMove(app, app->x, app->y, kbd->w, kbd->h); return 1; } @@ -69,37 +242,76 @@ int keyboardInit(Keyboard *kbd, App *app) { void keyboardDeinit(Keyboard *kbd) { LOGDBG("keyboard: deinit"); keyboardMouseUp(kbd); - for(int i = 0; i < kbd->keysCount; ++i) - keyDeinit(&kbd->keys[i]); + for(int i = 0; i < kbd->layoutsCount; ++i) + layoutDeinit(&kbd->layouts[i]); +} + + +void keyboardResize(Keyboard *kbd) { + LOGDBG("keyboard: resize"); + LOGWRN("keyboard: resize: not implemented yet"); + // todo: } void keyboardDraw(Keyboard *kbd, int cx, int cy, int cw, int ch) { - for(int i = 0; i < kbd->keysCount; ++i) - keyDraw(&kbd->keys[i], cx, cy, cw, ch); + if (!kbd->current) return; + layoutDraw(kbd->current, cx, cy, cw, ch); } void keyboardMouseDown(Keyboard *kbd, int x, int y) { - for(int i = kbd->keysCount - 1; i >= 0; --i) { - Key *k = &kbd->keys[i]; - if ( k->x <= x && x < k->x + k->w - && k->y <= y && y < k->y + k->h ) - { - if (kbd->downKey != k) { - keyboardMouseUp(kbd); - kbd->downKey = k; - keyMouseDown(k); - } - break; - } - } + LOGDBG("keyboard: mouse down: x=%d y=%d", x, y); + if (!kbd->current) return; + layoutMouseDown(kbd->current, x, y); +} + + +void keyboardMouseMotion(Keyboard *kbd, int x, int y) { + LOGDBG("keyboard: mouse motion: x=%d y=%d", x, y); + if (!kbd->current) return; + layoutMouseMotion(kbd->current, x, y); } void keyboardMouseUp(Keyboard *kbd) { - if (kbd->downKey) { - keyMouseUp(kbd->downKey); - kbd->downKey = NULL; + LOGDBG("keyboard: mouse up"); + if (!kbd->current) return; + layoutMouseUp(kbd->current); +} + + +void keyboardRelaseHeld(Keyboard *kbd) { + LOGDBG("keyboard: release held"); + while(kbd->firstHeld) keyUp(kbd->firstHeld, 1); +} + + +void keyboardSwitchLayout(Keyboard *kbd, int index) { + LOGDBG("keyboard: switch layout"); + + if (!kbd->layoutsCount) return; + + if (index == LI_PREV) { + index = kbd->current ? kbd->current - kbd->layouts - 1: 0; + if (index >= kbd->layoutsCount) index = 0; + } else + if (index == LI_NEXT) { + index = kbd->current ? kbd->current - kbd->layouts - 1: 0; + if (index < 0) index = kbd->layoutsCount - 1; + } else + if (index == LI_REVERT) { + if (kbd->current && kbd->current->prev) + kbd->current = kbd->current->prev; + return; + } + + if (index < 0 || index >= kbd->layoutsCount) { + LOGWRN("keyboard: switch layout: bad index %d", index); + return; } + + kbd->layouts[index].prev = kbd->current; + kbd->current = &kbd->layouts[index]; } + diff --git a/keyboard.h b/keyboard.h index 3e15996..0fa9f0a 100644 --- a/keyboard.h +++ b/keyboard.h @@ -6,52 +6,111 @@ #include "input.h" +// key flags +enum { + KF_DOUBLE = 1 << 0, // show two lables, insensitive for capslock + KF_HOLD = 1 << 1, // key to switch layout + KF_SHIFT = 1 << 2, // key to switch layout + KF_CAPS = 1 << 3, // key to switch layout + KF_LAYOUT = 1 << 4, // key to switch layout + KF_MOVE = 1 << 5, // handle to move the window + KF_SIZE = 1 << 6, // key to resize the window + KF_CLOSE = 1 << 7 }; // key to close the window + +// special layout indices +enum { + LI_PREV = -1, // switch to previous layout in the list + LI_NEXT = -2, // switch to next layout in the list + LI_REVERT = -3 }; // switch to previously used layout + + + +struct Key; +struct Layout; struct Keyboard; +typedef struct Key Key; +typedef struct Layout Layout; typedef struct Keyboard Keyboard; -typedef struct Key { +struct Key { // keep these filds at the begining for easyest static initialization int x, y, w, h; unsigned int keySym; + unsigned int keySym2; const char *label; const char *label2; + unsigned int flags; + int layoutIndex; // these fields will be set while initialization - Keyboard *kbd; + Layout *l; TextLayout tl; TextLayout tl2; // dynamic fields int down; -} Key; + int downKeycode; + int mx, my; // mouse down position, relative to key origin + Key *nextHeld, *prevHeld; +}; -struct Keyboard { +struct Layout { // keep these filds at the begining for easyest static initialization Key *keys; int keysCount; int w, h; // these fields will be set while initialization + Keyboard *kbd; + + // dynamic fields + Layout *prev; // previously used layout + Key *downKey; // key that received the last mouse down event +}; + + +struct Keyboard { + // keep these filds at the begining for easyest static initialization + Layout *layouts; + int layoutsCount; + int w, h; + + // these fields will be set while initialization App *app; // dynamic fields - Key *downKey; + Layout *current; + Key *firstHeld; + int shiftsDown; + int capslock; }; -int keyInit(Key *k, Keyboard *kbd); +int keyInit(Key *k, Layout *l); void keyDeinit(Key *k); void keyDraw(Key *k, int cx, int cy, int cw, int ch); -void keyDown(Key *k); -void keyUp(Key *k); +void keyDown(Key *k, int x, int y); +void keyMotion(Key *k, int x, int y); +void keyUp(Key *k, int force); + +int layoutInit(Layout *l, Keyboard *kbd); +void layoutDeinit(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); +void layoutMouseUp(Layout *l); int keyboardInit(Keyboard *kbd, App *app); void keyboardDeinit(Keyboard *kbd); +void keyboardResize(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); void keyboardMouseUp(Keyboard *kbd); +void keyboardSwitchLayout(Keyboard *kbd, int index); +void keyboardRelaseHeld(Keyboard *kbd); #endif diff --git a/main.c b/main.c index 62dfbdb..a134577 100644 --- a/main.c +++ b/main.c @@ -9,19 +9,28 @@ Key keys[] = { - { 10, 10, 30, 20, XK_Armenian_vyun, "ւ", "Ւ" }, - { 50, 10, 30, 20, XK_b, "b" }, - { 90, 10, 30, 20, XK_c, "c" }, - { 130, 10, 30, 20, XK_d, "d" }, - { 10, 40, 30, 20, XK_Cyrillic_a, "а" }, - { 50, 40, 30, 20, XK_Cyrillic_be, "б" }, - { 90, 40, 30, 20, XK_Cyrillic_ve, "в" }, - { 130, 40, 30, 20, XK_Cyrillic_ghe, "г" } }; + { 10, 10, 30, 20, XK_Armenian_vyun, XK_Armenian_VYUN, "ւ", "Ւ" }, + { 50, 10, 30, 20, XK_b, XK_B, "b", "B" }, + { 90, 10, 30, 20, XK_c, XK_X, "c", "X" }, + { 130, 10, 30, 20, XK_5, XK_percent, "5", "%", KF_DOUBLE }, + { 10, 40, 30, 20, XK_Cyrillic_a, XK_Cyrillic_a, "а", "а" }, + { 50, 40, 30, 20, XK_Cyrillic_be, XK_Cyrillic_BE, "б", "Б" }, + { 90, 40, 30, 20, XK_Cyrillic_ve, 0, "в" }, + { 130, 40, 30, 20, XK_6, XK_exclam, "6", "!", KF_DOUBLE }, + { 10, 70, 30, 20, 0, 0, "[x]", NULL, KF_CLOSE }, + { 50, 70, 30, 20, 0, 0, "[:::]", NULL, KF_MOVE }, + { 90, 70, 30, 20, 0, 0, "[<]", NULL, KF_LAYOUT, LI_REVERT }, + { 130, 70, 30, 20, 0, 0, "[x]", NULL, KF_SIZE }, +}; enum { keysCount = sizeof(keys)/sizeof(keys[0]) }; +Layout layouts[] = { + { keys, COUNTOF(keys), 170, 100 } }; + + App app = { - { keys, keysCount, 170, 70 } }; // keyboard + { layouts, COUNTOF(layouts), 170, 100 } }; // keyboard int main() {