/*
 * Copyright © 2008 Red Hat, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of Red Hat
 * not be used in advertising or publicity pertaining to distribution
 * of the software without specific, written prior permission.  Red
 * Hat makes no representations about the suitability of this software
 * for any purpose.  It is provided "as is" without express or implied
 * warranty.
 *
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Authors: Peter Hutterer
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <xorg-server.h>
#include "xf86Module.h"

#include <X11/Xatom.h>
#include <xf86.h>
#include <xf86Xinput.h>
#include <exevents.h>

#include "synaptics.h"
#include "synapticsstr.h"
#include "synaptics-properties.h"

#ifndef XATOM_FLOAT
#define XATOM_FLOAT "FLOAT"
#endif
static Atom float_type;

Atom prop_edges                 = 0;
Atom prop_finger                = 0;
Atom prop_tap_time              = 0;
Atom prop_tap_move              = 0;
Atom prop_tap_durations         = 0;
Atom prop_tap_fast              = 0;
Atom prop_middle_timeout        = 0;
Atom prop_twofinger_pressure    = 0;
Atom prop_twofinger_width       = 0;
Atom prop_scrolldist            = 0;
Atom prop_scrolledge            = 0;
Atom prop_scrolltwofinger       = 0;
Atom prop_speed                 = 0;
Atom prop_edgemotion_pressure   = 0;
Atom prop_edgemotion_speed      = 0;
Atom prop_edgemotion_always     = 0;
Atom prop_buttonscroll          = 0;
Atom prop_buttonscroll_repeat   = 0;
Atom prop_buttonscroll_time     = 0;
Atom prop_off                   = 0;
Atom prop_lockdrags             = 0;
Atom prop_lockdrags_time        = 0;
Atom prop_tapaction             = 0;
Atom prop_clickaction           = 0;
Atom prop_circscroll            = 0;
Atom prop_circscroll_dist       = 0;
Atom prop_circscroll_trigger    = 0;
Atom prop_circpad               = 0;
Atom prop_palm                  = 0;
Atom prop_palm_dim              = 0;
Atom prop_coastspeed            = 0;
Atom prop_pressuremotion        = 0;
Atom prop_pressuremotion_factor = 0;
Atom prop_grab                  = 0;
Atom prop_gestures              = 0;
Atom prop_capabilities          = 0;
Atom prop_resolution            = 0;
Atom prop_area                  = 0;
Atom prop_noise_cancellation    = 0;

static Atom
InitAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values)
{
    int i;
    Atom atom;
    uint8_t val_8[9]; /* we never have more than 9 values in an atom */
    uint16_t val_16[9];
    uint32_t val_32[9];
    pointer converted;


    for (i = 0; i < nvalues; i++)
    {
        switch(format)
        {
            case 8:  val_8[i]  = values[i]; break;
            case 16: val_16[i] = values[i]; break;
            case 32: val_32[i] = values[i]; break;
        }
    }

    switch(format)
    {
        case 8: converted = val_8; break;
        case 16: converted = val_16; break;
        case 32: converted = val_32; break;
    }

    atom = MakeAtom(name, strlen(name), TRUE);
    XIChangeDeviceProperty(dev, atom, XA_INTEGER, format,
                           PropModeReplace, nvalues,
                           converted, FALSE);
    XISetDevicePropertyDeletable(dev, atom, FALSE);
    return atom;
}

static Atom
InitFloatAtom(DeviceIntPtr dev, char *name, int nvalues, float *values)
{
    Atom atom;

    atom = MakeAtom(name, strlen(name), TRUE);
    XIChangeDeviceProperty(dev, atom, float_type, 32, PropModeReplace,
                           nvalues, values, FALSE);
    XISetDevicePropertyDeletable(dev, atom, FALSE);
    return atom;
}

