/*
 * Copyright © 2016 Quentin "Sardem FF7" Glidic
 *
 * 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 (including the next
 * paragraph) 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.
 */
/*
 * (C) Copyright 2014, Fuji Xerox Co.,Ltd.  All rights reserved.
 * File name : fx-shell.h
 * Title : entry of shell
 */
#include "config.h"
#include <string.h>
#include <assert.h>
#include <linux/input.h>

#include "shared/helpers.h"
#include "compositor/weston.h"
#include "shared/config-parser.h"
#include "compositor.h"
#include "qt-surface-extension-server-protocol.h"
#include "fx-surface-extension-server-protocol.h"
#include "fx-shell-controller.h"
#include "qt-surface-extension.h"
#include "fx-surface-extension.h"
#include "weston-fx-shell.h"
#include "fx-seat.h"
#include "fx-client.h"
#include "fx-swkeyboard.h"
#include "fx-shell.h"

/* Typedefs */
struct shell_surface_data {
    struct wl_signal
        destroy_signal;

    struct fx_shell_surface *
        shell_surface;
    struct weston_view *
        view;
    int32_t
        last_width;
    int32_t
        last_height;

    struct fx_shell *
        shell;

    struct weston_output *
        output;
};
struct shell_seat {
    struct weston_seat *
        seat;
    struct fx_shell *
        shell;
    struct wl_listener
        seat_destroy_listener;
    struct wl_listener
        caps_changed_listener;
/*
    struct wl_listener
        pointer_focus_listener;
    struct wl_listener
        touch_focus_listener;
*/
    struct wl_listener
        keyboard_focus_listener;

    struct wl_event_source *
        reset_keyboard_focus;
};

static void
activate(
        struct fx_shell *shell,
        struct weston_view *view,
        struct weston_seat *seat,
        uint32_t flags );
static void
create_shell_output(
        struct fx_shell *shell,
        struct weston_output *output );
static void
handle_output_create(
        struct wl_listener *listener,
        void *data );
 
/* Procedures */
static struct shell_surface_data *
get_shell_surface_data(struct weston_surface *surface)
{
    if ( fx_shell_surface_is_shell_surface( surface ) ) {
        struct fx_shell_surface *shell_surface =
                fx_shell_surface_get_shell_surface(surface);
        return fx_shell_surface_get_user_data( shell_surface );
    }
    return NULL;
}
/*
static struct weston_view *
get_default_view( struct weston_surface *surface )
{
    struct shell_surface_data *shell_surface_data;
    struct weston_view *view;

    if ( surface == NULL || wl_list_empty( &surface->views ) ) {
        return NULL;
    }

    shell_surface_data = get_shell_surface_data( surface );
    if ( shell_surface_data != NULL ) {
        return shell_surface_data->view;
    }

    wl_list_for_each( view, &surface->views, surface_link ) {
        if ( weston_view_is_mapped( view ) ) {
            return view;
        }
    }

    return container_of(
            surface->views.next, struct weston_view, surface_link );
}
*/
static enum fx_shell_animation_type
get_animation_type( const char *animation )
{
    if (!animation)
        return FX_SHELL_ANIMATION_NONE;

    if (!strcmp("zoom", animation))
        return FX_SHELL_ANIMATION_ZOOM;
    else if (!strcmp("fade", animation))
        return FX_SHELL_ANIMATION_FADE;
/*
    else if (!strcmp("dim-layer", animation))
        return ANIMATION_DIM_LAYER;
*/
    else if (!strcmp("slide", animation))
        return FX_SHELL_ANIMATION_SLIDE;
    else
        return FX_SHELL_ANIMATION_NONE;
}
static void
shell_configuration( struct fx_shell *shell )
{
    struct weston_config_section *section;
    char *s;

    section = weston_config_get_section(
            wet_get_config( shell->compositor ),
            "shell",
            NULL,
            NULL );

    weston_config_section_get_string(
            section,
            "animation",
            &s,
            "zoom");
    shell->win_animation_type = get_animation_type(s);
    weston_config_section_get_string(
            section,
            "close-animation",
            &s,
            "fade" );
        shell->win_close_animation_type = get_animation_type(s);

    free(s);
}
static struct weston_output *
get_default_output( struct weston_compositor *compositor )
{
    if ( wl_list_empty( &compositor->output_list ) ) {
        return NULL;
    }

    return container_of( compositor->output_list.next,
            struct weston_output, link );
}
static bool
is_suitable_view_of_keyboard_focus(
        struct fx_shell *shell,
        struct weston_view *view )
{
    struct shell_surface_data *shell_surface_data
        = get_shell_surface_data( view->surface );

    if ( shell_surface_data != NULL ) {
        int flags = fx_shell_surafce_get_flags(
                shell_surface_data->shell_surface );

        if ( ( flags & FX_EXTENDED_SURFACE_WINDOWFLAG_DOESNOTACCEPTFOCUS )
                == 0 ) {
            if ( weston_view_is_mapped( view ) ) {
                return true;
            }
        }
    }
    return false;
}
static bool
set_layer_keyboard_focus(
        struct weston_seat* seat,
        struct fx_shell* shell,
        struct weston_layer* layer )
{
    struct weston_view* view;

