diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index cc13904..11b2e67 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -14,13 +14,19 @@ # limitations under the License. # -# Define variables set(TARGET_NAME demo) -set(SOURCE_FILE demo.cc) -set(COMPILE_DEFINITIONS EGL_NO_X11 MESA_EGL_NO_X11_HEADERS) -set(LINK_LIBRARIES waypp GLESv2) -# Use variables in place of hard-coded values -add_executable(${TARGET_NAME} ${SOURCE_FILE}) -target_compile_definitions(${TARGET_NAME} PRIVATE ${COMPILE_DEFINITIONS}) -target_link_libraries(${TARGET_NAME} ${LINK_LIBRARIES}) \ No newline at end of file +find_package(PkgConfig REQUIRED) +pkg_check_modules(GLESv2 IMPORTED_TARGET glesv2) + +add_executable(${TARGET_NAME} demo.cc) +target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/include) + +target_compile_definitions(${TARGET_NAME} PRIVATE EGL_NO_X11 MESA_EGL_NO_X11_HEADERS) +target_link_libraries(${TARGET_NAME} PRIVATE waypp PkgConfig::GLESv2) + +if (IPO_SUPPORT_RESULT) + set_property(TARGET ${TARGET_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +endif () + +add_sanitizers(${TARGET_NAME}) \ No newline at end of file diff --git a/demo/demo.cc b/demo/demo.cc index 9475d3a..8265234 100644 --- a/demo/demo.cc +++ b/demo/demo.cc @@ -14,18 +14,20 @@ * limitations under the License. */ -#include +#include #include #include #include +#include "window_manager/registrar.h" #include "window/window_egl.h" -#include "window_manager/window_manager.h" +#include "window_manager/xdg_window_manager.h" + +#include "config.h" static volatile bool keep_running = true; -constexpr int WINDOW_HEIGHT = 200; -constexpr int WINDOW_WIDTH = 200; + /** * @brief Signal handler function to handle signals. @@ -38,8 +40,10 @@ constexpr int WINDOW_WIDTH = 200; * * @return void */ -void handle_signal(int /* signal */) { - keep_running = false; +void handle_signal(int signal) { + if (signal == SIGINT) { + keep_running = false; + } } /** @@ -93,10 +97,8 @@ static float calculate_hue() { * @param data A pointer to the WindowEgl object. * @param time The current time in milliseconds. */ -void frame_update(void *data, uint32_t time) { - std::cout << "draw_frame: " << time << std::endl; - auto obj = static_cast(data); - (void) obj->make_current(); +void draw_frame(WindowEgl *backend, uint32_t /* time */) { + (void) backend->make_current(); auto hue = calculate_hue(); float rgb[3] = {0, 0, 0}; @@ -105,8 +107,8 @@ void frame_update(void *data, uint32_t time) { glClear(GL_COLOR_BUFFER_BIT); glFinish(); - (void) obj->swap_buffers(); - (void) obj->clear_current(); + (void) backend->swap_buffers(); + (void) backend->clear_current(); } /** @@ -120,10 +122,15 @@ void frame_update(void *data, uint32_t time) { * @return An integer representing the exit status of the program. */ int main(int /* argc */, char ** /* argv */) { + std::signal(SIGINT, handle_signal); - WindowManager wm(Window::ShellType::XDG); - wm.create_window(WINDOW_WIDTH, WINDOW_HEIGHT, - WindowManager::WindowType::EGL, frame_update); + + XdgWindowManager wm("XDG Demo", "org.waypp.xdg_demo"); + if (wm.shm_has_format(WL_SHM_FORMAT_XRGB8888)) { + std::cout << "XRGB is supported" << std::endl; + } + wm.create_egl_window(640, 480, kEglContextAttribs.size(), kEglContextAttribs.data(), kEglConfigAttribs.size(), + kEglConfigAttribs.data(), kEglMinBufferSize, reinterpret_cast(draw_frame)); while (keep_running && wm.poll_events(0) >= 0); return EXIT_SUCCESS; diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..78c8854 --- /dev/null +++ b/include/config.h @@ -0,0 +1,46 @@ +/* + * Copyright 2024 Joel Winarske + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INCLUDE_CONFIG_H_ +#define INCLUDE_CONFIG_H_ + +#include + +#include + +static constexpr std::array kEglContextAttribs = { + { + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_MAJOR_VERSION, 2, + EGL_NONE + } +}; + +static constexpr std::array kEglConfigAttribs = { + { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE // termination sentinel + } +}; + +static constexpr int kEglMinBufferSize = 24; + +#endif // INCLUDE_CONFIG_H_ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2beb515..b797014 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,20 +19,20 @@ pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) find_package(OpenGL REQUIRED COMPONENTS EGL) set(WINDOW_MANAGER_SRC - window_manager/display.cc window_manager/output.cc + window_manager/registrar.cc window_manager/window_manager.cc - window_manager/xdg_wm.cc) + window_manager/xdg_window_manager.cc) set(SEAT_SRC seat/seat.cc seat/keyboard.cc seat/pointer.cc - seat/cursor.cc seat/touch.cc) set(WINDOW_SRC window/egl.cc + window/surface.cc window/window.cc window/window_egl.cc window/window_vulkan.cc) diff --git a/src/seat/cursor.cc b/src/seat/cursor.cc deleted file mode 100644 index f379bc4..0000000 --- a/src/seat/cursor.cc +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2024 Joel Winarske - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "cursor.h" - -#include - -constexpr int kCursorSize = 24; -constexpr char kCursorKindBasic[] = "left_ptr"; -constexpr char kCursorKindClick[] = "hand"; -constexpr char kCursorKindText[] = "left_ptr"; -constexpr char kCursorKindForbidden[] = "pirate"; - -/** - * @class Cursor - * @brief Represents a cursor for a Pointer. - */ -Cursor::Cursor(Pointer *parent, struct wl_pointer *pointer, struct wl_shm *shm, struct wl_compositor *compositor, - bool enable, const char *theme_name) - : parent_(parent), - wl_pointer_(pointer), - wl_shm_(shm), - wl_surface_(wl_compositor_create_surface(compositor)), - theme_name_(theme_name), - enable_(enable) { - if (enable) { - theme_ = wl_cursor_theme_load(theme_name_.c_str(), kCursorSize, wl_shm_); - } -} - -/** - * @brief Destructor for the Cursor class. - * - * This function destroys the Cursor object by freeing any allocated resources. - * It destroys the wl_cursor_theme object pointed to by the theme_ pointer. - * It also destroys the wl_surface object pointed to by the wl_surface_ pointer. - */ -Cursor::~Cursor() { - if (theme_) - wl_cursor_theme_destroy(theme_); - - if (wl_surface_) - wl_surface_destroy(wl_surface_); -} - -/** - * @brief Sets the cursor for the specified device and kind. - * - * @param device The device ID. - * @param kind The kind of cursor. - * @return True if the cursor was successfully set, false otherwise. - * - * This function sets the cursor for the specified device and kind. If the enable flag is false, - * the default cursor is set. If the enable flag is true, the cursor is set based on the specified kind. - * - * If the specified kind is not found or if an invalid cursor buffer is encountered, the function returns false. - * If the cursor is successfully set, the function returns true. - */ -bool Cursor::enable(const int32_t device, const char *kind) const { - (void) device; - if (!enable_) { - wl_pointer_set_cursor(wl_pointer_, parent_->get_serial(), - wl_surface_, 0, 0); - wl_surface_damage(wl_surface_, 0, 0, 0, 0); - wl_surface_commit(wl_surface_); - return true; - } - - if (wl_pointer_) { - const char *cursor_name; - if (strcmp(kind, "basic") == 0) { - cursor_name = kCursorKindBasic; - } else if (strcmp(kind, "click") == 0) { - cursor_name = kCursorKindClick; - } else if (strcmp(kind, "text") == 0) { - cursor_name = kCursorKindText; - } else if (strcmp(kind, "forbidden") == 0) { - cursor_name = kCursorKindForbidden; - } else { - // Cursor kind - return false; - } - - const auto cursor = wl_cursor_theme_get_cursor(theme_, cursor_name); - if (cursor == nullptr) { - // not found - return false; - } - const auto cursor_buffer = wl_cursor_image_get_buffer(cursor->images[0]); - if (cursor_buffer && wl_surface_) { - - wl_pointer_set_cursor(wl_pointer_, parent_->get_serial(), - wl_surface_, - static_cast(cursor->images[0]->hotspot_x), - static_cast(cursor->images[0]->hotspot_y)); - wl_surface_attach(wl_surface_, cursor_buffer, 0, 0); - wl_surface_damage(wl_surface_, 0, 0, - static_cast(cursor->images[0]->width), - static_cast(cursor->images[0]->height)); - wl_surface_commit(wl_surface_); - } else { - // Failed to set cursor: Invalid Cursor Buffer - return false; - } - } - - return true; -} diff --git a/src/seat/cursor.h b/src/seat/cursor.h deleted file mode 100644 index 2c9ae1c..0000000 --- a/src/seat/cursor.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2024 Joel Winarske - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SRC_SEAT_CURSOR_H_ -#define SRC_SEAT_CURSOR_H_ - -#include - -#include - -#include "window_manager/display.h" -#include "pointer.h" - -class Display; - -class Pointer; - -class Cursor { -public: - Cursor(Pointer *parent, struct wl_pointer *pointer, struct wl_shm *shm, struct wl_compositor *compositor, - bool enable, - const char *theme_name = "DMZ-White"); - - ~Cursor(); - - bool enable(int32_t device, const char *kind) const; - -private: - Pointer *parent_; - struct wl_pointer *wl_pointer_; - struct wl_surface *wl_surface_; - struct wl_cursor_theme *theme_; - struct wl_shm *wl_shm_; - bool enable_; - - std::string theme_name_; -}; - -#endif // SRC_SEAT_CURSOR_H_ \ No newline at end of file diff --git a/src/seat/keyboard.cc b/src/seat/keyboard.cc index ae79819..cb51eb7 100644 --- a/src/seat/keyboard.cc +++ b/src/seat/keyboard.cc @@ -33,7 +33,8 @@ */ Keyboard::Keyboard(struct wl_keyboard *keyboard) : keyboard_(keyboard), xkb_context_(xkb_context_new(XKB_CONTEXT_NO_FLAGS)) { - wl_keyboard_add_listener(keyboard, &listener_, this); + std::cout << "Keyboard" << std::endl; + wl_keyboard_add_listener(keyboard_, &listener_, this); } /** @@ -44,7 +45,6 @@ Keyboard::Keyboard(struct wl_keyboard *keyboard) : keyboard_(keyboard), */ Keyboard::~Keyboard() { wl_keyboard_release(keyboard_); - wl_keyboard_destroy(keyboard_); } /** @@ -61,12 +61,15 @@ Keyboard::~Keyboard() { * @return None */ void Keyboard::handle_enter(void *data, - struct wl_keyboard * /* keyboard */, + struct wl_keyboard *keyboard, uint32_t /* serial */, struct wl_surface *surface, struct wl_array * /* keys */) { - std::cerr << "handle_enter" << std::endl; const auto obj = static_cast(data); + if (obj->keyboard_ != keyboard) { + return; + } + std::cerr << "handle_enter" << std::endl; obj->active_surface_ = surface; } @@ -82,11 +85,14 @@ void Keyboard::handle_enter(void *data, * @param surface A pointer to the wl_surface object. */ void Keyboard::handle_leave(void *data, - struct wl_keyboard * /* keyboard */, + struct wl_keyboard *keyboard, uint32_t /* serial */, struct wl_surface * /* surface */) { - std::cerr << "handle_leave" << std::endl; const auto obj = static_cast(data); + if (obj->keyboard_ != keyboard) { + return; + } + std::cerr << "handle_leave" << std::endl; obj->active_surface_ = nullptr; } @@ -97,11 +103,14 @@ void Keyboard::handle_leave(void *data, * This class provides functionality to handle keyboard events such as keymap changes. */ void Keyboard::handle_keymap(void *data, - struct wl_keyboard * /* keyboard */, + struct wl_keyboard *keyboard, uint32_t /* format */, int fd, uint32_t size) { const auto obj = static_cast(data); + if (obj->keyboard_ != keyboard) { + return; + } char *keymap_string = static_cast(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0)); xkb_keymap_unref(obj->keymap_); obj->keymap_ = xkb_keymap_new_from_string(obj->xkb_context_, keymap_string, @@ -126,12 +135,15 @@ void Keyboard::handle_keymap(void *data, * @param state The state of the key (pressed or released). */ void Keyboard::handle_key(void *data, - struct wl_keyboard * /* keyboard */, + struct wl_keyboard *keyboard, uint32_t /* serial */, uint32_t /* time */, uint32_t key, uint32_t state) { const auto obj = static_cast(data); + if (obj->keyboard_ != keyboard) { + return; + } if (!obj->xkb_state_) return; @@ -166,13 +178,16 @@ void Keyboard::handle_key(void *data, /***************************************************************************/ void Keyboard::handle_modifiers(void *data, - struct wl_keyboard * /* keyboard */, + struct wl_keyboard *keyboard, uint32_t /* serial */, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { const auto obj = static_cast(data); + if (obj->keyboard_ != keyboard) { + return; + } xkb_state_update_mask(obj->xkb_state_, mods_depressed, mods_latched, mods_locked, 0, 0, group); } @@ -212,10 +227,13 @@ gboolean Keyboard::handle_repeat(Keyboard *keyboard) { * @param delay The delay before key repeat starts in milliseconds. */ void Keyboard::handle_repeat_info(void *data, - struct wl_keyboard * /* wl_keyboard */, + struct wl_keyboard *keyboard, int32_t rate, int32_t delay) { const auto obj = static_cast(data); + if (obj->keyboard_ != keyboard) { + return; + } obj->key_timeout_id_ = g_timeout_add(static_cast(delay), reinterpret_cast(handle_repeat), obj); obj->key_repeat_rate_ = rate; obj->key_timeout_id_ = g_timeout_add(static_cast(delay), diff --git a/src/seat/pointer.cc b/src/seat/pointer.cc index 848e2d3..60c3cdd 100644 --- a/src/seat/pointer.cc +++ b/src/seat/pointer.cc @@ -27,15 +27,10 @@ * * The Pointer class is responsible for handling Wayland pointer events and managing the cursor. */ -Pointer::Pointer(struct wl_pointer *pointer, struct wl_shm *shm, struct wl_compositor *compositor, bool enable_cursor) : - pointer_(pointer), - shm_(shm), - enable_cursor_(enable_cursor) { +Pointer::Pointer(struct wl_pointer *pointer) : + pointer_(pointer) { + std::cout << "Pointer" << std::endl; wl_pointer_add_listener(pointer, &listener_, this); - if (enable_cursor_) { - cursor_ = std::make_unique(this, pointer_, shm_, compositor, enable_cursor_); - cursor_->enable(0, "basic"); - } } /** @@ -50,11 +45,7 @@ Pointer::Pointer(struct wl_pointer *pointer, struct wl_shm *shm, struct wl_compo * @param enable_cursor A boolean flag indicating whether to enable cursor. */ Pointer::~Pointer() { - if (cursor_) - cursor_.reset(); - wl_pointer_release(pointer_); - wl_pointer_destroy(pointer_); } /** @@ -65,12 +56,16 @@ Pointer::~Pointer() { * such as enter, leave, motion, button, axis, frame, axis source, axis stop, * and axis discrete events. */ -void Pointer::handle_enter(void * /* data */, - struct wl_pointer * /* pointer */, +void Pointer::handle_enter(void *data, + struct wl_pointer *pointer, uint32_t /* serial */, struct wl_surface * /* surface */, wl_fixed_t /* sx */, wl_fixed_t /* sy */) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_enter" << std::endl; } @@ -85,10 +80,14 @@ void Pointer::handle_enter(void * /* data */, * @param serial The serial number of the event. * @param surface The surface that the pointer left. */ -void Pointer::handle_leave(void * /* data */, - struct wl_pointer * /* pointer */, +void Pointer::handle_leave(void *data, + struct wl_pointer *pointer, uint32_t /* serial */, struct wl_surface * /* surface */) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_leave" << std::endl; } @@ -104,11 +103,15 @@ void Pointer::handle_leave(void * /* data */, * @param sx The X coordinate of the pointer's absolute position. * @param sy The Y coordinate of the pointer's absolute position. */ -void Pointer::handle_motion(void * /* data */, - struct wl_pointer * /* pointer */, +void Pointer::handle_motion(void *data, + struct wl_pointer *pointer, uint32_t /* time */, wl_fixed_t /* sx */, wl_fixed_t /* sy */) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_motion" << std::endl; } @@ -163,12 +166,16 @@ enum xdg_toplevel_resize_edge component_edge(const int width, const int height, * @param button The button that triggered the event * @param state The state of the button (pressed or released) */ -void Pointer::handle_button(void * /* data */, - struct wl_pointer * /* wl_pointer */, +void Pointer::handle_button(void *data, + struct wl_pointer *pointer, uint32_t /* serial */, uint32_t /* time */, uint32_t button, uint32_t state) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_button" << std::endl; if (button == BTN_LEFT) { if (state == WL_POINTER_BUTTON_STATE_PRESSED) { @@ -189,11 +196,15 @@ void Pointer::handle_button(void * /* data */, * * @details Prints "Pointer::handle_axis" to the standard error output. */ -void Pointer::handle_axis(void * /* data */, - struct wl_pointer * /* wl_pointer */, +void Pointer::handle_axis(void *data, + struct wl_pointer *pointer, uint32_t /* time */, uint32_t /* axis */, wl_fixed_t /* value */) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_axis" << std::endl; } @@ -205,8 +216,12 @@ void Pointer::handle_axis(void * /* data */, * @param data The user data associated with the pointer. * @param wl_pointer The pointer object. */ -void Pointer::handle_frame(void * /* data */, - struct wl_pointer * /* wl_pointer */) { +void Pointer::handle_frame(void *data, + struct wl_pointer *pointer) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_frame" << std::endl; } @@ -220,9 +235,13 @@ void Pointer::handle_frame(void * /* data */, * This function is called when the axis source event is received for the Pointer object. * It prints a message to the standard error stream. */ -void Pointer::handle_axis_source(void * /* data */, - struct wl_pointer * /* wl_pointer */, +void Pointer::handle_axis_source(void *data, + struct wl_pointer *pointer, uint32_t /* axis_source */) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_axis_source" << std::endl; } @@ -236,10 +255,14 @@ void Pointer::handle_axis_source(void * /* data */, * @param time The timestamp of the event. * @param axis The axis that stopped. */ -void Pointer::handle_axis_stop(void * /* data */, - struct wl_pointer * /* wl_pointer */, +void Pointer::handle_axis_stop(void *data, + struct wl_pointer *pointer, uint32_t /* time */, uint32_t /* axis */) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_axis_stop" << std::endl; } @@ -253,10 +276,14 @@ void Pointer::handle_axis_stop(void * /* data */, * @param axis The axis value. * @param discrete The discrete value. */ -void Pointer::handle_axis_discrete(void * /* data */, - struct wl_pointer * /* wl_pointer */, +void Pointer::handle_axis_discrete(void *data, + struct wl_pointer *pointer, uint32_t /* axis */, int32_t /* discrete */) { + auto obj = static_cast(data); + if (obj->pointer_ != pointer) { + return; + } std::cerr << "Pointer::handle_axis_discrete" << std::endl; } diff --git a/src/seat/pointer.h b/src/seat/pointer.h index 021a701..6103663 100644 --- a/src/seat/pointer.h +++ b/src/seat/pointer.h @@ -19,76 +19,65 @@ #include -#include "cursor.h" - -class Cursor; - class Pointer { public: - explicit Pointer(struct wl_pointer *pointer_, struct wl_shm *shm, struct wl_compositor *compositor, - bool enable_cursor = true); + explicit Pointer(struct wl_pointer *pointer_); ~Pointer(); - friend class Cursor; - private: struct wl_pointer *pointer_; - struct wl_shm *shm_; - - bool enable_cursor_{}; - std::unique_ptr cursor_; uint32_t serial_{}; [[nodiscard]] uint32_t get_serial() const { return serial_; } - static void handle_enter(void * /* data */, - struct wl_pointer * /* pointer */, - uint32_t /* serial */, - struct wl_surface * /* surface */, - wl_fixed_t /* sx */, - wl_fixed_t /* sy */); - - static void handle_leave(void * /* data */, - struct wl_pointer * /* pointer */, - uint32_t /* serial */, - struct wl_surface * /* surface */); - - static void handle_motion(void * /* data */, - struct wl_pointer * /* pointer */, - uint32_t /* time */, - wl_fixed_t /* sx */, - wl_fixed_t /* sy */); - - static void handle_button(void * /* data */, - struct wl_pointer * /* wl_pointer */, - uint32_t /* serial */, - uint32_t /* time */, - uint32_t /* button */, - uint32_t /* state */); - - static void handle_axis(void * /* data */, - struct wl_pointer * /* wl_pointer */, - uint32_t /* time */, - uint32_t /* axis */, - wl_fixed_t /* value */); - - static void handle_frame(void * /* data */, - struct wl_pointer * /* wl_pointer */); - - static void handle_axis_source(void * /* data */, - struct wl_pointer * /* wl_pointer */, - uint32_t /* axis_source */); - - static void handle_axis_stop(void * /* data */, - struct wl_pointer * /* wl_pointer */, - uint32_t /* time */, - uint32_t /* axis */); - - static void handle_axis_discrete(void * /* data */, - struct wl_pointer * /* wl_pointer */, - uint32_t /* axis */, - int32_t /* discrete */); + static void handle_enter(void *data, + struct wl_pointer *pointer, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t sx, + wl_fixed_t sy); + + static void handle_leave(void *data, + struct wl_pointer *pointer, + uint32_t serial, + struct wl_surface *surface); + + static void handle_motion(void *data, + struct wl_pointer *pointer, + uint32_t time, + wl_fixed_t sx, + wl_fixed_t sy); + + static void handle_button(void *data, + struct wl_pointer *wl_pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state); + + static void handle_axis(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value); + + static void handle_frame(void *data, + struct wl_pointer *wl_pointer); + + static void handle_axis_source(void *data, + struct wl_pointer *wl_pointer, + uint32_t axis_source); + + static void handle_axis_stop(void *data, + struct wl_pointer *wl_pointer, + uint32_t time, + uint32_t axis); + + static void handle_axis_discrete(void *data, + struct wl_pointer *wl_pointer, + uint32_t axis, + int32_t discrete); static const struct wl_pointer_listener listener_; }; diff --git a/src/seat/seat.cc b/src/seat/seat.cc index 617cff5..5354142 100644 --- a/src/seat/seat.cc +++ b/src/seat/seat.cc @@ -16,7 +16,7 @@ #include "seat.h" -#include +#include /** * @class Seat @@ -25,13 +25,8 @@ * The Seat class provides a representation of a seat in a Wayland compositor. It is used to handle input events from * devices such as keyboards, pointers, and touchscreens. */ -Seat::Seat(struct wl_seat *seat, struct wl_shm *shm, struct wl_compositor *compositor, bool enable_cursor, - uint32_t version) : +Seat::Seat(struct wl_seat *seat) : wl_seat_(seat), - wl_shm_(shm), - wl_compositor_(compositor), - enable_cursor_(enable_cursor), - version_(version), capabilities_() { wl_seat_add_listener(seat, &listener_, this); } @@ -47,12 +42,10 @@ void Seat::handle_capabilities(void *data, struct wl_seat *seat, uint32_t caps) { const auto obj = static_cast(data); - assert(obj->wl_seat_ == seat); obj->capabilities_ = caps; if (caps & WL_SEAT_CAPABILITY_POINTER && !obj->pointer_) { - obj->pointer_ = std::make_unique(wl_seat_get_pointer(seat), obj->wl_shm_, obj->wl_compositor_, - obj->enable_cursor_); + obj->pointer_ = std::make_unique(wl_seat_get_pointer(seat)); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && obj->pointer_) { obj->pointer_.reset(); } @@ -85,8 +78,11 @@ void Seat::handle_name(void *data, struct wl_seat *seat, const char *name) { const auto obj = static_cast(data); - assert(obj->wl_seat_ == seat); + if (obj->wl_seat_ != seat) { + return; + } obj->name_ = name; + std::cout << "Seat: " << obj->name_ << std::endl; } const struct wl_seat_listener Seat::listener_ = { diff --git a/src/seat/seat.h b/src/seat/seat.h index 7a3928f..2e2be36 100644 --- a/src/seat/seat.h +++ b/src/seat/seat.h @@ -34,8 +34,7 @@ class Touch; class Seat { public: - explicit Seat(struct wl_seat *seat, struct wl_shm *shm, struct wl_compositor *compositor, bool enable_cursor, - uint32_t version); + explicit Seat(struct wl_seat *seat); [[nodiscard]] struct wl_seat *get_seat() const { return wl_seat_; }; @@ -43,14 +42,8 @@ class Seat { [[nodiscard]] const std::string &get_name() const { return name_; }; - [[nodiscard]] const uint32_t get_version() const { return version_; } - private: struct wl_seat *wl_seat_; - struct wl_shm *wl_shm_; - struct wl_compositor *wl_compositor_; - bool enable_cursor_; - uint32_t version_; uint32_t capabilities_; std::string name_; @@ -58,13 +51,13 @@ class Seat { std::unique_ptr pointer_; std::unique_ptr touch_; - static void handle_capabilities(void * /* data */, - struct wl_seat * /* seat */, - uint32_t /* caps */); + static void handle_capabilities(void *data, + struct wl_seat *seat, + uint32_t caps); - static void handle_name(void * /* data */, - struct wl_seat * /* seat */, - const char * /* name */); + static void handle_name(void *data, + struct wl_seat *seat, + const char *name); static const struct wl_seat_listener listener_; }; diff --git a/src/seat/touch.cc b/src/seat/touch.cc index efdb8e2..076137c 100644 --- a/src/seat/touch.cc +++ b/src/seat/touch.cc @@ -23,8 +23,9 @@ * * The Touch class represents a touch input device. */ -Touch::Touch(struct wl_touch *touch) : touch_(touch) { - wl_touch_add_listener(touch, &listener_, this); +Touch::Touch(struct wl_touch *wl_touch) : touch_(wl_touch) { + std::cout << "Touch" << std::endl; + wl_touch_add_listener(wl_touch, &listener_, this); } /** @@ -34,7 +35,6 @@ Touch::Touch(struct wl_touch *touch) : touch_(touch) { */ Touch::~Touch() { wl_touch_release(touch_); - wl_touch_destroy(touch_); } /** @@ -51,14 +51,18 @@ Touch::~Touch() { * @param x_w The X coordinate of the touch point in wl_fixed_t format. * @param y_w The Y coordinate of the touch point in wl_fixed_t format. */ -void Touch::handle_down(void * /* data */, - struct wl_touch * /* wl_touch */, +void Touch::handle_down(void *data, + struct wl_touch *touch, uint32_t /* serial */, uint32_t /* time */, struct wl_surface * /* surface */, int32_t /* id */, wl_fixed_t /* x_w */, wl_fixed_t /* y_w */) { + const auto obj = static_cast(data); + if (obj->touch_ != touch) { + return; + } std::cerr << "Touch::handle_down" << std::endl; } @@ -75,11 +79,15 @@ void Touch::handle_down(void * /* data */, * * @return None. */ -void Touch::handle_up(void * /* data */, - struct wl_touch * /* wl_touch */, +void Touch::handle_up(void *data, + struct wl_touch *touch, uint32_t /* serial */, uint32_t /* time */, int32_t /* id */) { + const auto obj = static_cast(data); + if (obj->touch_ != touch) { + return; + } std::cerr << "Touch::handle_up" << std::endl; } @@ -98,12 +106,16 @@ void Touch::handle_up(void * /* data */, * * @return None. */ -void Touch::handle_motion(void * /* data */, - struct wl_touch * /* wl_touch */, +void Touch::handle_motion(void *data, + struct wl_touch *touch, uint32_t /* time */, int32_t /* id */, wl_fixed_t /* x_w */, wl_fixed_t /* y_w */) { + const auto obj = static_cast(data); + if (obj->touch_ != touch) { + return; + } std::cerr << "Touch::handle_motion" << std::endl; } @@ -118,7 +130,11 @@ void Touch::handle_motion(void * /* data */, * * @return void */ -void Touch::handle_cancel(void * /* data */, struct wl_touch * /* wl_touch */) { +void Touch::handle_cancel(void *data, struct wl_touch *touch) { + const auto obj = static_cast(data); + if (obj->touch_ != touch) { + return; + } std::cerr << "Touch::handle_cancel" << std::endl; } @@ -128,8 +144,12 @@ void Touch::handle_cancel(void * /* data */, struct wl_touch * /* wl_touch */) { * * It handles touch events from a wl_touch object and provides callback functions for various touch events. */ -void Touch::handle_frame(void * /* data */, - struct wl_touch * /* wl_touch */) { +void Touch::handle_frame(void *data, + struct wl_touch *touch) { + const auto obj = static_cast(data); + if (obj->touch_ != touch) { + return; + } std::cerr << "Touch::handle_frame" << std::endl; } @@ -139,4 +159,4 @@ const struct wl_touch_listener Touch::listener_ = { .motion = handle_motion, .frame = handle_frame, .cancel = handle_cancel, -}; \ No newline at end of file +}; diff --git a/src/seat/touch.h b/src/seat/touch.h index e23fc8e..701a6f9 100644 --- a/src/seat/touch.h +++ b/src/seat/touch.h @@ -23,7 +23,7 @@ class Touch { public: - explicit Touch(struct wl_touch *touch); + explicit Touch(struct wl_touch *wl_touch); ~Touch(); @@ -31,31 +31,31 @@ class Touch { struct wl_touch *touch_; static void handle_down(void *data, - struct wl_touch * /* wl_touch */, - uint32_t /* serial */, - uint32_t /* time */, + struct wl_touch *wl_touch, + uint32_t serial, + uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); static void handle_up(void *data, - struct wl_touch * /* wl_touch */, - uint32_t /* serial */, - uint32_t /* time */, + struct wl_touch *wl_touch, + uint32_t serial, + uint32_t time, int32_t id); static void handle_motion(void *data, - struct wl_touch * /* wl_touch */, - uint32_t /* time */, + struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); - static void handle_cancel(void *data, struct wl_touch * /* wl_touch */); + static void handle_cancel(void *data, struct wl_touch *wl_touch); - static void handle_frame(void * /* data */, - struct wl_touch * /* wl_touch */); + static void handle_frame(void *data, + struct wl_touch *wl_touch); static const struct wl_touch_listener listener_; }; diff --git a/src/window/egl.cc b/src/window/egl.cc index 3ada1e5..b2b8ade 100644 --- a/src/window/egl.cc +++ b/src/window/egl.cc @@ -18,7 +18,6 @@ #include -#include #include @@ -28,62 +27,50 @@ * This class provides functionality for initializing EGL, choosing an EGL configuration, * creating an EGL context, and managing various EGL extensions. */ -Egl::Egl(struct wl_display *display) { - dpy_ = eglGetDisplay(display); +Egl::Egl(struct wl_display *display, const int32_t *context_attribs, size_t context_attribs_size, + const int32_t *config_attribs, size_t config_attribs_size, + int min_buffer_size) : + dpy_(eglGetDisplay(display)), + context_attribs_(context_attribs, context_attribs + context_attribs_size), + config_attribs_(config_attribs, config_attribs + config_attribs_size), + min_buffer_size_(min_buffer_size) { EGLBoolean ret = eglInitialize(dpy_, &major_, &minor_); if (ret == EGL_FALSE) { throw std::runtime_error("eglInitialize failed."); } + ret = eglBindAPI(EGL_OPENGL_ES_API); - if (ret == EGL_FALSE) { + if (ret != EGL_TRUE) { throw std::runtime_error("eglBindAPI failed."); } EGLint count; - ret = eglGetConfigs(dpy_, nullptr, 0, &count); - if (ret == EGL_FALSE) { - throw std::runtime_error("eglGetConfigs failed."); - } + eglGetConfigs(dpy_, nullptr, 0, &count); + std::cout << "EGL has " << count << " configs" << std::endl; - auto *configs = static_cast( + auto *configs = reinterpret_cast( calloc(static_cast(count), sizeof(EGLConfig))); EGLint n; - ret = eglChooseConfig(dpy_, kEglConfigAttribs.data(), configs, count, &n); - if (ret == EGL_FALSE) { - throw std::runtime_error("eglChooseConfig failed"); - } + ret = eglChooseConfig(dpy_, config_attribs_.data(), configs, count, &n); EGLint size; for (EGLint i = 0; i < n; i++) { - ret = eglGetConfigAttrib(dpy_, configs[i], EGL_BUFFER_SIZE, &size); - if (ret == EGL_FALSE) { - throw std::runtime_error("eglGetConfigAttrib failed"); - } - if (buffer_size_ <= size) { - std::memcpy(&config_, &configs[i], sizeof(EGLConfig)); + eglGetConfigAttrib(dpy_, configs[i], EGL_BUFFER_SIZE, &size); + std::cout << "Buffer size for config " << i << " is " << size << std::endl; + if (size >= min_buffer_size_) { + memcpy(&config_, &configs[i], sizeof(EGLConfig)); break; } } free(configs); + if (config_ == nullptr) { + std::cerr << "did not find config with buffer size " << min_buffer_size << std::endl; + } context_ = eglCreateContext(dpy_, config_, EGL_NO_CONTEXT, - kEglContextAttribs.data()); - - resource_context_ = - eglCreateContext(dpy_, config_, context_, kEglContextAttribs.data()); - - texture_context_ = - eglCreateContext(dpy_, config_, context_, kEglContextAttribs.data()); - - // frame callback requires non-blocking -#if 0 // Failing - ret = eglSwapInterval(dpy_, 0); - if (ret == EGL_FALSE) { - throw std::runtime_error("eglSwapInterval failed"); - } -#endif + context_attribs_.data()); (void) make_current(); @@ -167,37 +154,6 @@ bool Egl::swap_buffers() const { return true; } -/** - * @brief Checks if the resource context is currently active and makes it current if not. - * - * This function checks if the resource context is currently active using eglGetCurrentContext(). - * If the current context is not equal to the resource context, it calls eglMakeCurrent() to make - * the resource context current. Finally, it returns true to indicate that the operation was successful. - * - * @return true if the resource context is successfully made current, false otherwise. - */ -bool Egl::make_resource_current() const { - if (eglGetCurrentContext() != context_) { - eglMakeCurrent(dpy_, EGL_NO_SURFACE, EGL_NO_SURFACE, resource_context_); - } - return true; -} - -/** - * @brief Checks if the texture context is the current context and makes it the current context if not. - * - * This function checks if the texture context is the current context using eglGetCurrentContext() function. If it is not, - * it calls eglMakeCurrent() function to set the texture context as the current context. - * - * @return true if the texture context is made the current context or if it is already the current context, false otherwise. - */ -bool Egl::make_texture_current() const { - if (eglGetCurrentContext() != texture_context_) { - eglMakeCurrent(dpy_, EGL_NO_SURFACE, EGL_NO_SURFACE, texture_context_); - } - return true; -} - /** * @brief Checks if a given EGL extension is supported. * diff --git a/src/window/egl.h b/src/window/egl.h index ddaa287..b922d7c 100644 --- a/src/window/egl.h +++ b/src/window/egl.h @@ -17,14 +17,16 @@ #ifndef SRC_WINDOW_EGL_H_ #define SRC_WINDOW_EGL_H_ -#include +#include +#include #include #include class Egl { public: - explicit Egl(struct wl_display *display); + explicit Egl(struct wl_display *display, const int32_t *context_attribs, size_t context_attribs_size, + const int32_t *config_attribs, size_t config_attribs_size, int min_buffer_size); ~Egl(); @@ -34,65 +36,28 @@ class Egl { [[nodiscard]] bool swap_buffers() const; - [[nodiscard]] bool make_resource_current() const; - - [[nodiscard]] bool make_texture_current() const; - - [[nodiscard]] PFNEGLSETDAMAGEREGIONKHRPROC get_set_damage_region() const { - return pfSetDamageRegion_; - } - - [[nodiscard]] PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC - get_swap_buffers_with_damage() const { - return pfSwapBufferWithDamage_; - } - [[maybe_unused]] [[nodiscard]] bool has_ext_buffer_age() const { return has_egl_ext_buffer_age_; } [[maybe_unused]] EGLDisplay get_display() { return dpy_; } - [[maybe_unused]] EGLContext get_texture_context() { return texture_context_; } + // Disallow copy and assign. + Egl(const Egl &) = delete; + + Egl &operator=(const Egl &) = delete; friend class WindowEgl; private: - static constexpr std::array kEglContextAttribs = { - { - EGL_CONTEXT_MAJOR_VERSION, 3, - EGL_CONTEXT_MAJOR_VERSION, 2, - EGL_NONE - } - }; - - static constexpr std::array kEglConfigAttribs = { - { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_STENCIL_SIZE, 8, - EGL_DEPTH_SIZE, 16, - EGL_SAMPLE_BUFFERS, 1, - EGL_SAMPLES, 4, - EGL_NONE // termination sentinel - } - }; + EGLDisplay dpy_; + std::vector context_attribs_; + std::vector config_attribs_; + int min_buffer_size_; EGLSurface egl_surface_{}; EGLConfig config_{}; - EGLContext texture_context_{}; - - int buffer_size_ = 24; - - EGLDisplay dpy_{}; EGLContext context_{}; - EGLContext resource_context_{}; - EGLint major_{}; - EGLint minor_{}; + EGLint major_{}, minor_{}; PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC pfSwapBufferWithDamage_{}; PFNEGLSETDAMAGEREGIONKHRPROC pfSetDamageRegion_{}; diff --git a/src/window/surface.cc b/src/window/surface.cc new file mode 100644 index 0000000..1d2b3e8 --- /dev/null +++ b/src/window/surface.cc @@ -0,0 +1,95 @@ +/* + * Copyright 2024 Joel Winarske + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "surface.h" + +#include +#include + +Surface::Surface(struct wl_compositor *wl_compositor) { + assert(wl_compositor); + surface_ = wl_compositor_create_surface(wl_compositor); + wl_surface_add_listener(surface_, &listener_, this); +} + +Surface::~Surface() { + if (surface_) { + wl_surface_destroy(surface_); + } +} + +void Surface::handle_surface_enter(void *data, + struct wl_surface *surface, + struct wl_output *output) { + auto obj = static_cast(data); + if (obj->surface_ != surface) { + return; + } + std::cout << "handle_surface_enter: " << output << std::endl; + obj->output_ = output; +} + +void Surface::handle_surface_leave(void *data, + struct wl_surface *surface, + struct wl_output *output) { + auto obj = static_cast(data); + if (obj->surface_ != surface) { + return; + } + std::cout << "handle_surface_leave: " << output << std::endl; + obj->output_ = nullptr; +} + +#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) + +void Surface::handle_preferred_buffer_scale(void *data, + struct wl_surface *surface, + int32_t factor) { + auto obj = static_cast(data); + if (obj->surface_ != surface) { + return; + } + std::cout << "handle_preferred_buffer_scale: " << factor << std::endl; + obj->preferred_buffer_scale_ = factor; +} + +#endif + +#if defined(WL_SURFACE_PREFERRED_BUFFER_TRANSFORM_SINCE_VERSION) + +void Surface::handle_preferred_buffer_transform(void *data, + struct wl_surface *surface, + uint32_t transform) { + auto obj = static_cast(data); + if (obj->surface_ != surface) { + return; + } + std::cout << "handle_preferred_buffer_transform: " << transform << std::endl; + obj->preferred_buffer_transform_ = transform; +} + +#endif + +const struct wl_surface_listener Surface::listener_ = { + .enter = handle_surface_enter, + .leave = handle_surface_leave, +#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) + .preferred_buffer_scale = handle_preferred_buffer_scale, +#endif +#if defined(WL_SURFACE_PREFERRED_BUFFER_TRANSFORM_SINCE_VERSION) + .preferred_buffer_transform = handle_preferred_buffer_transform, +#endif +}; diff --git a/src/window/surface.h b/src/window/surface.h new file mode 100644 index 0000000..3955d9f --- /dev/null +++ b/src/window/surface.h @@ -0,0 +1,71 @@ +/* + * Copyright 2024 Joel Winarske + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_WINDOW_SURFACE_H_ +#define SRC_WINDOW_SURFACE_H_ + +#include +#include + +#include + + +class Surface { +public: + [[maybe_unused]] explicit Surface(struct wl_compositor *compositor); + + ~Surface(); + + [[nodiscard]] struct wl_surface *get_surface() const { return surface_; } + + // Disallow copy and assign. + Surface(const Surface &) = delete; + + Surface &operator=(const Surface &) = delete; + +private: + struct wl_surface *surface_{}; + struct wl_output *output_{}; + std::optional preferred_buffer_scale_{}; + std::optional preferred_buffer_transform_{}; + + static void handle_surface_enter(void *data, + struct wl_surface *surface, + struct wl_output *output); + + static void handle_surface_leave(void *data, + struct wl_surface *surface, + struct wl_output *output); + +#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) + + static void handle_preferred_buffer_scale(void *data, + struct wl_surface *wl_surface, + int32_t factor); + +#endif + +#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) + + static void handle_preferred_buffer_transform(void *data, + struct wl_surface *wl_surface, + uint32_t transform); + +#endif + static const struct wl_surface_listener listener_; +}; + +#endif // SRC_WINDOW_SURFACE_H_ \ No newline at end of file diff --git a/src/window/window.cc b/src/window/window.cc index 8dd1ff9..e92a28e 100644 --- a/src/window/window.cc +++ b/src/window/window.cc @@ -26,9 +26,8 @@ * methods for creating and managing a surface, as well as starting and * stopping frame updates. */ -Window::Window(struct wl_compositor *compositor, ShellType shell_type, +Window::Window(struct wl_compositor *compositor, const std::function &draw_callback) : - shell_type_(shell_type), draw_callback_(draw_callback) { wl_surface_ = wl_compositor_create_surface(compositor); start_frames(); diff --git a/src/window/window.h b/src/window/window.h index b388f4f..c38f7df 100644 --- a/src/window/window.h +++ b/src/window/window.h @@ -20,35 +20,33 @@ #include #include +#include "window_manager/window_manager.h" class Display; +class WindowManager; + class Window { public: typedef enum { - AGL, - IVI, - XDG, - NONE, - } ShellType; + EGL, + VULKAN, + } WindowType; - explicit Window(struct wl_compositor *compositor, ShellType shell_type = XDG, + explicit Window(struct wl_compositor *compositor, const std::function &draw_callback = nullptr); virtual ~Window() = 0; - friend class WindowEgl; - - friend class WindowVulkan; + // Disallow copy and assign. + Window(const Window &) = delete; - friend class WindowManager; + Window &operator=(const Window &) = delete; private: struct wl_surface *wl_surface_{}; struct wl_callback *wl_callback_{}; - ShellType shell_type_; - std::function draw_callback_; void start_frames(); diff --git a/src/window/window_egl.cc b/src/window/window_egl.cc index f1eb81f..079684d 100644 --- a/src/window/window_egl.cc +++ b/src/window/window_egl.cc @@ -27,19 +27,21 @@ * This class is responsible for managing an EGL window surface for rendering on a Wayland compositor. It creates an EGL window surface and initializes the EGL context. It also provides * a callback for rendering the window contents. */ -WindowEgl::WindowEgl(struct wl_display *display, struct wl_compositor * /* compositor */, struct wl_surface *surface, - int width, int height, - Window::ShellType /* shell_type */, - const std::function & /* draw_callback */) : - Egl(display) { +WindowEgl::WindowEgl(struct wl_display *wl_display, struct wl_compositor *wl_compositor, + int width, int height, const int32_t *context_attribs, size_t context_attribs_size, + const int32_t *config_attribs, size_t config_attribs_size, int min_buffer_size, + FrameCallback frame_callback) : + Surface(wl_compositor), + Egl(wl_display, context_attribs, context_attribs_size, config_attribs, config_attribs_size, min_buffer_size), + frame_callback_(frame_callback), + wl_surface_(get_surface()) { - std::cout << "width: " << width << std::endl; - std::cout << "height: " << height << std::endl; + std::cout << "Window EGL: " << width << "x" << height << std::endl; if (egl_window_) { wl_egl_window_destroy(egl_window_); } - egl_window_ = wl_egl_window_create(surface, width, height); + egl_window_ = wl_egl_window_create(wl_surface_, width, height); if (egl_surface_) { eglDestroySurface(dpy_, egl_surface_); @@ -55,6 +57,16 @@ WindowEgl::WindowEgl(struct wl_display *display, struct wl_compositor * /* compo egl_surface_ = eglCreateWindowSurface( dpy_, config_, reinterpret_cast(egl_window_), nullptr); } + + (void) make_current(); + + // frame callback requires non-blocking + EGLBoolean ret = eglSwapInterval(dpy_, 0); + if (ret == EGL_FALSE) { + throw std::runtime_error("eglSwapInterval failed"); + } + + on_frame(this, nullptr, 0); } /** @@ -71,3 +83,24 @@ WindowEgl::~WindowEgl() { wl_egl_window_destroy(egl_window_); } } + +void WindowEgl::on_frame(WindowEgl *obj, wl_callback *callback, uint32_t time) { + + obj->callback_ = nullptr; + + if (callback) { + wl_callback_destroy(callback); + } + + obj->frame_callback_(obj, time); + + obj->callback_ = wl_surface_frame(obj->get_surface()); + wl_callback_add_listener(obj->callback_, &WindowEgl::frame_listener_, + obj); + + wl_surface_commit(obj->get_surface()); +} + +const wl_callback_listener WindowEgl::frame_listener_ = { + .done = reinterpret_cast(on_frame) +}; diff --git a/src/window/window_egl.h b/src/window/window_egl.h index 6cd8502..e0c0ccd 100644 --- a/src/window/window_egl.h +++ b/src/window/window_egl.h @@ -17,22 +17,40 @@ #ifndef SRC_WINDOW_WINDOW_EGL_H_ #define SRC_WINDOW_WINDOW_EGL_H_ -#include "window.h" +#include "surface.h" #include "egl.h" -class WindowEgl : public Egl { +#include "wayland-client-protocol.h" + +class WindowManager; + +class WindowEgl : public Surface, public Egl { public: - explicit WindowEgl(struct wl_display *display, struct wl_compositor *compositor, struct wl_surface *surface, - int width, int height, - Window::ShellType shell_type = Window::ShellType::XDG, - const std::function &draw_callback = nullptr); + typedef void (*FrameCallback)( + void *userdata, + uint32_t time); + + explicit WindowEgl(struct wl_display *display, struct wl_compositor *compositor, + int width, int height, const int32_t *context_attribs, size_t context_attribs_size, + const int32_t *config_attribs, size_t config_attribs_size, int min_buffer_size, + FrameCallback frame_callback); ~WindowEgl(); - friend class Egl; + // Disallow copy and assign. + WindowEgl(const WindowEgl &) = delete; + + WindowEgl &operator=(const WindowEgl &) = delete; private: struct wl_egl_window *egl_window_{}; + struct wl_surface *wl_surface_; + FrameCallback frame_callback_; + struct wl_callback *callback_; + + static void on_frame(WindowEgl *data, wl_callback *callback, uint32_t time); + + static const wl_callback_listener frame_listener_; }; #endif // SRC_WINDOW_WINDOW_EGL_H_ \ No newline at end of file diff --git a/src/window/window_vulkan.cc b/src/window/window_vulkan.cc index 63fa465..8458b77 100644 --- a/src/window/window_vulkan.cc +++ b/src/window/window_vulkan.cc @@ -24,9 +24,8 @@ * It provides functionality to create and manage a Vulkan surface. */ WindowVulkan::WindowVulkan(struct wl_display * /* display */, struct wl_compositor *compositor, int /* width */, - int /* height */, - Window::ShellType shell_type, const std::function &draw_callback) : - Window(compositor, shell_type, draw_callback) { + int /* height */, const std::function &draw_callback) : + Window(compositor, draw_callback) { } /** diff --git a/src/window/window_vulkan.h b/src/window/window_vulkan.h index 77abe1d..041f1e9 100644 --- a/src/window/window_vulkan.h +++ b/src/window/window_vulkan.h @@ -23,11 +23,15 @@ class WindowVulkan : public Window { public: explicit WindowVulkan(struct wl_display *display, struct wl_compositor *compositor, int width, int height, - ShellType shell_type = XDG, const std::function &draw_callback = nullptr); ~WindowVulkan() override; + // Disallow copy and assign. + WindowVulkan(const WindowVulkan &) = delete; + + WindowVulkan &operator=(const WindowVulkan &) = delete; + private: }; diff --git a/src/window_manager/display.cc b/src/window_manager/display.cc deleted file mode 100644 index 1496bb3..0000000 --- a/src/window_manager/display.cc +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2024 Joel Winarske - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "display.h" -#include "output.h" - -#include -#include -#include - - -/** - * @class Display - * Represents a Wayland display connection. - */ -Display::Display(GMainContext *context, bool enable_cursor, const char *name) : - wl_display_(wl_display_connect(name)), - context_(context), - enable_cursor_(enable_cursor) { - if (wl_display_ == nullptr) { - std::cerr << "Failed to connect to Wayland display. " << strerror(errno) << std::endl; - exit(EXIT_FAILURE); - } - wl_registry_ = wl_display_get_registry(wl_display_); - wl_registry_add_listener(wl_registry_, &listener_, this); - wl_display_roundtrip(wl_display_); -} - -/** - * @brief Destructor for the Display class. - * - * This destructor destroys the wl_registry_, wl_shm_, wl_subcompositor_, and wl_compositor_ objects if they exist. - * - * @note The Display::Display() function sets the pointer values of wl_registry_, wl_shm_, wl_subcompositor_, and wl_compositor_ to valid instances of their respective structures. This -* destructor handles the cleanup of these instances. - * - * @see Display(), wl_registry_destroy(), wl_shm_destroy(), wl_subcompositor_destroy(), wl_compositor_destroy() - */ -Display::~Display() { - if (wl_registry_) { - wl_registry_destroy(wl_registry_); - } - - if (wl_shm_) { - wl_shm_destroy(wl_shm_); - } - - if (wl_subcompositor_) { - wl_subcompositor_destroy(wl_subcompositor_); - } - - if (wl_compositor_) { - wl_compositor_destroy(wl_compositor_); - } -} - -/** - * @class Display - * @brief The Display class represents a Wayland display. - * - * This class provides functionality for interacting with a Wayland display, - * such as managing various objects and handling events. - */ -void Display::shm_format(void *data, - struct wl_shm * /* wl_shm */, - uint32_t format) { - const auto obj = static_cast(data); - if (format == WL_SHM_FORMAT_XRGB8888) { - obj->has_xrgb_ = true; - } -} - -const struct wl_shm_listener Display::shm_listener_ = { - .format = shm_format -}; - -/** - * @brief Handles the global objects registered with the Wayland display. - * - * This method is called when a global object is added to the display registry. It checks - * the interface of the object and binds it to the appropriate Wayland client-side object. - * - * @param data A pointer to the Display object. - * @param registry The Wayland registry object. - * @param name The name of the global object. - * @param interface The interface name of the global object. - * @param version The version of the global object. - */ -void Display::registry_handle_global(void *data, - struct wl_registry *registry, - uint32_t name, - const char *interface, - uint32_t version) { - const auto obj = static_cast(data); - - if (strcmp(interface, wl_compositor_interface.name) == 0) { - obj->compositor_version_ = version; - obj->wl_compositor_ = static_cast( - wl_registry_bind(registry, name, &wl_compositor_interface, - std::min(static_cast(1), version))); - obj->buffer_scaling_enabled_ = (obj->compositor_version_ >= 3); - - } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { - obj->subcompositor_version_ = version; - obj->wl_subcompositor_ = static_cast( - wl_registry_bind(registry, name, &wl_subcompositor_interface, - std::min(static_cast(1), version))); - - } else if (strcmp(interface, wl_shm_interface.name) == 0) { - obj->wl_shm_ = static_cast( - wl_registry_bind(registry, name, &wl_shm_interface, - std::min(static_cast(1), version))); - wl_shm_add_listener(obj->wl_shm_, &shm_listener_, obj); - - } else if (strcmp(interface, wl_output_interface.name) == 0) { - auto output = static_cast( - wl_registry_bind(registry, name, &wl_output_interface, - std::min(static_cast(2), version))); - obj->wl_outputs_[output] = std::make_unique(output, version); - - } else if (strcmp(interface, wl_seat_interface.name) == 0) { - auto seat = static_cast( - wl_registry_bind(registry, name, &wl_seat_interface, - std::min(static_cast(5), version))); - obj->wl_seats_[seat] = std::make_unique(seat, obj->wl_shm_, obj->wl_compositor_, obj->enable_cursor_, - version); - } - - for (const auto &callback: obj->callbacks_) { - callback.first(callback.second, registry, name, interface, version); - } -} - -/** - * @brief Handles the removal of global objects from the registry. - * - * This function is invoked when a global object is removed from the registry. - * - * @param data Pointer to user data (unused). - * @param reg Pointer to the registry object. - * @param id The ID of the removed global object. - * - * @return None. - */ -void Display::registry_handle_global_remove(void * /* data */, - struct wl_registry * /* reg */, - uint32_t /* id */) { -} - -const struct wl_registry_listener Display::listener_ = { - registry_handle_global, - registry_handle_global_remove, -}; - -/** - * @brief Adds a registrar callback with associated data. - * - * This function allows you to register a callback function that will be called - * when a new global object is added to the Wayland registry. The callback - * function will be provided with the data parameter, the registry, the name of - * the interface, and the version of the interface. - * - * @param callback The callback function to register. - * @param data The data to associate with the callback. - * - * @see wl_registry_add_listener - * @see struct wl_registry_listener - */ -void Display::add_registrar_callback(const std::function &callback, void *data) { - callbacks_.emplace_back(std::move(std::make_pair(callback, data))); -} diff --git a/src/window_manager/display.h b/src/window_manager/display.h deleted file mode 100644 index 3ea448c..0000000 --- a/src/window_manager/display.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2024 Joel Winarske - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SRC_DISPLAY_H_ -#define SRC_DISPLAY_H_ - -#include -#include -#include -#include -#include - -#include -#include - -#include "output.h" -#include "seat/seat.h" - -class Output; - -class Seat; - -class Display { -public: - explicit Display(GMainContext *context = nullptr, bool enable_cursor = true, const char *name = nullptr); - - ~Display(); - - [[nodiscard]] struct wl_display *get_display() const { return wl_display_; } - - [[nodiscard]] const std::map> &get_seats() const { return wl_seats_; } - - [[nodiscard]] const std::map> & - get_outputs() const { return wl_outputs_; } - - struct wl_compositor *get_compositor() { return wl_compositor_; } - - void add_registrar_callback(const std::function &callback, void *data); - - friend class Cursor; - - friend class Window; - - friend class WindowEgl; - - friend class WindowVulkan; - - friend class WindowManager; - -private: - struct wl_compositor *wl_compositor_{}; - uint32_t compositor_version_{}; - struct wl_subcompositor *wl_subcompositor_{}; - uint32_t subcompositor_version_{}; - struct wl_display *wl_display_{}; - struct wl_registry *wl_registry_{}; - struct wl_shm *wl_shm_{}; - - GMainContext *context_; - bool enable_cursor_; - - std::map> wl_outputs_; - std::map> wl_seats_; - - bool has_xrgb_{}; - std::optional buffer_scaling_enabled_; - - std::vector, void * /* data */>> callbacks_; - - struct wl_compositor *get_compositor() const { return wl_compositor_; } - - static void registry_handle_global(void *data, - struct wl_registry *registry, - uint32_t name, - const char *interface, - uint32_t version); - - static void registry_handle_global_remove(void *data, - struct wl_registry *reg, - uint32_t id); - - static const struct wl_registry_listener listener_; - - static void shm_format(void * /* data */, - struct wl_shm * /* wl_shm */, - uint32_t /* format */); - - static const struct wl_shm_listener shm_listener_; - -}; - - -#endif //SRC_DISPLAY_H_ \ No newline at end of file diff --git a/src/window_manager/output.cc b/src/window_manager/output.cc index 28e6a0b..3c6343a 100644 --- a/src/window_manager/output.cc +++ b/src/window_manager/output.cc @@ -16,9 +16,9 @@ #include "output.h" -#include +#include -#include +#include /** * @class Output @@ -28,40 +28,12 @@ * access to the output's properties such as geometry and mode. It also handles * the events emitted by the output. */ -Output::Output(struct wl_output *output, uint32_t version) : version_( - version), wl_output_(output) { +Output::Output(struct wl_output *wl_output) : wl_output_(wl_output) { wl_output_add_listener(wl_output_, &listener_, this); } -/** - * @class Output - * @brief The Output class represents a Wayland output. - * - * The Output class provides methods to manage Wayland outputs, such as releasing and destroying the output. - */ -Output::~Output() { - wl_output_release(wl_output_); - wl_output_destroy(wl_output_); -} +Output::~Output() = default; -/** - * @brief Handle the geometry event of the wl_output interface. - * - * This function is called when the wl_output interface emits the - * geometry event, indicating changes in the output's position, size, - * and physical properties. - * - * @param data Pointer to the Output object. - * @param wl_output The wl_output object. - * @param x The x coordinate of the output's position. - * @param y The y coordinate of the output's position. - * @param physical_width The physical width of the output in millimeters. - * @param physical_height The physical height of the output in millimeters. - * @param subpixel The subpixel arrangement of the output. - * @param make The make of the output device. - * @param model The model of the output device. - * @param transform The transform applied to the output. - */ void Output::handle_geometry(void *data, struct wl_output *wl_output, int x, @@ -72,127 +44,132 @@ void Output::handle_geometry(void *data, const char *make, const char *model, int transform) { - const auto obj = static_cast(data); - assert(obj->wl_output_ == wl_output); - obj->output_ = { - .geometry = { - .x = x, - .y = y, - .physical_width = physical_width, - .physical_height = physical_height, - .subpixel = subpixel, - .make = make, - .model = model, - .transform = transform - }, - .mode = {}, - .done{}, - .name{}, - .description{}, + auto obj = static_cast(data); + if (obj->wl_output_ != wl_output) { + return; + } + obj->outputs_[wl_output].geometry = { + .x = x, + .y = y, + .physical_width = physical_width, + .physical_height = physical_height, + .subpixel = subpixel, + .make = make, + .model = model, + .transform = transform, }; } -/** -* @brief This function is responsible for handling the mode of the output. -* -* The handle_mode function is called when the mode of the output is updated. It sets the mode of the output -* structure to the provided values. -* -* @param data A pointer to the instance of the Output class. -* @param wl_output A pointer to the wl_output structure. -* @param flags The flags of the mode. -* @param width The width of the mode. -* @param height The height of the mode. -* @param refresh The refresh rate of the mode. -*/ void Output::handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int width, int height, int refresh) { - const auto obj = static_cast(data); - assert(obj->wl_output_ == wl_output); - obj->output_.mode = { + auto obj = static_cast(data); + if (obj->wl_output_ != wl_output) { + return; + } + obj->outputs_[wl_output].mode = { .flags = flags, .width = width, .height = height, - .refresh = refresh, + .refresh = refresh }; } -/** - * @brief Handle the completion of an output event. - * - * This function is a callback that is invoked when an output event is completed. - * - * @param data A pointer to the associated Output object. - * @param wl_output The Wayland output object. - */ -void Output::handle_done(void *data, struct wl_output *wl_output) { - const auto obj = static_cast(data); - assert(obj->wl_output_ == wl_output); - obj->output_.done = true; -} +#if defined(WL_OUTPUT_SCALE_SINCE_VERSION) -/** - * @brief Callback function for handling output scale change. - * - * This function is called when the scale of the output is changed. - * It updates the scale value of the Output object. - * - * @param data The user data associated with the Output object. - * @param wl_output The wl_output object associated with the event. - * @param scale The new scale value. - */ void Output::handle_scale(void *data, struct wl_output *wl_output, - int scale) { - const auto obj = static_cast(data); - assert(obj->wl_output_ == wl_output); - obj->output_.scale = scale; + int32_t factor) { + auto obj = static_cast(data); + if (obj->wl_output_ != wl_output) { + return; + } + obj->outputs_[wl_output].factor = factor; } -/** - * @brief Handle the name event from the wl_output interface. - * - * This function is called when the name property of the output is updated. - * - * @param data A pointer to the Output object. - * @param wl_output A pointer to the wl_output object. - * @param name The new name of the output. - */ +#endif + +void Output::handle_done(void *data, + struct wl_output *wl_output) { + auto obj = static_cast(data); + obj->outputs_[wl_output].done = true; + + auto output = obj->outputs_[wl_output]; + std::cout << "Output" << std::endl; + std::cout << "Mode" << std::endl; + std::cout << "\tSize: " << output.mode.width << "x" << output.mode.height << std::endl; + std::cout << "\tRefresh: " << output.mode.refresh << std::endl; + std::cout << "\tFlags: " << std::endl; + if ((output.mode.flags & WL_OUTPUT_MODE_CURRENT) == WL_OUTPUT_MODE_CURRENT) { + std::cout << "\t\tWL_OUTPUT_MODE_CURRENT" << std::endl; + } + if ((output.mode.flags & WL_OUTPUT_MODE_PREFERRED) == WL_OUTPUT_MODE_PREFERRED) { + std::cout << "\t\tWL_OUTPUT_MODE_PREFERRED" << std::endl; + } + std::cout << "Geometry" << std::endl; + std::cout << "\tMake: " << output.geometry.make << std::endl; + std::cout << "\tModel: " << output.geometry.model << std::endl; + std::cout << "\tPhysical: " << output.geometry.physical_width << "x" << output.geometry.physical_height + << std::endl; + std::cout << "\tSubpixel: " << output.geometry.subpixel << std::endl; + std::cout << "\tTransform: " << output.geometry.transform << std::endl; + std::cout << "\tx: " << output.geometry.x << ", y: " << output.geometry.y << std::endl; +#if defined(WL_OUTPUT_SCALE_SINCE_VERSION) + std::cout << "Scaling factor: " << output.factor << std::endl; +#endif +#if defined(WL_OUTPUT_NAME_SINCE_VERSION) + std::cout << "Name: " << output.name << std::endl; +#endif +#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) + std::cout << "Description: " << output.description << std::endl; +#endif +} + +#if defined(WL_OUTPUT_NAME_SINCE_VERSION) + void Output::handle_name(void *data, struct wl_output *wl_output, const char *name) { - const auto obj = static_cast(data); - assert(obj->wl_output_ == wl_output); - obj->output_.name = name; + auto obj = static_cast(data); + if (obj->wl_output_ != wl_output) { + return; + } + obj->outputs_[wl_output].name = name; } -/** - * @brief Handles the description of an output. - * - * This function is invoked when a description is received for a specific output. It updates the description in the - * `output_` member of the `Output` object. - * - * @param data A pointer to the `Output` object. - * @param wl_output A pointer to the `wl_output` object. - * @param description The description of the output. - */ -void Output::handle_description(void *data, - struct wl_output *wl_output, - const char *description) { - const auto obj = static_cast(data); - assert(obj->wl_output_ == wl_output); - obj->output_.description = description; +#endif + +#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) + +void Output::handle_desc(void *data, + struct wl_output *wl_output, + const char *desc) { + auto obj = static_cast(data); + if (obj->wl_output_ != wl_output) { + return; + } + obj->outputs_[wl_output].description = desc; } +#endif + const struct wl_output_listener Output::listener_ = { - .geometry = handle_geometry, - .mode = handle_mode, - .done = handle_done, - .scale = handle_scale, - .name = handle_name, - .description = handle_description, + handle_geometry, + handle_mode, + handle_done +#if defined(WL_OUTPUT_SCALE_SINCE_VERSION) + , + handle_scale +#endif +#if defined(WL_OUTPUT_NAME_SINCE_VERSION) + , + handle_name +#endif +#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) + , + handle_desc +#endif }; diff --git a/src/window_manager/output.h b/src/window_manager/output.h index 5ba082f..09a5235 100644 --- a/src/window_manager/output.h +++ b/src/window_manager/output.h @@ -21,52 +21,51 @@ #include #include #include +#include #include class Output { public: - struct geometry { - int x; - int y; - int physical_width; - int physical_height; - int subpixel; - const char *make; - const char *model; - int transform; - }; - - struct mode { - uint32_t flags; - int width; - int height; - int refresh; - }; - - Output(struct wl_output *output, uint32_t version); + explicit Output(struct wl_output *output); ~Output(); - [[nodiscard]] const struct geometry &get_geometry() const { return output_.geometry; } + // Disallow copy and assign. + Output(const Output &) = delete; - [[nodiscard]] const struct mode &get_mode() const { return output_.mode; } - - [[nodiscard]] uint32_t get_version() const { return version_; } + Output &operator=(const Output &) = delete; private: - struct { - struct geometry geometry; - struct mode mode; + struct wl_output *wl_output_; + + typedef struct { + struct { + int x; + int y; + int physical_width; + int physical_height; + int subpixel; + std::string_view make; + std::string_view model; + int transform; + } geometry; + + struct { + uint32_t flags; + int width; + int height; + int refresh; + } mode; + + int32_t factor; + std::string_view name; + std::string_view description; bool done; - std::optional scale; - std::string name; - std::string description; - } output_; - uint32_t version_; - struct wl_output *wl_output_; + } OUTPUT_INFO_T; + std::unordered_map outputs_; static void handle_geometry(void *data, struct wl_output *wl_output, @@ -86,21 +85,35 @@ class Output { int height, int refresh); - static void handle_done(void *data, struct wl_output *wl_output); + static void handle_done(void *data, + struct wl_output *wl_output); + +#if defined(WL_OUTPUT_SCALE_SINCE_VERSION) static void handle_scale(void *data, struct wl_output *wl_output, - int scale); + int32_t factor); + +#endif + +#if defined(WL_OUTPUT_NAME_SINCE_VERSION) static void handle_name(void *data, struct wl_output *wl_output, const char *name); - static void handle_description(void *data, - struct wl_output *wl_output, - const char *description); +#endif + +#if defined(WL_OUTPUT_DESCRIPTION_SINCE_VERSION) + + static void handle_desc(void *data, + struct wl_output *wl_output, + const char *desc); + +#endif static const struct wl_output_listener listener_; + }; #endif //SRC_OUTPUT_H_ \ No newline at end of file diff --git a/src/window_manager/registrar.cc b/src/window_manager/registrar.cc new file mode 100644 index 0000000..ae049bf --- /dev/null +++ b/src/window_manager/registrar.cc @@ -0,0 +1,155 @@ +/* + * Copyright 2024 Joel Winarske + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "registrar.h" + +#include + +#include "output.h" + +Registrar::Registrar(struct wl_display *wl_display, + uint32_t output_min_version, + uint32_t seat_min_version, + uint32_t shm_min_version, + uint32_t compositor_min_version, + uint32_t sub_compositor_min_version, + uint32_t xdg_wm_base_min_version) : wl_display_(wl_display), + wl_registry_(wl_display_get_registry(wl_display)), + output_{.min_version = output_min_version}, + seat_{.min_version = seat_min_version}, + shm_{.min_version = shm_min_version}, + compositor_{.min_version = compositor_min_version}, + sub_compositor_{.min_version = sub_compositor_min_version}, + xdg_wm_base_{.min_version = xdg_wm_base_min_version} { + wl_registry_add_listener(wl_registry_, &listener_, + this); + wl_display_dispatch(wl_display_); +} + +Registrar::~Registrar() { + + for (auto &it: output_.outputs) { + it.second.reset(); + wl_output_destroy(it.first); + } + + for (auto &it: seat_.seats) { + it.second.reset(); + wl_seat_destroy(it.first); + } + + if (wl_registry_) { + wl_registry_destroy(wl_registry_); + } + + if (shm_.wl_shm) { + wl_shm_destroy(shm_.wl_shm); + shm_.formats.clear(); + } + + if (compositor_.wl_compositor) { + wl_compositor_destroy(compositor_.wl_compositor); + } + + if (sub_compositor_.wl_subcompositor) { + wl_subcompositor_destroy(sub_compositor_.wl_subcompositor); + } + + if (xdg_wm_base_.xdg_wm_base.has_value()) { + xdg_wm_base_destroy(xdg_wm_base_.xdg_wm_base.value()); + } +} + +void Registrar::registry_handle_global(void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version) { + auto obj = static_cast(data); + //std::cout << "interface: " << interface << ", version: " << version << std::endl; + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + obj->compositor_.wl_compositor = static_cast( + wl_registry_bind(registry, name, &wl_compositor_interface, + std::min(static_cast(obj->compositor_.min_version), version))); + std::cout << "wl_compositor version: " << wl_compositor_get_version(obj->compositor_.wl_compositor) + << std::endl; + } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { + obj->sub_compositor_.wl_subcompositor = static_cast( + wl_registry_bind(registry, name, &wl_subcompositor_interface, + std::min(static_cast(obj->sub_compositor_.min_version), version))); + std::cout << "wl_subcompositor version: " << wl_subcompositor_get_version(obj->sub_compositor_.wl_subcompositor) + << std::endl; + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + obj->shm_.wl_shm = static_cast( + wl_registry_bind(registry, name, &wl_shm_interface, + std::min(static_cast(obj->shm_.min_version), version))); + wl_shm_add_listener(obj->shm_.wl_shm, &shm_listener_, obj); + std::cout << "wl_shm version: " << wl_shm_get_version(obj->shm_.wl_shm) << std::endl; + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + auto wl_seat = static_cast( + wl_registry_bind(registry, name, &wl_seat_interface, + std::min(static_cast(obj->seat_.min_version), version))); + obj->seat_.seats[wl_seat] = std::make_unique(wl_seat); + std::cout << "wl_seat version: " << wl_seat_get_version(wl_seat) << std::endl; + } else if (strcmp(interface, wl_output_interface.name) == 0) { + auto wl_output = static_cast( + wl_registry_bind(registry, name, &wl_output_interface, + std::min(static_cast(obj->output_.min_version), version))); + obj->output_.outputs[wl_output] = std::make_unique(wl_output); + std::cout << "wl_output version: " << wl_output_get_version(wl_output) << std::endl; + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + obj->xdg_wm_base_.xdg_wm_base = static_cast( + wl_registry_bind(registry, name, &xdg_wm_base_interface, + std::min(static_cast(obj->xdg_wm_base_.min_version), version))); + std::cout << "xdg_wm_base version: " << xdg_wm_base_get_version(obj->xdg_wm_base_.xdg_wm_base.value()) + << std::endl; + } else { + std::scoped_lock lock(obj->registrar_global_mutex_); + if (obj->registrar_add_.find(interface) != obj->registrar_add_.end()) { + auto p = obj->registrar_add_[interface]; + p.first(p.second, registry, name, interface, version); + } + } +} + +void Registrar::registry_handle_global_remove(void *data, + struct wl_registry *reg, + uint32_t id) { + auto obj = static_cast(data); + std::scoped_lock lock(obj->registrar_global_remove_mutex_); + if (obj->registrar_remove_.find(id) != obj->registrar_remove_.end()) { + auto p = obj->registrar_remove_[id]; + p.first(p.second, reg, id); + } +} + +const struct wl_registry_listener Registrar::listener_ = { + registry_handle_global, + registry_handle_global_remove, +}; + +void Registrar::shm_format(void *data, + struct wl_shm *wl_shm, + uint32_t format) { + auto obj = static_cast(data); + if (obj->shm_.wl_shm != wl_shm) { + return; + } + obj->shm_.formats.push_back(format); +} + +const struct wl_shm_listener Registrar::shm_listener_ = {.format = shm_format}; diff --git a/src/window_manager/registrar.h b/src/window_manager/registrar.h new file mode 100644 index 0000000..a423806 --- /dev/null +++ b/src/window_manager/registrar.h @@ -0,0 +1,162 @@ +/* + * Copyright 2024 Joel Winarske + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_WINDOW_MANAGER_REGISTRAR_H_ +#define SRC_WINDOW_MANAGER_REGISTRAR_H_ + +#include + +#include +#include +#include +#include +#include +#include "output.h" +#include "seat/seat.h" + +#include "xdg-shell-client-protocol.h" + +class WindowManager; + +class Registrar { +public: + typedef void (*RegistrarGlobalCallback)( + void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version); + + typedef void (*RegistrarGlobalRemoveCallback)( + void *data, + struct wl_registry *registry, + uint32_t id); + + explicit Registrar(struct wl_display *wl_display, + uint32_t output_min_version = 4, + uint32_t seat_min_version = 8, + uint32_t shm_min_version = 1, + uint32_t compositor_min_version = 5, + uint32_t sub_compositor_min_version = 1, + uint32_t xdg_wm_base_min_version = 6); + + ~Registrar(); + + [[nodiscard]] std::optional shm_has_format(enum wl_shm_format format) const { + if (shm_.wl_shm == nullptr) { + return {}; + } + if (std::find(shm_.formats.begin(), shm_.formats.end(), format) != shm_.formats.end()) { + return true; + } + return false; + } + + [[nodiscard]] std::optional get_compositor() const { + if (compositor_.wl_compositor == nullptr) { + return {}; + } + return compositor_.wl_compositor; + } + + [[nodiscard]] std::optional get_subcompositor() const { + if (sub_compositor_.wl_subcompositor == nullptr) { + return {}; + } + return sub_compositor_.wl_subcompositor; + } + + [[nodiscard]] std::optional get_shm() const { + if (shm_.wl_shm == nullptr) { + return {}; + } + return shm_.wl_shm; + } + + [[nodiscard]] const std::vector &get_shm_formats() const { + return shm_.formats; + } + + struct wl_registry *get_registry() const { return wl_registry_; } + + std::optional get_xdg_wm_base() const { return xdg_wm_base_.xdg_wm_base; } + + // Disallow copy and assign. + Registrar(const Registrar &) = delete; + + Registrar &operator=(const Registrar &) = delete; + + friend WindowManager; +private: + struct wl_display *wl_display_; + struct wl_registry *wl_registry_; + + struct { + uint32_t min_version; + std::map> outputs; + } output_; + + struct { + uint32_t min_version; + std::map> seats; + } seat_; + + struct { + struct wl_shm *wl_shm; + uint32_t min_version; + std::vector formats; + } shm_; + + struct { + struct wl_compositor *wl_compositor; + uint32_t min_version; + } compositor_; + + struct { + struct wl_subcompositor *wl_subcompositor; + uint32_t min_version; + } sub_compositor_; + + struct { + std::optional xdg_wm_base; + uint32_t min_version; + } xdg_wm_base_; + + std::mutex registrar_global_mutex_; + std::mutex registrar_global_remove_mutex_; + std::map> registrar_add_; + std::map> registrar_remove_; + + static void registry_handle_global(void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version); + + static void registry_handle_global_remove(void *data, + struct wl_registry *reg, + uint32_t id); + + static const struct wl_registry_listener listener_; + + static void shm_format(void *data, + struct wl_shm *wl_shm, + uint32_t format); + + static const struct wl_shm_listener shm_listener_; +}; + +#endif //SRC_WINDOW_MANAGER_REGISTRAR_H_ diff --git a/src/window_manager/window_manager.cc b/src/window_manager/window_manager.cc index a5d66b1..1c51004 100644 --- a/src/window_manager/window_manager.cc +++ b/src/window_manager/window_manager.cc @@ -21,8 +21,6 @@ #include #include -#include "window/window_vulkan.h" - /** * @class WindowManager @@ -35,30 +33,17 @@ * @see Window * @see XdgWm */ -WindowManager::WindowManager(Window::ShellType shell_type, GMainContext *context, bool enable_cursor, - const char *name) : - Display(context, enable_cursor, name), - Window(wl_compositor_, shell_type, - [&](void * /* data */, uint32_t /* time */) { std::cerr << "base draw" << std::endl; }), - shell_type_(shell_type) { - - if (shell_type == XDG) { - xdg_wm_ = std::make_unique(this->wl_display_, this->wl_surface_); - - // this makes the start-up from the beginning with the correct dimensions - // like starting as maximized/fullscreen, rather than starting up as floating - // width, height then performing a resize - while (xdg_wm_->get_wait_for_configure()) { - wl_display_dispatch(wl_display_); - - // wait until xdg_surface::configure ACKs the new dimensions - if (xdg_wm_->get_wait_for_configure()) - continue; - } - std::cout << "configured." << std::endl; +WindowManager::WindowManager(GMainContext */* context */, + bool /* enable_cursor */, + const char *name) : Registrar(get_display(name)) { + auto compositor = get_compositor(); + if (!compositor.has_value()) { + std::cerr << "Wayland compositor is not available." << std::endl; + exit(EXIT_FAILURE); } + base_surface_ = std::make_unique(get_compositor().value()); - start_frames(); +// start_frames(); } /** @@ -67,95 +52,41 @@ WindowManager::WindowManager(Window::ShellType shell_type, GMainContext *context * This destructor stops rendering frames for all windows controlled by the WindowManager. * It calls the stop_frames() function to stop rendering frames. */ -WindowManager::~WindowManager() { - stop_frames(); -} - -/** +WindowManager::~WindowManager() = default; - * @brief Handles the event when a surface enters the window manager - - * - * @param data The data associated with the event - * @param surface The surface that enters the window manager - * @param output The output associated with the surface - * - * This function is called when a surface enters the window manager. It prints a message indicating that the surface has entered. - * - * Example Usage: - * - * ``` - * WindowManager wm; - * wl_surface *surface; - * wl_output *output; - * wm.handle_surface_enter(nullptr, surface, output); - * ``` - * - */ -void WindowManager::handle_surface_enter(void * /* data */, - struct wl_surface * /* surface */, - struct wl_output * /* output */) { - std::cout << "WindowManager::handle_surface_enter" << std::endl; +struct wl_display *WindowManager::get_display(const char *name) { + wl_display_ = (wl_display_connect(name)); + if (wl_display_ == nullptr) { + std::cerr << "Failed to connect to Wayland display. " << strerror(errno) << std::endl; + exit(EXIT_FAILURE); + } + return wl_display_; } /** - * @brief Handles the event when a surface leaves an output. * - * This function is called when a surface leaves an output. It prints a message - * to the console indicating that the surface has left the output. - * - * @param data A pointer to the data associated with the event (unused). - * @param surface The surface that has left the output. - * @param output The output that the surface has left. - */ -void WindowManager::handle_surface_leave(void * /* data */, - struct wl_surface * /* surface */, - struct wl_output * /* output */) { - std::cout << "WindowManager::handle_surface_leave" << std::endl; -} - -const struct wl_surface_listener WindowManager::surface_listener_ = { - .enter = handle_surface_enter, - .leave = handle_surface_leave, -}; - -/** - * @brief Creates a new window and adds it to the WindowManager's list of windows. + * @brief Creates a new EGL window and adds it to the WindowManager's list of EGL windows. * - * The function creates a new window based on the given parameters and adds it to the list of windows managed by the - * WindowManager. The type of the window can be either EGL or VULKAN. If the window type is EGL, a WindowEgl object is - * created using the provided display, compositor, surface, width, height, shell type, and draw callback. If the shell - * type is XDG, additional actions can be performed. If the window type is VULKAN, a WindowVulkan object can be - * created, but this part of the code is currently commented out. + * This function creates a new EGL window based on the given parameters and adds it to the list of EGL windows managed by the + * WindowManager. * - * @param width The width of the window. - * @param height The height of the window. - * @param window_type The type of the window (EGL or VULKAN). - * @param draw_callback The function to be called when the window needs to be drawn. - * @return A pointer to the created window object, or nullptr if no window was created. + * @param width The width of the window + * @param height The height of the window + * @param context_attribs_size The array size of context attributes + * @param context_attribs Pointer to array of context attributes + * @param config_attribs_size The array size of config attributes + * @param config_attribs Pointer to array of config attributes + * @param draw_callback Frame callback function pointer */ -WindowEgl *WindowManager::create_window(int width, int height, WindowType window_type, - const std::function &draw_callback) { - WindowEgl *result = nullptr; - - std::unique_ptr window; - if (window_type == EGL) { - window = std::make_unique(this->wl_display_, this->wl_compositor_, this->wl_surface_, width, height, - shell_type_, - draw_callback); - if (shell_type_ == Window::ShellType::XDG) { - } - } else if (window_type == VULKAN) { - //window = std::make_unique(this->wl_display_, this->wl_compositor_, width, height, shell_type_, - //draw_callback); - } +void WindowManager::create_egl_window(int width, int height, size_t context_attribs_size, const int *context_attribs, + size_t config_attribs_size, const int *config_attribs, int min_buffer_size, + FrameCallback frame_callback) { + auto window = std::make_unique(wl_display_, get_compositor().value(), width, height, + context_attribs, context_attribs_size, config_attribs, + config_attribs_size, min_buffer_size, frame_callback); if (window) { - result = window.get(); - windows_.emplace_back(std::move(window)); + windows_egl_.push_back(std::move(window)); } - - start_frames(); - return result; } /** @@ -166,7 +97,7 @@ WindowEgl *WindowManager::create_window(int width, int height, WindowType window * @param timeout The maximum amount of time to wait for events, in milliseconds. * @return The number of events dispatched on success, or a negative error code on failure. */ -int WindowManager::dispatch(int timeout) const { +[[maybe_unused]] int WindowManager::dispatch(int timeout) const { struct pollfd fds[1]; int dispatch_count = 0; @@ -217,3 +148,4 @@ int WindowManager::poll_events(int /* timeout */) const { wl_display_read_events(wl_display_); return wl_display_dispatch_pending(wl_display_); } + diff --git a/src/window_manager/window_manager.h b/src/window_manager/window_manager.h index 791a666..e140e38 100644 --- a/src/window_manager/window_manager.h +++ b/src/window_manager/window_manager.h @@ -14,62 +14,66 @@ * limitations under the License. */ -#ifndef SRC_WINDOW_WINDOW_MANAGER_H_ -#define SRC_WINDOW_WINDOW_MANAGER_H_ - -#include "display.h" +#ifndef SRC_WINDOW_MANAGER_WINDOW_MANAGER_H_ +#define SRC_WINDOW_MANAGER_WINDOW_MANAGER_H_ #include +#include +#include "registrar.h" +#include "window/surface.h" #include "window/window.h" #include "window/window_egl.h" -#include "xdg_wm.h" - +class Registrar; -class Display; +class Window; class WindowEgl; -class Window; +typedef void (*FrameCallback)( + void *data, + uint32_t time); -class WindowManager : public Display, public Window { +class WindowManager : public Registrar { public: - typedef enum { - EGL, - VULKAN, - } WindowType; - - explicit WindowManager(Window::ShellType shell_type = Window::ShellType::XDG, GMainContext *context = nullptr, + typedef void (*RegistrarGlobalCallback)( + void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version); + + explicit WindowManager(GMainContext *context = nullptr, bool enable_cursor = true, const char *name = nullptr); - ~WindowManager() override; + ~WindowManager(); + + struct wl_display *get_display() const { return wl_display_; } - WindowEgl * - create_window(int width, int height, WindowType window_type = WindowType::EGL, - const std::function &draw_callback = nullptr); + void create_egl_window(int width, int height, size_t context_attribs_size, const int *context_attribs, + size_t config_attribs_size, + const int *config_attribs, int min_buffer_size, FrameCallback frame_callback = nullptr); [[nodiscard]] int poll_events(int timeout) const; - [[nodiscard]] int dispatch(int timeout) const; + [[maybe_unused]] [[nodiscard]] int dispatch(int timeout) const; -private: - // list of windows for z-order control - std::list> windows_; - std::unique_ptr xdg_wm_; + // Disallow copy and assign. + WindowManager(const WindowManager &) = delete; - Window::ShellType shell_type_; + WindowManager &operator=(const WindowManager &) = delete; - static void handle_surface_enter(void *data, - struct wl_surface *surface, - struct wl_output *output); +private: + std::unique_ptr base_surface_; + + // list of windows for z-order control + std::list> windows_egl_; - static void handle_surface_leave(void *data, - struct wl_surface *surface, - struct wl_output *output); + struct wl_display *wl_display_; - static const struct wl_surface_listener surface_listener_; + struct wl_display *get_display(const char *name); }; -#endif // SRC_WINDOW_WINDOW_MANAGER_H_ \ No newline at end of file +#endif // SRC_WINDOW_MANAGER_WINDOW_MANAGER_H_ \ No newline at end of file diff --git a/src/window_manager/xdg_wm.cc b/src/window_manager/xdg_window_manager.cc similarity index 54% rename from src/window_manager/xdg_wm.cc rename to src/window_manager/xdg_window_manager.cc index 0620ee2..68e04a5 100644 --- a/src/window_manager/xdg_wm.cc +++ b/src/window_manager/xdg_window_manager.cc @@ -14,9 +14,8 @@ * limitations under the License. */ -#include "xdg_wm.h" +#include "xdg_window_manager.h" -#include #include // workaround for Wayland macro not compiling in C++ @@ -27,22 +26,56 @@ /** - * @class XdgWm + * @class XdgWindowManager * * @brief XdgWm represents a window manager for a Wayland-based display. * * The XdgWm class is responsible for managing application windows using the XDG Shell protocol. */ -XdgWm::XdgWm(struct wl_display *display, struct wl_surface *base_surface) : wl_surface_(base_surface) { - wl_registry_ = wl_display_get_registry(display); - wl_registry_add_listener(wl_registry_, ®istry_listener_, this); +XdgWindowManager::XdgWindowManager(const char *title, + const char *app_id, + GMainContext *context, + bool enable_cursor, + const char *name) : WindowManager(context, enable_cursor, name), + Surface(get_compositor().value()), title_(title), + app_id_(app_id) { + auto xdg_wm_base = get_xdg_wm_base(); + if (!xdg_wm_base.has_value()) { + std::cerr << "XDG Window Manager is not supported." << std::endl; + exit(EXIT_FAILURE); + } + xdg_wm_base_ = xdg_wm_base.value(); + + xdg_wm_base_add_listener(xdg_wm_base_, &xdg_wm_base_listener_, this); + + xdg_surface_ = xdg_wm_base_get_xdg_surface(xdg_wm_base_, get_surface()); + xdg_surface_add_listener(xdg_surface_, &xdg_surface_listener_, this); + + xdg_toplevel_ = xdg_surface_get_toplevel(xdg_surface_); + xdg_toplevel_add_listener(xdg_toplevel_, &xdg_toplevel_listener_, this); + + xdg_toplevel_set_title(xdg_toplevel_, title_.c_str()); + xdg_toplevel_set_app_id(xdg_toplevel_, app_id_.c_str()); + + wl_surface_commit(get_surface()); // enables blocking caller until set false wait_for_configure_ = true; + + // this makes the start-up from the beginning with the correct dimensions + // like starting as maximized/fullscreen, rather than starting up as floating + // width, height then performing a resize + while (wait_for_configure_) { + wl_display_dispatch(get_display()); + + // wait until xdg_surface::configure ACKs the new dimensions + if (wait_for_configure_) + continue; + } } /** - * @class XdgWm + * @class XdgWindowManager * * @brief The XdgWm class represents a Wayland shell window manager. * @@ -51,93 +84,32 @@ XdgWm::XdgWm(struct wl_display *display, struct wl_surface *base_surface) : wl_s * It is responsible for handling interactions with the Wayland registry, creating and destroying the window manager base, * surface, and toplevel objects, and implementing the necessary event handling functions. */ -XdgWm::~XdgWm() { - if (wl_registry_) - wl_registry_destroy(wl_registry_); +XdgWindowManager::~XdgWindowManager() { if (xdg_toplevel_) xdg_toplevel_destroy(xdg_toplevel_); if (xdg_surface_) xdg_surface_destroy(xdg_surface_); - - if (xdg_wm_base_) - xdg_wm_base_destroy(xdg_wm_base_); } /** - * @brief Handles global registry events. - * - * This function is called when a new global object is added or removed from the registry. - * If the added global object is of type xdg_wm_base, it sets up the necessary listeners - * and binds the object to the XdgWm instance. * - * @param data The user data associated with the callback (XdgWm instance). - * @param registry The registry object. - * @param name The name of the global object. - * @param interface The interface name of the global object. - * @param version The version of the global object. + * @param data + * @param xdg_wm_base + * @param serial */ -void XdgWm::registry_handle_global(void *data, - struct wl_registry *registry, - uint32_t name, - const char *interface, - uint32_t version) { - - if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - const auto obj = static_cast(data); - - obj->xdg_wm_base_ = static_cast( - wl_registry_bind(registry, name, &xdg_wm_base_interface, - std::min(static_cast(3), version))); - xdg_wm_base_add_listener(obj->xdg_wm_base_, &xdg_wm_base_listener_, obj); - - obj->xdg_surface_ = xdg_wm_base_get_xdg_surface(obj->xdg_wm_base_, obj->wl_surface_); - xdg_surface_add_listener(obj->xdg_surface_, &xdg_surface_listener_, obj); - - obj->xdg_toplevel_ = xdg_surface_get_toplevel(obj->xdg_surface_); - xdg_toplevel_add_listener(obj->xdg_toplevel_, &xdg_toplevel_listener_, obj); - - xdg_toplevel_set_title(obj->xdg_toplevel_, "waypp"); - xdg_toplevel_set_app_id(obj->xdg_toplevel_, "waypp"); - - wl_surface_commit(obj->wl_surface_); +void XdgWindowManager::xdg_wm_base_ping(void *data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial) { + auto obj = static_cast(data); + if (obj->xdg_wm_base_ != xdg_wm_base) { + return; } -} - -/** - * @brief Handles the removal of a global from the registry. - * - * This function is a callback that is triggered when a global object is - * removed from the registry. It does not return anything. - * - * @param data A pointer to user-defined data. - * @param reg A pointer to the registry that triggered the event. - * @param id The ID of the removed global. - */ -void XdgWm::registry_handle_global_remove(void * /* data */, - struct wl_registry * /* reg */, - uint32_t /* id */) { -} - -const struct wl_registry_listener XdgWm::registry_listener_ = { - registry_handle_global, - registry_handle_global_remove, -}; - -/** - * @class XdgWm - * - * The XdgWm class represents a window manager for XDG surfaces. - */ -void XdgWm::xdg_wm_base_ping(void * /* data */, - struct xdg_wm_base *xdg_wm_base, - uint32_t serial) { - std::cout << "XdgWm::xdg_wm_base_ping" << std::endl; xdg_wm_base_pong(xdg_wm_base, serial); } -const struct xdg_wm_base_listener XdgWm::xdg_wm_base_listener_ = { +const struct xdg_wm_base_listener XdgWindowManager::xdg_wm_base_listener_ = { .ping = xdg_wm_base_ping, }; @@ -152,17 +124,18 @@ const struct xdg_wm_base_listener XdgWm::xdg_wm_base_listener_ = { * @param xdg_surface A pointer to the xdg_surface instance. * @param serial The serial number of the configure event. */ -void XdgWm::handle_xdg_surface_configure( +void XdgWindowManager::handle_xdg_surface_configure( void *data, struct xdg_surface *xdg_surface, uint32_t serial) { - auto *w = static_cast(data); + auto *w = static_cast(data); xdg_surface_ack_configure(xdg_surface, serial); w->wait_for_configure_ = false; } -const struct xdg_surface_listener XdgWm::xdg_surface_listener_ = { - .configure = handle_xdg_surface_configure}; +const struct xdg_surface_listener XdgWindowManager::xdg_surface_listener_ = { + .configure = handle_xdg_surface_configure +}; /** * @brief Handles the configure event for a toplevel surface. @@ -177,9 +150,9 @@ const struct xdg_surface_listener XdgWm::xdg_surface_listener_ = { * @param height The height of the surface. * @param states An array of states associated with the surface. */ -void XdgWm::handle_toplevel_configure( +void XdgWindowManager::handle_toplevel_configure( void *data, - struct xdg_toplevel * /* toplevel */, + struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { @@ -189,7 +162,10 @@ void XdgWm::handle_toplevel_configure( return; } - auto *w = static_cast(data); + auto *w = static_cast(data); + if (w->xdg_toplevel_ != toplevel) { + return; + } w->fullscreen_ = false; w->maximized_ = false; @@ -244,24 +220,99 @@ void XdgWm::handle_toplevel_configure( * @param data The user data associated with the XdgWm instance. * @param xdg_toplevel The xdg_toplevel object that received the close request. */ -void XdgWm::handle_toplevel_close( +void XdgWindowManager::handle_toplevel_close( void *data, - struct xdg_toplevel * /* xdg_toplevel */) { + struct xdg_toplevel *xdg_toplevel) { std::cout << "XdgWm::handle_toplevel_close" << std::endl; - auto *w = static_cast(data); + auto *w = static_cast(data); + if (w->xdg_toplevel_ != xdg_toplevel) { + return; + } w->running_ = false; } -const struct xdg_toplevel_listener XdgWm::xdg_toplevel_listener_ = { - handle_toplevel_configure, - handle_toplevel_close, +#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) + +/** + * + * @param data + * @param xdg_toplevel + * @param width + * @param height + */ +void XdgWindowManager::handle_configure_bounds(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height) { + auto *w = static_cast(data); + if (w->xdg_toplevel_ != xdg_toplevel) { + return; + } + std::cout << "Configure Bounds: " << width << "x" << height << std::endl; +} + +#endif + +#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) + +/** + * + * @param data + * @param xdg_toplevel + * @param capabilities + */ +void XdgWindowManager::handle_wm_capabilities(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities) { + auto *w = static_cast(data); + if (w->xdg_toplevel_ != xdg_toplevel) { + return; + } + std::cout << "WM Capabilities:" << std::endl; + const uint32_t *cap; + WL_ARRAY_FOR_EACH(cap, capabilities, const uint32_t*) { + switch (*cap) { + case XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU: + std::cout << "\tXDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU" << std::endl; + break; + case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE: + std::cout << "\tXDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE" << std::endl; + break; + case XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN: + std::cout << "\tXDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN" << std::endl; + break; + case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE: + std::cout << "\tXDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE" << std::endl; + break; + } + } +} + +#endif + +const struct xdg_toplevel_listener XdgWindowManager::xdg_toplevel_listener_ = { + .configure = handle_toplevel_configure, + .close = handle_toplevel_close +#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) + , + .configure_bounds = handle_configure_bounds +#endif +#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) + , + .wm_capabilities = handle_wm_capabilities +#endif }; /** -* -*/ -void XdgWm::toplevel_resize(int x, int y, int width, int height, int padding) { + * + * @param x + * @param y + * @param width + * @param height + * @param padding + */ +void XdgWindowManager::toplevel_resize(int x, int y, int width, int height, int padding) { const bool top = y < padding; const bool bottom = y > (height - padding); diff --git a/src/window_manager/xdg_wm.h b/src/window_manager/xdg_window_manager.h similarity index 53% rename from src/window_manager/xdg_wm.h rename to src/window_manager/xdg_window_manager.h index ff95baf..1dd2dcf 100644 --- a/src/window_manager/xdg_wm.h +++ b/src/window_manager/xdg_window_manager.h @@ -22,13 +22,20 @@ #include "xdg-shell-client-protocol.h" -class XdgWm { +#include "window_manager.h" +#include "window/surface.h" + +class XdgWindowManager : public WindowManager, public Surface { public: - XdgWm(struct wl_display *display, struct wl_surface *base_surface); + explicit XdgWindowManager(const char *title, + const char *app_id, + GMainContext *context = nullptr, + bool enable_cursor = true, + const char *name = nullptr); - ~XdgWm(); + ~XdgWindowManager(); - [[nodiscard]] const bool get_wait_for_configure() const { return wait_for_configure_; } + [[nodiscard]] bool get_wait_for_configure() const { return wait_for_configure_; } void set_app_id(const char *app_id) { xdg_toplevel_set_app_id(xdg_toplevel_, app_id); } @@ -36,16 +43,20 @@ class XdgWm { void toplevel_resize(int x, int y, int width, int height, int padding); + // Disallow copy and assign. + XdgWindowManager(const XdgWindowManager &) = delete; + + XdgWindowManager &operator=(const XdgWindowManager &) = delete; + private: - struct wl_surface *wl_surface_; - struct wl_registry *wl_registry_; - struct xdg_wm_base *xdg_wm_base_{}; - struct xdg_surface *xdg_surface_{}; - struct xdg_toplevel *xdg_toplevel_{}; + struct xdg_wm_base *xdg_wm_base_; + struct xdg_surface *xdg_surface_; + struct xdg_toplevel *xdg_toplevel_; + std::string title_; std::string app_id_; - volatile bool wait_for_configure_{}; + volatile bool wait_for_configure_; bool fullscreen_{}; bool maximized_{}; @@ -64,36 +75,33 @@ class XdgWm { } window_size_{}; static void handle_xdg_surface_configure( - void * /* data */, - struct xdg_surface * /* xdg_surface */, - uint32_t /* serial */); + void *data, + struct xdg_surface *xdg_surface, + uint32_t serial); static const struct xdg_surface_listener xdg_surface_listener_; static void handle_toplevel_configure( - void * /* data */, - struct xdg_toplevel * /* toplevel */, - int32_t /* width */, - int32_t /* height */, - struct wl_array * /* states */); + void *data, + struct xdg_toplevel *toplevel, + int32_t width, + int32_t height, + struct wl_array *states); static void handle_toplevel_close( - void * /* data */, - struct xdg_toplevel * /* xdg_toplevel */); + void *data, + struct xdg_toplevel *xdg_toplevel); - static const struct xdg_toplevel_listener xdg_toplevel_listener_; + static void handle_configure_bounds(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height); - static void registry_handle_global(void *data, - struct wl_registry *registry, - uint32_t name, - const char *interface, - uint32_t version); + static void handle_wm_capabilities(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities); - static void registry_handle_global_remove(void *data, - struct wl_registry *reg, - uint32_t id); - - static const struct wl_registry_listener registry_listener_; + static const struct xdg_toplevel_listener xdg_toplevel_listener_; static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, @@ -101,6 +109,7 @@ class XdgWm { static const struct xdg_wm_base_listener xdg_wm_base_listener_; + }; #endif // SRC_WINDOW_MANAGER_XDG_WM_H_ \ No newline at end of file