diff --git a/app.c b/app.c index 0af0ad3..7bf6c44 100644 --- a/app.c +++ b/app.c @@ -1,6 +1,9 @@ #include "app.h" +#include +#include +#include @@ -104,65 +107,111 @@ int appRun(App *app) { 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->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; - if (resized) graphResize(&app->graph); + + int fd = xcb_get_file_descriptor(app->xcb); + if (fd < 0) + LOGERR("app: run: cannot get xcb file descruptor"); + + int buttonDown = 0; + unsigned int buttonDownMs = 0; + while(1) { + xcb_generic_event_t *event = NULL; + xcb_flush(app->xcb); + + if (buttonDown == 1) { + event = xcb_poll_for_event(app->xcb); + if (!event) { + // manually call select for the X-socket to use timeout + unsigned int dt = monotonicMs() - buttonDownMs; + if (dt < LONGPRESS_MS) { + dt = LONGPRESS_MS - dt; + struct timeval t = {}; + t.tv_sec = dt/1000; + t.tv_usec = dt%1000*1000; + + 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); + select(fd + 1, &fds, NULL, NULL, &t); + + event = xcb_poll_for_event(app->xcb); + } } - break; + } else { + event = xcb_wait_for_event(app->xcb); + if (!event) 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); + + 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); + break; } - 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); + 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 )) + { + 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; + if (resized) graphResize(&app->graph); + } + break; } - 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 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); + if (!buttonDown) { buttonDown = 1; buttonDownMs = monotonicMs(); } + keyboardMouseDown(&app->keyboard, e->event_x, e->event_y); + } + break; } - break; - } - default: + case XCB_BUTTON_RELEASE: { + xcb_button_press_event_t *e = (xcb_button_press_event_t*)event; + if (e->detail == 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); + } + break; + } + default: + break; + } + free(event); + } + + if (buttonDown == 1 && monotonicMs() - buttonDownMs >= LONGPRESS_MS) { + LOGDBG("app: long press"); + buttonDown = 2; + keyboardMouseLongDown(&app->keyboard); } - free(event); if (app->run != 1) break; @@ -175,7 +224,6 @@ int appRun(App *app) { keyboardDraw(&app->keyboard, app->irx, app->iry, app->irw, app->irh); app->irw = 0; } - xcb_flush(app->xcb); } int err = app->run == -2; diff --git a/common.h b/common.h index b7c62ae..89bb61f 100644 --- a/common.h +++ b/common.h @@ -2,6 +2,9 @@ #define COMMON_H +#define _XOPEN_SOURCE 600 + +#include #include #include #include @@ -10,7 +13,10 @@ #include + #define NOBORDER +#define LONGPRESS_MS 1500 + #define COUNTOF(x) (sizeof(x)/sizeof((x)[0])) @@ -42,6 +48,14 @@ unsigned int keysymCharcode(unsigned int keySym); int keysymString(unsigned int keySym, char *buf5bytes); +// cyclic monotonic timer +static inline unsigned int monotonicMs() { + struct timespec t = {}; + clock_gettime(CLOCK_MONOTONIC, &t); + return (unsigned int)t.tv_sec*1000u + (unsigned int)t.tv_nsec/1000000u; +} + + 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; diff --git a/graph.c b/graph.c index 8c89dfd..ef5749e 100644 --- a/graph.c +++ b/graph.c @@ -13,8 +13,10 @@ int graphInit(Graph *g, App *app) { { 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 + { 0, 0, F }, { F, F, 0 }, // active text + { H, 0, F }, { F, F, 0 } }; // active text 2 // create palette g->app = app; @@ -119,7 +121,9 @@ 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) { - GraphStyle s = active ? GS_BUTTON_ACTIVE : GS_BUTTON_INACTIVE; + 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); } @@ -185,7 +189,9 @@ void textLayoutDeinit(TextLayout *tl) void textLayoutDraw(TextLayout *tl, int x, int y, int active) { if (!tl->len) return; - GraphStyle s = active ? GS_TEXT_ACTIVE : GS_TEXT_INACTIVE; + 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); } diff --git a/graph.h b/graph.h index c3b55ff..5605da6 100644 --- a/graph.h +++ b/graph.h @@ -12,10 +12,12 @@ typedef enum { GS_WINDOW, GS_BUTTON_INACTIVE, GS_BUTTON_ACTIVE, + GS_BUTTON_ACTIVE2, GS_TEXT_INACTIVE, - GS_TEXT_ACTIVE } GraphStyle; + GS_TEXT_ACTIVE, + GS_TEXT_ACTIVE2 } GraphStyle; enum { - GS_COUNT = GS_TEXT_ACTIVE + 1 }; + GS_COUNT = GS_TEXT_ACTIVE2 + 1 }; typedef struct Graph { diff --git a/keyboard.c b/keyboard.c index b762574..2a268c6 100644 --- a/keyboard.c +++ b/keyboard.c @@ -5,7 +5,7 @@ int keyInit(Key *k, Layout *l) { k->l = l; - k->down = 0; + k->down = k->longDown = 0; k->downKeycode = 0; k->mx = k->my = 0; k->isLetter = k->isKeypad = 0; @@ -47,10 +47,17 @@ void keyDraw(Key *k, int cx, int cy, int cw, int ch) { App *app = k->l->kbd->app; int active = k->down; - if (k->orig) + 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); + active = !!(app->input.modifiers & (unsigned int)k->optValue); graphDrawButton(&k->l->kbd->app->graph, k->x, k->y, k->w, k->h, active); @@ -131,6 +138,25 @@ void keyMotion(Key *k, int x, int y) { } +void keyLongDown(Key *k) { + if (k->orig) + return keyLongDown(k->orig); + if (k->down != 2 || k->longDown) + return; + + // remove from held list, this key will not unhold automatically + 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) { // clones must not be in held list @@ -143,6 +169,7 @@ void keyUp(Key *k, int force) { Keyboard *kbd = k->l->kbd; int wasDown = k->down; k->down = 0; + k->longDown = 0; // release key if (k->downKeycode) { @@ -286,6 +313,13 @@ void layoutMouseMotion(Layout *l, int x, int 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; @@ -384,6 +418,13 @@ void keyboardMouseMotion(Keyboard *kbd, int x, int 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; diff --git a/keyboard.h b/keyboard.h index 7349ea9..798dee6 100644 --- a/keyboard.h +++ b/keyboard.h @@ -54,6 +54,7 @@ struct Key { // dynamic fields int x, y, w, h; int down; + int longDown; int downKeycode; int mx, my; // values for move and size buttons Key *nextHeld, *prevHeld; @@ -99,6 +100,7 @@ void keyInvalidateRect(Key *k); void keyDraw(Key *k, int cx, int cy, int cw, int ch); void keyDown(Key *k, int x, int y); void keyMotion(Key *k, int x, int y); +void keyLongDown(Key *k); void keyUp(Key *k, int force); int layoutInit(Layout *l, Keyboard *kbd); @@ -107,6 +109,7 @@ 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); +void layoutMouseLongDown(Layout *l); void layoutMouseUp(Layout *l); int keyboardInit(Keyboard *kbd, App *app); @@ -116,6 +119,7 @@ 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); +void keyboardMouseLongDown(Keyboard *kbd); void keyboardMouseUp(Keyboard *kbd); void keyboardSwitchLayout(Keyboard *kbd, int index); void keyboardRelaseHeld(Keyboard *kbd);