Upload files to 'btk'

This commit is contained in:
bricks 2022-07-06 22:10:46 -04:00
parent 5e4aed7040
commit 6878bd881b
5 changed files with 1765 additions and 0 deletions

441
btk/btk-cell-render.c Normal file
View File

@ -0,0 +1,441 @@
void fill_cell (btk_cell_t *, cairo_t *, btk_rgb_t);
void guard_scroll (int *, int, int, unsigned int, int);
void render_button (btk_cell_t *, cairo_t *);
void render_editor (btk_cell_t *);
void render_gauge (btk_cell_t *, cairo_t *);
void render_input (btk_cell_t *, cairo_t *);
void render_list (btk_cell_t *);
void render_mark (btk_cell_t *, cairo_t *);
void render_prompt (btk_cell_t *, cairo_t *);
void render_table (btk_cell_t *);
void render_table_row (btk_cell_t *, char *, int, btk_rgb_t, btk_rgb_t);
void render_text (btk_cell_t *);
void render_switch (btk_cell_t *, cairo_t *);
void render_wheel (btk_cell_t *, cairo_t *);
void reset_field (btk_cell_t *);
void write_label (cairo_t *, int, int, unsigned int, int, char *, btk_rgb_t);
static cairo_font_options_t *font_opt;
static btk_pos_t label_offset;
static btk_size_t glyph;
/* ch is a modifier to force the heigth of a rendered cell to 1 cell unit */
void
fill_cell(btk_cell_t *c, cairo_t *c_ctx, btk_rgb_t cl)
{
cairo_set_source_rgb(c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c_ctx, c->pa.x, c->pa.y, c->pa.w, c->pa.h);
cairo_fill(c_ctx);
}
/* pi = pixel indent, of each scrollable element in the considered axis */
/* cl = cell length, in the considered axis */
void
guard_scroll(int *scroll_var, int pi, int cl, unsigned int items_n, int extra)
{
if (*scroll_var < 0 || !items_n) { /* no negative scroll position */
*scroll_var = 0;
} else if (cl >= pi * items_n + extra) { /* reset scroll pos if everything fits in */
*scroll_var = 0;
} else if (*scroll_var + cl > pi * items_n + extra) {
*scroll_var = pi * items_n - cl + extra;
}
}
void
render_button(btk_cell_t *c, cairo_t *c_ctx)
{
btk_rgb_t clt, cl;
if ((c->state & BTK_CELL_STATE_DISABLED)) {
cl = btk_cl_dyn_disabled;
clt = btk_cl_text_disabled;
} else if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
cl = btk_cl_dyn_idle;
clt = btk_cl_text_normal;
}
fill_cell(c, c_ctx, cl);
write_label(c_ctx, c->pa.x, c->pa.y, c->pa.w, BTK_JUSTIFY_LEFT, c->label, clt);
}
void
render_editor(btk_cell_t *c)
{
btk_pp_editor_t *pp = PP_EDITOR(c);
cairo_set_source_rgb(c->fd->c_ctx, btk_cl_field.r, btk_cl_field.g, btk_cl_field.b);
cairo_paint(c->fd->c_ctx);
int text_n = 0;
if (*pp->text)
text_n = strlen(pp->text);
guard_scroll(&pp->scroll, glyph.h + btk_text_spacing, c->pa.h, pp->par->rows_n, label_offset.y);
/* render caret */
if (!(c->state & BTK_CELL_STATE_DISABLED)) {
cairo_set_source_rgb(c->fd->c_ctx,
btk_cl_bell.r,
btk_cl_bell.g,
btk_cl_bell.b);
cairo_move_to(c->fd->c_ctx,
label_offset.x + pp->caret_2d.x * glyph.w,
pp->caret_2d.y * (glyph.h + btk_text_spacing) +
btk_caret_offset + label_offset.y - pp->scroll);
cairo_show_text(c->fd->c_ctx, "_");
}
/* just render label if there are no rows to draw */
if (!text_n || !pp->par) {
write_label(c->fd->c_ctx,
0,
0,
c->pa.w,
BTK_JUSTIFY_LEFT,
c->label,
btk_cl_text_lolight);
return;
}
btk_rgb_t cl;
/* else render row by row */
for (int i = 0; i < pp->par->rows_n; i++) {
cl = btk_cl[pp->par->rows_spot[i]];
cairo_set_source_rgb(c->fd->c_ctx, cl.r, cl.g, cl.b);
cairo_move_to(c->fd->c_ctx,
label_offset.x,
label_offset.y - pp->scroll + i * (glyph.h + btk_text_spacing));
cairo_show_text(c->fd->c_ctx, pp->par->rows + i * pp->par->rows_max_w);
}
}
void
render_input(btk_cell_t *c, cairo_t *c_ctx)
{
btk_pp_input_t *pp = PP_INPUT(c);
int text_n = strlen(pp->text);
btk_rgb_t clt, clt2;
/* passive components */
clt = (c->state & BTK_CELL_STATE_DISABLED) ? btk_cl_dyn_disabled : btk_cl_pass_on;
fill_cell(c, c_ctx, btk_cl_field);
cairo_set_source_rgb(c_ctx, clt.r, clt.g, clt.b);
cairo_move_to(c_ctx, c->pa.x + label_offset.x, c->pa.y + label_offset.y);
cairo_show_text(c_ctx, "");
/* limits */
int max_n = (c->pa.w - 3 * label_offset.x ) / glyph.w - 1;
if (pp->caret > pp->scroll + max_n - 1)
pp->scroll = pp->caret - max_n + 1;
if (pp->caret < pp->scroll)
pp->scroll = pp->caret;
if (text_n > max_n && pp->scroll + max_n > text_n)
pp->scroll -= pp->scroll + max_n - text_n;
/* render caret */
if (!(c->state & BTK_CELL_STATE_DISABLED)) {
cairo_set_source_rgb(c_ctx, btk_cl_bell.r, btk_cl_bell.g, btk_cl_bell.b);
cairo_move_to(c_ctx,
c->pa.x + 2 * label_offset.x + (1 + pp->caret - pp->scroll) * glyph.w,
c->pa.y + label_offset.y + btk_caret_offset);
cairo_show_text(c_ctx, "_");
}
/* prepare text to render */
char cut_text[max_n + 1];
if (text_n) {
strncpy(cut_text, pp->text + pp->scroll, max_n);
cut_text[max_n] = '\0';
clt2 = btk_cl_text_disabled;
} else {
strcpy(cut_text, c->label);
clt2 = btk_cl_text_lolight;
}
/* render text */
cairo_set_source_rgb(c_ctx, clt2.r, clt2.g, clt2.b);
cairo_move_to(c_ctx,
c->pa.x + 2 * label_offset.x + glyph.w,
c->pa.y + label_offset.y);
cairo_show_text(c_ctx, cut_text);
}
void
render_list(btk_cell_t *c)
{
reset_field(c);
guard_scroll(&(PP_LIST(c)->scroll),
btk_cp.h + btk_frame,
c->pa.h,
*(PP_LIST(c)->items_n),
0);
/* compute item range to render */
int item_min = PP_LIST(c)->scroll / (btk_cp.h + btk_frame);
int item_max = (PP_LIST(c)->scroll + c->pa.h) / (btk_cp.h + btk_frame) + 1;
item_max = BTK_MIN(item_max, *(PP_LIST(c)->items_n));
/* just render label if there are no rows to draw */
if (!*(PP_LIST(c)->items_n)) {
write_label(c->fd->c_ctx,
0,
0,
c->pa.w,
BTK_JUSTIFY_LEFT,
c->label,
btk_cl_text_lolight);
return;
}
/* render sub cells */
btk_rgb_t cl, clt;
int row_pos;
for (int i = item_min; i < item_max; i++) {
if (i == PP_LIST(c)->item_sel) {
if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
clt = btk_cl_text_select;
cl = btk_cl_select;
}
} else {
cl = btk_cl_field;
clt = btk_cl_text_disabled;
if (PP_LIST(c)->spot)
clt = btk_cl[*(*(PP_LIST(c)->spot) + i)];
}
row_pos = (btk_cp.h + btk_frame) * i - PP_LIST(c)->scroll;
cairo_set_source_rgb(c->fd->c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c->fd->c_ctx, 0, row_pos, c->pa.w, btk_cp.h);
cairo_fill(c->fd->c_ctx);
write_label(c->fd->c_ctx,
0,
row_pos,
c->pa.w,
BTK_JUSTIFY_LEFT,
*(PP_LIST(c)->items) + i * PP_LIST(c)->items_w,
clt);
}
}
void
render_mark(btk_cell_t *c, cairo_t *c_ctx)
{
fill_cell(c, c_ctx, btk_cl_pass_off);
btk_rgb_t clt, cl = {0.0f, 0.0f, 0.0f};
if ((c->state & BTK_CELL_STATE_BELL)) {
cl = btk_cl_bell;
clt = btk_cl_text_bell;
} else if ((c->state & BTK_CELL_STATE_ON)) {
cl = btk_cl_pass_on;
clt = btk_cl_text_hilight;
} else {
clt = c->state & BTK_CELL_STATE_DISABLED ?
btk_cl_text_disabled : btk_cl_text_normal;
}
if ((c->state & (BTK_CELL_STATE_BELL | BTK_CELL_STATE_ON))) {
cairo_set_source_rgb(c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c_ctx,
c->pa.x + btk_frame,
c->pa.y + btk_frame,
c->pa.w - 2 * btk_frame,
c->pa.h - 2 * btk_frame);
cairo_fill(c_ctx);
}
write_label(c_ctx, c->pa.x, c->pa.y, c->pa.w, PP_MARK(c)->justify, c->label, clt);
}
void
render_switch(btk_cell_t *c, cairo_t *c_ctx)
{
btk_rgb_t clt, cl;
if ((c->state & BTK_CELL_STATE_DISABLED)) {
cl = btk_cl_dyn_disabled;
clt = btk_cl_text_disabled;
} else if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
cl = btk_cl_dyn_idle;
clt = btk_cl_text_normal;
}
fill_cell(c, c_ctx, cl);
write_label(c_ctx,
c->pa.x,
c->pa.y,
c->pa.w - btk_cp.h,
BTK_JUSTIFY_LEFT,
c->label,
clt);
/* state indicator */
cl = (c->state & BTK_CELL_STATE_ON) ? btk_cl_pass_on : btk_cl_pass_off;
cairo_set_source_rgb(c_ctx, btk_cl_frame.r, btk_cl_frame.g, btk_cl_frame.b);
cairo_rectangle(c_ctx,
c->pa.x + c->pa.w - btk_cp.h + btk_padding,
c->pa.y + btk_padding,
btk_cp.h - 2 * btk_padding,
btk_cp.h - 2 * btk_padding);
cairo_fill(c_ctx);
cairo_set_source_rgb(c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c_ctx,
c->pa.x + c->pa.w - btk_cp.h + btk_padding + btk_frame,
c->pa.y + btk_padding + btk_frame,
btk_cp.h - 2 * (btk_padding + btk_frame),
btk_cp.h - 2 * (btk_padding + btk_frame));
cairo_fill(c_ctx);
}
void
render_table(btk_cell_t *c)
{
reset_field(c);
btk_pp_table_t *pp = PP_TABLE(c);
/* horizontal then vertical scroll */
guard_scroll(&pp->scroll.x, pp->tw, c->pa.w, 1, 0);
guard_scroll(&pp->scroll.y, btk_cp.h + btk_frame, c->pa.h, *pp->rows_n + 1, 0);
int header_indent = pp->header ? 1 : 0;
/* compute item range to render */
int item_min = pp->scroll.y / (btk_cp.h + btk_frame);
int item_max = (pp->scroll.y + c->pa.h) / (btk_cp.h + btk_frame);
item_max = BTK_MIN(item_max, *pp->rows_n);
/* just render label if there are no rows to draw */
if (!*pp->rows_n) {
write_label(c->fd->c_ctx,
0,
header_indent * (btk_cp.h + btk_frame),
c->pa.w,
BTK_JUSTIFY_LEFT,
c->label,
btk_cl_text_lolight);
goto header;
}
/* render rows */
btk_rgb_t cl, clt;
int row_pos;
for (int i = item_min; i < item_max; i++) {
if (i == pp->row_sel) {
if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
clt = btk_cl_text_select;
cl = btk_cl_select;
}
} else {
cl = btk_cl_field;
clt = btk_cl_text_disabled;
if (pp->spot)
clt = btk_cl[*(*(PP_TABLE(c)->spot) + i)];
}
row_pos = (btk_cp.h + btk_frame) * (i + header_indent) - pp->scroll.y;
render_table_row(c, btk_cell_table_get_item(c, 0, i), row_pos, cl, clt);
}
/* render header */
header:
if (pp->header)
render_table_row(c, pp->header, 0, btk_cl_pass_off, btk_cl_text_normal);
}
void
render_table_row(btk_cell_t *c,
char *row_data_start,
int row_pos,
btk_rgb_t cl,
btk_rgb_t clt)
{
btk_pp_table_t *pp = PP_TABLE(c);
char label[pp->items_w];
btk_rgb_t clt2;
for (int i = 0; i < pp->cols_n; i++) {
cairo_set_source_rgb(c->fd->c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c->fd->c_ctx,
pp->cols_px[i] - pp->scroll.x,
row_pos, pp->cols_pw[i],
btk_cp.h);
cairo_fill(c->fd->c_ctx);
clt2 = clt;
strcpy(label, row_data_start + i * pp->items_w);
if (label[0] == '\n' || label[0] == '\0') {
clt2 = btk_cl_text_lolight;
strncpy(label, "-/-", pp->items_w);
}
write_label(c->fd->c_ctx,
pp->cols_px[i] - pp->scroll.x,
row_pos,
pp->cols_pw[i],
BTK_JUSTIFY_LEFT,
label,
clt2);
}
}
void
reset_field(btk_cell_t *c)
{
if (!c->fd) {
btk_log_warning(BTK_WARNING_DRAW_NO_FIELD);
return;
}
cairo_set_source_rgb(c->fd->c_ctx, btk_cl_empty.r, btk_cl_empty.g, btk_cl_empty.b);
cairo_paint(c->fd->c_ctx);
}
void
write_label(cairo_t *c_ctx,
int px,
int py,
unsigned int pw,
int justify,
char *label,
btk_rgb_t clt)
{
int n = strlen(label);
int max_n = (pw - 2 * label_offset.x) / glyph.w;
char cut_label[max_n];
/* make label fit in cell if needed */
if (n > max_n) {
int caret;
if (justify == BTK_JUSTIFY_LEFT) {
caret = max_n - 3;
strncpy(cut_label, label, max_n);
} else {
caret = 0;
strncpy(cut_label, label + n - max_n, max_n);
}
cut_label[caret] = '.';
cut_label[++caret] = '.';
cut_label[++caret] = '.';
cut_label[max_n] = '\0';
}
/* draw label */
cairo_set_source_rgb(c_ctx, clt.r, clt.g, clt.b);
if (justify == BTK_JUSTIFY_LEFT) {
cairo_move_to(c_ctx, px + label_offset.x, py + label_offset.y);
} else {
int a = n > max_n ? max_n : n;
cairo_move_to(c_ctx, px + pw - label_offset.x - glyph.w * a, py + label_offset.y);
}
char *l = n > max_n ? cut_label : label;
cairo_show_text(c_ctx, l);
}