    wl_list_for_each(
            view,
            &layer->view_list.link,
            layer_link.link ) {
        if ( is_suitable_view_of_keyboard_focus(
                    shell,
                    view ) ) {
            weston_seat_set_keyboard_focus(
                    seat,
                    view->surface );
            return true;
        }
    }
    return false;
}
static void
layer_reset_keyboard_focus(
        struct weston_seat* seat,
        struct fx_shell* shell )
{
    struct weston_layer *layer, *end;

    if ( strcmp( seat->seat_name, FX_SHELL_SEAT_NAME ) == 0 ) return;

    layer = shell->layers.data + shell->layers.size;
    end = shell->layers.data;
    for ( layer -= 1; layer >= end; --layer ) {
        if ( set_layer_keyboard_focus(
                    seat,
                    shell,
                    layer ) ) {
            return;
        }
    }
}
static void
reset_keyboard_focus( void* data )
{
    struct shell_seat *shell_seat = data;
    struct weston_keyboard *keyboard
        = weston_seat_get_keyboard( shell_seat->seat );

    shell_seat->reset_keyboard_focus = NULL;
    if (
        keyboard->focus == NULL
        &&
        shell_seat->seat->saved_kbd_focus == NULL
    ) {
        layer_reset_keyboard_focus(
                shell_seat->seat,
                shell_seat->shell );

    }
}
static void
set_keyboard_focus( struct shell_seat *shell_seat )
{
    if ( shell_seat->reset_keyboard_focus == NULL ) {
        shell_seat->reset_keyboard_focus = wl_event_loop_add_idle(
                wl_display_get_event_loop(
                    shell_seat->shell->compositor->wl_display ),
                reset_keyboard_focus,
                shell_seat );
    }
}
static void
set_view_focus(
        struct fx_shell *shell,
        struct weston_seat *seat,
        struct weston_view *view )
{
    if ( view != NULL ) {
        if ( is_suitable_view_of_keyboard_focus(
                    shell,
                    view ) ) {
            weston_seat_set_keyboard_focus(
                    seat,
                    view->surface );
        }
    }
}
static void
touch_to_activate_binding(
        struct weston_touch *touch,
        const struct timespec *time,
        void *data )
{
    struct fx_shell *shell = data;

    if ( touch->grab != &touch->default_grab ) return;

    set_view_focus(
            shell,
            touch->seat,
            touch->focus );
}
static void
click_to_activate_binding(
        struct weston_pointer *pointer,
        const struct timespec *time,
        uint32_t button,
        void *data )
{
    struct fx_shell *shell = data;

    if ( pointer->grab != &pointer->default_grab ) return;

    set_view_focus(
            shell,
            pointer->seat,
            pointer->focus );
}
static void
handle_keyboard_focus(
        struct wl_listener *listener,
        void *data )
{
    struct shell_seat *shell_seat
        = container_of(listener, struct shell_seat, keyboard_focus_listener);

    set_keyboard_focus( shell_seat );
}
/* The surface will be inserted into the list immediately after the link
 * returned by this function (i.e. will be stacked immediately above the
 * returned link). */
static struct weston_layer_entry *
shell_surface_calculate_layer_link(
        struct shell_surface_data *shell_surface_data )
{
    int layer_index = fx_shell_surafce_get_layer_index(
            shell_surface_data->shell_surface );
    struct weston_layer* layer = shell_surface_data->shell->layers.data;

    return &layer[ layer_index ].view_list;
}
static void
shell_layer_entry_insert(
        struct weston_layer_entry *list,
        struct shell_surface_data *shell_surface_data )
{
    struct weston_layer_entry *prev = list;
    struct weston_view* view;

    wl_list_for_each( view, &list->link, layer_link.link ) {
        if ( view->parent_view == shell_surface_data->view->parent_view ) {
            struct shell_surface_data *ss_data
                = get_shell_surface_data( view->surface );

            if ( ss_data != NULL ) {
                const int32_t mask = QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP;
                int flags = fx_shell_surafce_get_flags(
                        shell_surface_data->shell_surface );
                int f = fx_shell_surafce_get_flags( ss_data->shell_surface );

                if (  ( flags & mask ) != 0 ) {
                    break;
                }
                if ( ( f & mask ) == 0 ) {
                    break;
                }
            }
        }
        prev = &view->layer_link;
    }
    weston_layer_entry_insert(
            prev,
            &shell_surface_data->view->layer_link );
}
static void
shell_surface_update_child_surface_layers (
        struct shell_surface_data *shell_surface_data)
{
    fx_shell_surface_propagate_layer( shell_surface_data->shell_surface );
}
/* Update the surface’s layer. Mark both the old and new views as having dirty
 * geometry to ensure the changes are redrawn.
 *
 * If any child surfaces exist and are mapped, ensure they’re in the same layer
 * as this surface. */
