/*
(C) Copyright 2014 Fuji Xerox Co.,Ltd. All rights are reserved.

File Name: compositor-prop.c

*/
#include "config.h"

#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "fxproptypes.h"
#include "libfxpropscreen.h"
#include "libfxpropserver.h"
#include "libfxpropclient.h"
#include "shared/helpers.h"
#include "fx-screen-share-server-protocol.h"
#include "compositor.h"
#include "pixman-renderer.h"
#include "compositor-fx-prop.h"

#define DEFAULT_AXIS_STEP_DISTANCE 10
#define PROP_MODE_FREQ 60 * 1000

struct fx_prop_output;

struct fx_prop_backend {
    struct weston_backend base;
    struct weston_compositor *compositor;

    struct wl_event_source *listener_event;
    struct fx_prop_output *fx_prop_output;
    int no_clients_resize;

    fxPropScreen_t fx_prop_screen;
    fxPropServer_t fx_prop_server;

    char* xkb_keymap_file;
    struct xkb_rule_names xkb_rule_names;

#ifdef ENABLE_FX_SCREEN_SHARING
    struct wl_list resource_list;
    int connected;
#endif
};

struct fx_prop_peer_context {
    struct fx_prop_backend *fx_prop_backend;
    struct wl_event_source *event;
    struct weston_seat seat;

    struct weston_position positions[ 4 ];
    fxPropClientPeer_t fx_prop_client_peer;

    struct xkb_context *xkb_context;
    struct xkb_keymap *xkb_keymap;
};

struct fx_prop_output {
    struct weston_output base;
    struct wl_event_source *finish_frame_timer;
    char* shared_fb_name;
    pixman_image_t *shadow_surface;
};

static int 
fx_prop_enable_screen(
        struct fx_prop_backend *b,
        struct fx_prop_output *fx_prop_output );
static int 
fx_prop_disable_screen(
        struct fx_prop_backend *b,
        struct fx_prop_output *fx_prop_output );

/* Procedures */
static inline struct fx_prop_output *
to_fx_prop_output(struct weston_output *base)
{
    return container_of(base, struct fx_prop_output, base);
}
static inline struct fx_prop_backend *
to_fx_prop_backend(struct weston_compositor *base)
{
    return container_of(base->backend, struct fx_prop_backend, base);
}
static int
remove_files(
        const char* name,
        const char* prefix )
{
    DIR *dir = opendir( name );
    size_t n = strlen( prefix );

    if ( dir != NULL ) {
        while ( true ) {
            struct dirent* dirent = readdir( dir );

            if ( dirent == NULL ) break;
            if ( strncmp( dirent->d_name, prefix, n ) == 0 ) {
                char path[ PATH_MAX ];

                snprintf(
                        path,
                        sizeof ( path ),
                        "%s/%s",
                        name,
                        dirent->d_name );
                unlink( path );
            }
        }
        closedir( dir );
    }
    return 0;
}
static void*
get_shared_fb(
        char* shared_fb_name,
        int length )
{
    char templ[] = "/tmp/dapp-XXXXXX";
    int result, fd;

    ( void ) remove_files(
            "/tmp",
            "dapp-" );
    strcpy( shared_fb_name,
            mktemp( templ ) );
    fd = open(
            shared_fb_name,
            O_CREAT | O_RDWR );
    if ( fd >= 0 ) {
        ( void ) fchmod(
                fd,
                S_IRWXU | S_IRWXG | S_IRWXO );
        result = ftruncate(
                fd,
                length );
        if ( result >= 0 ) {
            void* addr = mmap(
                    0,
                    length,
                    PROT_READ | PROT_WRITE,
                    MAP_SHARED,
                    fd,
                    0 );

            if ( addr != MAP_FAILED ) {
                close( fd );
                return addr;
            }
        }
        close( fd );
    }
    return MAP_FAILED;
}
static int
fx_prop_set_screen_image(
        struct fx_prop_backend *b,
        const char* shared_fb_name,
        pixman_image_t* image )
{
    if ( image != NULL ) {
        pixman_image_ref( image );
        ( void ) FxPropScreen_SetSharedFbName(
                b->fx_prop_screen,
                shared_fb_name );
        ( void ) FxPropScreen_SetImage(
                b->fx_prop_screen,
                image );
    }
    return 0;
}
static int
fx_prop_reset_screen_image( struct fx_prop_backend *b )
{
    pixman_image_t* image = FxPropScreen_GetImage( b->fx_prop_screen );

    if ( image != NULL ) {
        munmap( pixman_image_get_data( image ),
                pixman_image_get_stride( image ) *
                pixman_image_get_height( image ) );
        pixman_image_unref( image );
    }
    return 0;
}

