// SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: GPL-1.0-or-later

#include <assert.h>
#include <float.h>
#include <stdlib.h>
#include <wlr/types/wlr_xdg_shell.h>

#include "input/event.h"
#include "output.h"
#include "scene/xdg_shell.h"
#include "util/macros.h"
#include "view/action.h"
#include "view_p.h"

struct xdg_view {
    struct view view;
    struct wlr_xdg_surface *wlr_xdg_surface;

    struct wl_listener commit;

    struct wl_listener map;
    struct wl_listener unmap;
    struct wl_listener destroy;
    struct wl_listener new_popup;
    struct wl_listener ping_timeout;

    struct wl_listener request_move;
    struct wl_listener request_resize;
    struct wl_listener request_minimize;
    struct wl_listener request_maximize;
    struct wl_listener request_fullscreen;
    struct wl_listener request_show_window_menu;

    struct wl_listener set_parent;
    struct wl_listener set_title;
    struct wl_listener set_app_id;

    struct wl_list icon_buffers; // xdg_icon_buffer.link  from xdg toplevel icon
};

struct xdg_icon_buffer {
    struct wl_list link;

    int scale;
    struct wlr_buffer *wlr_buffer;
};

static bool xdg_view_hover(struct seat *seat, struct ky_scene_node *node, double x, double y,
                           uint32_t time, bool first, bool hold, void *data)
{
    struct wlr_surface *surface = wlr_surface_try_from_node(node);
    struct xdg_view *xdg_view = data;

    if (first) {
        view_hover(seat, &xdg_view->view);
        kywc_log(KYWC_DEBUG, "First hover surface %p (%f %f)", surface, x, y);
    }

    if (!hold) {
        seat_notify_motion(seat, surface, time, x, y, first);
        return false;
    }

    struct kywc_box *geometry = &xdg_view->view.base.geometry;
    double sx = x - geometry->x;
    double sy = y - geometry->y;
    // sx = sx < 0 ? 0 : (sx > geometry->width ? geometry->width : sx);
    // sy = sy < 0 ? 0 : (sy > geometry->height ? geometry->height : sy);
    sx += xdg_view->wlr_xdg_surface->current.geometry.x;
    sy += xdg_view->wlr_xdg_surface->current.geometry.y;

    seat_notify_motion(seat, surface, time, sx, sy, first);
    return true;
}

static void xdg_view_click(struct seat *seat, struct ky_scene_node *node, uint32_t button,
                           bool pressed, uint32_t time, enum click_state state, void *data)
{
    seat_notify_button(seat, time, button, pressed);

    /* only do activated when button pressed */
    if (!pressed) {
        return;
    }

    struct xdg_view *xdg_view = data;
    view_click(seat, &xdg_view->view, button, pressed, state);
}

static void xdg_view_leave(struct seat *seat, struct ky_scene_node *node, bool last, void *data)
{
    /* so surface will call set_cursor when enter again */
    struct wlr_surface *surface = wlr_surface_try_from_node(node);
    seat_notify_leave(seat, surface);
}

static struct ky_scene_node *xdg_view_get_root(void *data)
{
    struct xdg_view *xdg_view = data;
    return &xdg_view->view.tree->node;
}

static struct wlr_surface *xdg_view_get_toplevel(void *data)
{
    struct xdg_view *xdg_view = data;
    return xdg_view->view.surface;
}

static const struct input_event_node_impl xdg_view_event_node_impl = {
    .hover = xdg_view_hover,
    .click = xdg_view_click,
    .leave = xdg_view_leave,
};

static struct xdg_view *xdg_view_from_view(struct view *view)
{
    struct xdg_view *xdg_view = wl_container_of(view, xdg_view, view);
    return xdg_view;
}

static void xdg_view_close(struct view *view)
{
    struct xdg_view *xdg_view = xdg_view_from_view(view);
    wlr_xdg_toplevel_send_close(xdg_view->wlr_xdg_surface->toplevel);
}

static void xdg_view_ping(struct view *view)
{
    struct xdg_view *xdg_view = xdg_view_from_view(view);
    struct wlr_xdg_surface *wlr_xdg_surface = xdg_view->wlr_xdg_surface;
    wlr_xdg_surface_ping(wlr_xdg_surface);
}