static void
shell_surface_update_layer( struct shell_surface_data *shell_surface_data )
{
    struct weston_surface *surface =
        fx_shell_surface_get_surface( shell_surface_data->shell_surface );
    struct weston_layer_entry *new_layer_link;

    new_layer_link = shell_surface_calculate_layer_link( shell_surface_data );

    if ( new_layer_link == NULL ) {
        return;
    }
    if ( new_layer_link == &shell_surface_data->view->layer_link ) {
        return;
    }

    weston_view_geometry_dirty(shell_surface_data->view);
    weston_layer_entry_remove(&shell_surface_data->view->layer_link);
/*
        weston_layer_entry_insert(new_layer_link, &shell_surface_data->view->layer_link);
*/
    shell_layer_entry_insert(
            new_layer_link,
            shell_surface_data );
    weston_view_geometry_dirty( shell_surface_data->view );
    weston_surface_damage( surface );

    shell_surface_update_child_surface_layers( shell_surface_data );
}
static void
shell_surface_set_output(
        struct shell_surface_data *shell_surface_data,
        struct weston_output *output )
{
    struct weston_surface *surface
        = fx_shell_surface_get_surface( shell_surface_data->shell_surface );

    /* get the default output, if the client set it as NULL
     * check whether the ouput is available */
    if ( output != NULL ) {
        shell_surface_data->output = output;
    } else if ( surface->output != NULL ) {
        shell_surface_data->output = surface->output;
    } else {
        shell_surface_data->output = get_default_output( surface->compositor );
    }
}

static void
black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy)
{
}

static struct weston_view *
create_black_surface(
        struct weston_compositor *ec,
        float x,
        float y,
        int w,
        int h )
{
    struct weston_surface *surface = NULL;
    struct weston_view *view;

    surface = weston_surface_create(ec);
    if (surface == NULL) {
        weston_log("no memory\n");
        return NULL;
    }
    view = weston_view_create(surface);
    if (!view) {
        weston_surface_destroy(surface);
        weston_log("no memory\n");
        return NULL;
    }

    surface->committed = black_surface_committed;
    surface->committed_private = NULL;
    weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f);
    pixman_region32_fini(&surface->opaque);
    pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
    pixman_region32_fini(&surface->input);
    pixman_region32_init_rect(&surface->input, 0, 0, w, h);

    weston_surface_set_size(surface, w, h);
    weston_view_set_position(view, x, y);

    return view;
}
static struct weston_output *
get_focused_output( struct weston_compositor *compositor )
{
    struct weston_seat *seat;
    struct weston_output *output = NULL;

    wl_list_for_each( seat, &compositor->seat_list, link ) {
        struct weston_touch *touch = weston_seat_get_touch( seat );
        struct weston_pointer *pointer = weston_seat_get_pointer( seat );
        struct weston_keyboard *keyboard = weston_seat_get_keyboard( seat );

        /* Priority has touch focus, then pointer and
         * then keyboard focus. We should probably have
         * three for loops and check frist for touch,
         * then for pointer, etc. but unless somebody has some
         * objections, I think this is sufficient. */
        
        if ( touch != NULL && touch->focus != NULL ) {
            output = touch->focus->output;
        } else if ( pointer != NULL && pointer->focus != NULL ) {
            output = pointer->focus->output;
        } else if ( keyboard != NULL && keyboard->focus != NULL ) {
            output = keyboard->focus->output;
        }
 
        if ( output != NULL ) {
            break;
        }
    }

    return output;
}
static void 
destroy_shell_seat(
        struct wl_listener *listener,
        void *data )
{
    struct shell_seat *shell_seat
        = container_of(listener, struct shell_seat, seat_destroy_listener);

    wl_list_remove(&shell_seat->seat_destroy_listener.link);
    if ( shell_seat->reset_keyboard_focus != NULL ) {
        wl_event_source_remove( shell_seat->reset_keyboard_focus );
    }
    free( shell_seat );
}
static void
shell_seat_caps_changed(
        struct wl_listener *listener,
        void *data )
{
    struct shell_seat *shell_seat
        = container_of( listener, struct shell_seat, caps_changed_listener );
    struct weston_keyboard *keyboard
        = weston_seat_get_keyboard( shell_seat->seat );
/*
    struct weston_pointer *pointer
        = weston_seat_get_pointer( shell_seat->seat);
    struct weston_touch *touch
        = weston_seat_get_touch( shell_seat->seat);
*/

    if ( keyboard != NULL ) {
        if ( wl_list_empty( &shell_seat->keyboard_focus_listener.link ) ) {
                wl_signal_add(
                        &keyboard->focus_signal,
                        &shell_seat->keyboard_focus_listener );
        }
        set_keyboard_focus( shell_seat );
    } else if ( keyboard == NULL ) {
        wl_list_remove( &shell_seat->keyboard_focus_listener.link );
        wl_list_init( &shell_seat->keyboard_focus_listener.link );
        if ( shell_seat->reset_keyboard_focus != NULL ) {
            wl_event_source_remove( shell_seat->reset_keyboard_focus );
            shell_seat->reset_keyboard_focus = NULL;
        }
    }
/*
    if ( pointer != NULL
            && wl_list_empty( &shell_seat->pointer_focus_listener.link ) ) {
        wl_signal_add(
                &pointer->focus_signal,
                &shell_seat->pointer_focus_listener );
    } else if ( pointer == NULL ) {
        wl_list_remove( &shell_seat->pointer_focus_listener.link );
        wl_list_init( &shell_seat->pointer_focus_listener.link );
    }
    if ( touch != NULL
            && wl_list_empty( &shell_seat->touch_focus_listener.link ) ) {
        wl_signal_add(
                &touch->focus_signal,
                &shell_seat->touch_focus_listener );
    } else if ( touch == NULL ) {
        wl_list_remove( &shell_seat->touch_focus_listener.link );
        wl_list_init( &shell_seat->touch_focus_listener.link );
    }
*/
}
static struct shell_seat*
create_shell_seat(
        struct fx_shell *shell,
        struct weston_seat *seat )
{
    struct shell_seat *shell_seat;