712
btk/btk-cell.c Normal file
View File

@ -0,0 +1,712 @@
#include <cairo/cairo.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "btk-cell.h"
#include "btk-log.h"
#include "btk-cell-render.c"
void
btk_cell_set_button(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
char *label,
void (*func)(void))
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->group = BTK_CELL_GROUP_DYNAMIC;
c->type = BTK_CELL_TYPE_BUTTON;
c->lh = 1;
c->label = label;
NEW_PP(btk_pp_button_t, c->pp);
PP_BUTTON(c)->func = func;
}
void
btk_cell_set_editor(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch,
char *text,
unsigned int text_w)
{
btk_cell_set_empty(c, cx, cy, cw, ch);
c->group = BTK_CELL_GROUP_FIELD;
c->type = BTK_CELL_TYPE_EDITOR;
c->lh = 0;
c->label = "no data";
NEW_PP(btk_pp_editor_t, c->pp);
PP_EDITOR(c)->text = text;
PP_EDITOR(c)->text_w = text_w;
PP_EDITOR(c)->caret = 0;
PP_EDITOR(c)->caret_2d.x = 0;
PP_EDITOR(c)->caret_2d.y = 0;
PP_EDITOR(c)->scroll = 0;
PP_EDITOR(c)->par = NULL;
}
void
btk_cell_set_empty(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch)
{
c->group = BTK_CELL_GROUP_PASSIVE;
c->type = BTK_CELL_TYPE_EMPTY;
c->state = BTK_CELL_STATE_INITIAL;
c->ca.x = cx;
c->ca.y = cy;
c->ca.w = cw;
c->ca.h = ch;
c->lh = 0;
c->pp = NULL;
c->fd = NULL;
c->label = NULL;
}
void
btk_cell_set_mark(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
int justify,
char *label)
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->type = BTK_CELL_TYPE_MARK;
c->lh = 1;
c->label = label;
NEW_PP(btk_pp_mark_t, c->pp);
PP_MARK(c)->justify = justify;
}
void
btk_cell_set_input(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
char *text,
unsigned int text_w)
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->group = BTK_CELL_GROUP_DYNAMIC;
c->type = BTK_CELL_TYPE_INPUT;
c->lh = 1;
c->label = "-/-";
NEW_PP(btk_pp_input_t, c->pp);
PP_INPUT(c)->text = text;
PP_INPUT(c)->text_w = text_w;
PP_INPUT(c)->caret = 0;
PP_INPUT(c)->scroll = 0;
}
void
btk_cell_set_list(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch,
char **items,
unsigned int *items_n,
unsigned int items_w,
int **filter,
int **order,
int **spot,
void (*func_trigger)(int),
void (*func_sel)(int))
{
btk_cell_set_empty(c, cx, cy, cw, ch);
c->group = BTK_CELL_GROUP_FIELD;
c->type = BTK_CELL_TYPE_LIST;
c->label = "no data";
NEW_PP(btk_pp_list_t, c->pp);
PP_LIST(c)->items = items;
PP_LIST(c)->items_n = items_n;
PP_LIST(c)->items_w = items_w;
PP_LIST(c)->item_sel = -1;
PP_LIST(c)->filter = filter;
PP_LIST(c)->order = order;
PP_LIST(c)->spot = spot;
PP_LIST(c)->scroll = 0;
PP_LIST(c)->func_trigger = func_trigger;
PP_LIST(c)->func_sel = func_sel;
}
void
btk_cell_set_switch(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
char *label,
void (*func)(int, btk_arg_t),
btk_arg_t func_args)
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->group = BTK_CELL_GROUP_DYNAMIC;
c->type = BTK_CELL_TYPE_SWITCH;
c->lh = 1;
c->label = label;
NEW_PP(btk_pp_switch_t, c->pp);
PP_SWITCH(c)->func = func;
PP_SWITCH(c)->func_args = func_args;
}
void
btk_cell_set_table(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch,
char *header, /* must be same item_w as items */
char **items,
unsigned int items_w,
unsigned int cols_n,
unsigned int *rows_n,
int *cols_ena,
unsigned int *cols_cw,
unsigned int sc,
int **filter,
int **order,
int **spot,
void (*func_trigger)(int),
void (*func_sel)(int))
{
btk_cell_set_empty(c, cx, cy, cw, ch);
c->group = BTK_CELL_GROUP_FIELD;
c->type = BTK_CELL_TYPE_TABLE;
c->label = "no data";
NEW_PP(btk_pp_table_t, c->pp);
PP_TABLE(c)->header = header;
PP_TABLE(c)->items = items;
PP_TABLE(c)->items_w = items_w;
PP_TABLE(c)->rows_n = rows_n;
PP_TABLE(c)->cols_n = cols_n;
PP_TABLE(c)->row_sel = -1;
PP_TABLE(c)->cols_ena = cols_ena;
PP_TABLE(c)->cols_cw = cols_cw;
PP_TABLE(c)->sc = sc;
PP_TABLE(c)->tw = 0;
PP_TABLE(c)->filter = filter;
PP_TABLE(c)->order = order;
PP_TABLE(c)->spot = spot;
PP_TABLE(c)->scroll.x = 0;
PP_TABLE(c)->scroll.y = 0;
PP_TABLE(c)->func_trigger = func_trigger;
PP_TABLE(c)->func_sel = func_sel;
PP_TABLE(c)->cols_pw = (unsigned int*)malloc(cols_n * sizeof(int));
PP_TABLE(c)->cols_px = (unsigned int*)malloc(cols_n * sizeof(int));
}
/****************************************************************************************/
void
btk_cell_button_trigger(btk_cell_t *c, int button)
{
if (button == 1 && PP_BUTTON(c)->func)
PP_BUTTON(c)->func();
}
void
btk_cell_destroy(btk_cell_t *c)
{
if (c->pp) {
switch (c->type) {
case BTK_CELL_TYPE_TABLE:
free(PP_TABLE(c)->cols_pw);
free(PP_TABLE(c)->cols_px);
break;
case BTK_CELL_TYPE_EDITOR:
btk_text_destroy_par(PP_EDITOR(c)->par);
break;
default:
break;
}
free(c->pp);
}
free(c);
}
void
btk_cell_draw(btk_cell_t *c, cairo_t *c_ctx)
{
if (!c)
return;
if ((c->state & BTK_CELL_STATE_HIDDEN))
return;
switch(c->type) {
case BTK_CELL_TYPE_MARK:
render_mark(c, c_ctx);
break;
case BTK_CELL_TYPE_BUTTON:
render_button(c, c_ctx);
break;
case BTK_CELL_TYPE_SWITCH:
render_switch(c, c_ctx);
break;
case BTK_CELL_TYPE_LIST:
render_list(c);
break;
case BTK_CELL_TYPE_TABLE:
render_table(c);
break;
case BTK_CELL_TYPE_INPUT:
render_input(c, c_ctx);
break;
case BTK_CELL_TYPE_EDITOR:
render_editor(c);
break;
default:
fill_cell(c, c_ctx, btk_cl_empty);
break;
};
}
void
btk_cell_editor_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
/* scrolling if appropriate button is clicked */
switch (button) {
case 1:
break;
case 4:
PP_EDITOR(c)->scroll -= btk_scroll_speed;
return;
case 5:
PP_EDITOR(c)->scroll += btk_scroll_speed;
return;
default:
return;
}
/* caret positioning */
if (pt.x < label_offset.x || pt.x >= c->pa.w - label_offset.x ||
pt.y < label_offset.y - glyph.h)
return;
PP_EDITOR(c)->caret_2d.x = (pt.x - label_offset.x) / glyph.w;
PP_EDITOR(c)->caret_2d.y = (pt.y - label_offset.y + PP_EDITOR(c)->scroll + glyph.h) / (glyph.h + btk_text_spacing);
PP_EDITOR(c)->caret_2d = btk_text_guard_2d_caret(PP_EDITOR(c)->par, PP_EDITOR(c)->caret_2d);
PP_EDITOR(c)->caret = btk_text_caret_2dto1d(PP_EDITOR(c)->par, PP_EDITOR(c)->caret_2d);
//btk_text_caret_1dto2d(PP_EDITOR(c)->par, PP_EDITOR(c)->caret);
}
void
btk_cell_editor_input_key(btk_cell_t *c, uint32_t key)
{
btk_pp_editor_t *pp = PP_EDITOR(c);
int caret_type, modif = 0;
switch (key) {
case 0xff08:
pp->caret -= btk_text_del_char(pp->text, pp->caret, 0);
caret_type = 0;
modif = 1;
break;
case 0xff50:
pp->caret_2d.x = 0;
caret_type = 1;
break;
case 0xff51:
pp->caret--;
caret_type = 0;
break;
case 0xff52:
pp->caret_2d.y--;
caret_type = 1;
break;
case 0xff53:
pp->caret++;
caret_type = 0;
break;
case 0xff54:
pp->caret_2d.y++;
caret_type = 1;
break;
case 0xff57:
pp->caret_2d.x = pp->text_w;
caret_type = 1;
break;
case 0xffff:
btk_text_del_char(pp->text, pp->caret, 1);
caret_type = 0;
modif = 1;
break;
case 0xff0d:
pp->caret += btk_text_insert_char(pp->text, '\n', pp->caret, pp->text_w);
caret_type = 0;
modif = 1;
break;
case 0xff09:
pp->caret += btk_text_insert_char(pp->text, '\t', pp->caret, pp->text_w);
caret_type = 0;
modif = 1;
break;
default :
/* only allow ascii printable chars */
if (key < 32 || key >= 127)
return;
pp->caret += btk_text_insert_char(pp->text, key, pp->caret, pp->text_w);
caret_type = 0;
modif = 1;
break;
}
if (modif) {
btk_cell_editor_update_text(c);
}
if (caret_type) {
pp->caret_2d = btk_text_guard_2d_caret(pp->par, pp->caret_2d);
pp->caret = btk_text_caret_2dto1d(pp->par, pp->caret_2d);
} else {
pp->caret = btk_text_guard_1d_caret(pp->text, pp->caret);
pp->caret_2d = btk_text_caret_1dto2d(pp->par, pp->caret);
}
/* modify scroll to show caret if needed */
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) < pp->scroll)
pp->scroll = pp->caret_2d.y * (glyph.h + btk_text_spacing);
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) >= pp->scroll + c->pa.h - 2 * label_offset.y) {
pp->scroll = (pp->caret_2d.y + 1) * (glyph.h + btk_text_spacing) - c->pa.h + label_offset.y;
}
}
void
btk_cell_editor_reset_caret(btk_cell_t*c)
{
PP_EDITOR(c)->caret = 0;
PP_EDITOR(c)->caret_2d.x = 0;
PP_EDITOR(c)->caret_2d.y = 0;
PP_EDITOR(c)->scroll = 0;
}
void
btk_cell_editor_update_text(btk_cell_t *c)
{
btk_pp_editor_t *pp = PP_EDITOR(c);
int max_n = (c->pa.w - 2 * label_offset.x) / glyph.w + 1;
if (!pp->text)
return;
if (pp->par)
btk_text_destroy_par(pp->par);
pp->par = btk_text_get_par(pp->text, max_n);
pp->caret_2d = btk_text_caret_1dto2d(pp->par, pp->caret);
/* modify scroll to show caret if needed */
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) < pp->scroll)
pp->scroll = pp->caret_2d.y * (glyph.h + btk_text_spacing);
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) >= pp->scroll + c->pa.h - label_offset.y) {
pp->scroll = (pp->caret_2d.y + 1) * (glyph.h + btk_text_spacing) - c->pa.h + label_offset.y;
}
}
btk_field_t*
btk_cell_field_create(cairo_surface_t *c_srf, btk_area_t a)
{
btk_field_t *f = (btk_field_t *)malloc(sizeof(btk_field_t));
f->c_srf = cairo_surface_create_for_rectangle(c_srf, a.x, a.y, a.w, a.h);
f->c_ctx = cairo_create(f->c_srf);
cairo_set_font_options(f->c_ctx, font_opt);
cairo_set_font_size(f->c_ctx, btk_font_size);
cairo_select_font_face(f->c_ctx,
btk_font_name,
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
return f;
}
void
btk_cell_field_destroy(btk_field_t *f)
{
if (!f)
return;
cairo_destroy(f->c_ctx);
cairo_surface_destroy(f->c_srf);
free(f);
}
void
btk_cell_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
if (button != 1)
return;
if (pt.x >= 2 * label_offset.x + glyph.w && pt.x < c->pa.w - label_offset.x)
PP_INPUT(c)->caret = (pt.x - 2 * label_offset.x - glyph.w) /
glyph.w + PP_INPUT(c)->scroll;
int text_n = strlen(PP_INPUT(c)->text);
if (PP_INPUT(c)->caret < 0)
PP_INPUT(c)->caret = 0;
if (PP_INPUT(c)->caret > text_n)
PP_INPUT(c)->caret = text_n;
/* TODO combo clicks selections */
}
void
btk_cell_input_input_key(btk_cell_t *c, uint32_t key)
{
btk_pp_input_t *pp = PP_INPUT(c);
switch (key) {
case 0xff08:
pp->caret -= btk_text_del_char(pp->text, pp->caret, 0);
break;
case 0xff50:
pp->caret = 0;
break;
case 0xff51:
pp->caret--;
break;
case 0xff53:
pp->caret++;
break;
case 0xff57:
pp->caret = pp->text_w;
break;
case 0xffff:
btk_text_del_char(pp->text, pp->caret, 1);
break;
default :
/* only allow ascii printable chars */
if (key < 32 || key >= 127)
return;
pp->caret += btk_text_insert_char(pp->text, key, pp->caret, pp->text_w);
return;
}
int text_n = strlen(pp->text);
if (pp->caret < 0)
pp->caret = 0;
if (pp->caret > text_n)
pp->caret = text_n;
}
void
btk_cell_input_reset_caret(btk_cell_t *c)
{
PP_INPUT(c)->caret = 0;
}
void
btk_cell_list_deselect(btk_cell_t *c)
{
PP_LIST(c)->item_sel = -1;
}
void
btk_cell_list_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
btk_pp_list_t *pp = PP_LIST(c);
int prev_sel = pp->item_sel;
/* scrolling if appropriate button is clicked */
switch (button) {
case 4:
pp->scroll -= btk_scroll_speed;
return;
case 5:
pp->scroll += btk_scroll_speed;
return;
}
/* filter out remaining unwanted inputs */
if (button != 1)
return;
/* find item under pointer */
pp->item_sel = (pt.y + pp->scroll) / (btk_cp.h + btk_frame);
/* deselect if out of bounds */
if (pp->item_sel < 0 || pp->item_sel >= *(pp->items_n))
pp->item_sel = -1;
/* adjust scrolling so that select item is displayed in full */
// TODO
/* on selection change, exec sel func */
if (prev_sel != pp->item_sel && pp->func_sel)
pp->func_sel(pp->item_sel);
/* if double click, exec trigger func */
if (combo == 2 && pp->func_trigger)
pp->func_trigger(pp->item_sel);
}
int
btk_cell_list_get_sel(btk_cell_t *c)
{
return PP_LIST(c)->item_sel;
}
void
btk_cell_setup_font(cairo_t *c_ctx)
{
font_opt = cairo_font_options_create();
cairo_font_options_set_subpixel_order(font_opt, CAIRO_SUBPIXEL_ORDER_DEFAULT);
cairo_font_options_set_antialias(font_opt, CAIRO_ANTIALIAS_NONE);
cairo_font_options_set_hint_style(font_opt, CAIRO_HINT_STYLE_SLIGHT);
cairo_font_options_set_hint_metrics(font_opt, CAIRO_HINT_METRICS_ON);
cairo_set_font_options(c_ctx, font_opt);
cairo_set_font_size(c_ctx, btk_font_size);
cairo_select_font_face(c_ctx,
btk_font_name,
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_font_extents_t fe;
cairo_font_extents(c_ctx, &fe);
glyph.h = fe.height;
glyph.w = fe.max_x_advance;
label_offset.x = (btk_cp.h - fe.ascent) / 2 + btk_text_offset_tweak.x;
label_offset.y = (btk_cp.h - fe.ascent) / 2 + btk_text_offset_tweak.y + fe.ascent;
}
void
btk_cell_switch_toggle(btk_cell_t *c, int button)
{
if (button != 1)
return;
c->state ^= BTK_CELL_STATE_ON;
if (PP_SWITCH(c)->func)
PP_SWITCH(c)->func(((c->state & BTK_CELL_STATE_ON) >> 4), PP_SWITCH(c)->func_args);
}
void
btk_cell_table_deselect(btk_cell_t *c)
{
PP_TABLE(c)->row_sel = -1;
}
char*
btk_cell_table_get_item(btk_cell_t *c, int col, int row)
{
if (col < 0 || row < 0 || col >= PP_TABLE(c)->cols_n || row >= *(PP_TABLE(c)->rows_n))
return NULL;
return *(PP_TABLE(c)->items) +
PP_TABLE(c)->items_w * (col + row * PP_TABLE(c)->cols_n);
}
void
btk_cell_table_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
btk_pp_table_t *pp = PP_TABLE(c);
int prev_sel = pp->row_sel;
/* scrolling if appropriate button is clicked */
switch (button) {
case 4:
pp->scroll.y -= btk_scroll_speed;
return;
case 5:
pp->scroll.y += btk_scroll_speed;
return;
case 6:
pp->scroll.x -= btk_scroll_speed;
return;
case 7:
pp->scroll.x += btk_scroll_speed;
return;
}
/* TODO ugly af block, to redo */
/* if click on header */
if (pt.y < btk_cp.h) {
int ena;
/* find clicked column */
for (int i = 0; i < pp->cols_n; i++) {
ena = pp->cols_ena ? pp->cols_ena[i] : 1;
if (ena && pt.x > pp->cols_px[i] && pt.x <= pp->cols_px[i] + pp->cols_pw[i]) {
switch (button) {
case 1:
pp->cols_cw[i]++;
break;
case 2:
/* TODO sorting ? */
break;
case 3:
pp->cols_cw[i] -= pp->cols_cw[i] > 1 ? 1 : 0;
}
btk_cell_table_update_geometry(c);
break;
}
}
return;
}
/* remaining item list actions */
/* filter out unwanted inputs */
if (button != 1)
return;
/* find item under pointer */
pp->row_sel = (pt.y + pp->scroll.y) / (btk_cp.h + btk_frame) -1;
/* deselect if out of bounds */
if (pp->row_sel < 0 || pp->row_sel >= *(pp->rows_n))
pp->row_sel = -1;
/* on selection change, exec sel func */
if (prev_sel != pp->row_sel && pp->func_sel)
pp->func_sel(pp->row_sel);
/* if double click, exec trigger func */
if (combo == 2 && pp->func_trigger)
pp->func_trigger(pp->row_sel);
}
void
btk_cell_table_update_geometry(btk_cell_t *c)
{
btk_pp_table_t *pp = PP_TABLE(c);
unsigned int cw = 0; /* width in cell units */
unsigned int pw = 0; /* total minimum pixel width */
unsigned int ew = 0; /* extra width if tw > minimum width */
int ena;
/* calculate extra width in pixels */
for (int i = 0; i < pp->cols_n; i++) {
ena = pp->cols_ena ? pp->cols_ena[i] : 1;
cw += ena ? pp->cols_cw[i] : 0;
}
pw = cw * (btk_cp.w + btk_frame) - btk_frame;
ew = pw < c->pa.w ? c->pa.w - pw : 0;
pp->tw = pw + ew;
/* calculate each columns widths in pixels */
for (int i = 0; i < pp->cols_n; i++) {
pp->cols_pw[i] = pp->cols_cw[i] * (btk_cp.w + btk_frame) - btk_frame;
pp->cols_pw[i] += pp->sc == i ? ew : 0;
}
/* calculate columns positions */
int pos = 0;
for (int i = 0; i < pp->cols_n; i++) {
pp->cols_px[i] = pos;
ena = pp->cols_ena ? pp->cols_ena[i] : 1;
pos += ena ? pp->cols_pw[i] + btk_frame : 0;
}
}