static void xdg_view_configure(struct view *view)
{
    struct xdg_view *xdg_view = xdg_view_from_view(view);
    struct wlr_xdg_toplevel *wlr_xdg_toplevel = xdg_view->wlr_xdg_surface->toplevel;
    struct kywc_view *kywc_view = &xdg_view->view.base;

    /* do nothing when minimize */
    if (view->pending.action & VIEW_ACTION_MINIMIZE) {
        view->pending.action &= ~VIEW_ACTION_MINIMIZE;
    }

    /* ignore activate configure serial */
    if (view->pending.action & VIEW_ACTION_ACTIVATE) {
        view->pending.action &= ~VIEW_ACTION_ACTIVATE;
        wlr_xdg_toplevel_set_activated(wlr_xdg_toplevel, kywc_view->activated);
    }

    /* direct move when not changed size */
    if (view->pending.action & VIEW_ACTION_MOVE) {
        view->pending.action &= ~VIEW_ACTION_MOVE;
        if (!view_action_change_size(view->pending.configure_action)) {
            view_helper_move(view, view->pending.geometry.x, view->pending.geometry.y);
        } else {
            kywc_log(KYWC_DEBUG, "Skip move when pending configure action 0x%x",
                     view->pending.configure_action);
        }
    }

    if (view->pending.action == VIEW_ACTION_NOP) {
        return;
    }

    /* now, only changed size action left */
    assert(view_action_change_size(view->pending.action));

    uint32_t serial = 0;
    if (view->pending.action & VIEW_ACTION_FULLSCREEN) {
        serial = wlr_xdg_toplevel_set_fullscreen(wlr_xdg_toplevel, kywc_view->fullscreen);
    }

    if (view->pending.action & VIEW_ACTION_MAXIMIZE) {
        serial = wlr_xdg_toplevel_set_maximized(wlr_xdg_toplevel, kywc_view->maximized);
    }

    if (view->pending.action & VIEW_ACTION_TILE) {
        serial = wlr_xdg_toplevel_set_tiled(wlr_xdg_toplevel, kywc_view->tiled ? 0xf : 0);
    }

    struct kywc_box *current = &view->base.geometry;
    struct kywc_box *pending = &view->pending.geometry;

    /* If no need to resizing, process the move immediately */
    if (!view_action_change_size(view->pending.configure_action) &&
        current->width == pending->width && current->height == pending->height) {
        view->pending.action &= ~VIEW_ACTION_RESIZE;
        view_helper_move(view, pending->x, pending->y);
        view_configure(view, serial);
        return;
    }

    serial = wlr_xdg_toplevel_set_size(wlr_xdg_toplevel, pending->width, pending->height);
    view_configure(view, serial);
}

static void xdg_view_close_popups(struct view *view)
{
    struct xdg_view *xdg_view = xdg_view_from_view(view);

    struct wlr_xdg_popup *popup, *tmp;
    wl_list_for_each_safe(popup, tmp, &xdg_view->wlr_xdg_surface->popups, link) {
        wlr_xdg_popup_destroy(popup);
    }
}

static void xdg_view_destroy(struct view *view)
{
    struct xdg_view *xdg_view = xdg_view_from_view(view);
    free(xdg_view);
}

static void xdg_view_update_usable_area(struct view *view, struct kywc_output *output,
                                        struct kywc_box *usable_area, enum kywc_edges edge)
{
    struct kywc_box geo;
    kywc_output_effective_geometry(output, &geo);
    struct kywc_box *view_geo = &view->base.geometry;

    if (edge == KYWC_EDGE_TOP) {
        int y = view_geo->y + view_geo->height;
        geo.height -= y - geo.y;
        geo.y = y;
    } else if (edge == KYWC_EDGE_BOTTOM) {
        geo.height = view_geo->y - geo.y;
    } else if (edge == KYWC_EDGE_LEFT) {
        int x = view_geo->width + view_geo->x;
        geo.width -= x - geo.x;
        geo.x = x;
    } else if (edge == KYWC_EDGE_RIGHT) {
        geo.width = view_geo->x - geo.x;
    }

    /* intersect usable_area and geo */
    usable_area->x = MAX(geo.x, usable_area->x);
    usable_area->y = MAX(geo.y, usable_area->y);
    usable_area->width = MIN(geo.width, usable_area->width);
    usable_area->height = MIN(geo.height, usable_area->height);
}