    shell_seat = zalloc( sizeof *shell_seat );
    if ( shell_seat == NULL ) {
        return NULL;
    }

    shell_seat->seat = seat;
    shell_seat->shell = shell;
    shell_seat->seat_destroy_listener.notify = destroy_shell_seat;
    wl_signal_add(
            &seat->destroy_signal,
            &shell_seat->seat_destroy_listener );

    shell_seat->keyboard_focus_listener.notify = handle_keyboard_focus;
    wl_list_init( &shell_seat->keyboard_focus_listener.link );

    shell_seat->caps_changed_listener.notify = shell_seat_caps_changed;
    wl_signal_add(
            &seat->updated_caps_signal,
            &shell_seat->caps_changed_listener );
    shell_seat_caps_changed(
            &shell_seat->caps_changed_listener,
            NULL );
    return shell_seat;
}
static void
fade_out_done_idle_cb( void *data )
{
    struct shell_surface_data *shell_surface_data = data;

    weston_surface_destroy( shell_surface_data->view->surface );
    free( shell_surface_data );
}
static void
fade_out_done(
        struct weston_view_animation *animation,
        void *data )
{
    struct shell_surface_data *shell_surface_data = data;
    struct wl_event_loop *loop;

    loop = wl_display_get_event_loop(
            shell_surface_data->shell->compositor->wl_display );

    if ( weston_view_is_mapped( shell_surface_data->view )) {
        shell_surface_data->view->is_mapped = false;
        wl_event_loop_add_idle(
                loop,
                fade_out_done_idle_cb,
                shell_surface_data );
    }
}