223
btk/btk-cell.h Normal file
View File

@ -0,0 +1,223 @@
#include <cairo/cairo.h>
#include "btk-text.h"
#define NEW_PP(type, pp) (pp = (type*)malloc(sizeof(type)))
#define PP_BUTTON(c) ((btk_pp_button_t*)c->pp)
#define PP_EDITOR(c) ((btk_pp_editor_t*)c->pp)
#define PP_INPUT(c) ((btk_pp_input_t*)c->pp)
#define PP_LIST(c) ((btk_pp_list_t*)c->pp)
#define PP_MARK(c) ((btk_pp_mark_t*)c->pp)
#define PP_SWITCH(c) ((btk_pp_switch_t*)c->pp)
#define PP_TABLE(c) ((btk_pp_table_t*)c->pp)
enum btk_cell_states {
BTK_CELL_STATE_INITIAL = 0,
BTK_CELL_STATE_HIDDEN = 1U << 0,
BTK_CELL_STATE_DISABLED = 1U << 1,
BTK_CELL_STATE_FOCUSED = 1U << 2,
BTK_CELL_STATE_PRESSED = 1U << 3,
BTK_CELL_STATE_ON = 1U << 4,
BTK_CELL_STATE_BELL = 1U << 5,
BTK_CELL_STATE_IN = 1U << 6
};
enum btk_cell_group {
BTK_CELL_GROUP_PASSIVE,
BTK_CELL_GROUP_DYNAMIC,
BTK_CELL_GROUP_FIELD
};
enum btk_cell_type {
BTK_CELL_TYPE_EMPTY,
BTK_CELL_TYPE_MARK,
BTK_CELL_TYPE_GAUGE,
BTK_CELL_TYPE_BUTTON,
BTK_CELL_TYPE_SWITCH,
BTK_CELL_TYPE_WHEEL,
BTK_CELL_TYPE_LIST,
BTK_CELL_TYPE_TABLE,
BTK_CELL_TYPE_INPUT,
BTK_CELL_TYPE_PROMPT,
BTK_CELL_TYPE_EDITOR
};
enum btk_cell_table_col_state {
BTK_CELL_TABLE_COL_HIDDEN,
BTK_CELL_TABLE_COL_VISIBLE
};
typedef struct {
/* global properties */
unsigned int group;
unsigned int type;
unsigned int state;
btk_area_t ca; /* geometry in cell coordinates */
btk_area_t pa; /* geometry in pixel coordinates */
void *pp; /* type specific properties to cast */
btk_field_t *fd; /* for field group types only */
int lh; /* lock pixel height from stretching */
char *label;
} btk_cell_t;
typedef struct {
void (*func)(void);
} btk_pp_button_t;
typedef struct {
char *text;
unsigned int text_w;
btk_pos_t caret_2d;
int caret;
int scroll;
btk_par_t *par;
} btk_pp_editor_t;
typedef struct {
char *text;
unsigned int text_w; /* max text size */
int caret;
int scroll;
} btk_pp_input_t;
typedef struct {
char **items;
unsigned int *items_n;
unsigned int items_w;
int item_sel;
int **filter; // TODO
int **order; // TODO
int **spot;
int scroll;
void (*func_trigger)(int);
void (*func_sel)(int);
} btk_pp_list_t;
typedef struct {
int justify;
} btk_pp_mark_t;
typedef struct {
void (*func)(int, btk_arg_t);
btk_arg_t func_args;
} btk_pp_switch_t;
typedef struct {
char *header;
char **items;
unsigned int items_w;
unsigned int cols_n;
unsigned int *rows_n;
int row_sel;
int *cols_ena; /* hidden cols */
unsigned int *cols_cw; /* width in cell units of each col */
unsigned int *cols_pw; /* width in picels of each col */
unsigned int *cols_px; /* position of each col in pixels */
unsigned int sc; /* stretch col */
unsigned int tw; /* columns total pixel width */
int **filter; // TODO
int **order; // TODO
int **spot;
btk_pos_t scroll;
void (*func_trigger)(int);
void (*func_sel)(int);
} btk_pp_table_t;
void btk_cell_set_button (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
char *,
void (*)(void));
void btk_cell_set_editor (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
unsigned int,
char *,
unsigned int);
void btk_cell_set_empty (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
unsigned int);
void btk_cell_set_input (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
char *,
unsigned int);
void btk_cell_set_list (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
unsigned int,
char **,
unsigned int *,
unsigned int,
int **,
int **,
int **,
void (*)(int),
void (*)(int));
void btk_cell_set_mark (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
int,
char *);
void btk_cell_set_switch (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
char *,
void (*)(int, btk_arg_t),
btk_arg_t);
void btk_cell_set_table (btk_cell_t *,
unsigned int,
unsigned int,
unsigned int,
unsigned int,
char *,
char **,
unsigned int,
unsigned int,
unsigned int *,
int *,
unsigned int *,
unsigned int,
int **,
int **,
int **,
void (*)(int),
void (*)(int));
void btk_cell_button_trigger (btk_cell_t *, int);
void btk_cell_destroy (btk_cell_t *);
void btk_cell_draw (btk_cell_t *, cairo_t *);
void btk_cell_editor_input_button (btk_cell_t *, int, int, btk_pos_t);
void btk_cell_editor_input_key (btk_cell_t *, uint32_t);
void btk_cell_editor_reset_caret (btk_cell_t*);
void btk_cell_editor_update_text (btk_cell_t *);
btk_field_t* btk_cell_field_create (cairo_surface_t *, btk_area_t);
void btk_cell_input_button (btk_cell_t *, int, int, btk_pos_t);
void btk_cell_input_input_key (btk_cell_t *, uint32_t);
void btk_cell_input_reset_caret (btk_cell_t *);
void btk_cell_field_destroy (btk_field_t *);
void btk_cell_list_deselect (btk_cell_t*);
void btk_cell_list_input_button (btk_cell_t *, int, int, btk_pos_t);
int btk_cell_list_get_sel (btk_cell_t *);
void btk_cell_setup_font (cairo_t *c_ctx);
void btk_cell_switch_toggle (btk_cell_t *, int);
void btk_cell_table_deselect (btk_cell_t*);
char* btk_cell_table_get_item (btk_cell_t *, int, int);
void btk_cell_table_input_button (btk_cell_t *, int, int, btk_pos_t);
void btk_cell_table_update_geometry (btk_cell_t *);

