|
|
@@ -0,0 +1,476 @@ |
|
|
|
#include <cairo/cairo.h> |
|
|
|
#include <cairo/cairo-xcb.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include <xcb/xcb.h> |
|
|
|
|
|
|
|
#include "btk-window.h" |
|
|
|
#include "btk-log.h" |
|
|
|
|
|
|
|
void draw_overlay (btk_window_t *, btk_area_t); |
|
|
|
void update_cell_area (btk_window_t *, btk_cell_t *); |
|
|
|
void update_stretch_area (btk_window_t *); |
|
|
|
|
|
|
|
btk_window_t* |
|
|
|
btk_window_create(xcb_connection_t *x_con, |
|
|
|
xcb_screen_t *x_scr, |
|
|
|
xcb_visualtype_t *x_vis, |
|
|
|
unsigned int cw, |
|
|
|
unsigned int ch, |
|
|
|
int sc, |
|
|
|
int sr, |
|
|
|
unsigned int ew, |
|
|
|
unsigned int eh, |
|
|
|
unsigned int cells_n, |
|
|
|
void (*func_kill)(void)) |
|
|
|
{ |
|
|
|
btk_window_t *w = (btk_window_t*)malloc(sizeof(btk_window_t)); |
|
|
|
w->cells = (btk_cell_t*)(malloc(sizeof(btk_cell_t) * cells_n)); |
|
|
|
w->cells_n = cells_n; |
|
|
|
w->func_kill = func_kill; |
|
|
|
|
|
|
|
/* states setup */ |
|
|
|
w->state = BTK_WINDOW_STATE_INITIAL; |
|
|
|
w->cell_focus = -1; |
|
|
|
w->cell_press = -1; |
|
|
|
w->focus_in = -1; |
|
|
|
|
|
|
|
/* geometry setup */ |
|
|
|
w->cs.w = cw; |
|
|
|
w->cs.h = ch; |
|
|
|
w->es.w = sc < 0 ? 0 : ew; |
|
|
|
w->es.h = sr < 0 ? 0 : eh; |
|
|
|
w->ms.w = cw * (btk_cp.w + btk_frame) + btk_frame; |
|
|
|
w->ms.h = ch * (btk_cp.h + btk_frame) + btk_frame; |
|
|
|
w->ps.w = w->ms.w + w->es.w; |
|
|
|
w->ps.h = w->ms.h + w->es.h; |
|
|
|
w->sc.x = sc; |
|
|
|
w->sc.y = sr; |
|
|
|
update_stretch_area(w); |
|
|
|
|
|
|
|
/* xcb setup */ |
|
|
|
uint32_t mask_vals[2] = { |
|
|
|
x_scr->black_pixel, |
|
|
|
XCB_EVENT_MASK_EXPOSURE | |
|
|
|
XCB_EVENT_MASK_POINTER_MOTION | |
|
|
|
XCB_EVENT_MASK_BUTTON_PRESS | |
|
|
|
XCB_EVENT_MASK_BUTTON_RELEASE | |
|
|
|
XCB_EVENT_MASK_ENTER_WINDOW | |
|
|
|
XCB_EVENT_MASK_LEAVE_WINDOW | |
|
|
|
XCB_EVENT_MASK_KEY_PRESS |
|
|
|
}; |
|
|
|
w->x_con = x_con; |
|
|
|
w->x_win = xcb_generate_id(x_con); |
|
|
|
xcb_create_window(x_con, |
|
|
|
XCB_COPY_FROM_PARENT, |
|
|
|
w->x_win, |
|
|
|
x_scr->root, |
|
|
|
0, 0, |
|
|
|
w->ps.w, w->ps.h, |
|
|
|
0, |
|
|
|
XCB_WINDOW_CLASS_INPUT_OUTPUT, |
|
|
|
x_scr->root_visual, |
|
|
|
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, |
|
|
|
mask_vals); |
|
|
|
|
|
|
|
/* wm hints setup */ |
|
|
|
btk_wm_size_hint_t x_hints = { |
|
|
|
.flags = BTK_WM_SIZE_HINT_P_MIN_SIZE | BTK_WM_SIZE_HINT_P_MAX_SIZE, |
|
|
|
.min_w = w->ms.w, |
|
|
|
.min_h = w->ms.h |
|
|
|
}; |
|
|
|
if (sc < 0) x_hints.max_w = w->ms.w; |
|
|
|
if (sr < 0) x_hints.max_h = w->ms.h; |
|
|
|
xcb_change_property(w->x_con, |
|
|
|
XCB_PROP_MODE_REPLACE, |
|
|
|
w->x_win, |
|
|
|
XCB_ATOM_WM_NORMAL_HINTS, |
|
|
|
XCB_ATOM_WM_SIZE_HINTS, |
|
|
|
32, |
|
|
|
sizeof(btk_wm_size_hint_t), |
|
|
|
&x_hints); |
|
|
|
|
|
|
|
/* suscribe to WM_DELETE_WINDOW protocol from the wm */ |
|
|
|
xcb_intern_atom_cookie_t x_cookie_protocol = |
|
|
|
xcb_intern_atom(x_con, 0, strlen("WM_PROTOCOLS"), "WM_PROTOCOLS"); |
|
|
|
xcb_intern_atom_reply_t *x_reply_protocol = |
|
|
|
xcb_intern_atom_reply(x_con, x_cookie_protocol, NULL); |
|
|
|
|
|
|
|
xcb_intern_atom_cookie_t x_cookie_del = |
|
|
|
xcb_intern_atom(x_con, 0, strlen("WM_DELETE_WINDOW"), "WM_DELETE_WINDOW"); |
|
|
|
xcb_intern_atom_reply_t *x_reply_del = |
|
|
|
xcb_intern_atom_reply(x_con, x_cookie_del, NULL); |
|
|
|
|
|
|
|
xcb_change_property(x_con, |
|
|
|
XCB_PROP_MODE_REPLACE, |
|
|
|
w->x_win, |
|
|
|
x_reply_protocol->atom, |
|
|
|
XCB_ATOM_ATOM, |
|
|
|
32, |
|
|
|
1, |
|
|
|
&(x_reply_del->atom)); |
|
|
|
|
|
|
|
/* cairo setup */ |
|
|
|
w->c_srf = cairo_xcb_surface_create(x_con, w->x_win, x_vis, w->ps.w, w->ps.h); |
|
|
|
w->c_ctx = cairo_create(w->c_srf); |
|
|
|
|
|
|
|
/* setup font */ |
|
|
|
|
|
|
|
xcb_flush(x_con); |
|
|
|
cairo_surface_flush(w->c_srf); |
|
|
|
btk_log(BTK_LOG_WINDOW_CREATE); |
|
|
|
|
|
|
|
return w; |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_destroy(btk_window_t *w) |
|
|
|
{ |
|
|
|
if (!w) { |
|
|
|
btk_log_warning(BTK_WARNING_WINDOW_DESTROY); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
cairo_destroy(w->c_ctx); |
|
|
|
cairo_surface_destroy(w->c_srf); |
|
|
|
free(w->cells); |
|
|
|
free(w); |
|
|
|
|
|
|
|
btk_log(BTK_LOG_WINDOW_DESTROY); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_disable(btk_window_t *w) |
|
|
|
{ |
|
|
|
w->state |= BTK_WINDOW_STATE_DISABLED; |
|
|
|
btk_window_redraw(w); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_enable(btk_window_t *w) |
|
|
|
{ |
|
|
|
w->state &= ~BTK_WINDOW_STATE_DISABLED; |
|
|
|
btk_window_redraw(w); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
btk_window_init_cells(btk_window_t *w) |
|
|
|
{ |
|
|
|
for (int i = 0; i < w->cells_n; i++) { |
|
|
|
btk_window_update_cell_area(w, &(w->cells[i])); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_input_button(btk_window_t *w, int button, int combo) |
|
|
|
{ |
|
|
|
if ((w->state & BTK_WINDOW_STATE_DISABLED)) |
|
|
|
return; |
|
|
|
if (w->cell_focus < 0) |
|
|
|
return; |
|
|
|
|
|
|
|
btk_cell_t *c = &(w->cells[w->cell_focus]); |
|
|
|
|
|
|
|
if ((c->state & BTK_CELL_STATE_DISABLED) && button < 4) |
|
|
|
return; |
|
|
|
if (button == 1) { |
|
|
|
c->state |= BTK_CELL_STATE_PRESSED; |
|
|
|
w->cell_press = w->cell_focus; |
|
|
|
} |
|
|
|
|
|
|
|
/* relative pointer position the the pressed cell */ |
|
|
|
btk_pos_t rel_pt; |
|
|
|
rel_pt.x = w->pt.x - c->pa.x; |
|
|
|
rel_pt.y = w->pt.y - c->pa.y; |
|
|
|
|
|
|
|
/* forward press event to individual handlers for special actions */ |
|
|
|
switch (c->type) { |
|
|
|
case BTK_CELL_TYPE_BUTTON: |
|
|
|
btk_cell_button_trigger(c, button); |
|
|
|
break; |
|
|
|
case BTK_CELL_TYPE_INPUT: |
|
|
|
btk_cell_input_button(c, button, combo, rel_pt); |
|
|
|
break; |
|
|
|
case BTK_CELL_TYPE_LIST: |
|
|
|
btk_cell_list_input_button(c, button, combo, rel_pt); |
|
|
|
break; |
|
|
|
case BTK_CELL_TYPE_SWITCH: |
|
|
|
btk_cell_switch_toggle(c, button); |
|
|
|
break; |
|
|
|
case BTK_CELL_TYPE_TABLE: |
|
|
|
btk_cell_table_input_button(c, button, combo, rel_pt); |
|
|
|
break; |
|
|
|
case BTK_CELL_TYPE_EDITOR: |
|
|
|
btk_cell_editor_input_button(c, button, combo, rel_pt); |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
btk_cell_draw(c, w->c_ctx); |
|
|
|
draw_overlay(w, c->pa); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_input_key(btk_window_t *w, xcb_keysym_t key) |
|
|
|
{ |
|
|
|
if ((w->state & BTK_WINDOW_STATE_DISABLED)) |
|
|
|
return; |
|
|
|
if (w->cell_focus < 0) |
|
|
|
return; |
|
|
|
|
|
|
|
btk_cell_t *c = &(w->cells[w->cell_focus]); |
|
|
|
|
|
|
|
switch (c->type) { |
|
|
|
case BTK_CELL_TYPE_INPUT: |
|
|
|
btk_cell_input_input_key(c, (uint32_t)key); |
|
|
|
break; |
|
|
|
case BTK_CELL_TYPE_EDITOR: |
|
|
|
btk_cell_editor_input_key(c, (uint32_t)key); |
|
|
|
break; |
|
|
|
default: |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
btk_cell_draw(c, w->c_ctx); |
|
|
|
draw_overlay(w, c->pa); |
|
|
|
} |
|
|
|
|
|
|
|
/* updates the pointer position on the windows and the focused cell */ |
|
|
|
void |
|
|
|
btk_window_input_pointer(btk_window_t *w, btk_pos_t pos) |
|
|
|
{ |
|
|
|
if ((w->state & BTK_WINDOW_STATE_DISABLED)) |
|
|
|
return; |
|
|
|
|
|
|
|
int prev_focus = w->cell_focus; |
|
|
|
|
|
|
|
w->pt.x = pos.x; |
|
|
|
w->pt.y = pos.y; |
|
|
|
|
|
|
|
/* iteration instead of calculation to avoid frame interference in the |
|
|
|
* case of thick frames */ |
|
|
|
for (int i = 0; i < w->cells_n; i++) { |
|
|
|
if (w->pt.x >= w->cells[i].pa.x && |
|
|
|
w->pt.y >= w->cells[i].pa.y && |
|
|
|
w->pt.x <= w->cells[i].pa.x + w->cells[i].pa.w && |
|
|
|
w->pt.y <= w->cells[i].pa.y + w->cells[i].pa.h) { |
|
|
|
w->cell_focus = i; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
/* if focus didn't change, leave */ |
|
|
|
if (w->cell_focus == prev_focus) |
|
|
|
return; |
|
|
|
|
|
|
|
/* unfocus previous cell if any */ |
|
|
|
if (prev_focus >= 0) { |
|
|
|
w->cells[prev_focus].state &= ~BTK_CELL_STATE_FOCUSED; |
|
|
|
btk_window_redraw_cell_focus(w, &(w->cells[prev_focus])); |
|
|
|
} |
|
|
|
|
|
|
|
/* focus new cell */ |
|
|
|
w->cells[w->cell_focus].state |= BTK_CELL_STATE_FOCUSED; |
|
|
|
btk_window_redraw_cell_focus(w, &(w->cells[w->cell_focus])); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_redraw(btk_window_t *w) |
|
|
|
{ |
|
|
|
/* clear surface to redraw */ |
|
|
|
cairo_set_source_rgb(w->c_ctx, btk_cl_frame.r, btk_cl_frame.g, btk_cl_frame.b); |
|
|
|
cairo_paint(w->c_ctx); |
|
|
|
|
|
|
|
/* hilight frame around focused cell if any */ |
|
|
|
if (w->cell_focus >= 0) { |
|
|
|
btk_rgb_t cl = w->focus_in >= 0 ? btk_cl_frame_in : btk_cl_frame_focus; |
|
|
|
cairo_set_source_rgb(w->c_ctx, cl.r, cl.g, cl.b); |
|
|
|
cairo_rectangle(w->c_ctx, |
|
|
|
w->cells[w->cell_focus].pa.x - btk_frame, |
|
|
|
w->cells[w->cell_focus].pa.y - btk_frame, |
|
|
|
w->cells[w->cell_focus].pa.w + 2 * btk_frame, |
|
|
|
w->cells[w->cell_focus].pa.h + 2 * btk_frame); |
|
|
|
cairo_fill(w->c_ctx); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* render cells */ |
|
|
|
for (int i = 0; i < w->cells_n; i++) |
|
|
|
btk_cell_draw(&(w->cells[i]), w->c_ctx); |
|
|
|
|
|
|
|
/* disable overlay */ |
|
|
|
btk_area_t ov; |
|
|
|
ov.x = 0; |
|
|
|
ov.y = 0; |
|
|
|
ov.w = w->ps.w; |
|
|
|
ov.h = w->ps.h; |
|
|
|
draw_overlay(w, ov); |
|
|
|
|
|
|
|
cairo_surface_flush(w->c_srf); |
|
|
|
} |
|
|
|
|
|
|
|
/* to be used to regenretate cells externally */ |
|
|
|
void |
|
|
|
btk_window_redraw_cell(btk_window_t *w, unsigned int c_id) |
|
|
|
{ |
|
|
|
if ((c_id) >= w->cells_n) { |
|
|
|
btk_log_warning(BTK_WARNING_OUT_OF_BOUND_CELL); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
btk_cell_draw(&(w->cells[c_id]), w->c_ctx); |
|
|
|
draw_overlay(w, w->cells[c_id].pa); |
|
|
|
} |
|
|
|
|
|
|
|
/* only redraw a specific cell and not the whole window |
|
|
|
* unlike calling btk_winsow_redraw_cell, this function |
|
|
|
* also redraws the frame around the cell */ |
|
|
|
void |
|
|
|
btk_window_redraw_cell_focus(btk_window_t *w, btk_cell_t *c) |
|
|
|
{ |
|
|
|
/* redraw frame */ |
|
|
|
btk_rgb_t cl; |
|
|
|
if ((c->state & BTK_CELL_STATE_IN)) { |
|
|
|
cl = btk_cl_frame_in; |
|
|
|
} else if ((c->state & BTK_CELL_STATE_FOCUSED)) { |
|
|
|
cl = btk_cl_frame_focus; |
|
|
|
} else { |
|
|
|
cl = btk_cl_frame; |
|
|
|
} |
|
|
|
cairo_set_source_rgb(w->c_ctx, cl.r, cl.g, cl.b); |
|
|
|
cairo_rectangle(w->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(w->c_ctx); |
|
|
|
|
|
|
|
/* redraw cell */ |
|
|
|
btk_cell_draw(c, w->c_ctx); |
|
|
|
|
|
|
|
/* disable overlay */ |
|
|
|
btk_area_t ov; |
|
|
|
ov.x = c->pa.x - btk_frame; |
|
|
|
ov.y = c->pa.y - btk_frame; |
|
|
|
ov.w = c->pa.w + 2 * btk_frame; |
|
|
|
ov.h = c->pa.h + 2 * btk_frame; |
|
|
|
draw_overlay(w, ov); |
|
|
|
|
|
|
|
cairo_surface_flush(w->c_srf); |
|
|
|
} |
|
|
|
|
|
|
|
/* only called by an expose event */ |
|
|
|
int |
|
|
|
btk_window_resize(btk_window_t *w) |
|
|
|
{ |
|
|
|
/* check if window geometry changed, update window size if it did */ |
|
|
|
xcb_get_geometry_reply_t *x_geom; |
|
|
|
xcb_get_geometry_cookie_t x_cookie = xcb_get_geometry(w->x_con, w->x_win); |
|
|
|
x_geom = xcb_get_geometry_reply(w->x_con, x_cookie, NULL); |
|
|
|
if (!x_geom) |
|
|
|
return 0; |
|
|
|
|
|
|
|
if (w->ps.w == x_geom->width && w->ps.h == x_geom->height) |
|
|
|
return 0; |
|
|
|
|
|
|
|
/* update window's geometry */ |
|
|
|
w->ps.w = x_geom->width; |
|
|
|
w->ps.h = x_geom->height; |
|
|
|
w->es.w = w->ps.w - w->ms.w; |
|
|
|
w->es.h = w->ps.h - w->ms.h; |
|
|
|
update_stretch_area(w); |
|
|
|
cairo_xcb_surface_set_size(w->c_srf, w->ps.w, w->ps.h); |
|
|
|
|
|
|
|
/* update cell's geometry */ |
|
|
|
/* only for those after the stretch col and row */ |
|
|
|
btk_cell_t *c; |
|
|
|
for (int i = 0; i < w->cells_n; i++) { |
|
|
|
c = &(w->cells[i]); |
|
|
|
if (c->ca.x + c->ca.w >= w->sc.x || c->ca.y + c->ca.h >= w->sc.y) |
|
|
|
btk_window_update_cell_area(w, c); |
|
|
|
} |
|
|
|
|
|
|
|
cairo_surface_flush(w->c_srf); |
|
|
|
btk_log(BTK_LOG_WINDOW_RESIZE); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_set_name(btk_window_t *w, char *name) |
|
|
|
{ |
|
|
|
xcb_change_property(w->x_con, |
|
|
|
XCB_PROP_MODE_REPLACE, |
|
|
|
w->x_win, |
|
|
|
XCB_ATOM_WM_NAME, |
|
|
|
XCB_ATOM_STRING, |
|
|
|
8, |
|
|
|
strlen(name), |
|
|
|
name); |
|
|
|
|
|
|
|
btk_log(BTK_LOG_WINDOW_RENAME); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
btk_window_update_cell_area(btk_window_t *w, btk_cell_t *c) |
|
|
|
{ |
|
|
|
unsigned int ex, ey, ew, eh = 0; /* extra to account for stretched columns & rows */ |
|
|
|
|
|
|
|
ex = c->ca.x > w->sc.x ? w->es.w : 0; |
|
|
|
ey = c->ca.y > w->sc.y ? w->es.h : 0; |
|
|
|
ew = c->ca.x <= w->sc.x && c->ca.x + c->ca.w > w->sc.x ? w->es.w : 0; |
|
|
|
if (!(c->lh)) |
|
|
|
eh = c->ca.y <= w->sc.y && c->ca.y + c->ca.h > w->sc.y ? w->es.h : 0; |
|
|
|
|
|
|
|
c->pa.x = c->ca.x * (btk_cp.w + btk_frame) + btk_frame + ex; |
|
|
|
c->pa.y = c->ca.y * (btk_cp.h + btk_frame) + btk_frame + ey; |
|
|
|
c->pa.w = c->ca.w * (btk_cp.w + btk_frame) - btk_frame + ew; |
|
|
|
c->pa.h = c->ca.h * (btk_cp.h + btk_frame) - btk_frame + eh; |
|
|
|
|
|
|
|
/* field re-creation with correct size */ |
|
|
|
if (c->group == BTK_CELL_GROUP_FIELD) { |
|
|
|
if (c->fd) |
|
|
|
btk_cell_field_destroy(c->fd); |
|
|
|
c->fd = btk_cell_field_create(w->c_srf, c->pa); |
|
|
|
} |
|
|
|
|
|
|
|
/* special geometry depending on cell type */ |
|
|
|
switch (c->type) { |
|
|
|
case BTK_CELL_TYPE_TABLE: |
|
|
|
btk_cell_table_update_geometry(c); |
|
|
|
break; |
|
|
|
case BTK_CELL_TYPE_EDITOR: |
|
|
|
btk_cell_editor_update_text(c); |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* add semi-transparent block overlay on areas to redraw if the window is disabled */ |
|
|
|
void |
|
|
|
draw_overlay(btk_window_t *w, btk_area_t a) |
|
|
|
{ |
|
|
|
if (!(w->state & BTK_WINDOW_STATE_DISABLED)) |
|
|
|
return; |
|
|
|
|
|
|
|
if ((w->state & BTK_WINDOW_STATE_DISABLED)) { |
|
|
|
cairo_set_source_rgba(w->c_ctx, |
|
|
|
btk_cl_window_overlay.r, |
|
|
|
btk_cl_window_overlay.g, |
|
|
|
btk_cl_window_overlay.b, |
|
|
|
btk_cl_window_overlay.a); |
|
|
|
cairo_rectangle(w->c_ctx, a.x, a.y, a.w, a.h); |
|
|
|
cairo_fill(w->c_ctx); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
update_stretch_area(btk_window_t *w) |
|
|
|
{ |
|
|
|
w->sa.x = w->sc.x * (btk_cp.w + btk_frame); |
|
|
|
w->sa.y = w->sc.y * (btk_cp.h + btk_frame); |
|
|
|
w->sa.w = w->sa.x + w->es.w + btk_cp.w; |
|
|
|
w->sa.h = w->sa.x + w->es.h + btk_cp.h; |
|
|
|
} |