static void
shell_surface_added(
        struct fx_shell_surface *shell_surface,
        void *shell )
{
    struct fx_shell_client *shell_client =
        fx_shell_surface_get_client( shell_surface );
    struct wl_client *client =
         fx_shell_client_get_client( shell_client );
    struct weston_view *view;
    struct shell_surface_data *shell_surface_data;

    view = fx_shell_surface_create_view( shell_surface );
    if (!view)
        return;

    shell_surface_data =
        calloc(
            1,
            sizeof *shell_surface_data );
    if ( shell_surface_data == NULL ) {
        if ( client != NULL ) {
            wl_client_post_no_memory( client );
        } else {
            weston_log("no memory to allocate shell surface\n");
        }
        return;
    }

/*
    weston_surface_set_label_func(
            surface,
            shell_surface_get_label );
*/

    shell_surface_data->shell = ( struct fx_shell* ) shell;
    shell_surface_data->shell_surface = shell_surface;
    shell_surface_data->view = view;

    shell_surface_data->output =
        get_default_output( shell_surface_data->shell->compositor );
    
    wl_signal_init( &shell_surface_data->destroy_signal );

    /* empty when not in use */
    fx_shell_surface_set_user_data(
           shell_surface,
           shell_surface_data );
/*
    fx_shell_surface_set_activated(
           shell_surface,
           true );
*/
}
static void
shell_surface_removed(
        struct fx_shell_surface *shell_surface,
        void* data )
{
    struct shell_surface_data *shell_surface_data =
        fx_shell_surface_get_user_data( shell_surface );
    struct weston_surface *surface =
        fx_shell_surface_get_surface( shell_surface );
    bool anim_enabled;

    if ( shell_surface_data == NULL ) {
        return;
    }
    anim_enabled = fx_shell_surafce_get_anim_enabled(
            shell_surface_data->shell_surface );

    wl_signal_emit(
            &shell_surface_data->destroy_signal,
            shell_surface_data );

    weston_surface_set_label_func(
            surface,
            NULL );
    fx_shell_surface_set_user_data(
            shell_surface_data->shell_surface,
            NULL );
    shell_surface_data->shell_surface = NULL;

    fx_shell_surface_unlink_view( shell_surface_data->view );

    if (weston_surface_is_mapped( surface ) &&
            anim_enabled && 
            ( shell_surface_data->shell->win_close_animation_type
              == FX_SHELL_ANIMATION_FADE
              || shell_surface_data->shell->win_close_animation_type
              == FX_SHELL_ANIMATION_SLIDE ) ) {
        pixman_region32_fini( &surface->pending.input);
        pixman_region32_init( &surface->pending.input);
        pixman_region32_fini( &surface->input);
        pixman_region32_init( &surface->input);
        switch ( shell_surface_data->shell->win_close_animation_type ) {
            case FX_SHELL_ANIMATION_FADE:
                weston_fade_run(
                        shell_surface_data->view,
                        1.0,
                        0.0,
                        300.0,
                        fade_out_done,
                        shell_surface_data );
                break;
            case FX_SHELL_ANIMATION_SLIDE:
                weston_slide_ex_run(
                        shell_surface_data->view,
                        shell_surface_data->view->geometry.y,
                        shell_surface_data->view->output->height,
                        fade_out_done,
                        shell_surface_data );
                break;
            default:
                break;
        }
    } else {
        weston_view_destroy( shell_surface_data->view );
        free( shell_surface_data );
    }
}
static void
map(    struct fx_shell *shell,
        struct shell_surface_data *shell_surface_data,
        int32_t sx,
        int32_t sy )
{
    struct weston_compositor *compositor = shell->compositor;
    struct weston_seat *seat;
/*
    int flags;
*/

    /* initial positioning, see also configure() */
/*
    float from_x, from_y;
    float to_x, to_y;
*/
    float x, y;

/*
    weston_view_to_global_float(
            shell_surface_data->view,
            0,
            0,
            &from_x,
            &from_y );
    weston_view_to_global_float(
            shell_surface_data->view,
            sx,
            sy,
            &to_x,
            &to_y );
    x = shell_surface_data->view->geometry.x + to_x - from_x;
    y = shell_surface_data->view->geometry.y + to_y - from_y;
*/
    x = shell_surface_data->view->geometry.x;
    y = shell_surface_data->view->geometry.y;
    x += sx;
    y += sy;

    weston_view_set_position(
        shell_surface_data->view,
        x,
        y );

    /* Surface stacking order, see also activate(). */
    shell_surface_update_layer( shell_surface_data );

    weston_view_update_transform( shell_surface_data->view );
    shell_surface_data->view->is_mapped = true;

    wl_list_for_each( seat, &compositor->seat_list, link ) {
        activate(
                shell,
                shell_surface_data->view,
                seat,
                WESTON_ACTIVATE_FLAG_CONFIGURE );
    }

    if (
        fx_shell_surafce_get_anim_enabled( shell_surface_data->shell_surface )
    ) {
        switch ( shell->win_animation_type ) {
            case FX_SHELL_ANIMATION_FADE:
                weston_fade_run(
                        shell_surface_data->view,
                        0.0,
                        1.0,
                        300.0,
                        NULL,
                        NULL );
                break;
            case FX_SHELL_ANIMATION_ZOOM:
                weston_zoom_run(
                        shell_surface_data->view,
                        0.5,
                        1.0,
                        NULL,
                        NULL );
                break;
            case FX_SHELL_ANIMATION_SLIDE:
                weston_slide_ex_run(
                        shell_surface_data->view,
                        shell_surface_data->view->output->height,
                        y,
                        NULL,
                        NULL );
                break;
            default:
                break;
        }
    }
}
static struct fx_shell_output *
find_shell_output_from_weston_output(
        struct fx_shell *shell,
        struct weston_output *output )
{
    struct fx_shell_output *shell_output;

