#ifndef SAW_UNIX_XCB #error "XCB is not supported" #endif #include "xcb.h" namespace saw { namespace gfx { device::device(::Display* disp, int screen, xcb_connection_t *xcb_connection, xcb_screen_t *xcb_screen, own&& an): display_{disp}, screen_{screen}, xcb_connection_{xcb_connection}, xcb_screen_{xcb_screen}, async_notifier_{std::move(an)}, async_conveyor_{async_notifier_->read_ready() .then([this]() { handle_events(); }) .sink()} {} device::~device(){ if (display_) { xcb_flush(xcb_connection_); ::XCloseDisplay(display_); } } void device::xcb_window_was_destroyed(xcb_window_t window_id){ windows_.erase(window_id); } void device::handle_events(){ while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection_)) { pending_events_.push_back(event); } for (size_t i = 0; i < pending_events_.size(); ++i) { xcb_generic_event_t *event = pending_events_.at(i); switch (event->response_type & ~0x80) { case XCB_MOTION_NOTIFY: { xcb_motion_notify_event_t *motion = reinterpret_cast(event); auto find = windows_.find(motion->event); if (find != windows_.end()) { assert(find->second); find->second->mouse_move_event(motion->event_x, motion->event_y); } } break; case XCB_EXPOSE: { xcb_expose_event_t *expose = reinterpret_cast(event); auto find = windows_.find(expose->window); if (find != windows_.end()) { assert(find->second); find->second->resize_event(static_cast(expose->x), static_cast(expose->y), static_cast(expose->width), static_cast(expose->height)); } } break; case XCB_BUTTON_RELEASE: { xcb_button_release_event_t *button = reinterpret_cast(event); auto find = windows_.find(button->event); if (find != windows_.end()) { assert(find->second); find->second->mouse_event(button->event_x, button->event_y, button->detail, false); } } break; case XCB_BUTTON_PRESS: { xcb_button_press_event_t *button = reinterpret_cast(event); auto find = windows_.find(button->event); if (find != windows_.end()) { assert(find->second); find->second->mouse_event(button->event_x, button->event_y, button->detail, true); } } break; case XCB_KEY_RELEASE: { xcb_key_release_event_t *key = reinterpret_cast(event); bool repeat = false; /* * Peek into future events */ for (size_t j = i + 1; j < pending_events_.size(); ++j) { xcb_generic_event_t *f_ev = pending_events_.at(j); if ((f_ev->response_type & ~0x80) == XCB_KEY_PRESS) { xcb_key_press_event_t *f_key = reinterpret_cast(f_ev); if (key->detail == f_key->detail && key->event == f_key->event) { auto iterator = pending_events_.begin() + j; assert(iterator != pending_events_.end()); free(*iterator); pending_events_.erase(iterator); repeat = true; break; } } } auto find = windows_.find(key->event); if (find != windows_.end()) { assert(find->second); find->second->keyboard_event(key->event_x, key->event_y, key->detail, repeat, repeat); } } break; case XCB_KEY_PRESS: { xcb_key_press_event_t *key = reinterpret_cast(event); auto find = windows_.find(key->event); if (find != windows_.end()) { assert(find->second); find->second->keyboard_event(key->event_x, key->event_y, key->detail, true, false); } } break; default: break; } } for (xcb_generic_event_t *event : pending_events_) { free(event); } pending_events_.clear(); } own> device::create_xcb_window(const video_mode &vid_mode, std::string_view title_view, int visual_id) { assert(xcb_screen_); assert(xcb_connection_); xcb_colormap_t xcb_colormap = xcb_generate_id(xcb_connection_); xcb_window_t xcb_window = xcb_generate_id(xcb_connection_); xcb_create_colormap(xcb_connection_, XCB_COLORMAP_ALLOC_NONE, xcb_colormap, xcb_screen_->root, visual_id); uint32_t eventmask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_MOTION; uint32_t valuelist[] = {eventmask, xcb_colormap, 0}; uint32_t valuemask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; xcb_create_window(xcb_connection_, XCB_COPY_FROM_PARENT, xcb_window, xcb_screen_->root, 0, 0, vid_mode.width, vid_mode.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visual_id, valuemask, valuelist); xcb_change_property(xcb_connection_, XCB_PROP_MODE_REPLACE, xcb_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, title_view.size(), title_view.data()); xcb_flush(xcb_connection_); auto win = heap>(*this, xcb_window, xcb_colormap, vid_mode, title_view); // Insert into map windows_[xcb_window] = win.get(); return win; } own> device::create_window(const video_mode& vid_mode, std::string_view title_view){ assert(xcb_screen_); return create_xcb_window(vid_mode, title_view, xcb_screen_->root_visual); } void device::flush(){ assert(xcb_connection_); xcb_flush(xcb_connection_); } error_or>> create_xcb_device(io_provider& provider){ ::Display *display = ::XOpenDisplay(nullptr); if (!display) { /// @todo log errors return make_error(); } int screen = ::XDefaultScreen(display); xcb_connection_t *xcb_connection = ::XGetXCBConnection(display); if (!xcb_connection) { /// @todo log errors ::XCloseDisplay(display); return make_error(); } int fd = xcb_get_file_descriptor(xcb_connection); own fd_wrapped = provider.wrap_input_fd(fd); if (!fd_wrapped) { ::XCloseDisplay(display); return make_error(); } ::XSetEventQueueOwner(display, XCBOwnsEventQueue); xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(xcb_get_setup(xcb_connection)); for (int screen_i = screen; screen_iter.rem && screen_i > 0; --screen_i, xcb_screen_next(&screen_iter)) ; xcb_screen_t *xcb_screen = screen_iter.data; return heap>(display, screen, xcb_connection, xcb_screen, std::move(fd_wrapped)); } window::window(device& dev_, xcb_window_t xcb_win, xcb_colormap_t xcb_colormap_, const video_mode& vid_mode_, std::string_view& title_view_): device_{&dev_}, xcb_window_{xcb_win}, xcb_colormap_{xcb_colormap_}, video_mode_{vid_mode_}, window_title_{title_view_} { // TODO } window::~window(){ // TODO } void window::show(){ // TODO } void window::hide(){ // TODO } } }