| |
| |
| |
| |
| |
| int keyInit(Key *k, Layout *l) { |
| CLEARFROM(k, l); |
| k->l = l; |
| |
| 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->tl2, &k->l->kbd->app->graph, k->label2); |
| |
| return 1; |
| } |
| |
| |
| void keyDeinit(Key *k) { |
| keyUp(k, 1); |
| textLayoutDeinit(&k->tl); |
| textLayoutDeinit(&k->tl2); |
| } |
| |
| |
| void keyInvalidateRect(Key* k) { |
| if (k->orig) k = k->orig; |
| while(k) { |
| appInvalidateRect(k->l->kbd->app, k->x, k->y, k->w, k->h); |
| k = k->clone; |
| } |
| } |
| |
| |
| 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; |
| |
| App *app = k->l->kbd->app; |
| |
| int active = k->down; |
| int longActive = k->longDown; |
| if (k->orig) { |
| active = k->orig->down; |
| longActive = k->orig->longDown; |
| } |
| |
| active = !!active; |
| if (active && longActive) active = 2; |
| |
| if (!active && (k->flags & KF_MOD)) |
| active = !!(app->input.modifiers & (unsigned int)k->optValue); |
| |
| int w = k->w - 2*KEY_BORDER; |
| int h = k->h - 2*KEY_BORDER; |
| int highlight = !!(k->flags & KF_HIGHLIGHT); |
| graphDrawButton(&k->l->kbd->app->graph, k->x + KEY_BORDER, k->y + KEY_BORDER, w, h, active, highlight); |
| |
| TextLayout *tl = &k->tl; |
| TextLayout *tl2 = &k->tl2; |
| if (inputChooseKeysym(app->input.modifiers, k->keySym, k->keySym2, k->isLetter, k->isKeypad)) |
| tl = tl2, tl2 = &k->tl; |
| |
| int lx, lx2, ly, ly2, lw, lw2, lh, lh2; |
| lx = lx2 = k->x + w/2; |
| ly = ly2 = k->y + h/2; |
| lw = lw2 = w - w/8; |
| lh = lh2 = h - h/8; |
| if (!k->isLetter && k->label2[0]) { |
| lx -= w/8; |
| ly += h/8; |
| lw -= w/4; |
| lh -= h/4; |
| |
| lx2 += w/4; |
| ly2 -= h/4; |
| lw2 -= w/2; |
| lh2 -= h/2; |
| } |
| |
| textLayoutDraw(tl, lx, ly, lw, lh, active, highlight); |
| if (!k->isLetter) |
| textLayoutDraw(tl2, lx2, ly2, lw2, lh2, active, highlight); |
| } |
| |
| |
| void keyDown(Key *k, int x, int y) { |
| if (k->orig) |
| return keyDown(k->orig, x, y); |
| if (k->down == 2) k->down = 1; |
| if (k->down) return; |
| |
| Keyboard *kbd = k->l->kbd; |
| k->down = 1; |
| |
| if (k->flags & KF_MOVE) { |
| k->mx = x; |
| k->my = y; |
| } else |
| if (k->flags & KF_SIZE) { |
| |
| k->my = y; |
| |
| k->mx = kbd->app->w - x; |
| k->my = kbd->app->h - y; |
| |
| } |
| |
| if (k->flags & KF_HOLD) { |
| k->down = 2; |
| |
| if (!k->prevHeld && !k->nextHeld && kbd->firstHeld != k) { |
| k->nextHeld = kbd->firstHeld; |
| if (k->nextHeld) k->nextHeld->prevHeld = k; |
| kbd->firstHeld = k; |
| } |
| } |
| |
| |
| unsigned int keySym = inputChooseKeysym(kbd->app->input.modifiers, k->keySym, k->keySym2, k->isLetter, k->isKeypad) |
| ? k->keySym2 : k->keySym; |
| |
| |
| if (keySym) { |
| k->downKeycode = inputKeycode(&kbd->app->input, keySym); |
| if (k->downKeycode) inputEvent(&kbd->app->input, k->downKeycode, 1); |
| } |
| |
| |
| keyInvalidateRect(k); |
| } |
| |
| |
| void keyMotion(Key *k, int x, int y) { |
| if (k->orig) |
| return keyMotion(k->orig, x, y); |
| App *app = k->l->kbd->app; |
| if (k->flags & KF_MOVE) { |
| appMove(app, app->x + x - k->mx, app->y + y - k->my, app->w, app->h); |
| } else |
| if (k->flags & KF_SIZE) { |
| |
| appMove(app, app->x, app->y - k->my + y, app->w, app->h + k->my - y); |
| |
| appMove(app, app->x, app->y, x + k->mx, y + k->my); |
| |
| } |
| } |
| |
| |
| void keyLongDown(Key *k) { |
| if (k->orig) |
| return keyLongDown(k->orig); |
| if (k->down != 2 || k->longDown) |
| return; |
| |
| |
| if (k->prevHeld || k->nextHeld || k->l->kbd->firstHeld == k) { |
| if (k->nextHeld) k->nextHeld->prevHeld = k->prevHeld; |
| if (k->prevHeld) k->prevHeld->nextHeld = k; else k->l->kbd->firstHeld = k->nextHeld; |
| k->nextHeld = k->prevHeld = NULL; |
| } |
| |
| LOGDBG("key: long down: activated"); |
| k->longDown = 1; |
| keyInvalidateRect(k); |
| } |
| |
| |
| void keyUp(Key *k, int force) { |
| if (k->orig) { |
| |
| assert( !(k->prevHeld || k->nextHeld || k->l->kbd->firstHeld == k) ); |
| return keyUp(k->orig, force); |
| } |
| if (!force && k->down != 1) |
| return; |
| |
| Keyboard *kbd = k->l->kbd; |
| int wasDown = k->down; |
| k->down = 0; |
| k->longDown = 0; |
| |
| |
| if (k->downKeycode) { |
| inputEvent(&kbd->app->input, k->downKeycode, 0); |
| k->downKeycode = 0; |
| } |
| |
| |
| |
| 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; |
| } |
| |
| |
| if (!wasDown) |
| return; |
| |
| if (k->flags & KF_CLOSE) |
| appStop(kbd->app, 0); |
| if (k->flags & KF_LAYOUT) |
| keyboardSwitchLayout(kbd, k->optValue); |
| |
| |
| if (!force && !(k->flags & KF_SPECIAL)) |
| keyboardRelaseHeld(kbd); |
| |
| |
| keyInvalidateRect(k); |
| } |
| |
| |
| |
| int layoutInit(Layout *l, Keyboard *kbd) { |
| LOGDBG("layout: init"); |
| |
| CLEARFROM(l, kbd); |
| l->kbd = kbd; |
| |
| if (!l->keysCount) |
| return 1; |
| |
| l->hidden = 0; |
| |
| |
| Key *k = &l->keys[0]; |
| 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; |
| |
| |
| |
| 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; |
| |
| |
| 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; |
| |
| if ((k->flags & KF_LAYOUT) && k->optValue == LI_REVERT) |
| l->hidden = 1; |
| } |
| |
| |
| 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, hedden: %d", x0, y0, x1, y1, l->ow, l->oh, l->hidden); |
| |
| |
| 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 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->ox + k->ow)*kx + 0.5f) - k->x; |
| k->h = (int)((k->oy + k->oh)*ky + 0.5f) - k->y; |
| } |
| } |
| |
| |
| 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 layoutMouseLongDown(Layout *l) { |
| LOGDBG("layout: mouse long down"); |
| if (!l->downKey) return; |
| keyLongDown(l->downKey); |
| } |
| |
| |
| void layoutMouseUp(Layout *l) { |
| LOGDBG("layout: mouse up"); |
| if (!l->downKey) return; |
| keyUp(l->downKey, 0); |
| l->downKey = NULL; |
| } |
| |
| |
| |
| int keyboardInit(Keyboard *kbd, App *app) { |
| LOGDBG("keyboard: init"); |
| |
| CLEARFROM(kbd, app); |
| kbd->app = app; |
| |
| kbd->lastModifiers = kbd->app->input.modifiers; |
| kbd->current = kbd->layoutsCount > 0 ? kbd->layouts : NULL; |
| kbd->lastVisible = kbd->current; |
| |
| 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; |
| } |
| } |
| |
| |
| for(int i = 0; i < kbd->layoutsCount; ++i) { |
| 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 ow=%d oh=%d", kbd->ow, kbd->oh); |
| |
| |
| for(int ila = 0; ila < kbd->layoutsCount; ++ila) |
| for(int ika = 0; ika < kbd->layouts[ila].keysCount; ++ika) { |
| Key *a = &kbd->layouts[ila].keys[ika]; |
| |
| if (!a->orig) |
| for(int ilb = ila; ilb < kbd->layoutsCount; ++ilb) |
| for(int ikb = ilb==ila ? ika+1: 0; ikb < kbd->layouts[ilb].keysCount; ++ikb) { |
| Key *b = &kbd->layouts[ilb].keys[ikb]; |
| if ( a->flags == b->flags |
| && a->optValue == b->optValue |
| && a->keySym == b->keySym |
| && a->keySym2 == b->keySym2 ) |
| { |
| b->orig = a; |
| b->clone = a->clone; |
| a->clone = b; |
| } |
| } |
| } |
| |
| |
| return 1; |
| } |
| |
| |
| void keyboardDeinit(Keyboard *kbd) { |
| LOGDBG("keyboard: deinit"); |
| keyboardMouseUp(kbd); |
| for(int i = 0; i < kbd->layoutsCount; ++i) |
| layoutDeinit(&kbd->layouts[i]); |
| } |
| |
| |
| void keyboardResize(Keyboard *kbd) { |
| 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); |
| } |
| |
| |
| void keyboardDraw(Keyboard *kbd, int cx, int cy, int cw, int ch) { |
| if (!kbd->current) return; |
| layoutDraw(kbd->current, cx, cy, cw, ch); |
| } |
| |
| |
| void keyboardMouseDown(Keyboard *kbd, int x, int y) { |
| 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 keyboardMouseLongDown(Keyboard *kbd) { |
| LOGDBG("keyboard: mouse long down"); |
| if (!kbd->current) return; |
| layoutMouseLongDown(kbd->current); |
| } |
| |
| |
| void keyboardMouseUp(Keyboard *kbd) { |
| 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) { |
| |
| if (!kbd->layoutsCount) return; |
| |
| int prevIndex = (kbd->lastVisible ? kbd->lastVisible : kbd->current) - kbd->layouts; |
| |
| LOGDBG("keyboard: switch layout: prevIndex=%d nextIndex=%d", prevIndex, index); |
| |
| |
| if (index == LI_PREV) { |
| for(int i = 1; i <= kbd->layoutsCount; ++i) { |
| index = (prevIndex + kbd->layoutsCount - i) % kbd->layoutsCount; |
| if (!kbd->layouts[index].hidden) break; |
| } |
| } else |
| if (index == LI_NEXT) { |
| for(int i = 1; i <= kbd->layoutsCount; ++i) { |
| index = (prevIndex + i) % kbd->layoutsCount; |
| if (!kbd->layouts[index].hidden) break; |
| } |
| } else |
| if (index == LI_REVERT) { |
| if (!kbd->lastVisible) { |
| LOGWRN("keyboard: switch layout: cannot revert"); |
| return; |
| } |
| index = kbd->lastVisible - kbd->layouts; |
| } |
| |
| if (index < 0 || index >= kbd->layoutsCount) { |
| LOGWRN("keyboard: switch layout: bad index %d", index); |
| return; |
| } |
| |
| LOGDBG("keyboard: switch layout: actual index=%d", index); |
| kbd->current = &kbd->layouts[index]; |
| if (!kbd->current->hidden) |
| kbd->lastVisible = kbd->current; |
| |
| keyboardResize(kbd); |
| appInvalidateRect(kbd->app, 0, 0, kbd->app->w, kbd->app->h); |
| } |
| |