#include #include #include #include #include "wsi.h" static struct { xcb_connection_t* conn; xcb_screen_t* screen; xcb_window_t win; xcb_intern_atom_reply_t* atom_wm_delete_window; WsiExposeEvent expose_event; } xcb_data = { 0, }; static bool wsi_handle_event_xcb (xcb_generic_event_t* event) { while (event != NULL) { uint8_t event_code = event->response_type & 0x7f; switch (event_code) { case XCB_EXPOSE: if (xcb_data.expose_event != NULL) xcb_data.expose_event (); break; case XCB_CLIENT_MESSAGE: if ((* (xcb_client_message_event_t*) event).data.data32[0] == (* xcb_data.atom_wm_delete_window).atom) { return false; } break; case XCB_KEY_RELEASE: { const xcb_key_release_event_t* key = (const xcb_key_release_event_t*) event; switch (key->detail) { case 0x9: /* ESC key */ return false; break; case 0x29: /* F key */ wsi_toggle_fullscreen (); break; default: printf ("key pressed: %x\n", key->detail); break; } break; } default: break; } free (event); event = xcb_poll_for_event (xcb_data.conn); } return true; } void wsi_toggle_fullscreen (void) { static bool fullscreen_mode = false; fullscreen_mode = ! fullscreen_mode; xcb_intern_atom_cookie_t cookie = xcb_intern_atom (xcb_data.conn, 0, 13, "_NET_WM_STATE"); xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply (xcb_data.conn, cookie, 0); xcb_atom_t atom1 = reply->atom; free (reply); cookie = xcb_intern_atom (xcb_data.conn, 0, 24, "_NET_WM_STATE_FULLSCREEN"); reply = xcb_intern_atom_reply (xcb_data.conn, cookie, 0); xcb_atom_t atom2 = reply->atom; free (reply); xcb_client_message_event_t msg = {0}; msg.response_type = XCB_CLIENT_MESSAGE; msg.window = xcb_data.win; msg.format = 32; msg.type = atom1; memset (msg.data.data32, 0, 5 * sizeof (uint32_t)); msg.data.data32[0] = fullscreen_mode ? 1 : 0; msg.data.data32[1] = atom2; xcb_send_event (xcb_data.conn, true, xcb_data.screen->root, XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, (const char *) &msg); xcb_flush (xcb_data.conn); } bool wsi_init (const char* win_title, uint32_t width, uint32_t height, WsiExposeEvent expose_event) { /* connection to the X server */ xcb_data.conn = xcb_connect (NULL, NULL); if (xcb_data.conn == NULL) { printf ("XCB: Error: Failed to connect to X server\n"); return false; } printf ("XCB: Connected to the X server\n"); /* Get the first screen */ const xcb_setup_t* xcb_setup = xcb_get_setup (xcb_data.conn); xcb_screen_iterator_t iter = xcb_setup_roots_iterator (xcb_setup); xcb_data.screen = iter.data; /* Create the window */ xcb_data.win = xcb_generate_id (xcb_data.conn); uint32_t value_mask, value_list[32]; value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; value_list[0] = xcb_data.screen->black_pixel; value_list[1] = XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; xcb_create_window (xcb_data.conn, /* Connection */ XCB_COPY_FROM_PARENT, /* depth (same as root)*/ xcb_data.win, /* window Id */ xcb_data.screen->root, /* parent window */ 0, 0, /* x, y */ width, height, /* width, height */ 0, /* border_width */ XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */ xcb_data.screen->root_visual, /* visual */ value_mask, value_list); /* masks, not used yet */ xcb_intern_atom_cookie_t cookie = xcb_intern_atom (xcb_data.conn, 1, 12, "WM_PROTOCOLS"); xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply (xcb_data.conn, cookie, 0); xcb_intern_atom_cookie_t cookie1 = xcb_intern_atom (xcb_data.conn, 0, 16, "WM_DELETE_WINDOW"); xcb_data.atom_wm_delete_window = xcb_intern_atom_reply (xcb_data.conn, cookie1, 0); xcb_change_property (xcb_data.conn, XCB_PROP_MODE_REPLACE, xcb_data.win, (*reply).atom, 4, 32, 1, &(*xcb_data.atom_wm_delete_window).atom); free (reply); /* Make sure commands are sent before we pause so that the window gets * shown. */ xcb_flush (xcb_data.conn); xcb_data.expose_event = expose_event; return true; } void wsi_get_connection_and_window (const void** conn, const void** win) { if (conn != NULL) *conn = (const void*) xcb_data.conn; if (win != NULL) *win = (const void*) &xcb_data.win; } bool wsi_wait_for_events (void) { xcb_generic_event_t* event; event = xcb_wait_for_event (xcb_data.conn); return wsi_handle_event_xcb (event); } void wsi_window_show (void) { xcb_map_window (xcb_data.conn, xcb_data.win); } void wsi_finish (void) { if (xcb_data.conn == NULL) return; /* Unmap the window from the screen */ xcb_unmap_window (xcb_data.conn, xcb_data.win); free (xcb_data.atom_wm_delete_window); /* disconnect from the X server */ xcb_disconnect (xcb_data.conn); }