static struct wlr_buffer *xdg_view_get_icon_buffer(struct view *view, int size, float scale)
{
    struct xdg_view *xdg_view = xdg_view_from_view(view);

    if (wl_list_empty(&xdg_view->icon_buffers)) {
        return NULL;
    }

    float scale_width = size * scale;
    float min_abs = FLT_MAX;
    float tmp_abs;
    struct xdg_icon_buffer *icon_buffer_similar = NULL;
    struct xdg_icon_buffer *icon_buffer;
    wl_list_for_each(icon_buffer, &xdg_view->icon_buffers, link) {
        // maybe equal to size
        if ((icon_buffer->wlr_buffer->width == size && FLOAT_EQUAL(icon_buffer->scale, scale)) ||
            (icon_buffer->wlr_buffer->width == scale_width)) {
            return icon_buffer->wlr_buffer;
        }
        tmp_abs = fabs(icon_buffer->wlr_buffer->width - scale_width);
        if (tmp_abs < min_abs) {
            min_abs = tmp_abs;
            icon_buffer_similar = icon_buffer;
        }
    }
    return icon_buffer_similar->wlr_buffer;
}

static const struct view_impl xdg_surface_impl = {
    .ping = xdg_view_ping,
    .configure = xdg_view_configure,
    .close_popups = xdg_view_close_popups,
    .close = xdg_view_close,
    .destroy = xdg_view_destroy,
    .update_usable_area = xdg_view_update_usable_area,
    .get_icon_buffer = xdg_view_get_icon_buffer,
};

static void xdg_view_update_geometry(struct xdg_view *xdg_view)
{
    struct wlr_xdg_surface *wlr_xdg_surface = xdg_view->wlr_xdg_surface;
    struct wlr_surface *wlr_surface = wlr_xdg_surface->surface;

    struct wlr_box *geo = &wlr_xdg_surface->current.geometry;
    int width = geo->width, height = geo->height;

    if (!width || !height) {
        width = wlr_surface->current.width;
        height = wlr_surface->current.height;
    }

    struct wlr_xdg_toplevel_state *current = &wlr_xdg_surface->toplevel->current;
    view_update_size(&xdg_view->view, width, height, current->min_width, current->min_height,
                     current->max_width, current->max_height);

    struct kywc_view *kywc_view = &xdg_view->view.base;
    /* padding if used CSD with drop-shadow */
    if (kywc_view->ssd == KYWC_SSD_NONE) {
        kywc_view->padding.left = geo->x;
        kywc_view->padding.right = wlr_surface->current.width - geo->x - width;
        kywc_view->padding.top = geo->y;
        kywc_view->padding.bottom = wlr_surface->current.height - geo->y - height;
    }
}

static void xdg_view_handle_commit(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, commit);
    struct view *view = &xdg_view->view;

    xdg_view_update_geometry(xdg_view);

    enum view_action pending_action = view->pending.configure_action;
    uint32_t pending_serial = view->pending.configure_serial;
    if (pending_action == VIEW_ACTION_NOP && pending_serial == 0) {
        return;
    }

    assert(view_action_change_size(pending_action));

    struct kywc_box *current = &view->base.geometry;
    struct kywc_box *pending = &view->pending.configure_geometry;
    int x = pending->x, y = pending->y;

    uint32_t current_serial = xdg_view->wlr_xdg_surface->current.configure_serial;
    /* pending configure has not been acked yet, fix wobbling when resize */
    if (pending_serial >= current_serial && pending_action & VIEW_ACTION_RESIZE) {
        uint32_t resize_edges = xdg_view->view.current_resize_edges;
        if (resize_edges & KYWC_EDGE_LEFT) {
            x += pending->width - current->width;
        }
        if (resize_edges & KYWC_EDGE_TOP) {
            y += pending->height - current->height;
        }
        if (pending_serial > current_serial) {
            view_helper_move(view, x, y);
        }
    }

    /* last configure has been acked */
    if (current_serial >= pending_serial) {
        view_helper_move(view, x, y);
        view_configured(&xdg_view->view, true);
    }
}

static void xdg_view_handle_ping_timeout(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, ping_timeout);
    view_show_hang_window(&xdg_view->view);
}