void
InitDeviceProperties(InputInfoPtr pInfo)
{
    SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
    SynapticsParameters *para = &priv->synpara;
    int values[9]; /* we never have more than 9 values in an atom */
    float fvalues[4]; /* never have more than 4 float values */

    float_type = XIGetKnownProperty(XATOM_FLOAT);
    if (!float_type)
    {
        float_type = MakeAtom(XATOM_FLOAT, strlen(XATOM_FLOAT), TRUE);
        if (!float_type)
        {
            xf86Msg(X_ERROR, "%s: Failed to init float atom. "
                             "Disabling property support.\n", pInfo->name);
            return;
        }
    }

    values[0] = para->left_edge;
    values[1] = para->right_edge;
    values[2] = para->top_edge;
    values[3] = para->bottom_edge;

    prop_edges = InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGES, 32, 4, values);

    values[0] = para->finger_low;
    values[1] = para->finger_high;
    values[2] = para->finger_press;

    prop_finger = InitAtom(pInfo->dev, SYNAPTICS_PROP_FINGER, 32, 3, values);
    prop_tap_time = InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_TIME, 32, 1, &para->tap_time);
    prop_tap_move = InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_MOVE, 32, 1, &para->tap_move);

    values[0] = para->single_tap_timeout;
    values[1] = para->tap_time_2;
    values[2] = para->click_time;

    prop_tap_durations = InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_DURATIONS, 32, 3, values);
    prop_tap_fast = InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_FAST, 8, 1, &para->fast_taps);
    prop_middle_timeout = InitAtom(pInfo->dev, SYNAPTICS_PROP_MIDDLE_TIMEOUT,
                                   32, 1, &para->emulate_mid_button_time);
    prop_twofinger_pressure = InitAtom(pInfo->dev, SYNAPTICS_PROP_TWOFINGER_PRESSURE,
                                       32, 1, &para->emulate_twofinger_z);
    prop_twofinger_width = InitAtom(pInfo->dev, SYNAPTICS_PROP_TWOFINGER_WIDTH,
                                       32, 1, &para->emulate_twofinger_w);

    values[0] = para->scroll_dist_vert;
    values[1] = para->scroll_dist_horiz;
    prop_scrolldist = InitAtom(pInfo->dev, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 2, values);

    values[0] = para->scroll_edge_vert;
    values[1] = para->scroll_edge_horiz;
    values[2] = para->scroll_edge_corner;
    prop_scrolledge = InitAtom(pInfo->dev, SYNAPTICS_PROP_SCROLL_EDGE,8, 3, values);
    values[0] = para->scroll_twofinger_vert;
    values[1] = para->scroll_twofinger_horiz;
    prop_scrolltwofinger = InitAtom(pInfo->dev, SYNAPTICS_PROP_SCROLL_TWOFINGER,8, 2, values);

    fvalues[0] = para->min_speed;
    fvalues[1] = para->max_speed;
    fvalues[2] = para->accl;
    fvalues[3] = para->trackstick_speed;
    prop_speed = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_SPEED, 4, fvalues);

    values[0] = para->edge_motion_min_z;
    values[1] = para->edge_motion_max_z;
    prop_edgemotion_pressure = InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGEMOTION_PRESSURE, 32, 2, values);

    values[0] = para->edge_motion_min_speed;
    values[1] = para->edge_motion_max_speed;
    prop_edgemotion_speed = InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGEMOTION_SPEED, 32, 2, values);
    prop_edgemotion_always = InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGEMOTION, 8, 1, &para->edge_motion_use_always);

    if (priv->has_scrollbuttons)
    {
        values[0] = para->updown_button_scrolling;
        values[1] = para->leftright_button_scrolling;
        prop_buttonscroll = InitAtom(pInfo->dev, SYNAPTICS_PROP_BUTTONSCROLLING, 8, 2, values);

        values[0] = para->updown_button_repeat;
        values[1] = para->leftright_button_repeat;
        prop_buttonscroll_repeat = InitAtom(pInfo->dev, SYNAPTICS_PROP_BUTTONSCROLLING_REPEAT, 8, 2, values);
        prop_buttonscroll_time = InitAtom(pInfo->dev, SYNAPTICS_PROP_BUTTONSCROLLING_TIME, 32, 1, &para->scroll_button_repeat);
    }

    prop_off = InitAtom(pInfo->dev, SYNAPTICS_PROP_OFF, 8, 1, &para->touchpad_off);
    prop_lockdrags = InitAtom(pInfo->dev, SYNAPTICS_PROP_LOCKED_DRAGS, 8, 1, &para->locked_drags);
    prop_lockdrags_time = InitAtom(pInfo->dev, SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT, 32, 1, &para->locked_drag_time);

    memcpy(values, para->tap_action, MAX_TAP * sizeof(int));
    prop_tapaction = InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_ACTION, 8, MAX_TAP, values);

    memcpy(values, para->click_action, MAX_CLICK * sizeof(int));
    prop_clickaction = InitAtom(pInfo->dev, SYNAPTICS_PROP_CLICK_ACTION, 8, MAX_CLICK, values);

    prop_circscroll = InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_SCROLLING, 8, 1, &para->circular_scrolling);

    fvalues[0] = para->scroll_dist_circ;
    prop_circscroll_dist = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST, 1, fvalues);

    prop_circscroll_trigger = InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER, 8, 1, &para->circular_trigger);
    prop_circpad = InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_PAD, 8, 1, &para->circular_pad);
    prop_palm = InitAtom(pInfo->dev, SYNAPTICS_PROP_PALM_DETECT, 8, 1, &para->palm_detect);

    values[0] = para->palm_min_width;
    values[1] = para->palm_min_z;

    prop_palm_dim = InitAtom(pInfo->dev, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 2, values);

    fvalues[0] = para->coasting_speed;
    fvalues[1] = para->coasting_friction;
    prop_coastspeed = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_COASTING_SPEED, 2, fvalues);

    values[0] = para->press_motion_min_z;
    values[1] = para->press_motion_max_z;
    prop_pressuremotion = InitAtom(pInfo->dev, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 2, values);

    fvalues[0] = para->press_motion_min_factor;
    fvalues[1] = para->press_motion_max_factor;

    prop_pressuremotion_factor = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 2, fvalues);

    prop_grab = InitAtom(pInfo->dev, SYNAPTICS_PROP_GRAB, 8, 1, &para->grab_event_device);

    values[0] = para->tap_and_drag_gesture;
    prop_gestures = InitAtom(pInfo->dev, SYNAPTICS_PROP_GESTURES, 8, 1, values);

    values[0] = priv->has_left;
    values[1] = priv->has_middle;
    values[2] = priv->has_right;
    values[3] = priv->has_double;
    values[4] = priv->has_triple;
    values[5] = priv->has_pressure;
    values[6] = priv->has_width;
    prop_capabilities = InitAtom(pInfo->dev, SYNAPTICS_PROP_CAPABILITIES, 8, 7, values);

    values[0] = para->resolution_vert;
    values[1] = para->resolution_horiz;
    prop_resolution = InitAtom(pInfo->dev, SYNAPTICS_PROP_RESOLUTION, 32, 2, values);

    values[0] = para->area_left_edge;
    values[1] = para->area_right_edge;
    values[2] = para->area_top_edge;
    values[3] = para->area_bottom_edge;
    prop_area = InitAtom(pInfo->dev, SYNAPTICS_PROP_AREA, 32, 4, values);

    values[0] = para->hyst_x;
    values[1] = para->hyst_y;
    prop_noise_cancellation = InitAtom(pInfo->dev,
            SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 2, values);

}