static void
fx_prop_refresh_region(
        struct fx_prop_output* fx_prop_output,
        pixman_region32_t *region )
{
    struct weston_compositor *compositor = fx_prop_output->base.compositor;
    struct fx_prop_backend *b = ( struct fx_prop_backend * ) compositor->backend;
    pixman_box32_t *rect, subrect;
    int nrects, i;
    int remainingHeight, top;

    rect = pixman_region32_rectangles(
            region,
            &nrects );
    if (!nrects){
        return;
    }

    for (i = 0; i < nrects; i++, rect++) {
        remainingHeight = rect->y2 - rect->y1;
        top = rect->y1;

        subrect.x1 = rect->x1;
        subrect.x2 = rect->x2;

        while (remainingHeight) {
            subrect.y1 = top;
            subrect.y2 = top + remainingHeight;

            ( void ) FxPropScreen_AddDirty(
                    b->fx_prop_screen,
                    &subrect );
            break;
        }
    }
}

static void
fx_prop_output_start_repaint_loop(struct weston_output *fx_prop_output)
{
    struct timespec ts;

    weston_compositor_read_presentation_clock(fx_prop_output->compositor, &ts);
    weston_output_finish_frame(fx_prop_output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
}

static int
fx_prop_output_repaint(
        struct weston_output *output,
        pixman_region32_t *damage,
        void *repaint_data )
{
    struct fx_prop_output *fx_prop_output = container_of(
                output,
                struct fx_prop_output,
                base );
    struct weston_compositor *compositor = fx_prop_output->base.compositor;
    struct fx_prop_backend *b = ( struct fx_prop_backend * ) compositor->backend;

    wl_event_source_timer_update(
            fx_prop_output->finish_frame_timer,
            16 );
    pixman_renderer_output_set_buffer(
            output,
            fx_prop_output->shadow_surface );
    compositor->renderer->repaint_output(
            &fx_prop_output->base,
            damage );

    if ( pixman_region32_not_empty( damage ) ) {
        fx_prop_refresh_region(
                fx_prop_output,
                damage);
    }
    pixman_region32_subtract(
            &compositor->primary_plane.damage,
            &compositor->primary_plane.damage,
            damage );

    ( void ) FxPropServer_Update( b->fx_prop_server );

    return 0;
}
static int
finish_frame_handler(void *data)
{
    struct fx_prop_output *fx_prop_output = data;
    struct timespec ts;

    weston_compositor_read_presentation_clock(fx_prop_output->base.compositor, &ts);
    weston_output_finish_frame(&fx_prop_output->base, &ts, 0);

    return 1;
}
static struct weston_mode *
fx_prop_insert_new_mode(
        struct weston_output *output,
        int width,
        int height,
        int rate ) 
{
    struct weston_mode *ret;

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

    ret->width = width;
    ret->height = height;
    ret->refresh = rate;
    wl_list_insert(
            &output->mode_list,
            &ret->link );
    return ret;
}

static struct weston_mode *
ensure_matching_mode(
        struct weston_output *output,
        struct weston_mode *target ) 
{
    struct weston_mode *local;

    wl_list_for_each(local, &output->mode_list, link) {
        if (
            (local->width == target->width)
            &&
            (local->height == target->height)
        ) {
            return local;
        }
    }

    return fx_prop_insert_new_mode(
            output,
            target->width,
            target->height,
            PROP_MODE_FREQ );
}

static int
fx_prop_switch_mode(
        struct weston_output *output,
        struct weston_mode *target_mode ) 
{
    struct fx_prop_output *fx_prop_output = container_of(
            output,
            struct fx_prop_output,
            base );
    struct weston_compositor *compositor = fx_prop_output->base.compositor;
    struct fx_prop_backend *b = ( struct fx_prop_backend* ) compositor->backend;
    char shared_fb_name[ PATH_MAX ];
    pixman_image_t *new_shadow_buffer;
    struct weston_mode *local_mode;
    void* addr;
    int result;

    local_mode = ensure_matching_mode(
            output,
            target_mode );
    if (!local_mode) {
        weston_log(
                "mode %dx%d not available\n",
                target_mode->width,
                target_mode->height);
        return -ENOENT;
    }
    if ( local_mode == output->current_mode ) {
        return 0;
    }

    output->current_mode->flags &= ~WL_OUTPUT_MODE_CURRENT;

    output->current_mode = local_mode;
    output->current_mode->flags |= WL_OUTPUT_MODE_CURRENT;

    pixman_renderer_output_destroy( output );
    pixman_renderer_output_create(
            output,
            0 );

    addr = get_shared_fb(
            shared_fb_name,
            target_mode->width * 4 * target_mode->height );
    if ( addr == MAP_FAILED ) {
        weston_log(
                "share fb is not available\n" );
        return -ENOENT;
    }
    new_shadow_buffer = pixman_image_create_bits(
            PIXMAN_x8r8g8b8,
            target_mode->width,
            target_mode->height,
            addr,
            target_mode->width * 4 );
    pixman_image_composite32(
            PIXMAN_OP_SRC,
            fx_prop_output->shadow_surface,
            0,
            new_shadow_buffer,
            0, 0, 0, 0, 0, 0,
            target_mode->width,
            target_mode->height );
    if ( fx_prop_output->shared_fb_name != NULL ) {
        free( fx_prop_output->shared_fb_name );
    }
    if ( fx_prop_output->shadow_surface ) {
        munmap( pixman_image_get_data( fx_prop_output->shadow_surface ),
                pixman_image_get_stride( fx_prop_output->shadow_surface ) *
                pixman_image_get_height( fx_prop_output->shadow_surface ) );
        pixman_image_unref( fx_prop_output->shadow_surface );
    }
    fx_prop_output->shared_fb_name = strdup( shared_fb_name );
    fx_prop_output->shadow_surface = new_shadow_buffer;

    if ( b->fx_prop_server != NULL ) {
         ( void ) FxPropServer_DestroyClients( b->fx_prop_server );
    }
    if ( b->fx_prop_screen != NULL ) {
        ( void ) fx_prop_reset_screen_image( b );
        result = fx_prop_set_screen_image(
                b,
                shared_fb_name,
                new_shadow_buffer );
        if ( result < 0 ) {
            return -EINVAL;
        }
    }
    return 0;
}
static int
fx_prop_output_set_size(
        struct weston_output *base,
        int width,
        int height )
{
    struct fx_prop_output *fx_prop_output = to_fx_prop_output(base);
    struct weston_mode *currentMode;
    struct weston_mode initMode;

    /* We can only be called once. */
    assert(!fx_prop_output->base.current_mode);

    initMode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
    initMode.width = width;
    initMode.height = height;
    initMode.refresh = PROP_MODE_FREQ;

    currentMode = ensure_matching_mode(&fx_prop_output->base, &initMode);
    if (!currentMode)
        return -1;

    fx_prop_output->base.current_mode = fx_prop_output->base.native_mode = currentMode;
    fx_prop_output->base.make = "weston";
    fx_prop_output->base.model = "prop";

    /* XXX: Calculate proper size. */
    fx_prop_output->base.mm_width = width;
    fx_prop_output->base.mm_height = height;

    fx_prop_output->base.start_repaint_loop = fx_prop_output_start_repaint_loop;
    fx_prop_output->base.repaint = fx_prop_output_repaint;
    fx_prop_output->base.assign_planes = NULL;
    fx_prop_output->base.set_backlight = NULL;
    fx_prop_output->base.set_dpms = NULL;
    fx_prop_output->base.switch_mode = fx_prop_switch_mode;

    return 0;
}

static int
fx_prop_output_enable( struct weston_output *base )
{
    struct fx_prop_output *fx_prop_output = to_fx_prop_output(base);
    struct fx_prop_backend *b = to_fx_prop_backend(base->compositor);
    struct wl_event_loop *loop;
    char shared_fb_name[ PATH_MAX ];
    void* addr;
    int result;

    addr = get_shared_fb(
            shared_fb_name,
            fx_prop_output->base.current_mode->width * 4
            * fx_prop_output->base.current_mode->height );
    if ( addr == MAP_FAILED ) {
        weston_log(
                "share fb is not available\n" );
        return -ENOENT;
    }
    fx_prop_output->shared_fb_name = strdup( shared_fb_name );
    fx_prop_output->shadow_surface
        = pixman_image_create_bits(
                PIXMAN_x8r8g8b8,
                fx_prop_output->base.current_mode->width,
                fx_prop_output->base.current_mode->height,
                addr,
                fx_prop_output->base.current_mode->width * 4);
    if ( fx_prop_output->shadow_surface == NULL ) {
        weston_log("Failed to create surface for frame buffer.\n");
        return -1;
    }

    if (pixman_renderer_output_create( &fx_prop_output->base, 0 ) < 0 ) {
        pixman_image_unref(fx_prop_output->shadow_surface);
        return -1;
    }
    result = fx_prop_enable_screen(
            b,
            fx_prop_output );
    if ( result < 0 ) {
        weston_log("Failed to create prop screen.\n");
        return -1;
    }
    loop = wl_display_get_event_loop( b->compositor->wl_display );
    fx_prop_output->finish_frame_timer
            = wl_event_loop_add_timer(
                    loop,
                    finish_frame_handler,
                    fx_prop_output );

    b->fx_prop_output = fx_prop_output;

    return 0;
}
static int
fx_prop_output_disable( struct weston_output *base )
{
    struct fx_prop_output *fx_prop_output = to_fx_prop_output( base );
    struct fx_prop_backend *b = to_fx_prop_backend( base->compositor );
    int result;

    if ( !fx_prop_output->base.enabled )
        return 0;

    pixman_image_unref( fx_prop_output->shadow_surface );
    pixman_renderer_output_destroy( &fx_prop_output->base );

    wl_event_source_remove( fx_prop_output->finish_frame_timer );
    b->fx_prop_output = NULL;
    result = fx_prop_disable_screen(
            b,
            fx_prop_output );
    if ( result < 0 ) {
        weston_log("Failed to destroy prop screen.\n");
        return -1;
    }
    return 0;
}
static void
fx_prop_output_destroy(struct weston_output *output)
{
    struct fx_prop_output *fx_prop_output = to_fx_prop_output(output);

    wl_event_source_remove(fx_prop_output->finish_frame_timer);
    weston_output_release(&fx_prop_output->base);
    free(fx_prop_output);
}

static const struct weston_fx_prop_output_api api = {
    fx_prop_output_set_size,
};

static int
fx_prop_backend_create_output(
        struct fx_prop_backend *b,
        struct weston_fx_prop_backend_config* config )
{
    struct fx_prop_output *fx_prop_output;

    fx_prop_output = zalloc(sizeof *fx_prop_output);
    if (fx_prop_output == NULL)
        return -1;

    weston_output_init(
            &fx_prop_output->base,
            b->compositor,
            "fx prop" );
            
    fx_prop_output->base.destroy = fx_prop_output_destroy;
    fx_prop_output->base.disable = fx_prop_output_disable;
    fx_prop_output->base.enable = fx_prop_output_enable;

    weston_compositor_add_pending_output(
            &fx_prop_output->base,
            b->compositor );

    return 0;
}

static void
fx_prop_destroy(struct weston_compositor *compositor)
{
    struct fx_prop_backend *b = to_fx_prop_backend( compositor );

    weston_compositor_shutdown(compositor);

    if ( b->listener_event != NULL ) {
        wl_event_source_remove( b->listener_event );
    }
    if ( b->fx_prop_server != NULL ) {
        ( void ) FxPropServer_DestroyClients( b->fx_prop_server );
    }
    if ( b->fx_prop_screen != NULL ) {
        ( void ) fx_prop_reset_screen_image( b );
        ( void ) FxPropScreen_Destroy( b->fx_prop_screen );
    }
    if ( b->fx_prop_server != NULL ) {
        FxPropServer_Destroy( b->fx_prop_server );
    }
    free( ( void* ) b->xkb_keymap_file );
    free( ( void* ) b->xkb_rule_names.model );
    free( ( void* ) b->xkb_rule_names.layout );
    free( ( void* ) b->xkb_rule_names.variant );

    free( b );
}
static int
fx_prop_listener_activity(
        int fd,
        uint32_t mask,
        void *data ) 
{
    struct fx_prop_backend *b = ( struct fx_prop_backend * ) data;

    ( void ) FxPropServer_Dispatch(
            b->fx_prop_server,
            fd );
    return 0;
}

#ifdef ENABLE_FX_SCREEN_SHARING
static void
send_connection( struct fx_prop_backend *b )
{
    struct wl_resource *resource;

    wl_resource_for_each(resource, &b->resource_list ) {
        fx_rdp_send_connection(
                resource,
                b->connected != 0 );
    }
}
#endif
static int
fx_prop_client_activity(
        int fd,
        uint32_t mask,
        void *data ) 
{
    struct fx_prop_peer_context* peer_context = ( struct fx_prop_peer_context* ) data;
    fxPropClientPeer_t fx_prop_client_peer = peer_context->fx_prop_client_peer;
    fxPropIo_t fx_prop_io = FxPropClientPeer_GetIo( fx_prop_client_peer );

    ( void ) FxPropClientPeer_Dispatch( fx_prop_client_peer );
    ( void ) FxPropIo_Flush( fx_prop_io );

    return 0;
}

static int
new_client(
        void* client_data,
        fxPropClientPeer_t fx_prop_client_peer )
{
    struct fx_prop_backend *b = ( struct fx_prop_backend * ) client_data;
    struct fx_prop_peer_context* peer_context = zalloc( sizeof *peer_context );

    if ( peer_context != NULL ) {
        struct wl_event_loop * loop
            = wl_display_get_event_loop( b->compositor->wl_display );

        peer_context->fx_prop_client_peer = fx_prop_client_peer;
        peer_context->fx_prop_backend = b;

        peer_context->event = wl_event_loop_add_fd(
                loop,
                FxPropIo_GetFd(
                    FxPropClientPeer_GetIo( fx_prop_client_peer ) ),
                WL_EVENT_READABLE,
                fx_prop_client_activity,
                peer_context );

        peer_context->xkb_keymap = NULL;

        if ( b->xkb_keymap_file != NULL || b->xkb_rule_names.layout != NULL ) {
            peer_context->xkb_context = xkb_context_new( 0 );
            if  ( peer_context->xkb_context == NULL ) {
                weston_log("unable to create a xkb_context\n");
                return false;
            }
            if ( b->xkb_keymap_file != NULL ) {
                FILE *fp = fopen( b->xkb_keymap_file, "r" );

                if ( fp != NULL ) {
                    peer_context->xkb_keymap = xkb_map_new_from_file(
                            peer_context->xkb_context,
                            fp,
                            XKB_KEYMAP_FORMAT_TEXT_V1,
                            0 );
                    fclose( fp );
                } else {
                    weston_log("unable to open xkb_keymap_file %s\n", b->xkb_keymap_file);
                }
            } else {
                peer_context->xkb_keymap = xkb_keymap_new_from_names(
                        peer_context->xkb_context,
                        &b->xkb_rule_names,
                        0 );
            }
        }
        weston_seat_init(
                &peer_context->seat,
                b->compositor,
                "prop" );
        weston_seat_init_touch( &peer_context->seat );
        weston_seat_init_keyboard(
                &peer_context->seat,
                peer_context->xkb_keymap );
        weston_seat_init_pointer( &peer_context->seat );
        ( void ) FxPropClientPeer_SetClientData(
                fx_prop_client_peer,
                peer_context );
#ifdef ENABLE_FX_SCREEN_SHARING
        b->connected += 1;
        send_connection( b );
#endif
        return 0;
    }
    return -1;
}
static int
discard_client(
        void* client_data,
        fxPropClientPeer_t fx_prop_client_peer )
{
    struct fx_prop_peer_context* peer_context
        = ( struct fx_prop_peer_context *) client_data;
    struct fx_prop_backend *b = peer_context->fx_prop_backend;

    wl_event_source_remove( peer_context->event );
    weston_seat_release_keyboard( &peer_context->seat );
    weston_seat_release_touch( &peer_context->seat );
    weston_seat_release_pointer( &peer_context->seat );
    weston_seat_release( &peer_context->seat );

    if ( peer_context->xkb_keymap != NULL ) {
        xkb_keymap_unref( peer_context->xkb_keymap );
    }
    if ( peer_context->xkb_context != NULL ) {
        xkb_context_unref( peer_context->xkb_context );
    }
    free( peer_context );
#ifdef ENABLE_FX_SCREEN_SHARING
    b->connected -= 1;
    send_connection( b );
#endif
    return 0;
}
static int
key_event(
        void* client_data,
        fxPropClientPeer_t fx_prop_client_peer,
        bool down,
        uint32_t key_sym )
{
    struct fx_prop_peer_context* peer_context
        = ( struct fx_prop_peer_context *) client_data;

    struct timespec time;
    weston_compositor_get_time(&time);
    if ( down ) {
        notify_key(
                &peer_context->seat,
                &time,
                key_sym - 8,
                WL_KEYBOARD_KEY_STATE_PRESSED,
                STATE_UPDATE_AUTOMATIC );
    } else {
        notify_key(
                &peer_context->seat,
                &time,
                key_sym - 8,
                WL_KEYBOARD_KEY_STATE_RELEASED,
                STATE_UPDATE_AUTOMATIC );
    }
    return 0;
}
static int
touch_event(
        void* client_data,
        fxPropClientPeer_t fx_prop_client_peer,
        uint8_t type,
        uint8_t id,
        int16_t x,
        int16_t y )
{
    struct fx_prop_peer_context* peer_context
        = ( struct fx_prop_peer_context *) client_data;
    bool need_frame = false;
    struct timespec time;

    if ( id >= sizeof ( peer_context->positions )
            / sizeof ( peer_context->positions[ 0 ] ) ) {
        weston_log("touch id is too big\n");
        return;
    }
    weston_compositor_get_time(&time);
    switch ( type ) {
        case eFxPropTouchTypeRelease:
            if ( peer_context->positions[ id ].x != x
                    || peer_context->positions[ id ].y != y ) {
                notify_touch(
                        &peer_context->seat,
                        &time,
                        id,
                        x,
                        y,
                        WL_TOUCH_MOTION );
                notify_touch_frame( &peer_context->seat );
            }
            notify_touch(
                &peer_context->seat,
                &time,
                id,
                0,
                0,
                WL_TOUCH_UP );
            need_frame = false;
            break;
        case eFxPropTouchTypePress:
            notify_touch(
                &peer_context->seat,
                &time,
                id,
                x,
                y,
                WL_TOUCH_DOWN );
            need_frame = true;
            break;
        case eFxPropTouchTypeMove:
            notify_touch(
                &peer_context->seat,
                &time,
                id,
                x,
                y,
                WL_TOUCH_MOTION );
            need_frame = true;
            break;
    }
    if ( need_frame ) {
        notify_touch_frame( &peer_context->seat );
    }
    peer_context->positions[ id ].x = x;
    peer_context->positions[ id ].y = y;
    return 0;
}

static int 
fx_prop_enable_screen(
        struct fx_prop_backend *b,
        struct fx_prop_output *fx_prop_output )
{
    int result;

    b->fx_prop_screen = FxPropScreen_Create();
    if ( b->fx_prop_screen != NULL ) {
        result = FxPropScreen_Init( b->fx_prop_screen );
        if ( result >= 0 ) {
            result = fx_prop_set_screen_image(
                    b,
                    fx_prop_output->shared_fb_name,
                    fx_prop_output->shadow_surface );
            if ( result >= 0 ) {
                result = FxPropServer_SetScreen(
                        b->fx_prop_server,
                        b->fx_prop_screen );
                if ( result >= 0 ) {
                    return 0;
                }
            }
        }
        ( void ) FxPropScreen_Destroy( b->fx_prop_screen );
        b->fx_prop_screen = NULL;
    }
    return -1;
}
static int 
fx_prop_disable_screen(
        struct fx_prop_backend *b,
        struct fx_prop_output *fx_prop_output )
{
    int result;

    if ( b->fx_prop_screen != NULL ) {
        ( void ) fx_prop_reset_screen_image( b );
        ( void ) FxPropScreen_Destroy( b->fx_prop_screen );
        b->fx_prop_screen = NULL;

        result = FxPropServer_SetScreen(
                b->fx_prop_server,
                b->fx_prop_screen );
        if ( result >= 0 ) {
            return 0;
        }
    }
    return -1;
}

static int 
fx_prop_init_server(
        struct fx_prop_backend *b,
        struct weston_fx_prop_backend_config* config )
{
    char *fd_str;
    int fd;

    if ( !config->env_socket ) {
        b->fx_prop_server = FxPropServer_Create();
    } else {
        fd_str = getenv("PROP_FD");
        if (!fd_str) {
            weston_log("PROP_FD env variable not set\n");
            return -1;
        }

        fd = strtoul(fd_str, NULL, 10);
        b->fx_prop_server = FxPropServer_CreateFd( fd );
    }
    if ( b->fx_prop_server != NULL ) {
        int result = FxPropServer_Init( b->fx_prop_server );

        if ( result >= 0 ) {
            static fxPropServerEventListener_t eventListener = {
                new_client,
                discard_client,
                touch_event,
                key_event,
            };
 
            result = FxPropServer_SetEventListener(
                    b->fx_prop_server,
                    &eventListener,
                    b );
            if ( result >= 0 ) {
                return 0;
            }
            FxPropServer_Destroy( b->fx_prop_server );
        }
    }
    return -1;
}
static int
fx_prop_start_server(
        struct fx_prop_backend *b,
        struct weston_fx_prop_backend_config* config )
{
    struct wl_event_loop *loop = wl_display_get_event_loop(
            b->compositor->wl_display);
    int result;

    result = fx_prop_init_server(
            b,
            config );
    if ( result < 0 ) {
        return -1;
    }
    b->listener_event
        = wl_event_loop_add_fd(
                loop,
                FxPropServer_GetListenerFd( b->fx_prop_server ),
                WL_EVENT_READABLE,
                fx_prop_listener_activity,
                b );
    return 0;
}

#ifdef ENABLE_FX_SCREEN_SHARING
static void
unbind_resource(struct wl_resource *resource)
{
    wl_list_remove(wl_resource_get_link(resource));
}

static void
bind_screen_share(
        struct wl_client *client,
        void *data,
        uint32_t version,
        uint32_t id )
{
    struct fx_prop_backend *b = data;
    struct wl_resource *resource;

    resource = wl_resource_create(
            client,
            &fx_rdp_interface,
            version,
            id );
    if (resource == NULL) {
        wl_client_post_no_memory(client);
        return;
    }
    wl_list_insert(
            &b->resource_list,
            wl_resource_get_link( resource ));
    wl_resource_set_implementation(
            resource,
            NULL,
            data,
            unbind_resource );
    fx_rdp_send_connection(
            resource,
            b->connected != 0 );
}
#endif /* ENABLE_FX_SCREEN_SHARING */
static struct fx_prop_backend *
fx_prop_backend_create(
        struct weston_compositor *compositor,
        struct weston_fx_prop_backend_config *config )
{
    struct fx_prop_backend *b;
    int result;

    b = zalloc(sizeof *b);
    if (b == NULL){
        return NULL;
    }
    b->compositor = compositor;
    b->base.destroy = fx_prop_destroy;
    // b->base.restore = fx_prop_restore;
    b->no_clients_resize = config->no_clients_resize;
    wl_list_init( &b->resource_list );

    if (weston_compositor_set_presentation_clock_software(compositor) < 0) {
        goto err_compositor;
    }

    if (pixman_renderer_init( compositor ) < 0){
        goto err_compositor;
    }

    if ( fx_prop_backend_create_output(
            b,
            config ) < 0 ) {
        goto err_compositor;
    }

    compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;

    if ( fx_prop_start_server(
                b,
                config ) < 0 ) {
        goto err_output;
    }
    if ( config->xkb_keymap_file != NULL ) {
        b->xkb_keymap_file = strdup( config->xkb_keymap_file );
    }
    if ( config->xkb_model != NULL ) {
        b->xkb_rule_names.model = strdup( config->xkb_model );
    }
    if ( config->xkb_layout != NULL ) {
        b->xkb_rule_names.layout = strdup( config->xkb_layout );
    }
    if ( config->xkb_variant != NULL ) {
        b->xkb_rule_names.variant = strdup( config->xkb_variant );
    }
#ifdef ENABLE_FX_SCREEN_SHARING
    wl_global_create(
            compositor->wl_display,
            &fx_rdp_interface,
            1,
            b,
            bind_screen_share );
#endif
    compositor->backend = &b->base;
    result = weston_plugin_api_register(
            compositor,
            WESTON_FX_PROP_OUTPUT_API_NAME,
            &api,
            sizeof ( api ) );

    if ( result < 0 ) {
        weston_log("Failed to register output API.\n");
        goto err_output;
    }
    return b;

err_output:
    weston_output_release( &b->fx_prop_output->base );
err_compositor:
    weston_compositor_shutdown( compositor );
/* err_free_strings: */
    free( ( void* ) b->xkb_keymap_file );
    free( ( void* ) b->xkb_rule_names.model );
    free( ( void* ) b->xkb_rule_names.layout );
    free( ( void* ) b->xkb_rule_names.variant );
    free(b);
    return NULL;
}
static void
config_init_to_defaults( struct weston_fx_prop_backend_config *config ) 
{
    config->no_clients_resize = 0;
    config->xkb_keymap_file = strdup( "integ/data/fxprop_keymap" );
}

WL_EXPORT int
weston_backend_init(
        struct weston_compositor *compositor,
        struct weston_backend_config *config_base )
{
    struct fx_prop_backend *b;
    struct weston_fx_prop_backend_config config = {{ 0, }};

    if (config_base == NULL ||
        config_base->struct_version != WESTON_FX_PROP_BACKEND_CONFIG_VERSION ||
        config_base->struct_size > sizeof(struct weston_fx_prop_backend_config)) {
        weston_log("FX PROP backend config structure is invalid\n");
        return -1;
    }
    config_init_to_defaults( &config );
    memcpy(&config, config_base, config_base->struct_size);

    b = fx_prop_backend_create(
            compositor,
            &config );
    if (b == NULL)
        return -1;
    return 0;
}
