summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/window/backends.h3
-rw-r--r--src/window/xcb.cpp179
-rw-r--r--src/window/xcb.h8
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);