    wl_list_for_each( shell_output, &shell->output_list, link ) {
        if ( shell_output->output == output ) return shell_output;
    }
    return NULL;
}
static void
shell_surface_committed(
        struct fx_shell_surface *shell_surface,
        int32_t sx,
        int32_t sy,
        void* data )
{
    struct shell_surface_data *shell_surface_data =
        fx_shell_surface_get_user_data( shell_surface );
    struct weston_surface *surface =
        fx_shell_surface_get_surface( shell_surface );
    struct weston_view *view = shell_surface_data->view;
    struct fx_shell *shell = data;

    if ( surface->width == 0 ) {
        return;
    }

    if ( !weston_surface_is_mapped( surface ) ) {
        struct fx_shell_output* shell_output;

        map(    shell,
                shell_surface_data,
                sx,
                sy );
        surface->is_mapped = true;
        if ( fx_shell_surafce_get_anim_enabled(
                shell_surface_data->shell_surface ) ) {
            if ( shell_surface_data ->shell->win_close_animation_type ==
                    FX_SHELL_ANIMATION_FADE
                    || shell_surface_data->shell->win_close_animation_type
                    == FX_SHELL_ANIMATION_SLIDE ) {
                ++surface->ref_count;
            }
        }
        shell_output = find_shell_output_from_weston_output(
                shell,
                surface->output );

        if ( shell_output != NULL ) {
            if ( !weston_view_is_mapped( shell_output->black_view ) ) {
                shell_output->black_view->surface->is_mapped = true;
                shell_output->black_view->is_mapped = true;
                weston_layer_entry_insert(
                        &shell->background_layer.view_list,
                        &shell_output->black_view->layer_link );
            }
        }
        return;
    }
    if ( sx == 0 && sy == 0 &&
            shell_surface_data->last_width == surface->width &&
            shell_surface_data->last_height == surface->height ) {
        return;
    }
/*
    float from_x, from_y;
    float to_x, to_y;
*/
    float x, y;

    shell_surface_data->last_width = surface->width;
    shell_surface_data->last_height = surface->height;

/*
    weston_view_to_global_float(
            shell_surface_data->view,
            0,
            0,
            &from_x,
            &from_y );
    weston_view_to_global_float(
            shell_surface_data->view,
            sx,
            sy,
            &to_x,
            &to_y );
    x = shell_surface_data->view->geometry.x + to_x - from_x;
    y = shell_surface_data->view->geometry.y + to_y - from_y;
*/
    x = shell_surface_data->view->geometry.x;
    y = shell_surface_data->view->geometry.y;
    x += sx;
    y += sy;
    weston_view_set_position(
            shell_surface_data->view,
            x,
            y );
    if ( surface->output != NULL ) {
        wl_list_for_each( view, &surface->views, surface_link ) {
            weston_view_update_transform( view );
        }
    }
}
static void
set_fullscreen(
        struct shell_surface_data *shell_surface_data,
        bool fullscreen,
        struct weston_output *output )
{
    struct fx_shell_surface *shell_surface =
        shell_surface_data->shell_surface;
    struct weston_surface *surface =
        fx_shell_surface_get_surface(
                shell_surface_data->shell_surface );
    int32_t width = 0, height = 0;

    if ( fullscreen ) {
        /* handle clients launching in fullscreen */
        if ( output == NULL && !weston_surface_is_mapped( surface ) ) {
            /* Set the output to the one that has focus currently. */
            output = get_focused_output( surface->compositor );
        }
        
        shell_surface_set_output(shell_surface_data, output);
        
        width = shell_surface_data->output->width;
        height = shell_surface_data->output->height;
    }
    fx_shell_surface_set_fullscreen(
            shell_surface,
            fullscreen );
    fx_shell_surface_set_size(
            shell_surface,
            width,
            height );
}
static void
shell_surface_fullscreen_requested(
        struct fx_shell_surface *shell_surface,
        bool fullscreen,
        struct weston_output *output,
        void *user_data )
{
    struct shell_surface_data *shell_surface_data
        = fx_shell_surface_get_user_data( shell_surface );

    set_fullscreen(
            shell_surface_data,
            fullscreen,
            output );
}
static const struct weston_fx_shell_api weston_fx_shell_api = {
    .struct_size = sizeof ( struct weston_fx_shell_api ),
    .surface_added = shell_surface_added,
    .surface_removed = shell_surface_removed,
    .committed = shell_surface_committed,
    .fullscreen_requested = shell_surface_fullscreen_requested,
};

#if 0
static int
background_get_label(
        struct weston_surface *surface,
        char *buf,
        size_t len )
{
        return snprintf(
            buf,
            len,
            "background for output %s",
                        surface->output->name );
}
static void
background_committed(
        struct weston_surface *surface,
        int32_t sx,
        int32_t sy )
{
        struct desktop_shell *shell = surface->committed_private;
        struct weston_view *view;

        view =
        container_of( surface->views.next, struct weston_view, surface_link );

        configure_static_view( view,
            &shell->background_layer,
            0,
            0 );
}
static void
handle_background_surface_destroy(
        struct wl_listener *listener,
        void *data )
{
        struct fx_shell_output *shell_output =
            container_of( listener, struct shell_output,
                background_surface_listener );

        weston_log( "background surface gone\n" );
        shell_output->background_surface = NULL;
}

static void
desktop_shell_set_background(
        struct wl_client *client,
        struct wl_resource *resource,
        struct wl_resource *output_resource,
        struct wl_resource *surface_resource )
{
        struct desktop_shell *shell = wl_resource_get_user_data( resource );
        struct weston_surface *surface =
                wl_resource_get_user_data( surface_resource );
        struct shell_output *sh_output;
        struct weston_view *view, *next;

        if ( surface->committed ) {
                wl_resource_post_error(
                surface_resource,
                                WL_DISPLAY_ERROR_INVALID_OBJECT,
                                "surface role already assigned");
                return;
        }

        wl_list_for_each_safe( view, next, &surface->views, surface_link ) {
                weston_view_destroy(view);
    }
        view = weston_view_create( surface);