int
SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
            BOOL checkonly)
{
    InputInfoPtr pInfo = dev->public.devicePrivate;
    SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
    SynapticsParameters *para = &priv->synpara;
    SynapticsParameters tmp;

    /* If checkonly is set, no parameters may be changed. So just let the code
     * change temporary variables and forget about it. */
    if (checkonly)
    {
        tmp = *para;
        para = &tmp;
    }

    if (property == prop_edges)
    {
        INT32 *edges;
        if (prop->size != 4 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        edges = (INT32*)prop->data;
        if (edges[0] > edges[1] || edges[2] > edges[3])
            return BadValue;

        para->left_edge   = edges[0];
        para->right_edge  = edges[1];
        para->top_edge    = edges[2];
        para->bottom_edge = edges[3];

    } else if (property == prop_finger)
    {
        INT32 *finger;
        if (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        finger = (INT32*)prop->data;
        if (finger[0] > finger[1])
            return BadValue;

        para->finger_low   = finger[0];
        para->finger_high  = finger[1];
        para->finger_press = finger[2];

    } else if (property == prop_tap_time)
    {
        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        para->tap_time = *(INT32*)prop->data;

    } else if (property == prop_tap_move)
    {
        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        para->tap_move = *(INT32*)prop->data;
    } else if (property == prop_tap_durations)
    {
        INT32 *timeouts;

        if (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        timeouts = (INT32*)prop->data;

        para->single_tap_timeout = timeouts[0];
        para->tap_time_2         = timeouts[1];
        para->click_time         = timeouts[2];

    } else if (property == prop_tap_fast)
    {
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        para->fast_taps = *(BOOL*)prop->data;

    } else if (property == prop_middle_timeout)
    {
        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        para->emulate_mid_button_time = *(INT32*)prop->data;
    } else if (property == prop_twofinger_pressure)
    {
        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        para->emulate_twofinger_z = *(INT32*)prop->data;
    } else if (property == prop_twofinger_width)
    {
        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        para->emulate_twofinger_w = *(INT32*)prop->data;
    } else if (property == prop_scrolldist)
    {
        INT32 *dist;
        if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        dist = (INT32*)prop->data;
        para->scroll_dist_vert = dist[0];
        para->scroll_dist_horiz = dist[1];
    } else if (property == prop_scrolledge)
    {
        CARD8 *edge;
        if (prop->size != 3 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        edge = (BOOL*)prop->data;
        para->scroll_edge_vert   = edge[0];
        para->scroll_edge_horiz  = edge[1];
        para->scroll_edge_corner = edge[2];
    } else if (property == prop_scrolltwofinger)
    {
        CARD8 *twofinger;

        if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        twofinger = (BOOL*)prop->data;
        para->scroll_twofinger_vert  = twofinger[0];
        para->scroll_twofinger_horiz = twofinger[1];
    } else if (property == prop_speed)
    {
        float *speed;

        if (prop->size != 4 || prop->format != 32 || prop->type != float_type)
            return BadMatch;

        speed = (float*)prop->data;
        para->min_speed = speed[0];
        para->max_speed = speed[1];
        para->accl = speed[2];
        para->trackstick_speed = speed[3];

    } else if (property == prop_edgemotion_pressure)
    {
        CARD32 *pressure;

        if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        pressure = (CARD32*)prop->data;
        if (pressure[0] > pressure[1])
            return BadValue;

        para->edge_motion_min_z = pressure[0];
        para->edge_motion_max_z = pressure[1];

    } else if (property == prop_edgemotion_speed)
    {
        CARD32 *speed;

        if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        speed = (CARD32*)prop->data;
        if (speed[0] > speed[1])
            return BadValue;

        para->edge_motion_min_speed = speed[0];
        para->edge_motion_max_speed = speed[1];

    } else if (property == prop_edgemotion_always)
    {
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        para->edge_motion_use_always = *(BOOL*)prop->data;

    } else if (property == prop_buttonscroll)
    {
        BOOL *scroll;

        if (!priv->has_scrollbuttons)
            return BadMatch;

        if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        scroll = (BOOL*)prop->data;
        para->updown_button_scrolling    = scroll[0];
        para->leftright_button_scrolling = scroll[1];

    } else if (property == prop_buttonscroll_repeat)
    {
        BOOL *repeat;

        if (!priv->has_scrollbuttons)
            return BadMatch;

        if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        repeat = (BOOL*)prop->data;
        para->updown_button_repeat    = repeat[0];
        para->leftright_button_repeat = repeat[1];
    } else if (property == prop_buttonscroll_time)
    {
        if (!priv->has_scrollbuttons)
            return BadMatch;

        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        para->scroll_button_repeat = *(INT32*)prop->data;

    } else if (property == prop_off)
    {
        CARD8 off;
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        off = *(CARD8*)prop->data;

        if (off > 2)
            return BadValue;

        para->touchpad_off = off;
    } else if (property == prop_gestures)
    {
        BOOL *gestures;

        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        gestures = (BOOL*)prop->data;
        para->tap_and_drag_gesture = gestures[0];
    } else if (property == prop_lockdrags)
    {
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        para->locked_drags = *(BOOL*)prop->data;
    } else if (property == prop_lockdrags_time)
    {
        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        para->locked_drag_time = *(INT32*)prop->data;
    } else if (property == prop_tapaction)
    {
        int i;
        CARD8 *action;

        if (prop->size > MAX_TAP || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        action = (CARD8*)prop->data;

        for (i = 0; i < MAX_TAP; i++)
            para->tap_action[i] = action[i];
    } else if (property == prop_clickaction)
    {
        int i;
        CARD8 *action;

        if (prop->size > MAX_CLICK || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        action = (CARD8*)prop->data;

        for (i = 0; i < MAX_CLICK; i++)
            para->click_action[i] = action[i];
    } else if (property == prop_circscroll)
    {
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        para->circular_scrolling = *(BOOL*)prop->data;

    } else if (property == prop_circscroll_dist)
    {
        float circdist;

        if (prop->size != 1 || prop->format != 32 || prop->type != float_type)
            return BadMatch;

        circdist = *(float*)prop->data;
        para->scroll_dist_circ = circdist;
    } else if (property == prop_circscroll_trigger)
    {
        int trigger;
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        trigger = *(CARD8*)prop->data;
        if (trigger > 8)
            return BadValue;

        para->circular_trigger = trigger;

    } else if (property == prop_circpad)
    {
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        para->circular_pad = *(BOOL*)prop->data;
    } else if (property == prop_palm)
    {
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        para->palm_detect = *(BOOL*)prop->data;
    } else if (property == prop_palm_dim)
    {
        INT32 *dim;

        if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        dim = (INT32*)prop->data;

        para->palm_min_width = dim[0];
        para->palm_min_z     = dim[1];
    } else if (property == prop_coastspeed)
    {
        float *coast_speeds;

        if (prop->size != 2 || prop->format != 32 || prop->type != float_type)
            return BadMatch;

        coast_speeds = (float*)prop->data;
        para->coasting_speed = coast_speeds[0];
        para->coasting_friction = coast_speeds[1];
    } else if (property == prop_pressuremotion)
    {
        float *press;
        if (prop->size != 2 || prop->format != 32 || prop->type != float_type)
            return BadMatch;

        press = (float*)prop->data;
        if (press[0] > press[1])
            return BadValue;

        para->press_motion_min_z = press[0];
        para->press_motion_max_z = press[1];
    } else if (property == prop_grab)
    {
        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
            return BadMatch;

        para->grab_event_device = *(BOOL*)prop->data;
    } else if (property == prop_capabilities)
    {
        /* read-only */
        return BadValue;
    } else if (property == prop_resolution)
    {
        /* read-only */
        return BadValue;
    } else if (property == prop_area)
    {
        INT32 *area;
        if (prop->size != 4 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        area = (INT32*)prop->data;
        if ((((area[0] != 0) && (area[1] != 0)) && (area[0] > area[1]) ) || (((area[2] != 0) && (area[3] != 0)) && (area[2] > area[3])))
            return BadValue;

        para->area_left_edge   = area[0];
        para->area_right_edge  = area[1];
        para->area_top_edge    = area[2];
        para->area_bottom_edge = area[3];
    } else if (property == prop_noise_cancellation) {
        INT32 *hyst;
        if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
            return BadMatch;

        hyst = (INT32*)prop->data;
        if (hyst[0] < 0 || hyst[1] < 0)
            return BadValue;
        para->hyst_x = hyst[0];
        para->hyst_y = hyst[1];
    }

    return Success;
}

