From 45287099ad8c8fca97e5ddce71bc939e796d307f Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Aug 13 2023 05:20:46 +0000 Subject: initial commit --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..224fa4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +* +!*.* +*.kdev4 diff --git a/app.c b/app.c new file mode 100644 index 0000000..e5b22f0 --- /dev/null +++ b/app.c @@ -0,0 +1,157 @@ + +#include "app.h" + + + + +int appInit(App *app) { + LOGDBG("app: init"); + + LOGDBG("app: init: connect to xcb"); + app->xcb = xcb_connect(NULL, NULL); + if (!app->xcb) + 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->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 + XCB_EVENT_MASK_EXPOSURE // event mask + | XCB_EVENT_MASK_STRUCTURE_NOTIFY + | XCB_EVENT_MASK_BUTTON_PRESS + | 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 + 0, 0, // x, y + app->w, app->h, // width, height + 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 ); + + // init submodules + if (graphInit(&app->graph, app)) { + if (inputInit(&app->input, app)) { + if (keyboardInit(&app->keyboard, app)) { + return 1; + } + inputDeinit(&app->input); + } + graphDeinit(&app->graph); + } + } else { + LOGERR("app: init: no screen found"); + } + + xcb_disconnect(app->xcb); + return 0; +} + + +void appDeinit(App *app) { + LOGDBG("app: deinit"); + keyboardDeinit(&app->keyboard); + inputDeinit(&app->input); + graphDeinit(&app->graph); + xcb_disconnect(app->xcb); +} + + +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); + + LOGDBG("app: run: show window"); + xcb_map_window(app->xcb, app->win); + xcb_flush(app->xcb); + + LOGDBG("app: run: event loop"); + xcb_generic_event_t *event; + while ((event = xcb_wait_for_event(app->xcb))) { + 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); + break; + } + 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); + app->w = e->width; + app->h = e->height; + 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); + keyboardMouseDown(&app->keyboard, e->event_x, e->event_y); + } + break; + } + case XCB_BUTTON_RELEASE: { + xcb_button_press_event_t *e = (xcb_button_press_event_t*)event; + if (e->detail == 1) { + LOGDBG("app: mouse up"); + keyboardMouseUp(&app->keyboard); + } + break; + } + default: + break; + } + free(event); + + 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); + keyboardDraw(&app->keyboard, app->irx, app->iry, app->irw, app->irh); + xcb_flush(app->xcb); + app->irw = 0; + } + } + + LOGDBG("app: run: done"); + return 1; +} + + +void appInvalidateRect(App *app, int x, int y, int w, int h) { + LOGDBG("app: invalidate rect: x=%d, y=%d, w=%d, h=%d", x, y, w, h); + rectIntersect(&x, &y, &w, &h, 0, 0, app->w, app->h); + rectMerge(&app->irx, &app->iry, &app->irw, &app->irh, x, y, w, h); +} + diff --git a/app.h b/app.h new file mode 100644 index 0000000..eb02b98 --- /dev/null +++ b/app.h @@ -0,0 +1,34 @@ +#ifndef APP_H +#define APP_H + + +#include "common.h" +#include "graph.h" +#include "input.h" +#include "keyboard.h" + + +struct App { + // keep these filds at the begining for easyest static initialization + Keyboard keyboard; + Input input; + Graph graph; + + // these fields will be set while initialization + xcb_connection_t *xcb; + xcb_screen_t *screen; + xcb_window_t win; + int w, h; + + // dynamic fields + int irx, iry, irw, irh; +}; + + +int appInit(App *app); +void appDeinit(App *app); +int appRun(App *app); +void appInvalidateRect(App *app, int x, int y, int w, int h); + + +#endif diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..6d2b5cc --- /dev/null +++ b/build.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +FLAGS="$(pkg-config --cflags --libs xcb x11 xtst)" +FLAGS="$FLAGS -Wall" +if [ "$1" = "debug" ]; then + FLAGS="$FLAGS -g -O0" +else + FLAGS="$FLAGS -O3 -DNDEBUG" +fi + +cc $FLAGS main.c -o coolkbd + +echo done diff --git a/common.c b/common.c new file mode 100644 index 0000000..0701fbc --- /dev/null +++ b/common.c @@ -0,0 +1,4 @@ + +#include "common.h" + + diff --git a/common.h b/common.h new file mode 100644 index 0000000..7359851 --- /dev/null +++ b/common.h @@ -0,0 +1,61 @@ +#ifndef COMMON_H +#define COMMON_H + + +#include +#include +#include + +#include +#include + + + + +#define LOG(stream, prefix, suffix, result, ...) \ + ( fputs(prefix, stream), fprintf(stream, __VA_ARGS__), fputs(suffix, stream), fflush(stream), dummy(result) ) + +#ifdef NDEBUG + #define LOGDBG(...) dummy(1) +#else + #define LOGDBG(...) LOG(stdout, "DEBUG: ", "\n", 1, __VA_ARGS__) +#endif + +#define LOGINF(...) LOG(stdout, "INFO: ", "\n", 1, __VA_ARGS__) +#define LOGWRN(...) LOG(stderr, "WARNING: ", "\n", 1, __VA_ARGS__) +#define LOGERR(...) LOG(stderr, "ERROR: ", "\n", 0, __VA_ARGS__) + + + +struct App; +typedef struct App App; + + + +static inline int dummy(int i) { return i; } + + +static inline void rectIntersect(int *x, int *y, int *w, int *h, int xx, int yy, int ww, int hh) { + if (*x < xx) *w += *x - xx, *x = xx; + if (*y < yy) *h += *y - yy, *y = xx; + ww += xx; hh += yy; + if (*x + *w > ww) *w = ww - *x; + if (*y + *h > hh) *h = hh - *y; +} + + +static inline void rectMerge(int *x, int *y, int *w, int *h, int xx, int yy, int ww, int hh) { + if (ww <= 0 || hh <= 0) + return; + if (*w <= 0 || *h <= 0) + { *x = xx, *y = yy, *w = ww, *h = hh; return; } + if (*x > xx) *w += *x - xx, *x = xx; + if (*x > xx) *h += *y - yy, *y = yy; + ww += xx; hh += yy; + if (*x + *w < ww) *w = ww - *x; + if (*y + *h < hh) *h = hh - *y; +} + + + +#endif diff --git a/graph.c b/graph.c new file mode 100644 index 0000000..7a9244d --- /dev/null +++ b/graph.c @@ -0,0 +1,91 @@ + +#include "app.h" + + + +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 + { 0, 0, H }, { F, F, 0 }, // inactive text + { 0, 0, F }, { F, F, 0 } }; // active text + + // create palette + 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); + + // create contexts + uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_GRAPHICS_EXPOSURES; + uint32_t values[3] = {0, 0, 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); + } + + return 1; +} + + +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_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); +} + + +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); +} + + +void graphDrawButton(Graph *g, int x, int y, int w, int h, int active) { + GraphStyle s = active ? GS_BUTTON_ACTIVE : 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); +} + + + +int textLayoutInit(TextLayout *tl, Graph *g, int cx, int cy, const char *text) { + tl->g = g; + tl->text = text; + return 1; +} + +void textLayoutDeinit(TextLayout *tl) + { } + +void textLayoutDraw(TextLayout *tl, int active) + { } diff --git a/graph.h b/graph.h new file mode 100644 index 0000000..95fd7d7 --- /dev/null +++ b/graph.h @@ -0,0 +1,45 @@ +#ifndef GRAPH_H +#define GRAPH_H + + +#include "common.h" + + +typedef enum { + GS_WINDOW, + GS_BUTTON_INACTIVE, + GS_BUTTON_ACTIVE, + GS_TEXT_INACTIVE, + GS_TEXT_ACTIVE } GraphStyle; +enum { + GS_COUNT = GS_TEXT_ACTIVE + 1 }; + + +typedef struct Graph { + App *app; + xcb_colormap_t cm; + xcb_gcontext_t gc[GS_COUNT]; +} Graph; + + +typedef struct TextLayout { + Graph *g; + int x, y; + const char *text; +} TextLayout; + + + + +int graphInit(Graph *g, App *app); +void graphDeinit(Graph *g); +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 textLayoutDeinit(TextLayout *tl); +void textLayoutDraw(TextLayout *tl, int active); + + +#endif diff --git a/input.c b/input.c new file mode 100644 index 0000000..7db0763 --- /dev/null +++ b/input.c @@ -0,0 +1,82 @@ + +#include "app.h" + +#include +#include + + +int inputInit(Input *in, App *app) { + LOGDBG("input: init"); + + in->app = app; + in->mappedKeyCode = 0; + in->mappedPressed = 0; + + 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: found keycode %d", in->mappedKeyCode); + if (!in->mappedKeyCode) + LOGWRN("input: init: cannot found free keycode for temporary mappings, some keys may not work"); + + return 1; +} + + +void inputDeinit(Input *in) { + LOGDBG("input: deinit"); + XCloseDisplay(in->dpy); +} + + +void inputEvent(Input *in, unsigned int keySym, int press) { + LOGDBG("input: event: keySym=%u press=%d", keySym, press); + + KeyCode keycode = XKeysymToKeycode(in->dpy, keySym); + + if (!keycode) { + if (!in->mappedKeyCode) { + LOGWRN("input: event: no keycode mapped to keySym[%u], and no free keycodes for temporary mapping", keySym); + return; + } + + keycode = in->mappedKeyCode; + LOGDBG("input: event: temporary remap: keySym[%u] -> keycode[%hhu]", keySym, keycode); + + 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); + } + + 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 (keycode == in->mappedKeyCode) + in->mappedPressed = press; + + LOGDBG("input: event: keycode=%hhu press=%d", keycode, press); + XTestFakeKeyEvent(in->dpy, keycode, press, CurrentTime); + XSync(in->dpy, False); +} + diff --git a/input.h b/input.h new file mode 100644 index 0000000..c434efc --- /dev/null +++ b/input.h @@ -0,0 +1,26 @@ +#ifndef INPUT_H +#define INPUT_H + + +#include "common.h" + +#include +#include +#include + + +typedef struct Input { + App *app; + Display *dpy; + int mappedKeyCode; + int mappedPressed; +} Input; + + +int inputInit(Input *in, App *app); +void inputDeinit(Input *in); + +void inputEvent(Input *in, unsigned int keySym, int press); + + +#endif diff --git a/keyboard.c b/keyboard.c new file mode 100644 index 0000000..3e2e1e4 --- /dev/null +++ b/keyboard.c @@ -0,0 +1,105 @@ + +#include "app.h" + + + +int keyInit(Key *k, Keyboard *kbd) { + k->kbd = kbd; + 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; + } + + return 1; +} + + +void keyDeinit(Key *k) { + if (k->label) textLayoutDeinit(&k->tl); + if (k->label2) 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); +} + + +void keyMouseDown(Key *k) { + k->down = 1; + inputEvent(&k->kbd->app->input, k->keySym, 1); + appInvalidateRect(k->kbd->app, k->x, k->y, k->w, k->h); +} + + +void keyMouseUp(Key *k) { + k->down = 0; + inputEvent(&k->kbd->app->input, k->keySym, 0); + appInvalidateRect(k->kbd->app, k->x, k->y, k->w, k->h); +} + + + +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]); + return 0; + } + return 1; +} + + +void keyboardDeinit(Keyboard *kbd) { + LOGDBG("keyboard: deinit"); + keyboardMouseUp(kbd); + for(int i = 0; i < kbd->keysCount; ++i) + keyDeinit(&kbd->keys[i]); +} + + +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); +} + + +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; + } + } +} + + +void keyboardMouseUp(Keyboard *kbd) { + if (kbd->downKey) { + keyMouseUp(kbd->downKey); + kbd->downKey = NULL; + } +} diff --git a/keyboard.h b/keyboard.h new file mode 100644 index 0000000..3e15996 --- /dev/null +++ b/keyboard.h @@ -0,0 +1,57 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + + +#include "graph.h" +#include "input.h" + + +struct Keyboard; +typedef struct Keyboard Keyboard; + + +typedef struct Key { + // keep these filds at the begining for easyest static initialization + int x, y, w, h; + unsigned int keySym; + const char *label; + const char *label2; + + // these fields will be set while initialization + Keyboard *kbd; + TextLayout tl; + TextLayout tl2; + + // dynamic fields + int down; +} Key; + + +struct Keyboard { + // keep these filds at the begining for easyest static initialization + Key *keys; + int keysCount; + int w, h; + + // these fields will be set while initialization + App *app; + + // dynamic fields + Key *downKey; +}; + + +int keyInit(Key *k, Keyboard *kbd); +void keyDeinit(Key *k); +void keyDraw(Key *k, int cx, int cy, int cw, int ch); +void keyDown(Key *k); +void keyUp(Key *k); + +int keyboardInit(Keyboard *kbd, App *app); +void keyboardDeinit(Keyboard *kbd); +void keyboardDraw(Keyboard *kbd, int cx, int cy, int cw, int ch); +void keyboardMouseDown(Keyboard *kbd, int x, int y); +void keyboardMouseUp(Keyboard *kbd); + + +#endif diff --git a/keyboard.inc.c b/keyboard.inc.c new file mode 100644 index 0000000..0a2f1a2 --- /dev/null +++ b/keyboard.inc.c @@ -0,0 +1,117 @@ +#ifndef KEYBOARD_INC_C +#define KEYBOARD_INC_C + + +struct Keyboard; + + +typedef struct Key { + int x, y, w, h; + const char *label; + + struct Keyboard *kbd; + int lx, ly; + + int pressed; +} Key; + + +typedef struct Keyboard { + Key *keys; + int keysCount; + + Display* dpy; + Window win; + GC gc; + GContext gcid; + XFontStruct *font; + + Key *keyMousePressed; +} Keyboard; + + + +void keyInit(Key *k, Keyboard *kbd) { + k->pressed = 0; + k->kbd = kbd; + XFontStruct *font = XQueryFont(k->kbd->dpy, k->kbd->gcid); + XRectangle r = {}; + XTextExtents(font, k->label, strlen(k->label), NULL, &r); + k->lx = k->x + (k->w - r.width)/2 - r.x; + k->ly = k->y + (k->h - r.height)/2 - r.y; +} + +void keyDeinit(Key *k) { + k->pressed = 0; +} + +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; + if (k->pressed) + XDrawRectangle(k->kbd->dpy, k->kbd->win, k->kbd->gc, k->x, k->y, k->w, k->h); + XDrawString(k->kbd->dpy, k->kbd->win); +} + +void keyMouseDown(Key *k) { + k->pressed = 1; +} + +void keyMouseUp(Key *k) { + k->pressed = 0; +} + + +void keyboardInit(Keyboard *kbd, Display *dpy, Window win) { + kbd->keyMousePressed = NULL; + kbd->dpy = dpy; + kbd->win = win; + kbd->gc = DefaultGC(kbd->dpy, kbd->win); + kbd->gcid = XGContextFromGC(kbd->gc); + kbd->font = XQueryFont(k->kbd->dpy, k->kbd->gcid); + for(int i = 0; i < kbd->keysCount; ++i) + keyInit(&kbd->keys[i], kbd); +} + +void keyboardDeinit(Keyboard *kbd) { + for(int i = 0; i < kbd->keysCount; ++i) + keyDeinit(kbd->keys[i]); + kbd->keyMousePressed = NULL; + kbd->font = XFreeFont(kbd->dpy, kbd->font); + kbd->gcid = NULL; + kbd->gc = NULL; + kbd->win = NULL; + kbd->dpy = NULL; +} + +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); +} + +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->keyMousePressed != k) { + keyboardMouseUp(kbd); + kbd->keyMousePressed = k; + keyMouseDown(k); + } + break; + } + } +} + +void keyboardMouseUp(Keyboard *kbd) { + if (kbd->keyMousePressed) { + kbd->keyMouseUp(kbd->keyMousePressed); + kbd->keyMousePressed = NULL; + } +} + + + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..62dfbdb --- /dev/null +++ b/main.c @@ -0,0 +1,35 @@ + +#include "app.h" + +#include "common.c" +#include "app.c" +#include "graph.c" +#include "input.c" +#include "keyboard.c" + + +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, "г" } }; +enum { keysCount = sizeof(keys)/sizeof(keys[0]) }; + + +App app = { + { keys, keysCount, 170, 70 } }; // keyboard + + +int main() { + if (!appInit(&app)) + return 1; + if (!appRun(&app)) + return 1; + appDeinit(&app); + return 0; +} +