        surface->committed = background_committed;
        surface->committed_private = shell;
        weston_surface_set_label_func(
            surface,
            background_get_label );
        surface->output = wl_resource_get_user_data( output_resource );
        view->output = surface->output;
        weston_desktop_shell_send_configure(
            resource,
            0,
            surface_resource,
            surface->output->width,
            surface->output->height );

        sh_output = find_shell_output_from_weston_output(
            shell,
            surface->output );
        sh_output->background_surface = surface;

        sh_output->background_surface_listener.notify =
        handle_background_surface_destroy;
        wl_signal_add(
            &surface->destroy_signal,
            &sh_output->background_surface_listener );
}
#endif /* 0 */

static void
activate(
        struct fx_shell *shell,
        struct weston_view *view,
        struct weston_seat *seat,
        uint32_t flags )
{
    struct weston_surface *surface = view->surface;
    struct weston_keyboard *keyboard = weston_seat_get_keyboard( seat );
    struct weston_surface *main_surface;
    struct shell_surface_data *shell_surface_data;

    main_surface = weston_surface_get_main_surface( surface );
    shell_surface_data = get_shell_surface_data( main_surface );
    assert( shell_surface_data != NULL );

/*
    if ( ( shell_surface->flags
           & FX_EXTENDED_SURFACE_WINDOWFLAG_DOESNOTACCEPTFOCUS ) == 0 ) {
        weston_surface_activate(
                surface,
                seat );
    }
*/

/*
    if ( fx_shell_surface_get_fullscreen( shell_surface_data->shell_surface ) &&
            flags & WESTON_ACTIVATE_FLAG_CONFIGURE ) {
        shell_configure_fullscreen(shell_surface_data);
    }
*/

    /* Update the surface’s layer. This brings it to the top of the stacking
     * order as appropriate. */
    shell_surface_update_layer( shell_surface_data );

    if ( keyboard != NULL /* && keyboard->focus == NULL */ ) {
        layer_reset_keyboard_focus(
                seat,
                shell );
    }
    if ( strcmp( seat->seat_name, FX_SHELL_SEAT_NAME ) == 0 ) {
        struct fx_shell_surface *shell_surface =
            fx_shell_surface_get_shell_surface(surface);

        if ( shell_surface != NULL &&
                fx_shell_surafce_get_shadow_focus( shell_surface ) ) {
            weston_keyboard_set_focus(
                    keyboard,
                    surface );
        }
    }
}
static void
handle_output_destroy(
        struct wl_listener *listener,
        void *data )
{
    struct fx_shell_output *output_listener
        = container_of(listener, struct fx_shell_output, destroy_listener);

    wl_list_remove(&output_listener->destroy_listener.link);
    wl_list_remove(&output_listener->link);
    free(output_listener);
}
static void
setup_output_destroy_handler(
        struct weston_compositor *compositor,
        struct fx_shell *shell )
{
    struct weston_output *output;

    wl_list_init( &shell->output_list );
    wl_list_for_each( output, &compositor->output_list, link ) {
        create_shell_output(
                shell,
                output );
    }
    shell->output_create_listener.notify = handle_output_create;
    wl_signal_add(
            &compositor->output_created_signal,
            &shell->output_create_listener );
}
static void
create_shell_output(
        struct fx_shell *shell,
        struct weston_output *output )
{
    struct fx_shell_output *shell_output;

    shell_output = zalloc( sizeof *shell_output );
    if ( shell_output == NULL ) {
        return;
    }
    shell_output->output = output;
    shell_output->shell = shell;
    shell_output->destroy_listener.notify = handle_output_destroy;
    wl_signal_add(
            &output->destroy_signal,
            &shell_output->destroy_listener );
    wl_list_insert(
            shell->output_list.prev,
            &shell_output->link );
    shell_output->black_view = create_black_surface(
            shell->compositor,
            output->x,
            output->y,
            output->width,
            output->height );
}
static void
handle_output_create(
        struct wl_listener *listener,
        void *data )
{
    struct fx_shell *shell =
        container_of(listener, struct fx_shell, output_create_listener);
    struct weston_output *output = (struct weston_output *)data;

    create_shell_output(
            shell,
            output );
}
static void
shell_destroy(
        struct wl_listener *listener,
        void *data )
{
    struct fx_shell *shell =
        container_of(listener, struct fx_shell, seat_create_listener);
    struct fx_shell_output *shell_output, *tmp;

    wl_array_release( &shell->layers );
    wl_list_for_each_safe(shell_output, tmp, &shell->output_list, link) {
        wl_list_remove(&shell_output->destroy_listener.link);
        wl_list_remove(&shell_output->link);
        free(shell_output);
    }
    wl_list_remove(&shell->output_create_listener.link);
    free(shell);
}
static void
handle_seat_created(
        struct wl_listener *listener,
        void *data )
{
    struct fx_shell *shell =
        container_of(listener, struct fx_shell, seat_create_listener);
    struct weston_seat *seat = data;

    create_shell_seat(
            shell,
            seat );
}