359
btk/btk.c Normal file
View File

@ -0,0 +1,359 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "btk.h"
#include "btk-log.h"
void event_button_press (btk_session_t *, xcb_button_press_event_t *);
void event_button_release (btk_session_t *, xcb_button_release_event_t *);
void event_client_message (btk_session_t *, xcb_client_message_event_t *);
void event_enter_window (btk_session_t *, xcb_enter_notify_event_t *);
void event_expose (btk_session_t *, xcb_expose_event_t *);
void event_key_press (btk_session_t *, xcb_key_press_event_t *);
void event_leave_window (btk_session_t *, xcb_leave_notify_event_t *);
void event_pointer_motion (btk_session_t *, xcb_motion_notify_event_t *);
int get_btk_window_id (btk_session_t *, xcb_window_t);
void
btk_close(btk_session_t *s)
{
for (int i = 0; i < s->windows_n; i++)
if (!s->windows[i])
btk_window_destroy(s->windows[i]);
xcb_disconnect(s->x_con);
btk_log(BTK_LOG_SESSION_CLOSE);
}
void
btk_loop(btk_session_t *s, btk_window_t **w, int windows_n)
{
if (!w || !windows_n) {
btk_log_warning(BTK_WARNING_EMPTY_LOOP);
btk_log(BTK_LOG_EVENT_LOOP_OUT);
return;
}
s->windows = w;
s->windows_n = windows_n;
s->window_focus = 0;
s->window_press = -1;
/* set initial cell geometry and fonts */
for (int i = 0; i < windows_n; i++) {
btk_cell_setup_font(w[i]->c_ctx);
btk_window_init_cells(w[i]);
}
/* actual event loop */
btk_log(BTK_LOG_EVENT_LOOP_IN);
xcb_generic_event_t *x_ev;
while ((x_ev = xcb_wait_for_event(s->x_con))) {
switch (x_ev->response_type & ~0x80) {
case XCB_EXPOSE:
event_expose(s, (xcb_expose_event_t*)x_ev);
break;
case XCB_MOTION_NOTIFY:
event_pointer_motion(s, (xcb_motion_notify_event_t*)x_ev);
break;
case XCB_BUTTON_PRESS:
event_button_press(s, (xcb_button_press_event_t*)x_ev);
break;
case XCB_BUTTON_RELEASE:
event_button_release(s, (xcb_button_release_event_t*)x_ev);
break;
case XCB_KEY_PRESS:
event_key_press(s, (xcb_key_press_event_t*)x_ev);
break;
case XCB_ENTER_NOTIFY:
event_enter_window(s, (xcb_enter_notify_event_t*)x_ev);
break;
case XCB_LEAVE_NOTIFY:
event_leave_window(s, (xcb_leave_notify_event_t*)x_ev);
break;
case XCB_CLIENT_MESSAGE:
event_client_message(s, (xcb_client_message_event_t*)x_ev);
break;
default:
btk_log(BTK_LOG_GENERIC_EVENT_NOTIFY);
break;
}
free(x_ev);
xcb_flush(s->x_con);
}
btk_log(BTK_LOG_EVENT_LOOP_OUT);
}
void
btk_map(btk_session_t *s, btk_window_t *w)
{
w->state |= BTK_WINDOW_STATE_MAPPED;
xcb_map_window(s->x_con, w->x_win);
xcb_flush(s->x_con);
btk_log(BTK_LOG_WINDOW_MAP);
}
btk_session_t*
btk_open()
{
btk_session_t *s = (btk_session_t*)malloc(sizeof(btk_session_t));
/* base X stuff */
s->x_con = xcb_connect(NULL, NULL);
if (!s->x_con)
btk_log_error(BTK_ERROR_X_CONNECTION);
s->x_scr = xcb_setup_roots_iterator(xcb_get_setup(s->x_con)).data;
if (!s->x_scr)
btk_log_error(BTK_ERROR_X_SCREEN);
/* setup x visual type for cairo surfaces */
/* magic function from documentation */
xcb_depth_iterator_t x_di = xcb_screen_allowed_depths_iterator(s->x_scr);
for (; x_di.rem; xcb_depth_next(&x_di)) {
xcb_visualtype_iterator_t x_vi;
x_vi = xcb_depth_visuals_iterator(x_di.data);
for (; x_vi.rem; xcb_visualtype_next(&x_vi)) {
if (s->x_scr->root_visual == x_vi.data->visual_id) {
s->x_vis = x_vi.data;
break;
}
}
}
if (!s->x_vis)
btk_log_error(BTK_ERROR_X_VISUAL);
/* setup keysym table for keyboard input */
const xcb_setup_t *x_stp = xcb_get_setup(s->x_con);
xcb_get_keyboard_mapping_reply_t* x_km =
xcb_get_keyboard_mapping_reply(
s->x_con,
xcb_get_keyboard_mapping(
s->x_con,
x_stp->min_keycode,
x_stp->max_keycode - x_stp->min_keycode + 1),
NULL);
if (!x_km)
btk_log_error(BTK_ERROR_X_KEYSYMS);
xcb_keysym_t *x_ks = (xcb_keysym_t*)(x_km + 1);
s->x_nks = x_km->keysyms_per_keycode;
s->x_nkc = x_km->length / s->x_nks;
/* copy x_ks into s->x_ksm because x_ks breaks due to cairo for some reason */
s->x_ksm = (xcb_keysym_t*)malloc(sizeof(xcb_keysym_t) * s->x_nkc * s->x_nks);
for (int i = 0; i < s->x_nkc * s->x_nks; i++) {
s->x_ksm[i] = x_ks[i];
}
free(x_km);
xcb_flush(s->x_con);
btk_log(BTK_LOG_SESSION_OPEN);
return s;
}
void
btk_unmap(btk_session_t *s, btk_window_t *w)
{
w->state &= ~BTK_WINDOW_STATE_MAPPED;
xcb_unmap_window(s->x_con, w->x_win);
xcb_flush(s->x_con);
btk_log(BTK_LOG_WINDOW_UNMAP);
/* undo all and focus press states if any */
if (w->cell_press >= 0) {
w->cells[w->cell_press].state &= ~BTK_CELL_STATE_PRESSED;
w->cell_press = -1;
}
if (w->cell_focus >= 0) {
w->cells[w->cell_focus].state &= ~BTK_CELL_STATE_FOCUSED;
w->cell_focus = -1;
}
}
void
event_button_press(btk_session_t *s, xcb_button_press_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_BUTTON_PRESS);
if (x_ev->time - s->combo_t < btk_click_speed && x_ev->detail == s->combo_b) {
s->combo_n++;
btk_log(BTK_LOG_EVENT_BUTTON_COMBO);
} else {
s->combo_n = 1;
s->combo_b = x_ev->detail;
btk_log(BTK_LOG_EVENT_BUTTON_COMBO_BREAK);
}
s->combo_t = x_ev->time;
/* only applies for left click */
if (s->combo_b == 1)
s->window_press = s->window_focus;
btk_window_input_button(s->windows[s->window_focus], s->combo_b, s->combo_n);
}
void
event_button_release(btk_session_t *s, xcb_button_release_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_BUTTON_RELEASE);
/* only applies for left-middle-right click */
if (x_ev->detail > 1)
return;
/* undo all press states */
btk_window_t *w = s->windows[s->window_press];
s->window_press = -1;
if (w->cell_press < 0)
return;
btk_cell_t *c = &(w->cells[w->cell_press]);
c->state &= ~BTK_CELL_STATE_PRESSED;
btk_window_redraw_cell(w, w->cell_press);
w->cell_press = -1;
}
void
event_client_message(btk_session_t *s, xcb_client_message_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_CLIENT_MESSAGE);
btk_window_t *w = s->windows[get_btk_window_id(s, x_ev->window)];
/* when receiving a WM_DELETE_WINDOW atom, execute func_kill of the
* corresponding window, it func_kill = NULL, destroy window */
if (x_ev->type && 384) { /* 384 = id of atom for WM_WINDOW_DELETE */
if (w->func_kill) {
w->func_kill();
} else {
btk_window_destroy(w);
}
}
/* if anyone ever reads this, I know that it is a wrong method,
* that I first need to check to an WM_PROTOCOL atom then for
* WM_DELETE_WINDOW one */
}
void
event_enter_window(btk_session_t *s, xcb_enter_notify_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_WINDOW_ENTER);
int w_id = get_btk_window_id(s, x_ev->event);
s->window_focus = w_id;
s->windows[w_id]->state |= BTK_WINDOW_STATE_FOCUSED;
}
void
event_expose(btk_session_t *s, xcb_expose_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_EXPOSE);
btk_window_t *w = s->windows[get_btk_window_id(s, x_ev->window)];
/* c->fd need to be created when window is mapped to be able to display stuff */
/* TODO better way to do the first field creations */
if (!s->start) {
btk_window_init_cells(w);
s->start = 1;
}
btk_window_resize(w);
btk_window_redraw(w);
}
void
event_key_press(btk_session_t *s, xcb_key_press_event_t *x_ev)
{
/* XCB_MOD_MASK_SHIFT (1) : shift
* XCB_MOD_MASK_LOCK (2) : caps
* XCB_MOD_MASK_CONTROL (4) : ctrl
* XCB_MOD_MASK_1 (8) : alt
* XCB_MOD_MASK_2 (16) : numlock
* XCB_MOD_MASK_3 (32) : ???
* XCB_MOD_MASK_4 (64) : super
* XCB_MOD_MASK_5 (128) : alt-gr */
btk_log(BTK_LOG_EVENT_KEY_PRESS);
unsigned int keysym_mod = 0;
if (((x_ev->state & XCB_MOD_MASK_LOCK) >> 1) ^ (x_ev->state & XCB_MOD_MASK_SHIFT))
keysym_mod = 1;
if ((x_ev->state & XCB_MOD_MASK_5))
keysym_mod = 4;
unsigned int keysym = s->x_ksm[(x_ev->detail - 8) * s->x_nks + keysym_mod];
/* convert utf-8 numpad chars to equivalent ascii */
if ((x_ev->state & XCB_MOD_MASK_2)) {
switch (keysym) {
case 0xffaf: keysym = '/'; break;
case 0xffaa: keysym = '*'; break;
case 0xffad: keysym = '-'; break;
case 0xff95: keysym = '7'; break;
case 0xff97: keysym = '8'; break;
case 0xff9a: keysym = '9'; break;
case 0xff96: keysym = '4'; break;
case 0xff9d: keysym = '5'; break;
case 0xff98: keysym = '6'; break;
case 0xff9c: keysym = '1'; break;
case 0xff99: keysym = '2'; break;
case 0xff9b: keysym = '3'; break;
case 0xff9e: keysym = '0'; break;
case 0xff9f: keysym = '.'; break;
case 0xffab: keysym = '+'; break;
default: break;
}
}
/* custom keysym conversions for reasons */
switch (keysym) {
case 0x00a7: keysym = 0x0000; break;
default: break;
}
/* special cases that are keycode dependent only */
switch (x_ev->detail) {
case 65: keysym = ' '; break;
default: break;
}
btk_window_input_key(s->windows[s->window_focus], keysym);
}
void
event_leave_window(btk_session_t *s, xcb_leave_notify_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_WINDOW_LEAVE);
/* remove all focuses */
/* TODO
btk_window_t *w = s->windows[get_btk_window_id(s, x_ev->event)];
if (w->cell_focus >= 0) {
btk_cell_t *c = &(w->cells[w->cell_focus]);
c->state &= ~(BTK_CELL_STATE_FOCUSED);
btk_window_redraw_cell_focus(w, c);
}
w->state &= ~(BTK_WINDOW_STATE_FOCUSED);
w->cell_focus = -1;
*/
}
void
event_pointer_motion(btk_session_t *s, xcb_motion_notify_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_POINTER_MOTION);
btk_pos_t pt;
pt.x = x_ev->event_x;
pt.y = x_ev->event_y;
btk_window_input_pointer(s->windows[s->window_focus], pt);
}
int
get_btk_window_id(btk_session_t *s, xcb_window_t x_win)
{
for (int i = 0; i < s->windows_n; i++) {
if (s->windows[i]->x_win == x_win)
return i;
}
btk_log_warning(BTK_WARNING_WINDOW_GET);
return -1;
}

30
btk/btk.h Normal file
View File

@ -0,0 +1,30 @@
#include <xcb/xcb.h>
#include "btk-window.h"
typedef struct {
xcb_connection_t *x_con;
int pad0;
xcb_screen_t *x_scr;
int pad1;
xcb_visualtype_t *x_vis;
int pad2;
xcb_keysym_t *x_ksm; /* keysym table */
int pad3;
int x_nks; /* keysyms per keycode */
int x_nkc; /* number of keycodes */
btk_window_t **windows;
unsigned int windows_n;
int window_focus;
int window_press;
xcb_timestamp_t combo_t; /* timestamp of last click */
int combo_n; /* number of successive clicks */
int combo_b; /* button used for combo */
int start; /* TODO, see expose event handler */
} btk_session_t;
void btk_close (btk_session_t *);
void btk_loop (btk_session_t *, btk_window_t **, int);
void btk_map (btk_session_t *, btk_window_t *);
btk_session_t* btk_open ();
void btk_unmap (btk_session_t *, btk_window_t *);