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

File Name: compositor-vnc.c

*/
#include "config.h"

#include <string.h>
#include <errno.h>
#include <assert.h>
#include <linux/input.h>
#include "rfbtypes.h"
#include "libfxvncscreen.h"
#include "libfxvncserver.h"
#include "libfxvncclient.h"
#include "shared/helpers.h"
#include "fx-screen-share-server-protocol.h"
#include "compositor.h"
#include "pixman-renderer.h"
#include "compositor-fx-vnc.h"

#define DEFAULT_AXIS_STEP_DISTANCE 10
#define VNC_MODE_FREQ 60 * 1000

#define ULTRA_VNC_PATCH 1

struct vnc_output;

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

    int num_listener_events;
    struct wl_event_source *listener_events[ 4 ];
    struct vnc_output *vnc_output;
    int no_clients_resize;

    vncScreen_t vnc_screen;
    vncServer_t vnc_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 vnc_peer_context {
    struct vnc_backend *vnc_backend;
    struct wl_event_source *event;
    struct weston_seat seat;

    uint32_t button;
    vncClient_t vnc_client;

    struct xkb_context *xkb_context;
    struct xkb_keymap *xkb_keymap;
#ifdef ULTRA_VNC_PATCH
    struct wl_event_source *key_repeat_inhibit_timer;
    bool down;
    uint32_t key_sym;
#endif
};

struct vnc_output {
    struct weston_output base;
    struct wl_event_source *finish_frame_timer;
    pixman_image_t *shadow_surface;
};

static int 
vnc_enable_screen(
        struct vnc_backend *b,
        struct vnc_output *vnc_output );
static int 
vnc_disable_screen(
        struct vnc_backend *b,
        struct vnc_output *vnc_output );

/* Procedures */
static inline struct vnc_output *
to_vnc_output(struct weston_output *base)
{
    return container_of(base, struct vnc_output, base);
}
static inline struct vnc_backend *
to_vnc_backend(struct weston_compositor *base)
{
    return container_of(base->backend, struct vnc_backend, base);
}

static int
vnc_set_screen_image(
        struct vnc_backend *b,
        pixman_image_t* image )
{
    if ( image != NULL ) {
        pixman_image_ref( image );
        ( void ) VncScreen_SetImage(
                b->vnc_screen,
                image );
    }
    return 0;
}
static int
vnc_reset_screen_image( struct vnc_backend *b )
{
    pixman_image_t* image = VncScreen_GetImage( b->vnc_screen );

    if ( image != NULL ) {
        pixman_image_unref( image );
    }
    return 0;
}

static void
vnc_refresh_region(
        struct vnc_output* vnc_output,
        pixman_region32_t *region )
{
    struct weston_compositor *compositor = vnc_output->base.compositor;
    struct vnc_backend *b = ( struct vnc_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 ) VncScreen_AddDirty(
                    b->vnc_screen,
                    &subrect );
            break;
        }
    }
}

static void
vnc_output_start_repaint_loop(struct weston_output *vnc_output)
{
    struct timespec ts;

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

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

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

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

    ( void ) VncServer_Update( b->vnc_server );

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

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

    return 1;
}
static struct weston_mode *
vnc_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 vnc_insert_new_mode(
            output,
            target->width,
            target->height,
            VNC_MODE_FREQ );
}

