diff options
Diffstat (limited to 'src/connect.c')
| -rw-r--r-- | src/connect.c | 408 |
1 files changed, 0 insertions, 408 deletions
diff --git a/src/connect.c b/src/connect.c deleted file mode 100644 index aaced77..0000000 --- a/src/connect.c +++ /dev/null @@ -1,408 +0,0 @@ -#include <glib.h> -#include <xcb/xcb.h> -#include <xcb/xcb_aux.h> -#include <xcb/xcb_ewmh.h> -#include <xcb/xcb_xrm.h> -#include <xcb/xcb_errors.h> -#include <xcb/randr.h> - -#include "log.h" -#include "connect.h" -#include "state.h" - -static bool query_xrm(Connection *con, const char *res, char **value) -{ - if (xcb_xrm_resource_get_string(con->database, res, res, value) >= 0) { - log_debug("Xrm query '%s' found '%s'", res, *value); - return true; - } - - log_debug("Xrm query '%s' not found", res); - return false; -} - -static void update_xrm(Connection *con) -{ - xcb_flush(con->connection); - xcb_xrm_database_t *database = xcb_xrm_database_from_default(con->connection); - - if (database == NULL) { - log_warning("Xrm database couldn't be updated"); - return; - } - - xcb_xrm_database_free(con->database); - con->database = database; - log_debug("Xrm database updated"); -} - -static void update_scale(Connection *con) -{ - char *dpi_value; - if (query_xrm(con, "Xft.dpi", &dpi_value)) { - con->screen_dpi = strtod(dpi_value, NULL); - g_free(dpi_value); - } else { - con->screen_dpi = (double)con->screen_size->height * 25.4 / (double)con->screen_size->mheight; - log_debug("Fallback dpi value '%.2lf'", con->screen_dpi); - } -} - -// Check if point (px, py) is inside a rectangle in (x, y), (x+w, y), (x, y+h) and (w+h, y+h) -static inline bool in_rect(int px, int py, int x, int y, int w, int h) -{ - return px >= x && px <= x + w && py >= y && py <= y + h; -} - -// Check if point (px, py) is inside a circle of radius r and center (x, y) -static inline bool in_circle(int px, int py, int x, int y, int r) -{ - int dx = x - px; - int dy = y - py; - return (dx * dx + dy * dy) <= r * r; -} - -// Check if point (px, py) is inside a capsule in (x, y), (x+w, y), (x, y+h) and (w+h, y+h) -static inline bool in_capsule(int px, int py, int x, int y, int w, int h) -{ - g_assert(w >= h); - int radius = h / 2; - - // Circle case - if (w == h) return in_circle(px, py, x + radius, y + radius, radius); - - // Capsule case - return in_circle(px, py, x + radius, y + radius, radius) - || in_circle(px, py, x + w - radius, y + radius, radius) - || in_rect(px, py, x + radius, y, w - 2 * radius, h); -} - -static bool button_action_find(State *state, GList *layouts, int x, int y) -{ - for (GList *it = layouts; it; it = it->next) { - const Layout *layout = it->data; - - // Skip - if (layout->x + layout->width < x) continue; - if (layout->x > x) break; - - // Bound check click coordinates - int w = layout->width - 2 * layout->x_pad; - int h = layout->height - 2 * layout->y_pad; - - if (!in_capsule(x, y, layout->x + layout->x_pad, layout->y + layout->y_pad, w, h)) - continue; - - Button *btn = layout->btn; - if (!layout->btn->simple) { - log_debug("Button group layout [x=%d, y=%d, w=%d, h=%d, button=%p]", - layout->x, layout->y, layout->width, layout->height, btn); - - // Check the group children - return button_action_find(state, layout->children, x, y); - } - - log_debug("Button layout [x=%d, y=%d, w=%d, h=%d, button=%p]", - layout->x, layout->y, layout->width, layout->height, btn); - - ButtonAction action = button_simple_get_action(btn); - if (action == NULL) { - // TODO: Button labels - log_debug("Ignoring button without action [button=%p]", btn); - return true; - } - - // NOTE: Animations may change the layouts! - Animation *anim = animation_shine_create(MILLIS(250)); - //Animation *anim = animation_pulse_create(MILLIS(200)); - - if (button_set_animation(btn, anim)) - state_request_animation(state); - else - animation_destroy(anim); - - log_info("Triggering action for button [text=\"%s\", button=%p]", button_simple_get_text(btn), btn); - action(btn); - return true; - } - - return false; -} - -static void button_action(State *state, const char *event, int x, int y) -{ - double scale = window_get_scale(state->win); - int digital_x = x / scale; - int digital_y = y / scale; - - log_debug("Checking %s event [device_x=%d, device_y=%d, x=%d, y=%d]", event, x, y, digital_x, digital_y); - if (!button_action_find(state, state->draw->layouts, digital_x, digital_y)) - log_debug("Ignoring %s", event); -} - -typedef struct { - GSource source; - Connection *con; - gpointer fd_tag; - xcb_generic_event_t *event; -} EventSource; - -static gboolean source_check(GSource *source) -{ - EventSource *xsource = (EventSource *)source; - xcb_flush(xsource->con->connection); - g_assert_null(xsource->event); - - GIOCondition flags = g_source_query_unix_fd(source, xsource->fd_tag); - if (flags & G_IO_IN) { - g_assert_false(xcb_connection_has_error(xsource->con->connection)); - xsource->event = xcb_poll_for_event(xsource->con->connection); - } - - return xsource->event != NULL; -} - -static gboolean source_dispatch(GSource *source, GSourceFunc callback, gpointer data) -{ - EventSource *xsource = (EventSource *)source; - g_assert_nonnull(xsource->event); - - do { - switch (xsource->event->response_type & ~0x80) { - case 0: { - xcb_generic_error_t *error = (xcb_generic_error_t *)xsource->event; - - const char *extension; - const char *name = xcb_errors_get_name_for_error(xsource->con->errors, error->error_code, &extension); - const char *major = xcb_errors_get_name_for_major_code(xsource->con->errors, error->major_code); - const char *minor = xcb_errors_get_name_for_minor_code(xsource->con->errors, error->major_code, error->minor_code); - - // TODO: Handle errors instead of aborting - log_error("Xcb error '%s' [extension=%s, major=%s, minor=%s, resource=%u, sequence=%u]", - name, - extension ? extension : "none", - major, - minor ? minor : "none", - (unsigned int)error->resource_id, - (unsigned int)error->sequence); - break; - } - - case XCB_EXPOSE: { - xcb_expose_event_t *expose = (xcb_expose_event_t *)xsource->event; - log_debug("Processing event 'Expose' [type=%d]", XCB_EXPOSE); - - for (GList *it = xsource->con->states; it != NULL; it = it->next) { - State *state = it->data; - if (state->win->window == expose->window) { - state_request_redraw(state, true); - break; - } - } - break; - } - - case XCB_CREATE_NOTIFY: { - xcb_create_notify_event_t *create = (xcb_create_notify_event_t *)xsource->event; - log_debug("Processing event 'CreateNotify' [type=%d]", XCB_CREATE_NOTIFY); - - // TODO: Circulate top the window if override_redirect == 0 - - break; - } - - case XCB_BUTTON_RELEASE: { - xcb_button_release_event_t *button = (xcb_button_release_event_t *)xsource->event; - log_debug("Processing event 'ButtonRelease' [type=%d]", XCB_BUTTON_RELEASE); - - // TODO: Handle different actions properly - switch (button->detail) { - case XCB_BUTTON_INDEX_2: // left click - case XCB_BUTTON_INDEX_1: // right click - case XCB_BUTTON_INDEX_3: // middle click - for (GList *it = xsource->con->states; it != NULL; it = it->next) { - State *state = it->data; - if (state->win->window == button->event) { - button_action(state, "button release", button->event_x, button->event_y); - break; - } - } - break; - - default: - log_debug("Ignoring button release [button=%d]", button->detail); - break; - } - break; - } - - // TODO: Implement correctly hovering - //case XCB_MOTION_NOTIFY: { - // xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)xsource->event; - // log_debug("Processing event 'MotionNotify' [type=%d]", XCB_MOTION_NOTIFY); - // button_action(xsource->con->state, "motion notify", motion->event_x, motion->event_y); - // break; - //} - - case XCB_PROPERTY_NOTIFY: { - xcb_property_notify_event_t *property = (xcb_property_notify_event_t *)xsource->event; - log_debug("Processing event 'PropertyNotify' [type=%d]", XCB_PROPERTY_NOTIFY); - - if (property->atom == XCB_ATOM_RESOURCE_MANAGER) { - update_xrm(xsource->con); - update_scale(xsource->con); - for (GList *it = xsource->con->states; it != NULL; it = it->next) - state_request_redraw(it->data, true); - } - break; - } - - default: { - const char *extension; - const char *name = xcb_errors_get_name_for_xcb_event(xsource->con->errors, xsource->event, &extension); - - // TODO: Handle XCB_RANDR_SCREEN_CHANGE_NOTIFY - - log_debug("Ignoring event '%s' [type=%d, extension=%s]", - name, - xsource->event->response_type & 0x7f, - extension ? extension : "none"); - } - } - g_free(xsource->event); - } while ((xsource->event = xcb_poll_for_event(xsource->con->connection)) != NULL); - - return G_SOURCE_CONTINUE; -} - -static GSourceFuncs source_fns = { - NULL, - source_check, - source_dispatch, - NULL, -}; - -static void attach_source(Connection *con) -{ - EventSource *source = (EventSource *)g_source_new(&source_fns, sizeof(EventSource)); - con->source = (GSource *)source; - g_source_set_static_name(con->source, "EventSource"); - - source->con = con; - source->event = NULL; - source->fd_tag = g_source_add_unix_fd(con->source, xcb_get_file_descriptor(con->connection), G_IO_IN | G_IO_HUP | G_IO_ERR); - - g_source_attach(con->source, NULL); -} - -Connection *connect_create() -{ - Connection *con = g_malloc0(sizeof(Connection)); - g_assert_nonnull(con); - - int preferred_screen = 0; - con->connection = xcb_connect(NULL, &preferred_screen); - g_assert_true(con->connection != NULL && !xcb_connection_has_error(con->connection)); - log_debug("Xcb connection established"); - - log_debug("Default screen '%d'", preferred_screen); - xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(con->connection)); - - while (preferred_screen != 0 && iter.rem) { - xcb_screen_next(&iter); - preferred_screen--; - } - - con->screen = iter.data; - xcb_generic_error_t *error; - - xcb_randr_query_version_cookie_t version_cookie = xcb_randr_query_version(con->connection, - XCB_RANDR_MAJOR_VERSION, - XCB_RANDR_MINOR_VERSION); - - xcb_randr_query_version_reply_t *randr_version = xcb_randr_query_version_reply(con->connection, - version_cookie, - &error); - - g_assert_null(error); - log_debug("RandR loaded [version=%d.%d]", randr_version->major_version, randr_version->minor_version); - g_assert_cmpint(randr_version->major_version, >=, 1); - g_free(randr_version); - - xcb_randr_get_screen_info_cookie_t cookie = xcb_randr_get_screen_info(con->connection, con->screen->root); - con->info_reply = xcb_randr_get_screen_info_reply(con->connection, cookie, &error); - g_assert_null(error); - - con->screen_size = xcb_randr_get_screen_info_sizes(con->info_reply); - g_assert_nonnull(con->screen_size); - log_debug("Screen size [width=%d, height=%d]", con->screen_size->width, con->screen_size->height); - - xcb_randr_select_input(con->connection, - con->screen->root, - XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | - XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | - XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); - - log_debug("Xcb searching 32 bit visual"); - con->visual_type = xcb_aux_find_visual_by_attrs(con->screen, XCB_VISUAL_CLASS_TRUE_COLOR, 32); - con->screen_depth = 32; - - if (con->visual_type == NULL) { - // NOTE: 24 bit visuals don't have the alpha channel for transparency - log_debug("Fallback to 24 bit visual"); - con->visual_type = xcb_aux_find_visual_by_attrs(con->screen, XCB_VISUAL_CLASS_TRUE_COLOR, 24); - con->screen_depth = 24; - } - - g_assert_nonnull(con->visual_type); - log_debug("Xcb visual type found [id=%u]", con->visual_type->visual_id); - - xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(con->connection, &con->ewmh); - g_assert_true(xcb_ewmh_init_atoms_replies(&con->ewmh, ewmh_cookie, &error)); - g_assert_null(error); - log_debug("Xcb ewmh initialized"); - - con->database = xcb_xrm_database_from_default(con->connection); - g_assert_nonnull(con->database); - - // TODO: Dpi aware scaling - update_scale(con); - - g_assert(xcb_errors_context_new(con->connection, &con->errors) == 0); - log_debug("Xcb errors loaded"); - - xcb_flush(con->connection); - log_debug("Xcb set up"); - - return con; -} - -void connect_attach_source(Connection *con) -{ - g_assert_null(con->source); - attach_source(con); - log_debug("Xcb event loop attached"); -} - -void connect_add_state(Connection *con, State *state) -{ - g_assert_nonnull(state); - con->states = g_list_append(con->states, state); - log_debug("Add state to event loop [state=%p, state=\"%s\"]", state, state->label); -} - -void connect_destroy(Connection *con) -{ - g_source_destroy(con->source); - g_source_unref(con->source); - - xcb_ewmh_connection_wipe(&con->ewmh); - xcb_errors_context_free(con->errors); - xcb_xrm_database_free(con->database); - xcb_disconnect(con->connection); - - g_free(con->info_reply); - g_free(con); -} - -// vim: ts=4 sw=4 et |
