diff options
Diffstat (limited to 'src/window')
-rw-r--r-- | src/window/backends.h | 3 | ||||
-rw-r--r-- | src/window/xcb.cpp | 179 | ||||
-rw-r--r-- | src/window/xcb.h | 8 |
3 files changed, 168 insertions, 22 deletions
diff --git a/src/window/backends.h b/src/window/backends.h index 2548dc5..e129037 100644 --- a/src/window/backends.h +++ b/src/window/backends.h @@ -3,7 +3,8 @@ namespace saw { namespace gfx { namespace backend { -struct xcb {}; +struct linux_xcb {}; +struct wasm {}; } } } diff --git a/src/window/xcb.cpp b/src/window/xcb.cpp index 82f2f41..e08291f 100644 --- a/src/window/xcb.cpp +++ b/src/window/xcb.cpp @@ -5,33 +5,176 @@ #include "xcb.h" namespace saw { -device::device(::Display* disp, int screen, xcb_connection_t *xcb_connection, xcb_screen_t *xcb_screen, own<input_stream>&& an): - display_{disp}, screen_{screen}, xcb_connection_{xcb_connection}, xcb_screen_{xcb_screen}, async_notifier_{std::move(an)} -{ - // TODO +device<backend::linux_xcb>::device(::Display* disp, int screen, xcb_connection_t *xcb_connection, xcb_screen_t *xcb_screen, own<input_stream>&& 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<backend::linux_xcb>::~device(){ + if (display_) { + xcb_flush(xcb_connection_); + ::XCloseDisplay(display_); + } } -device::~device(){ - // TODO +void device<backend::linux_xcb>::xcb_window_was_destroyed(xcb_window_t window_id){ + windows_.erase(window_id); } -void device::xcb_window_was_destroyed(xcb_window_t window_id){ - // TODO +void device<backend::linux_xcb>::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<xcb_motion_notify_event_t *>(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<xcb_expose_event_t *>(event); + auto find = windows.find(expose->window); + if (find != windows.end()) { + assert(find->second); + find->second->resize_event(static_cast<size_t>(expose->x), + static_cast<size_t>(expose->y), + static_cast<size_t>(expose->width), + static_cast<size_t>(expose->height)); + } + } break; + case XCB_BUTTON_RELEASE: { + xcb_button_release_event_t *button = + reinterpret_cast<xcb_button_release_event_t *>(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<xcb_button_press_event_t *>(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<xcb_key_release_event_t *>(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<xcb_key_press_event_t *>(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<xcb_key_press_event_t *>(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(); } -void device::handle_events(){ - // TODO +own<window> device<backend::linux_xcb>::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); } -own<window> device::create_window(const video_mode& vid_mode, std::string_view title_view){ - // TODO +void device<backend::linux_xcb>::flush(){ + assert(xcb_connection_); + xcb_flush(xcb_connection_); } -void device::flush(){ - // TODO +error_or<own<device<backend::linux_xcb>>> create_xcb_device(){ + ::Display *display = ::XOpenDisplay(nullptr); + if (!display) { + /// @todo log errors + return make_error<err::critical>(); + } + + int screen = ::XDefaultScreen(display); + + xcb_connection_t *xcb_connection = ::XGetXCBConnection(display); + if (!xcb_connection) { + /// @todo log errors + ::XCloseDisplay(display); + return make_error<err::critical>(); + } + + int fd = xcb_get_file_descriptor(xcb_connection); + + own<input_stream> fd_wrapped = provider.wrap_input_fd(fd); + if (!fd_wrapped) { + ::XCloseDisplay(display); + return make_error<err::critical>(); + } + + ::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<device<backend::linux_xcb>>(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): +window<backend::linux_xcb>::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_}, @@ -41,15 +184,15 @@ window::window(device& dev_, xcb_window_t xcb_win, xcb_colormap_t xcb_colormap_, // TODO } -window::~window(){ +window<backend::linux_xcb>::~window(){ // TODO } -void window::show(){ +void window<backend::linux_xcb>::show(){ // TODO } -void window::hide(){ +void window<backend::linux_xcb>::hide(){ // TODO } diff --git a/src/window/xcb.h b/src/window/xcb.h index bbdf1ed..e2928b6 100644 --- a/src/window/xcb.h +++ b/src/window/xcb.h @@ -12,7 +12,7 @@ namespace gfx { class window; template<> -class device<backend::xcb> final { +class device<backend::linux_xcb> final { private: ::Display *display_; int screen_; @@ -35,11 +35,13 @@ public: void xcb_window_was_destroyed(xcb_window_t window_id); void handle_events(); - own<window> create_window(const video_mode& vid_mod, std::string_view title_view); + window<backend::linux_xcb> create_window(const video_mode& vid_mod, std::string_view title_view); void flush(); }; +error_or<device<backend::linux_xcb>> create_xcb_device(); + class window { private: device *device_; @@ -65,7 +67,7 @@ public: void resize(uint64_t width, uint64_t height); - conveyor<schema::WindowEvents> on_event(); + conveyor<data<schema::WindowEvents>> on_event(); void resize_event(uint64_t x, uint64_t y, uint64_t width, uint64_t height); void mouse_event(int16_t x, int16_t y, uint16_t state, bool pressed); |