static int
vnc_switch_mode(
        struct weston_output *output,
        struct weston_mode *target_mode ) 
{
    struct vnc_output *vnc_output = container_of(
            output,
            struct vnc_output,
            base );
    struct weston_compositor *compositor = vnc_output->base.compositor;
    struct vnc_backend *b = ( struct vnc_backend* ) compositor->backend;
    pixman_image_t *new_shadow_buffer;
    struct weston_mode *local_mode;
    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 );

    new_shadow_buffer = pixman_image_create_bits(
            PIXMAN_x8r8g8b8,
            target_mode->width,
            target_mode->height,
            0,
            target_mode->width * 4 );
    pixman_image_composite32(
            PIXMAN_OP_SRC,
            vnc_output->shadow_surface,
            0,
            new_shadow_buffer,
            0, 0, 0, 0, 0, 0,
            target_mode->width,
            target_mode->height );
    pixman_image_unref( vnc_output->shadow_surface );
    vnc_output->shadow_surface = new_shadow_buffer;

    if ( b->vnc_server != NULL ) {
         ( void ) VncServer_DestroyClients( b->vnc_server );
    }
    if ( b->vnc_screen != NULL ) {
        ( void ) vnc_reset_screen_image( b );
        result = vnc_set_screen_image(
                b,
                new_shadow_buffer );
        if ( result < 0 ) {
            return -EINVAL;
        }
    }
    return 0;
}
static int
vnc_output_set_size(
        struct weston_output *base,
        int width,
        int height )
{
    struct vnc_output *vnc_output = to_vnc_output(base);
    struct weston_mode *currentMode;
    struct weston_mode initMode;

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

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

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

    vnc_output->base.current_mode = vnc_output->base.native_mode = currentMode;
    vnc_output->base.make = "weston";
    vnc_output->base.model = "vnc";

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

    vnc_output->base.start_repaint_loop = vnc_output_start_repaint_loop;
    vnc_output->base.repaint = vnc_output_repaint;
    vnc_output->base.assign_planes = NULL;
    vnc_output->base.set_backlight = NULL;
    vnc_output->base.set_dpms = NULL;
    vnc_output->base.switch_mode = vnc_switch_mode;

    return 0;
}