static void xdg_view_handle_new_popup(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, new_popup);
    struct view *view = &xdg_view->view;
    struct wlr_xdg_popup *wlr_xdg_popup = data;

    struct view_layer *view_layer = view_manager_get_layer_by_node(&view->tree->node, false);
    enum layer layer = view_layer->layer > LAYER_POPUP ? view_layer->layer : LAYER_POPUP;
    struct view_layer *popup_layer = view_manager_get_layer(layer, false);
    xdg_popup_create(wlr_xdg_popup, xdg_view->view.surface_tree, popup_layer,
                     view->base.role == KYWC_VIEW_ROLE_PANEL);
}

static void xdg_view_handle_request_move(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, request_move);
    struct wlr_xdg_toplevel_move_event *event = data;
    struct seat *seat = seat_from_wlr_seat(event->seat->seat);

    if (view_validate_move_or_resize_request(&xdg_view->view, seat)) {
        window_begin_move(&xdg_view->view, seat);
    }
}

static void xdg_view_handle_request_resize(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, request_resize);
    struct wlr_xdg_toplevel_resize_event *event = data;
    struct seat *seat = seat_from_wlr_seat(event->seat->seat);

    if (view_validate_move_or_resize_request(&xdg_view->view, seat)) {
        window_begin_resize(&xdg_view->view, event->edges, seat);
    }
}

static void xdg_view_handle_request_minimize(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, request_minimize);
    struct wlr_xdg_toplevel *toplevel = xdg_view->wlr_xdg_surface->toplevel;

    kywc_view_set_minimized(&xdg_view->view.base, toplevel->requested.minimized);
}

static void xdg_view_handle_request_maximize(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, request_maximize);
    struct wlr_xdg_toplevel *toplevel = xdg_view->wlr_xdg_surface->toplevel;

    kywc_view_set_maximized(&xdg_view->view.base, toplevel->requested.maximized, NULL);
}

static void xdg_view_handle_request_fullscreen(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, request_fullscreen);
    struct wlr_xdg_toplevel *toplevel = xdg_view->wlr_xdg_surface->toplevel;
    struct wlr_xdg_toplevel_requested *requested = &toplevel->requested;
    struct kywc_view *kywc_view = &xdg_view->view.base;

    struct kywc_output *kywc_output = NULL;
    if (requested->fullscreen_output) {
        struct output *output = output_from_wlr_output(requested->fullscreen_output);
        if (output && !output->base.destroying && output->base.state.enabled) {
            kywc_output = &output->base;
        }
    }

    kywc_view_set_fullscreen(kywc_view, requested->fullscreen, kywc_output);
}

static void xdg_view_handle_show_window_menu(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, request_show_window_menu);
    struct wlr_xdg_toplevel_show_window_menu_event *event = data;
    struct kywc_view *kywc_view = &xdg_view->view.base;

    struct seat *seat = seat_from_wlr_seat(event->seat->seat);
    int off_x = kywc_view->geometry.x - xdg_view->wlr_xdg_surface->current.geometry.x;
    int off_y = kywc_view->geometry.y - xdg_view->wlr_xdg_surface->current.geometry.y;
    view_show_window_menu(&xdg_view->view, seat, off_x + event->x, off_y + event->y);
}

static void xdg_view_handle_set_parent(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, set_parent);
    struct wlr_xdg_toplevel *parent = xdg_view->wlr_xdg_surface->toplevel->parent;
    struct xdg_view *parent_xdg_view = parent ? parent->base->data : NULL;
    struct view *parent_view = parent_xdg_view ? &parent_xdg_view->view : NULL;

    view_set_parent(&xdg_view->view, parent_view);
}

static void xdg_view_handle_set_title(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, set_title);
    const char *title = xdg_view->wlr_xdg_surface->toplevel->title;

    view_set_title(&xdg_view->view, title);
}

static void xdg_view_handle_set_app_id(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, set_app_id);
    const char *app_id = xdg_view->wlr_xdg_surface->toplevel->app_id;

    view_set_app_id(&xdg_view->view, app_id);
}