void
fx_shell_raise_shell_surface(
        struct fx_shell_surface *shell_surface )
{
    struct shell_surface_data *shell_surface_data =
        fx_shell_surface_get_user_data( shell_surface );

    if ( shell_surface_data == NULL ) return;

    struct weston_layer* layer = shell_surface_data->view->layer_link.layer;

    if ( layer != NULL ) {
        weston_layer_entry_remove( &shell_surface_data->view->layer_link );

        shell_layer_entry_insert(
                &layer->view_list,
                shell_surface_data );
        shell_surface_update_child_surface_layers( shell_surface_data );
    }
}
void
fx_shell_lower_shell_surface(
        struct fx_shell_surface *shell_surface )
{
    struct shell_surface_data *shell_surface_data =
        fx_shell_surface_get_user_data( shell_surface );

    if ( shell_surface_data == NULL ) return;

    struct weston_layer* layer = shell_surface_data->view->layer_link.layer;

    if ( layer != NULL ) {
        struct weston_layer_entry *prev = &layer->view_list;
        struct weston_view* view;
        
        weston_layer_entry_remove( &shell_surface_data->view->layer_link );

        wl_list_for_each(
                view,
                &layer->view_list.link,
                layer_link.link ) {
            prev = &view->layer_link;
            if ( view->parent_view == shell_surface_data->view->parent_view ) {
                struct fx_shell_surface *ss
                    = fx_shell_surface_get_shell_surface( view->surface );

                if ( ss != NULL ) {
                    struct shell_surface_data *ss_data =
                        fx_shell_surface_get_user_data( ss );
                    const int32_t mask =
                        QT_EXTENDED_SURFACE_WINDOWFLAG_STAYSONTOP;
                    int flags = fx_shell_surafce_get_flags( shell_surface );
                    int f = fx_shell_surafce_get_flags(
                            ss_data->shell_surface );

                    if ( ( flags & mask ) != 0 ) {
                        if ( ( f & mask ) == 0 ) break;
                    }
                }
            }
        }
        weston_layer_entry_insert(
                prev,
                &shell_surface_data->view->layer_link );
        shell_surface_update_child_surface_layers( shell_surface_data );
    }
}
struct weston_view*
fx_shell_get_shell_surface_view( struct fx_shell_surface *shell_surface )
{

    struct shell_surface_data *shell_surface_data =
        fx_shell_surface_get_user_data( shell_surface );

    return shell_surface_data->view;
}

WL_EXPORT int
wet_shell_init(
        struct weston_compositor *compositor,
        int *argc,
        char *argv[] )
{
    const int max_layers = 5;
    struct fx_shell *shell;
    struct weston_seat *seat;
    int i;

    shell = zalloc( sizeof *shell );
    if ( shell == NULL ) {
        return -1;
    }
    shell->compositor = compositor;

    shell->destroy_listener.notify = shell_destroy;
    wl_signal_add(
            &compositor->destroy_signal,
            &shell->destroy_listener );

    /* don't enter idle state forever */
    compositor->idle_inhibit++;

    wl_array_init( &shell->layers );
    wl_array_add(
            &shell->layers,
            sizeof ( struct weston_layer ) * max_layers );
    for ( i = 0; i < max_layers; ++i ) {
        struct weston_layer* layer = shell->layers.data;

        weston_layer_init(
                layer + i,
                compositor );
        weston_layer_set_position(
                layer + i,
                WESTON_LAYER_POSITION_NORMAL + i );
    }
    weston_layer_init(
            &shell->background_layer,
            compositor );
    weston_layer_set_position(
            &shell->background_layer,
            WESTON_LAYER_POSITION_BACKGROUND );

    shell_configuration( shell );

    setup_output_destroy_handler(
            compositor,
            shell );

    wl_list_for_each(seat, &compositor->seat_list, link)
        create_shell_seat(
                shell,
                seat );
    shell->seat_create_listener.notify = handle_seat_created;
    wl_signal_add(
            &compositor->seat_created_signal,
            &shell->seat_create_listener );

    weston_compositor_add_button_binding(
            compositor,
            BTN_LEFT,
            0,
            click_to_activate_binding,
            shell );
    weston_compositor_add_touch_binding(
            compositor,
            0,
            touch_to_activate_binding,
            shell );

    shell->weston_fx_shell =
        weston_fx_shell_create(
                compositor,
                &weston_fx_shell_api,
                shell );
    shell->controller = fx_shell_controller_create( shell );
    if ( shell->controller == NULL ) {
        return -1;
    }
    shell->qt_surface_extension = qt_surface_extension_create( shell );
    if ( shell->qt_surface_extension == NULL ) {
        return -1;
    }
    shell->fx_surface_extension = fx_surface_extension_create( shell );
    if ( shell->fx_surface_extension == NULL ) {
        return -1;
    }
    shell->fx_swkeyboard = fx_swkeyboard_create( shell );
    if ( shell->fx_swkeyboard == NULL ) {
        return -1;
    }
    pixman_region32_clear( &compositor->primary_plane.damage );

    return 0;
}
