/*
PolyGun

Copyright (c) 2023 mrkubax10 <mrkubax10@onet.pl>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "game/window/wayland_window.hpp"

#include <cstring>
#include <sys/mman.h>
#include <unistd.h>

#include "common/logger.hpp"

using namespace polygun::window;

static struct xkb_context* g_xkb = nullptr;
static unsigned g_xkb_refs = 0;
static const KeyCode g_evdev_keycode_to_keycode[] = {
	KeyCode::KEY_NONE,
	KeyCode::KEY_ESCAPE,
	KeyCode::KEY_1,
	KeyCode::KEY_2,
	KeyCode::KEY_3,
	KeyCode::KEY_4,
	KeyCode::KEY_5,
	KeyCode::KEY_6,
	KeyCode::KEY_7,
	KeyCode::KEY_8,
	KeyCode::KEY_9,
	KeyCode::KEY_0,
	KeyCode::KEY_MINUS,
	KeyCode::KEY_EQUALS,
	KeyCode::KEY_BACKSPACE,
	KeyCode::KEY_TAB,
	KeyCode::KEY_Q,
	KeyCode::KEY_W,
	KeyCode::KEY_E,
	KeyCode::KEY_R,
	KeyCode::KEY_T,
	KeyCode::KEY_Y,
	KeyCode::KEY_U,
	KeyCode::KEY_I,
	KeyCode::KEY_O,
	KeyCode::KEY_P,
	KeyCode::KEY_LEFT_SQUARE_BRACKET,
	KeyCode::KEY_RIGHT_SQUARE_BRACKET,
	KeyCode::KEY_RETURN,
	KeyCode::KEY_LEFT_CONTROL,
	KeyCode::KEY_A,
	KeyCode::KEY_S,
	KeyCode::KEY_D,
	KeyCode::KEY_F,
	KeyCode::KEY_G,
	KeyCode::KEY_H,
	KeyCode::KEY_J,
	KeyCode::KEY_K,
	KeyCode::KEY_L,
	KeyCode::KEY_SEMICOLON,
	KeyCode::KEY_QUOTE,
	KeyCode::KEY_TILDE,
	KeyCode::KEY_LEFT_SHIFT,
	KeyCode::KEY_BACKSLASH,
	KeyCode::KEY_Z,
	KeyCode::KEY_X,
	KeyCode::KEY_C,
	KeyCode::KEY_V,
	KeyCode::KEY_B,
	KeyCode::KEY_N,
	KeyCode::KEY_M,
	KeyCode::KEY_COMMA,
	KeyCode::KEY_DOT,
	KeyCode::KEY_SLASH,
	KeyCode::KEY_RIGHT_SHIFT,
	KeyCode::KEY_NUM_MULTIPLY,
	KeyCode::KEY_LEFT_ALT,
	KeyCode::KEY_SPACE,
	KeyCode::KEY_CAPS_LOCK,
	KeyCode::KEY_F1,
	KeyCode::KEY_F2,
	KeyCode::KEY_F3,
	KeyCode::KEY_F4,
	KeyCode::KEY_F5,
	KeyCode::KEY_F6,
	KeyCode::KEY_F7,
	KeyCode::KEY_F8,
	KeyCode::KEY_F9,
	KeyCode::KEY_F10,
	KeyCode::KEY_NUM_LOCK,
	KeyCode::KEY_SCROLL_LOCK,
	KeyCode::KEY_NUM_7,
	KeyCode::KEY_NUM_8,
	KeyCode::KEY_NUM_9,
	KeyCode::KEY_NUM_MINUS,
	KeyCode::KEY_NUM_4,
	KeyCode::KEY_NUM_5,
	KeyCode::KEY_NUM_6,
	KeyCode::KEY_NUM_PLUS,
	KeyCode::KEY_NUM_1,
	KeyCode::KEY_NUM_2,
	KeyCode::KEY_NUM_3,
	KeyCode::KEY_NUM_0,
	KeyCode::KEY_NUM_DOT,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_F11,
	KeyCode::KEY_F12,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NUM_RETURN,
	KeyCode::KEY_RIGHT_CONTROL,
	KeyCode::KEY_NUM_SLASH,
	KeyCode::KEY_NONE,
	KeyCode::KEY_RIGHT_ALT,
	KeyCode::KEY_NONE,
	KeyCode::KEY_HOME,
	KeyCode::KEY_UP,
	KeyCode::KEY_PAGE_UP,
	KeyCode::KEY_LEFT,
	KeyCode::KEY_RIGHT,
	KeyCode::KEY_END,
	KeyCode::KEY_DOWN,
	KeyCode::KEY_PAGE_DOWN,
	KeyCode::KEY_INSERT,
	KeyCode::KEY_DELETE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_PAUSE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_LEFT_ALT,
	KeyCode::KEY_RIGHT_ALT,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_CONTEXT,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_NONE,
	KeyCode::KEY_PRINT_SCREEN
};

static const MouseButton g_evdev_mouse_button_to_mouse_button[] = {
	MouseButton::MOUSE_BUTTON_LEFT,
	MouseButton::MOUSE_BUTTON_RIGHT,
	MouseButton::MOUSE_BUTTON_MIDDLE,
	MouseButton::MOUSE_BUTTON_AUX_1,
	MouseButton::MOUSE_BUTTON_AUX_2,
	MouseButton::MOUSE_BUTTON_AUX_3
};

WaylandWindow::WaylandWindow(const std::string& title, unsigned width, unsigned height, renderer::RendererType renderer_type) :
	m_width(width),
	m_height(height),
#if defined(RENDERER_GL)
	m_egl_context(nullptr),
	m_egl_window(nullptr),
#endif
	m_display(wl_display_connect(nullptr)),
	m_registry(nullptr),
	m_compositor(nullptr),
	m_surface(nullptr),
	m_seat(nullptr),
	m_mouse(nullptr),
	m_keyboard(nullptr),
	m_shm(nullptr),
	m_mouse_theme(nullptr),
	m_default_mouse(nullptr),
	m_mouse_surface(nullptr),
	m_relative_mouse_manager(nullptr),
	m_relative_mouse(nullptr),
	m_mouse_constraints(nullptr),
	m_locked_mouse(nullptr),
	m_configured(false),
	m_mouse_inside_surface(false),
	m_was_mouse_grabbed(false),
	m_mouse_serial(0),
	m_libdecor_context(nullptr),
	m_frame(nullptr),
	m_keymap(nullptr),
	m_xkb_state(nullptr)
{
	m_type = WindowType::WINDOW_TYPE_WAYLAND;
	if(!m_display)
		throw std::runtime_error("Failed to connect to Wayland display");

	if(g_xkb_refs++==0)
		g_xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);

	m_registry = wl_display_get_registry(m_display);
	const struct wl_registry_listener registry_listener = {
		handle_registry,
		handle_remove_registry
	};
	wl_registry_add_listener(m_registry, &registry_listener, reinterpret_cast<void*>(this));
	wl_display_roundtrip(m_display);

	switch(renderer_type) {
	case renderer::RendererType::RENDERER_TYPE_GL:
	case renderer::RendererType::RENDERER_TYPE_LEGACY_GL:
#if defined(RENDERER_GL)
		m_egl_context = new renderer::EGLGLContext(this);
#else
		LOG_FATAL("Request to setup EGL while OpenGL renderer is not compiled in");
#endif
		break;
	}

	m_surface = wl_compositor_create_surface(m_compositor);
	m_mouse_surface = wl_compositor_create_surface(m_compositor);

#if defined(RENDERER_GL)
	if(m_egl_context) {
		m_egl_window = wl_egl_window_create(m_surface, width, height);
		m_egl_context->activate(reinterpret_cast<NativeWindowType>(m_egl_window));
	}
#endif

	static struct libdecor_interface libdecor_interf = {
		handle_libdecor_error
	};
	m_libdecor_context = libdecor_new(m_display, &libdecor_interf);
	static struct libdecor_frame_interface frame_interface = {
		configure_frame,
		close_frame,
		commit_frame
	};
	m_frame = libdecor_decorate(m_libdecor_context, m_surface, &frame_interface, reinterpret_cast<void*>(this));
	libdecor_frame_set_app_id(m_frame, "polygun");
	libdecor_frame_set_title(m_frame, title.c_str());
	libdecor_frame_map(m_frame);
	while(!m_configured) {
		if(libdecor_dispatch(m_libdecor_context, 0)<0) {
			Event new_event;
			new_event.m_type = EventType::EVENT_TYPE_WINDOW_CLOSE;
			push_event(new_event);
		}
	}
}

WaylandWindow::~WaylandWindow() {
	libdecor_frame_unref(m_frame);
	libdecor_unref(m_libdecor_context);
#if defined(RENDERER_GL)
	if(m_egl_context) {
		delete m_egl_context;
		wl_egl_window_destroy(m_egl_window);
	}
#endif
	wl_surface_destroy(m_mouse_surface);
	wl_surface_destroy(m_surface);
	if(m_keyboard)
		wl_keyboard_destroy(m_keyboard);
	if(m_mouse_theme)
		wl_cursor_theme_destroy(m_mouse_theme);
	if(m_mouse)
		wl_pointer_destroy(m_mouse);
	if(m_locked_mouse)
		zwp_locked_pointer_v1_destroy(m_locked_mouse);
	if(m_mouse_constraints)
		zwp_pointer_constraints_v1_destroy(m_mouse_constraints);
	if(m_relative_mouse)
		zwp_relative_pointer_v1_destroy(m_relative_mouse);
	if(m_relative_mouse_manager)
		zwp_relative_pointer_manager_v1_destroy(m_relative_mouse_manager);
	if(--g_xkb_refs==0)
		xkb_context_unref(g_xkb);
	if(m_display)
		wl_display_disconnect(m_display);
}

size_t WaylandWindow::poll_events(Event& event) {
	if(libdecor_dispatch(m_libdecor_context, 0)<0) {
		Event new_event;
		new_event.m_type = EventType::EVENT_TYPE_WINDOW_CLOSE;
		push_event(new_event);
	}
	return Window::poll_events(event);
}

void WaylandWindow::finish_frame() {
#if defined(RENDERER_GL)
	if(m_egl_context)
		m_egl_context->swap_buffers();
#endif
}

void WaylandWindow::grab_mouse(bool grab) {
	m_mouse_grabbed = grab;
	if(grab) {
		wl_pointer_set_cursor(m_mouse, m_mouse_serial, nullptr, 0, 0);
		m_locked_mouse = zwp_pointer_constraints_v1_lock_pointer(m_mouse_constraints, m_surface, m_mouse, nullptr, 2);
	}
	else if(m_default_mouse) {
		struct wl_cursor_image* img = m_default_mouse->images[0];
		struct wl_buffer* img_buffer = wl_cursor_image_get_buffer(img);
		wl_pointer_set_cursor(m_mouse, m_mouse_serial, m_mouse_surface, img->hotspot_x, img->hotspot_y);
		wl_surface_attach(m_mouse_surface, img_buffer, 0, 0);
		wl_surface_damage(m_mouse_surface, 0, 0, img->width, img->height);
		wl_surface_commit(m_mouse_surface);
		if(m_locked_mouse)
			zwp_locked_pointer_v1_destroy(m_locked_mouse);
		m_locked_mouse = nullptr;
	}
}

void WaylandWindow::handle_registry(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	bool registered = false;
	if(strcmp(interface, "wl_compositor")==0) {
		window->m_compositor = reinterpret_cast<wl_compositor*>(wl_registry_bind(registry, name, &wl_compositor_interface, 1));
		registered = true;
	}
	else if(strcmp(interface, "wl_seat")==0) {
		static const struct wl_seat_listener seat_listener = {
			handle_seat_capabilities
		};
		window->m_seat = reinterpret_cast<wl_seat*>(wl_registry_bind(registry, name, &wl_seat_interface, 1));
		wl_seat_add_listener(window->m_seat, &seat_listener, data);
		registered = true;
	}
	else if(strcmp(interface, "wl_shm")==0) {
		window->m_shm = reinterpret_cast<wl_shm*>(wl_registry_bind(registry, name, &wl_shm_interface, 1));
		window->m_mouse_theme = wl_cursor_theme_load(nullptr, 32, window->m_shm);
		window->m_default_mouse = wl_cursor_theme_get_cursor(window->m_mouse_theme, "left_ptr");
		registered = true;
	}
	else if(strcmp(interface, "zwp_relative_pointer_manager_v1")==0) {
		window->m_relative_mouse_manager = reinterpret_cast<zwp_relative_pointer_manager_v1*>(wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1));
		registered = true;
	}
	else if(strcmp(interface, "zwp_pointer_constraints_v1")==0) {
		window->m_mouse_constraints = reinterpret_cast<zwp_pointer_constraints_v1*>(wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1));
		registered = true;
	}

	if(registered)
		LOG_VERBOSE("Registered Wayland interface '%s'", interface);
}

void WaylandWindow::handle_remove_registry(void* data, struct wl_registry* registry, uint32_t name) {}

void WaylandWindow::handle_seat_capabilities(void* data, struct wl_seat* seat, uint32_t capability) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	if((capability & WL_SEAT_CAPABILITY_POINTER) && !window->m_mouse) {
		static const struct wl_pointer_listener mouse_listener = {
			handle_pointer_enter,
			handle_pointer_leave,
			handle_pointer_motion,
			handle_pointer_button,
			handle_pointer_axis
		};
		window->m_mouse = wl_seat_get_pointer(seat);
		wl_pointer_add_listener(window->m_mouse, &mouse_listener, data);
		window->m_relative_mouse = zwp_relative_pointer_manager_v1_get_relative_pointer(window->m_relative_mouse_manager, window->m_mouse);
		static const struct zwp_relative_pointer_v1_listener relative_listener = {
			handle_relative_pointer_motion
		};
		zwp_relative_pointer_v1_add_listener(window->m_relative_mouse, &relative_listener, data);
	}
	else if(!(capability & WL_SEAT_CAPABILITY_POINTER) && window->m_mouse) {
		wl_pointer_destroy(window->m_mouse);
		window->m_mouse = nullptr;
		if(window->m_relative_mouse) {
			zwp_relative_pointer_v1_destroy(window->m_relative_mouse);
			window->m_relative_mouse = nullptr;
		}
	}
	if((capability & WL_SEAT_CAPABILITY_KEYBOARD) && !window->m_keyboard) {
		static const struct wl_keyboard_listener keyboard_listener = {
			handle_keyboard_keymap,
			handle_keyboard_enter,
			handle_keyboard_leave,
			handle_keyboard_key,
			handle_keyboard_modifiers
		};
		window->m_keyboard = wl_seat_get_keyboard(seat);
		wl_keyboard_add_listener(window->m_keyboard, &keyboard_listener, data);
	}
	else if(!(capability & WL_SEAT_CAPABILITY_KEYBOARD) && window->m_keyboard) {
		wl_keyboard_destroy(window->m_keyboard);
		window->m_keyboard = nullptr;
	}
}

void WaylandWindow::handle_keyboard_keymap(void* data, struct wl_keyboard* keyboard, uint32_t format, int fd, uint32_t size) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	char* keymap = reinterpret_cast<char*>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
	if(keymap==MAP_FAILED) {
		LOG_ERROR("Failed to mmap Wayland keyboard with fd %d: %s. Text composition won't be possible.", fd, strerror(errno));
		close(fd);
		return;
	}
	if(window->m_keymap)
		xkb_keymap_unref(window->m_keymap);
	window->m_keymap = xkb_keymap_new_from_string(g_xkb, keymap, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
	if(!window->m_keymap) {
		LOG_ERROR("Failed to create XKBCommon keymap. Text composition won't be possible.");
		munmap(keymap, size);
		close(fd);
		return;
	}
	munmap(keymap, size);
	close(fd);
	window->m_xkb_state = xkb_state_new(window->m_keymap);
}

void WaylandWindow::handle_keyboard_enter(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface, struct wl_array* keys) {}

void WaylandWindow::handle_keyboard_leave(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface) {}

void WaylandWindow::handle_keyboard_key(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
	// TODO: Implement key repeat
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	Event new_event;
	new_event.m_type = state?EventType::EVENT_TYPE_KEY_DOWN:EventType::EVENT_TYPE_KEY_UP;
	new_event.m_data.m_key_event.m_key = g_evdev_keycode_to_keycode[key];
	window->m_pressed_keyboard_keys[new_event.m_data.m_key_event.m_key] = state;
	window->push_event(new_event);

	if(state) {
		const xkb_keycode_t xkb_key = key+8;
		int size = xkb_state_key_get_utf8(window->m_xkb_state, xkb_key, nullptr, 0);
		if(size<=0 || size+1>4)
			return; // unsupported key
		new_event.m_type = EventType::EVENT_TYPE_TEXT_ENTER;
		xkb_state_key_get_utf8(window->m_xkb_state, xkb_key, new_event.m_data.m_key_event.m_character_bytes, size+1);
		if(!isprint(new_event.m_data.m_key_event.m_character_bytes[0]))
			return;
		new_event.m_data.m_key_event.m_character_byte_count = static_cast<unsigned>(size);
		window->push_event(new_event);
	}
}

void WaylandWindow::handle_keyboard_modifiers(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t mods_unpressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	xkb_state_update_mask(window->m_xkb_state, mods_unpressed, mods_latched, mods_locked, 0, 0, group);
}

void WaylandWindow::handle_pointer_enter(void* data, struct wl_pointer* mouse, uint32_t serial, struct wl_surface* surface, wl_fixed_t x, wl_fixed_t y) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	window->m_mouse_inside_surface = surface==window->m_surface;
	if(window->m_mouse_inside_surface) {
		window->m_mouse_serial = serial;
		window->grab_mouse(window->m_was_mouse_grabbed);
	}
}

void WaylandWindow::handle_pointer_leave(void* data, struct wl_pointer* mouse, uint32_t serial, struct wl_surface* surface) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	if(surface==window->m_surface) {
		window->m_mouse_inside_surface = false;
		window->m_was_mouse_grabbed = window->m_mouse_grabbed;
	}
}

void WaylandWindow::handle_pointer_motion(void* data, struct wl_pointer* mouse, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	if(window->m_mouse_inside_surface && !window->m_mouse_grabbed) {
		Event new_event;
		new_event.m_type = EventType::EVENT_TYPE_MOUSE_MOVE;
		window->m_mouse_x = new_event.m_data.m_mouse_event.m_x = wl_fixed_to_int(x);
		window->m_mouse_y = new_event.m_data.m_mouse_event.m_y = wl_fixed_to_int(y);
		window->push_event(new_event);
	}
}

void WaylandWindow::handle_pointer_button(void* data, struct wl_pointer* mouse, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	if(window->m_mouse_inside_surface) {
		Event new_event;
		new_event.m_type = state?EventType::EVENT_TYPE_MOUSE_BUTTON_DOWN:EventType::EVENT_TYPE_MOUSE_BUTTON_UP;
		if(button<=0x115)
			new_event.m_data.m_mouse_event.m_button = g_evdev_mouse_button_to_mouse_button[button-0x110];
		else
			new_event.m_data.m_mouse_event.m_button = static_cast<MouseButton>(button-0x110);
		window->m_pressed_mouse_buttons[new_event.m_data.m_mouse_event.m_button] = state;
		window->push_event(new_event);
	}
}

void WaylandWindow::handle_pointer_axis(void* data, struct wl_pointer* mouse, uint32_t time, uint32_t axis, wl_fixed_t value) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	if(window->m_mouse_inside_surface) {
		int val = wl_fixed_to_int(value);
		Event new_event;
		new_event.m_type = EventType::EVENT_TYPE_MOUSE_WHEEL;
		new_event.m_data.m_mouse_event.m_wheel_offset = (val<0)-(val>0);
		window->push_event(new_event);
	}
}

void WaylandWindow::handle_relative_pointer_motion(void* data, struct zwp_relative_pointer_v1* relative_mouse, uint32_t time_hi, uint32_t time_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t acc_x, wl_fixed_t acc_y) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	if(window->m_mouse_grabbed) {
		Event new_event;
		new_event.m_type = EventType::EVENT_TYPE_MOUSE_MOVE;
		window->m_mouse_x = new_event.m_data.m_mouse_event.m_x = window->m_width/2+wl_fixed_to_int(dx);
		window->m_mouse_x = new_event.m_data.m_mouse_event.m_y = window->m_height/2+wl_fixed_to_int(dy);
		window->push_event(new_event);
	}
}

void WaylandWindow::handle_libdecor_error(struct libdecor* context, enum libdecor_error err, const char* message) {
	LOG_FATAL("libdecor error (error code: %d): %s", err, message);
}

void WaylandWindow::configure_frame(struct libdecor_frame* frame, struct libdecor_configuration* config, void* data) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);

	int width;
	int height;
	if(!libdecor_configuration_get_content_size(config, frame, &width, &height)) {
		width = static_cast<int>(window->m_width);
		height = static_cast<int>(window->m_height);
	}

#if defined(RENDERER_GL)
	if(window->m_egl_window)
		wl_egl_window_resize(window->m_egl_window, width, height, 0, 0);
#endif

	struct libdecor_state* state = libdecor_state_new(width, height);
	libdecor_frame_commit(frame, state, config);
	libdecor_state_free(state);

	if(window->m_configured) {
		Event new_event;
		new_event.m_type = EventType::EVENT_TYPE_WINDOW_RESIZE;
		new_event.m_data.m_window_event.m_width = static_cast<unsigned>(width);
		new_event.m_data.m_window_event.m_height = static_cast<unsigned>(height);
		window->push_event(new_event);
	}

	window->m_width = static_cast<unsigned>(width);
	window->m_height = static_cast<unsigned>(height);

	window->m_configured = true;
}

void WaylandWindow::close_frame(struct libdecor_frame* frame, void* data) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	Event new_event;
	new_event.m_type = EventType::EVENT_TYPE_WINDOW_CLOSE;
	window->push_event(new_event);
}

void WaylandWindow::commit_frame(struct libdecor_frame* frame, void* data) {
	WaylandWindow* window = reinterpret_cast<WaylandWindow*>(data);
	window->finish_frame();
}