static void xdg_view_handle_map(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, map);
    struct wlr_xdg_surface *wlr_xdg_surface = xdg_view->wlr_xdg_surface;
    struct wlr_surface *wlr_surface = wlr_xdg_surface->surface;
    struct wlr_xdg_toplevel *toplevel = wlr_xdg_surface->toplevel;
    struct kywc_view *kywc_view = &xdg_view->view.base;

    xdg_view_update_geometry(xdg_view);
    kywc_log(KYWC_DEBUG, "Kywc_view %p padding: (← %d), (↑ %d), (→ %d), (↓ %d)", kywc_view,
             kywc_view->padding.left, kywc_view->padding.top, kywc_view->padding.right,
             kywc_view->padding.bottom);

    /* all states are ready when map */
    view_set_app_id(&xdg_view->view, toplevel->app_id);
    view_set_title(&xdg_view->view, toplevel->title);
    xdg_view_handle_set_parent(&xdg_view->set_parent, NULL);

    xdg_view_handle_request_minimize(&xdg_view->request_minimize, NULL);
    xdg_view_handle_request_maximize(&xdg_view->request_maximize, NULL);
    xdg_view_handle_request_fullscreen(&xdg_view->request_fullscreen, NULL);

    /* some requesets before map are cached in toplevel */
    xdg_view->request_move.notify = xdg_view_handle_request_move;
    wl_signal_add(&toplevel->events.request_move, &xdg_view->request_move);
    xdg_view->request_resize.notify = xdg_view_handle_request_resize;
    wl_signal_add(&toplevel->events.request_resize, &xdg_view->request_resize);
    xdg_view->request_minimize.notify = xdg_view_handle_request_minimize;
    wl_signal_add(&toplevel->events.request_minimize, &xdg_view->request_minimize);
    xdg_view->request_maximize.notify = xdg_view_handle_request_maximize;
    wl_signal_add(&toplevel->events.request_maximize, &xdg_view->request_maximize);
    xdg_view->request_fullscreen.notify = xdg_view_handle_request_fullscreen;
    wl_signal_add(&toplevel->events.request_fullscreen, &xdg_view->request_fullscreen);
    xdg_view->request_show_window_menu.notify = xdg_view_handle_show_window_menu;
    wl_signal_add(&toplevel->events.request_show_window_menu, &xdg_view->request_show_window_menu);
    xdg_view->set_parent.notify = xdg_view_handle_set_parent;
    wl_signal_add(&toplevel->events.set_parent, &xdg_view->set_parent);
    xdg_view->set_title.notify = xdg_view_handle_set_title;
    wl_signal_add(&toplevel->events.set_title, &xdg_view->set_title);
    xdg_view->set_app_id.notify = xdg_view_handle_set_app_id;
    wl_signal_add(&toplevel->events.set_app_id, &xdg_view->set_app_id);

    xdg_view->ping_timeout.notify = xdg_view_handle_ping_timeout;
    wl_signal_add(&wlr_xdg_surface->events.ping_timeout, &xdg_view->ping_timeout);
    xdg_view->new_popup.notify = xdg_view_handle_new_popup;
    wl_signal_add(&wlr_xdg_surface->events.new_popup, &xdg_view->new_popup);
    xdg_view->commit.notify = xdg_view_handle_commit;
    wl_signal_add(&wlr_surface->events.commit, &xdg_view->commit);

    struct wl_client *client = wl_resource_get_client(wlr_surface->resource);
    wl_client_get_credentials(client, &xdg_view->view.pid, NULL, NULL);
    view_map(&xdg_view->view);
}

static void xdg_view_handle_unmap(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, unmap);

    wl_list_remove(&xdg_view->commit.link);
    wl_list_remove(&xdg_view->ping_timeout.link);
    wl_list_remove(&xdg_view->new_popup.link);
    wl_list_remove(&xdg_view->request_move.link);
    wl_list_remove(&xdg_view->request_minimize.link);
    wl_list_remove(&xdg_view->request_maximize.link);
    wl_list_remove(&xdg_view->request_fullscreen.link);
    wl_list_remove(&xdg_view->request_resize.link);
    wl_list_remove(&xdg_view->request_show_window_menu.link);
    wl_list_remove(&xdg_view->set_parent.link);
    wl_list_remove(&xdg_view->set_title.link);
    wl_list_remove(&xdg_view->set_app_id.link);

    view_unmap(&xdg_view->view);
}

