#include "core/types.h" #include "core/window.h" #include "internal/utils.h" #include "core/event.h" #include #include #include #include #include /* TODO(roberto): put the correct error values */ static void _xcb_handle_error(int error) { switch (error) { case XCB_WINDOW: crash_error("Invalid window error\n"); case XCB_PIXMAP: crash_error("Invalid pixmap error\n"); case XCB_ATOM: crash_error("Invalid atom error\n"); case XCB_VALUE: crash_error("Invalid value error\n"); case XCB_MATCH: crash_error("Match error (incompatible parameters)\n"); case XCB_DRAWABLE: crash_error("Invalid drawable error\n"); case XCB_ACCESS: crash_error("Access denied error\n"); case XCB_ALLOC: crash_error("Resource allocation error\n"); default: crash_error("Unknown error\n"); } } static xcb_atom_t _get_atom_xcb(xcb_connection_t *conn, const char *name) { xcb_intern_atom_cookie_t cookie; xcb_intern_atom_reply_t *reply; xcb_atom_t atom; cookie = xcb_intern_atom(conn, 0, strlen(name), name); reply = xcb_intern_atom_reply(conn, cookie, NULL); if (reply) { atom = reply->atom; free(reply); return atom; } return XCB_ATOM_NONE; } int _init_xcb_backend(RuimToplevelBackend *backend) { struct backend_x11 *x11; xcb_screen_iterator_t iter; int error = 0; x11 = (struct backend_x11 *)malloc(sizeof(struct backend_x11)); x11->connection = xcb_connect(NULL, &x11->screen_num); if ((error = xcb_connection_has_error(x11->connection)) != 0) { return error; } x11->poll.fd = xcb_get_file_descriptor(x11->connection); x11->poll.events = POLLIN; x11->setup = xcb_get_setup(x11->connection); iter = xcb_setup_roots_iterator(x11->setup); x11->screen = iter.data; x11->wm_protocols = _get_atom_xcb(x11->connection, "WM_PROTOCOLS"); x11->wm_delete_window = _get_atom_xcb(x11->connection, "WM_DELETE_WINDOW"); backend->data = (void *)x11; return 0; } int _deinit_xcb_backend(RuimToplevelBackend *backend) { struct backend_x11 *x11 = (struct backend_x11 *)backend->data; xcb_disconnect(x11->connection); free(backend->data); return 0; } /* TODO: support more toplevel types */ int _create_toplevel_xcb(RuimToplevel *toplevel) { int error; struct backend_x11 *x11_backend; struct toplevel_x11 *x11_toplevel; x11_toplevel = (struct toplevel_x11 *)malloc(sizeof(struct toplevel_x11)); x11_toplevel->event_values = (uint32_t *)malloc(sizeof(uint32_t) * 2); x11_backend = (struct backend_x11 *)toplevel->backend->data; if ((error = xcb_connection_has_error(x11_backend->connection)) != 0) { return error; } x11_toplevel->window = xcb_generate_id(x11_backend->connection); x11_toplevel->event_mask = XCB_CW_EVENT_MASK; x11_toplevel->event_values[0] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_STRUCTURE_NOTIFY; xcb_create_window(x11_backend->connection, XCB_COPY_FROM_PARENT, x11_toplevel->window, x11_backend->screen->root, 0, 0, 150, 150, 10, XCB_WINDOW_CLASS_INPUT_OUTPUT, x11_backend->screen->root_visual, x11_toplevel->event_mask, x11_toplevel->event_values); xcb_change_property(x11_backend->connection, XCB_PROP_MODE_REPLACE, x11_toplevel->window, x11_backend->wm_protocols, XCB_ATOM_ATOM, 32, 1, &x11_backend->wm_delete_window); xcb_flush(x11_backend->connection); toplevel->data = (void *)x11_toplevel; return 0; } int _display_toplevel_xcb(RuimToplevel *toplevel) { struct backend_x11 *x11_backend = (struct backend_x11 *)toplevel->backend->data; struct toplevel_x11 *x11_toplevel = (struct toplevel_x11 *)toplevel->data; xcb_map_window(x11_backend->connection, x11_toplevel->window); /* FIXME(roberto): decide where to flush the connection */ return xcb_flush(x11_backend->connection) > 0; } int _poll_event_xcb(RuimToplevelBackend *backend, RuimEvent *event) { struct backend_x11 *x11_backend = (struct backend_x11 *)backend->data; xcb_generic_event_t *x11_event; int error = 0; /* FIXME(roberto): make it actually poll */ /* FIXME(roberto): too laggy */ x11_event = xcb_poll_for_event(x11_backend->connection); if (x11_event != NULL) { switch (x11_event->response_type & ~0x80) { case XCB_EXPOSE: { RuimEventRedraw redraw; xcb_configure_notify_event_t *cfg = (xcb_configure_notify_event_t *)x11_event; event->type = RUIM_EVENT_REDRAW; redraw.width = cfg->width; redraw.height = cfg->height; redraw.x = cfg->x; redraw.y = cfg->y; event->data.redraw = redraw; } break; case XCB_CLIENT_MESSAGE: { xcb_client_message_event_t *cm = (xcb_client_message_event_t *)x11_event; if (cm->data.data32[0] == x11_backend->wm_delete_window) { event->type = RUIM_EVENT_QUIT; } } break; case XCB_KEY_PRESS: { xcb_key_press_event_t *key = (xcb_key_press_event_t *)x11_event; event->type = RUIM_EVENT_KEYDOWN; event->data.mouse.keycode = key->state; event->data.mouse.x = key->event_x; event->data.mouse.y = key->event_y; } break; case XCB_KEY_RELEASE: { xcb_key_release_event_t *key = (xcb_key_press_event_t *)x11_event; event->type = RUIM_EVENT_KEYUP; event->data.mouse.keycode = key->state; event->data.mouse.x = key->event_x; event->data.mouse.y = key->event_y; } break; case XCB_BUTTON_PRESS: { xcb_button_press_event_t *button = (xcb_button_press_event_t *)x11_event; event->type = RUIM_EVENT_MOUSEDOWN; event->data.mouse.keycode = button->state; event->data.mouse.x = button->event_x; event->data.mouse.y = button->event_y; } break; case XCB_BUTTON_RELEASE: { xcb_button_release_event_t *button = (xcb_button_press_event_t *)x11_event; event->type = RUIM_EVENT_MOUSEUP; event->data.mouse.keycode = button->state; event->data.mouse.x = button->event_x; event->data.mouse.y = button->event_y; } break; case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t *cfg = (xcb_configure_notify_event_t *)x11_event; event->type = RUIM_EVENT_WINDOW; event->data.window.width = cfg->width; event->data.window.height = cfg->height; event->data.window.x = cfg->x; event->data.window.y = cfg->y; } break; default: break; } } else if ((error = xcb_connection_has_error(x11_backend->connection)) != 0) { _xcb_handle_error(error); return error; } else { event->type = RUIM_EVENT_NOTHING; } free(x11_event); return 0; } int _wait_event_xcb(RuimToplevel *toplevel) { struct backend_x11 *backend = (struct backend_x11 *)toplevel->backend->data; poll(&backend->poll, 1, 1); return 0; } int _destroy_toplevel_xcb(RuimToplevel *toplevel) { free(toplevel->data); return 0; }