static int
vnc_output_enable( struct weston_output *base )
{
    struct vnc_output *vnc_output = to_vnc_output(base);
    struct vnc_backend *b = to_vnc_backend(base->compositor);
    struct wl_event_loop *loop;
    int result;

    vnc_output->shadow_surface
        = pixman_image_create_bits(
                PIXMAN_x8r8g8b8,
                vnc_output->base.current_mode->width,
                vnc_output->base.current_mode->height,
                NULL,
                vnc_output->base.current_mode->width * 4);
    if ( vnc_output->shadow_surface == NULL ) {
        weston_log("Failed to create surface for frame buffer.\n");
        return -1;
    }

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

    b->vnc_output = vnc_output;

    return 0;
}
static int
vnc_output_disable( struct weston_output *base )
{
    struct vnc_output *vnc_output = to_vnc_output( base );
    struct vnc_backend *b = to_vnc_backend( base->compositor );
    int result;

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

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

    wl_event_source_remove( vnc_output->finish_frame_timer );
    b->vnc_output = NULL;
    result = vnc_disable_screen(
            b,
            vnc_output );
    if ( result < 0 ) {
        weston_log("Failed to destroy vnc screen.\n");
        return -1;
    }
    return 0;
}
static void
vnc_output_destroy(struct weston_output *output)
{
    struct vnc_output *vnc_output = to_vnc_output(output);

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

static const struct weston_fx_vnc_output_api api = {
    vnc_output_set_size,
};

static int
vnc_backend_create_output(
        struct vnc_backend *b,
        struct weston_fx_vnc_backend_config* config )
{
    struct vnc_output *vnc_output;

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

    weston_output_init(
            &vnc_output->base,
            b->compositor,
            "vnc" );
            
    vnc_output->base.destroy = vnc_output_destroy;
    vnc_output->base.disable = vnc_output_disable;
    vnc_output->base.enable = vnc_output_enable;

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

    return 0;
}

static void
vnc_destroy(struct weston_compositor *compositor)
{
    struct vnc_backend *b = to_vnc_backend( compositor );
    int i;

    weston_compositor_shutdown(compositor);

    for ( i = 0; i < b->num_listener_events; ++i ) {
        wl_event_source_remove( b->listener_events[ i ] );
    }
    if ( b->vnc_server != NULL ) {
        ( void ) VncServer_DestroyClients( b->vnc_server );
    }
    if ( b->vnc_screen != NULL ) {
        ( void ) vnc_reset_screen_image( b );
        ( void ) VncScreen_Destroy( b->vnc_screen );
    }
    if ( b->vnc_server != NULL ) {
        VncServer_Destroy( b->vnc_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
vnc_listener_activity(
        int fd,
        uint32_t mask,
        void *data ) 
{
    struct vnc_backend *b = ( struct vnc_backend * ) data;

    ( void ) VncServer_Dispatch(
            b->vnc_server,
            fd );
    return 0;
}

#ifdef ULTRA_VNC_PATCH
static bool
is_key_repeat_inhibits( uint32_t key_sym )
{
    static uint32_t key_repeat_inhibits[] = {
        0xffbe /* XK_F1 */,
        0xff50 /* XK_Home */,
    } ;
    unsigned int i;

    for (
        i = 0;
        i < sizeof ( key_repeat_inhibits )
                / sizeof ( key_repeat_inhibits[ 0 ] );
        ++i
    ) {
        if ( key_repeat_inhibits[ i ] == key_sym ) {
            return true;
        }
    }
    return false;
}
static void
reset_key_repeat_inhibit( struct vnc_peer_context* peer_context )
{
    if ( !peer_context->down ) {
        struct timespec time;
        weston_compositor_get_time(&time);
        notify_key(
                &peer_context->seat,
                &time,
                peer_context->key_sym - 8,
                WL_KEYBOARD_KEY_STATE_RELEASED,
                STATE_UPDATE_AUTOMATIC );
    }
    wl_event_source_remove( peer_context->key_repeat_inhibit_timer );
    peer_context->key_repeat_inhibit_timer = NULL;
}
static int
key_repeat_inhibit_handler( void *data )
{
    struct vnc_peer_context* peer_context = data;

    reset_key_repeat_inhibit( peer_context );
    return 1;
}
static void
set_key_repeat_inhibit(
        struct vnc_peer_context* peer_context,
        bool down,
        uint32_t key_sym )
{
    if ( is_key_repeat_inhibits( key_sym ) ) {
        struct wl_event_loop * loop
            = wl_display_get_event_loop(
                    peer_context->vnc_backend->compositor->wl_display );

        peer_context->key_sym = key_sym;
        peer_context->down = down;
        peer_context->key_repeat_inhibit_timer
            = wl_event_loop_add_timer(
                    loop,
                    key_repeat_inhibit_handler,
                    peer_context );
        wl_event_source_timer_update(
                peer_context->key_repeat_inhibit_timer,
                1000 );
    }
}
static bool
update_key_repeat_inhibit(
        struct vnc_peer_context* peer_context,
        bool down,
        uint32_t key_sym )
{
    if ( peer_context->key_repeat_inhibit_timer != NULL ) {
        if ( peer_context->key_sym == key_sym ) {
            peer_context->down = down;
            wl_event_source_timer_update(
                    peer_context->key_repeat_inhibit_timer,
                    1000 );
            return true;
        }
        reset_key_repeat_inhibit( peer_context );
    }
    return false;
}
#endif /* ULTRA_VNC_PATCH */

#ifdef ENABLE_FX_SCREEN_SHARING
static void
send_connection( struct vnc_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
vnc_client_activity(
        int fd,
        uint32_t mask,
        void *data ) 
{
    struct vnc_peer_context* peer_context = ( struct vnc_peer_context* ) data;
    vncClient_t vnc_client = peer_context->vnc_client;
    vncIo_t vnc_io = VncClient_GetIo( vnc_client );

    ( void ) VncClient_Dispatch( vnc_client );
    ( void ) VncIo_Flush( vnc_io );

    return 0;
}

static int
new_client(
        void* client_data,
        vncClient_t vnc_client )
{
    struct vnc_backend *b = ( struct vnc_backend * ) client_data;
    struct vnc_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->vnc_client = vnc_client;
        peer_context->vnc_backend = b;

        peer_context->event = wl_event_loop_add_fd(
                loop,
                VncIo_GetFd(
                    VncClient_GetIo( vnc_client ) ),
                WL_EVENT_READABLE,
                vnc_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,
                "vnc" );
        weston_seat_init_pointer( &peer_context->seat );
        weston_seat_init_keyboard(
                &peer_context->seat,
                peer_context->xkb_keymap );
        ( void ) VncClient_SetClientData(
                vnc_client,
                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,
        vncClient_t vnc_client )
{
    struct vnc_peer_context* peer_context
        = ( struct vnc_peer_context *) client_data;
    struct vnc_backend *b = peer_context->vnc_backend;

    wl_event_source_remove( peer_context->event );
    weston_seat_release_keyboard( &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 );
    }
#ifdef ULTRA_VNC_PATCH
    if ( peer_context->key_repeat_inhibit_timer != NULL ) {
        wl_event_source_remove( peer_context->key_repeat_inhibit_timer );
    }
#endif
    free( peer_context );
#ifdef ENABLE_FX_SCREEN_SHARING
    b->connected -= 1;
    send_connection( b );
#endif
    return 0;
}
static int
key_event(
        void* client_data,
        vncClient_t vnc_client,
        bool down,
        uint32_t key_sym )
{
    struct vnc_peer_context* peer_context
        = ( struct vnc_peer_context *) client_data;

#ifdef ULTRA_VNC_PATCH
    if ( update_key_repeat_inhibit(
                peer_context,
                down,
                key_sym ) ) {
        return 0;
    }
#endif
    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 );
    }
#ifdef ULTRA_VNC_PATCH
    set_key_repeat_inhibit(
            peer_context,
            down,
            key_sym );
#endif
    return 0;
}
static int
pointer_event(
        void* client_data,
        vncClient_t vnc_client,
        unsigned int buttons,
        uint16_t x,
        uint16_t y )
{
    struct vnc_peer_context* peer_context
        = ( struct vnc_peer_context *) client_data;
    struct vnc_output *vnc_output = peer_context->vnc_backend->vnc_output;
    uint32_t button = 0;
    bool need_frame = false;
    struct timespec time;

    if (x < vnc_output->base.width && y < vnc_output->base.height) {
        weston_compositor_get_time(&time);
        notify_motion_absolute(
                &peer_context->seat,
                &time,
                x,
                y );
    }
    if ( ( eRfbButton1Mask & buttons ) != 0 ){
        button |= BTN_LEFT;
    }
    if ( ( eRfbButton2Mask & buttons ) != 0 ){
        button |= BTN_MIDDLE;
    }
    if ( ( eRfbButton3Mask & buttons ) != 0 ){
        button |= BTN_RIGHT;
    }
    if ( button != peer_context->button ) {
        uint32_t btn = button ^ peer_context->button;

        if ( ( button & btn ) != 0 ) {
            weston_compositor_get_time(&time);
            notify_button(
                    &peer_context->seat,
                    &time,
                    btn,
                    WL_POINTER_BUTTON_STATE_PRESSED
            );
        } else {
            weston_compositor_get_time(&time);
            notify_button(
                    &peer_context->seat,
                    &time,
                    btn,
                    WL_POINTER_BUTTON_STATE_RELEASED
            );
        }
        peer_context->button = button;
        need_frame = true;
    }
    button = 0;
    if ( ( eRfbButton4Mask & buttons ) != 0 ){
        button = BTN_GEAR_UP;
    }
    if( ( eRfbButton5Mask & buttons ) != 0 ){
        button = BTN_GEAR_DOWN;
    }
    if ( button != 0 ) {
        struct weston_pointer_axis_event weston_event;
        double value = DEFAULT_AXIS_STEP_DISTANCE;
        char *v = getenv ( "AXIS_STEP_DISTANCE" );
        
        if ( v != NULL ) value = atoi( v );

        if ( button == BTN_GEAR_UP ) value = -value;

        weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
        weston_event.value = value;
        weston_event.discrete = (int)( value / DEFAULT_AXIS_STEP_DISTANCE );
        weston_event.has_discrete = true;

        weston_compositor_get_time(&time);
        notify_axis(
                &peer_context->seat,
                &time,
                &weston_event);
        need_frame = true;
    }
    if ( need_frame ) {
        notify_pointer_frame( &peer_context->seat );
    }
    return 0;
}

static int 
vnc_enable_screen(
        struct vnc_backend *b,
        struct vnc_output *vnc_output )
{
    int result;

    b->vnc_screen = VncScreen_Create();
    if ( b->vnc_screen != NULL ) {
        result = VncScreen_Init( b->vnc_screen );
        if ( result >= 0 ) {
            result = vnc_set_screen_image(
                    b,
                    vnc_output->shadow_surface );
            if ( result >= 0 ) {
                result = VncServer_SetScreen(
                        b->vnc_server,
                        b->vnc_screen );
                if ( result >= 0 ) {
                    return 0;
                }
            }
        }
        ( void ) VncScreen_Destroy( b->vnc_screen );
        b->vnc_screen = NULL;
    }
    return -1;
}
static int 
vnc_disable_screen(
        struct vnc_backend *b,
        struct vnc_output *vnc_output )
{
    int result;

    if ( b->vnc_screen != NULL ) {
        ( void ) vnc_reset_screen_image( b );
        ( void ) VncScreen_Destroy( b->vnc_screen );
        b->vnc_screen = NULL;

        result = VncServer_SetScreen(
                b->vnc_server,
                b->vnc_screen );
        if ( result >= 0 ) {
            return 0;
        }
    }
    return -1;
}

static int 
vnc_init_server(
        struct vnc_backend *b,
        struct weston_fx_vnc_backend_config* config )
{
    char *fd_str;
    int fd;

    if ( !config->env_socket ) {
        b->vnc_server = VncServer_Create(
                config->addr,
                5900 );
    } else {
        fd_str = getenv("VNC_FD");
        if (!fd_str) {
            weston_log("VNC_FD env variable not set\n");
            return -1;
        }

        fd = strtoul(fd_str, NULL, 10);
        b->vnc_server = VncServer_CreateFd( fd );
    }
    if ( b->vnc_server != NULL ) {
        int result = VncServer_Init( b->vnc_server );

        if ( result >= 0 ) {
            static vncServerEventListener_t eventListener = {
                new_client,
                discard_client,
                pointer_event,
                key_event,
            };
 
            result = VncServer_SetEventListener(
                    b->vnc_server,
                    &eventListener,
                    b );
            if ( result >= 0 ) {
                return 0;
            }
            VncServer_Destroy( b->vnc_server );
        }
    }
    return -1;
}
static int
vnc_start_server(
        struct vnc_backend *b,
        struct weston_fx_vnc_backend_config* config )
{
    struct wl_event_loop *loop = wl_display_get_event_loop(
            b->compositor->wl_display);
    int result;
    int i, numFds;

    result = vnc_init_server(
            b,
            config );
    if ( result < 0 ) {
        return -1;
    }
    numFds = VncServer_GetListenerNumFds( b->vnc_server );
    for ( i = 0; i < numFds; ++i ) {
        b->listener_events[ b->num_listener_events ]
            = wl_event_loop_add_fd(
                    loop,
                    VncServer_GetListenerFd(
                        b->vnc_server,
                        i ),
                    WL_EVENT_READABLE,
                    vnc_listener_activity,
                    b );
        b->num_listener_events += 1;
    }
    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 vnc_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 vnc_backend *
vnc_backend_create(
        struct weston_compositor *compositor,
        struct weston_fx_vnc_backend_config *config )
{
    struct vnc_backend *b;
    int result;

    b = zalloc(sizeof *b);
    if (b == NULL){
        return NULL;
    }
    b->compositor = compositor;
    b->base.destroy = vnc_destroy;
    // b->base.restore = vnc_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 ( vnc_backend_create_output(
            b,
            config ) < 0 ) {
        goto err_compositor;
    }

    compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;

    if ( vnc_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_VNC_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->vnc_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_vnc_backend_config *config ) 
{
    config->no_clients_resize = 0;
}

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

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

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