static void xdg_view_handle_destroy(struct wl_listener *listener, void *data)
{
    struct xdg_view *xdg_view = wl_container_of(listener, xdg_view, destroy);

    wl_list_remove(&xdg_view->destroy.link);
    wl_list_remove(&xdg_view->map.link);
    wl_list_remove(&xdg_view->unmap.link);

    xdg_view_clear_icon_buffer(xdg_view->wlr_xdg_surface);
    xdg_view->wlr_xdg_surface->surface->data = NULL;
    /* scene tree destroy will be called before by scene */
    view_destroy(&xdg_view->view);
}

static void handle_new_xdg_surface(struct wl_listener *listener, void *data)
{
    struct wlr_xdg_surface *wlr_xdg_surface = data;

    /* popup is handled in surface new_popup listener */
    if (wlr_xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
        return;
    }

    struct xdg_view *xdg_view = calloc(1, sizeof(*xdg_view));
    if (!xdg_view) {
        wl_resource_post_no_memory(wlr_xdg_surface->surface->resource);
        return;
    }

    xdg_view->view.surface = wlr_xdg_surface->surface;
    view_init(&xdg_view->view, &xdg_surface_impl, xdg_view);

    xdg_view->wlr_xdg_surface = wlr_xdg_surface;
    wlr_xdg_surface->data = xdg_view;
    wlr_xdg_surface->surface->data = &xdg_view->view;
    wl_list_init(&xdg_view->icon_buffers);

    xdg_view->unmap.notify = xdg_view_handle_unmap;
    wl_signal_add(&wlr_xdg_surface->surface->events.unmap, &xdg_view->unmap);

    /* create tree for surface and all sub-surfaces */
    xdg_view->view.surface_tree = ky_scene_xdg_surface_create(xdg_view->view.tree, wlr_xdg_surface);
    /* event node will be destroyed when xdg_surface destroy */
    input_event_node_create(&xdg_view->view.surface_tree->node, &xdg_view_event_node_impl,
                            xdg_view_get_root, xdg_view_get_toplevel, xdg_view);

    /* others will add in map and remove in unmap */
    xdg_view->map.notify = xdg_view_handle_map;
    wl_signal_add(&wlr_xdg_surface->surface->events.map, &xdg_view->map);
    xdg_view->destroy.notify = xdg_view_handle_destroy;
    wl_signal_add(&wlr_xdg_surface->events.destroy, &xdg_view->destroy);
}

void xdg_view_add_icon_buffer(struct wlr_xdg_surface *wlr_xdg_surface, int scale,
                              struct wlr_buffer *wlr_buffer)
{
    struct xdg_view *xdg_view = wlr_xdg_surface->data;
    if (!xdg_view) {
        return;
    }

    if (scale <= 0 || !wlr_buffer) {
        return;
    }

    struct xdg_icon_buffer *icon_buffer = calloc(1, sizeof(*icon_buffer));
    if (!icon_buffer) {
        return;
    }

    icon_buffer->scale = scale;
    icon_buffer->wlr_buffer = wlr_buffer;
    wlr_buffer_lock(icon_buffer->wlr_buffer);
    wl_list_insert(&xdg_view->icon_buffers, &icon_buffer->link);
}

void xdg_view_clear_icon_buffer(struct wlr_xdg_surface *wlr_xdg_surface)
{
    struct xdg_view *xdg_view = wlr_xdg_surface->data;
    if (!xdg_view) {
        return;
    }

    struct xdg_icon_buffer *icon_buffer, *tmp;
    wl_list_for_each_safe(icon_buffer, tmp, &xdg_view->icon_buffers, link) {
        wlr_buffer_unlock(icon_buffer->wlr_buffer);
        wl_list_remove(&icon_buffer->link);
        free(icon_buffer);
    }
}

bool xdg_shell_init(struct view_manager *view_manager)
{
    struct server *server = view_manager->server;
    struct wlr_xdg_shell *xdg_shell = wlr_xdg_shell_create(server->display, 5);
    if (!xdg_shell) {
        kywc_log(KYWC_ERROR, "Unable to create xdg shell");
        return false;
    }

    view_manager->new_xdg_surface.notify = handle_new_xdg_surface;
    wl_signal_add(&xdg_shell->events.new_surface, &view_manager->new_xdg_surface);

    return true;
}
