Esempio n. 1
0
VOID
CyapaBootWorkItem(
	IN WDFWORKITEM  WorkItem
	)
{
	WDFDEVICE Device = (WDFDEVICE)WdfWorkItemGetParentObject(WorkItem);
	PDEVICE_CONTEXT pDevice = GetDeviceContext(Device);

	cyapa_boot_regs boot;

	csgesture_softc *sc = &pDevice->sc;

	if (!sc->infoSetup) {
		struct cyapa_cap cap;
		SpbReadDataSynchronously(&pDevice->I2CContext, CMD_QUERY_CAPABILITIES, &cap, sizeof(cap));
		if (strncmp((const char *)cap.prod_ida, "CYTRA", 5) != 0) {
			CyapaPrint(DEBUG_LEVEL_ERROR, DBG_PNP, "[cyapainit] Product ID \"%5.5s\" mismatch\n",
				cap.prod_ida);
			SpbReadDataSynchronously(&pDevice->I2CContext, CMD_QUERY_CAPABILITIES, &cap, sizeof(cap));
		}

		sc->resx = ((cap.max_abs_xy_high << 4) & 0x0F00) |
			cap.max_abs_x_low;
		sc->resy = ((cap.max_abs_xy_high << 8) & 0x0F00) |
			cap.max_abs_y_low;
		sc->phyx = ((cap.phy_siz_xy_high << 4) & 0x0F00) |
			cap.phy_siz_x_low;
		sc->phyy = ((cap.phy_siz_xy_high << 8) & 0x0F00) |
			cap.phy_siz_y_low;
		CyapaPrint(DEBUG_LEVEL_INFO, DBG_PNP, "[cyapainit] %5.5s-%6.6s-%2.2s buttons=%c%c%c res=%dx%d\n",
			cap.prod_ida, cap.prod_idb, cap.prod_idc,
			((cap.buttons & CYAPA_FNGR_LEFT) ? 'L' : '-'),
			((cap.buttons & CYAPA_FNGR_MIDDLE) ? 'M' : '-'),
			((cap.buttons & CYAPA_FNGR_RIGHT) ? 'R' : '-'),
			sc->resx,
			sc->resy);

		for (int i = 0; i < 5; i++) {
			sc->product_id[i] = cap.prod_ida[i];
		}
		sc->product_id[5] = '-';
		for (int i = 0; i < 6; i++) {
			sc->product_id[i + 6] = cap.prod_idb[i];
		}
		sc->product_id[12] = '-';
		for (int i = 0; i < 2; i++) {
			sc->product_id[i + 13] = cap.prod_idc[i];
		}
		sc->product_id[15] = '\0';

		sprintf(sc->firmware_version, "%d.%d", cap.fw_maj_ver, cap.fw_min_ver);
		sc->infoSetup = true;
	}

	cyapa_set_power_mode(pDevice, CMD_POWER_MODE_FULL);

	SpbReadDataSynchronously(&pDevice->I2CContext, CMD_BOOT_STATUS, &boot, sizeof(boot));
	WdfObjectDelete(WorkItem);
}
Esempio n. 2
0
VOID
OnTopLevelIoDefault(
    _In_  WDFQUEUE    FxQueue,
    _In_  WDFREQUEST  FxRequest
    )
/*++

  Routine Description:

    Accepts all incoming requests and pends or forwards appropriately.

  Arguments:

    FxQueue -  Handle to the framework queue object that is associated with the
        I/O request.
    FxRequest - Handle to a framework request object.

  Return Value:

    None.

--*/
{
    FuncEntry(TRACE_FLAG_SPBAPI);
    
    UNREFERENCED_PARAMETER(FxQueue);

    WDFDEVICE device;
    PDEVICE_CONTEXT pDevice;
    WDF_REQUEST_PARAMETERS params;
    NTSTATUS status;

    device = WdfIoQueueGetDevice(FxQueue);
    pDevice = GetDeviceContext(device);

    WDF_REQUEST_PARAMETERS_INIT(&params);

    WdfRequestGetParameters(FxRequest, &params);

	status = WdfRequestForwardToIoQueue(FxRequest, pDevice->SpbQueue);

	if (!NT_SUCCESS(status))
	{
		CyapaPrint(
			DEBUG_LEVEL_ERROR,
			DBG_IOCTL,
			"Failed to forward WDFREQUEST %p to SPB queue %p - %!STATUS!",
			FxRequest,
			pDevice->SpbQueue,
			status);
		
		WdfRequestComplete(FxRequest, status);
	}

    FuncExit(TRACE_FLAG_SPBAPI);
}
Esempio n. 3
0
VOID
OnIoDeviceControl(
    _In_  WDFQUEUE    FxQueue,
    _In_  WDFREQUEST  FxRequest,
    _In_  size_t      OutputBufferLength,
    _In_  size_t      InputBufferLength,
    _In_  ULONG       IoControlCode
    )
/*++
Routine Description:

    This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
    requests from the system.

Arguments:

    FxQueue - Handle to the framework queue object that is associated
        with the I/O request.
    FxRequest - Handle to a framework request object.
    OutputBufferLength - length of the request's output buffer,
        if an output buffer is available.
    InputBufferLength - length of the request's input buffer,
        if an input buffer is available.
    IoControlCode - the driver-defined or system-defined I/O control code
        (IOCTL) that is associated with the request.

Return Value:

   VOID

--*/
{
    FuncEntry(TRACE_FLAG_SPBAPI);

    WDFDEVICE device;
    PDEVICE_CONTEXT pDevice;
    BOOLEAN fSync = FALSE;
    NTSTATUS status = STATUS_SUCCESS;
    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);

	device = WdfIoQueueGetDevice(FxQueue);
	pDevice = GetDeviceContext(device);

	CyapaPrint(
		DEBUG_LEVEL_INFO, DBG_IOCTL,
        "DeviceIoControl request %p received with IOCTL=%lu",
        FxRequest,
        IoControlCode);
	CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL,
		"%s, Queue:0x%p, Request:0x%p\n",
		DbgHidInternalIoctlString(IoControlCode),
		FxQueue,
		FxRequest
		);

    //
    // Translate the test IOCTL into the appropriate 
    // SPB API method.  Open and close are completed 
    // synchronously.
    //

    switch (IoControlCode)
    {
	case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
		//
		// Retrieves the device's HID descriptor.
		//
		status = CyapaGetHidDescriptor(device, FxRequest);
		fSync = TRUE;
		break;

	case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
		//
		//Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure.
		//
		status = CyapaGetDeviceAttributes(FxRequest);
		fSync = TRUE;
		break;

	case IOCTL_HID_GET_REPORT_DESCRIPTOR:
		//
		//Obtains the report descriptor for the HID device.
		//
		status = CyapaGetReportDescriptor(device, FxRequest);
		fSync = TRUE;
		break;

	case IOCTL_HID_GET_STRING:
		//
		// Requests that the HID minidriver retrieve a human-readable string
		// for either the manufacturer ID, the product ID, or the serial number
		// from the string descriptor of the device. The minidriver must send
		// a Get String Descriptor request to the device, in order to retrieve
		// the string descriptor, then it must extract the string at the
		// appropriate index from the string descriptor and return it in the
		// output buffer indicated by the IRP. Before sending the Get String
		// Descriptor request, the minidriver must retrieve the appropriate
		// index for the manufacturer ID, the product ID or the serial number
		// from the device extension of a top level collection associated with
		// the device.
		//
		status = CyapaGetString(FxRequest);
		fSync = TRUE;
		break;

	case IOCTL_HID_WRITE_REPORT:
	case IOCTL_HID_SET_OUTPUT_REPORT:
		//
		//Transmits a class driver-supplied report to the device.
		//
		status = CyapaWriteReport(pDevice, FxRequest);
		fSync = TRUE;
		break;

	case IOCTL_HID_READ_REPORT:
	case IOCTL_HID_GET_INPUT_REPORT:
		//
		// Returns a report from the device into a class driver-supplied buffer.
		// 
		status = CyapaReadReport(pDevice, FxRequest, &fSync);
		break;

	case IOCTL_HID_GET_FEATURE:
		//
		// returns a feature report associated with a top-level collection
		//
		status = CyapaGetFeature(pDevice, FxRequest, &fSync);
		break;
	case IOCTL_HID_ACTIVATE_DEVICE:
		//
		// Makes the device ready for I/O operations.
		//
	case IOCTL_HID_DEACTIVATE_DEVICE:
		//
		// Causes the device to cease operations and terminate all outstanding
		// I/O requests.
		//
    default:
        fSync = TRUE;
		status = STATUS_NOT_SUPPORTED;
		CyapaPrint(
			DEBUG_LEVEL_INFO, DBG_IOCTL,
            "Request %p received with unexpected IOCTL=%lu",
            FxRequest,
            IoControlCode);
    }

    //
    // Complete the request if necessary.
    //

    if (fSync)
    {
		CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL,
			"%s completed, Queue:0x%p, Request:0x%p\n",
			DbgHidInternalIoctlString(IoControlCode),
			FxQueue,
			FxRequest
			);

        WdfRequestComplete(FxRequest, status);
	}
	else {
		CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL,
			"%s deferred, Queue:0x%p, Request:0x%p\n",
			DbgHidInternalIoctlString(IoControlCode),
			FxQueue,
			FxRequest
			);
	}

    FuncExit(TRACE_FLAG_SPBAPI);
}
Esempio n. 4
0
NTSTATUS
OnDeviceAdd(
    _In_    WDFDRIVER       FxDriver,
    _Inout_ PWDFDEVICE_INIT FxDeviceInit
    )
/*++
 
  Routine Description:

    This routine creates the device object for an SPB 
    controller and the device's child objects.

  Arguments:

    FxDriver - the WDF driver object handle
    FxDeviceInit - information about the PDO that we are loading on

  Return Value:

    Status

--*/
{
    FuncEntry(TRACE_FLAG_WDFLOADING);

    PDEVICE_CONTEXT pDevice;
	WDFDEVICE fxDevice;
	WDF_INTERRUPT_CONFIG interruptConfig;
    NTSTATUS status;
    
    UNREFERENCED_PARAMETER(FxDriver);

	//
	// Tell framework this is a filter driver. Filter drivers by default are  
	// not power policy owners. This works well for this driver because
	// HIDclass driver is the power policy owner for HID minidrivers.
	//
	WdfFdoInitSetFilter(FxDeviceInit);

    //
    // Setup PNP/Power callbacks.
    //

    {
        WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks;
        WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks);

        pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware;
        pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware;
        pnpCallbacks.EvtDeviceD0Entry = OnD0Entry;
        pnpCallbacks.EvtDeviceD0Exit = OnD0Exit;

        WdfDeviceInitSetPnpPowerEventCallbacks(FxDeviceInit, &pnpCallbacks);
    }
	
    //
    // Set request attributes.
    //

    {
        WDF_OBJECT_ATTRIBUTES attributes;
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
            &attributes,
            REQUEST_CONTEXT);

        WdfDeviceInitSetRequestAttributes(FxDeviceInit, &attributes);
    }

    //
    // Create the device.
    //

    {
        WDF_OBJECT_ATTRIBUTES deviceAttributes;
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);

        status = WdfDeviceCreate(
            &FxDeviceInit, 
            &deviceAttributes,
            &fxDevice);

        if (!NT_SUCCESS(status))
        {
			CyapaPrint(
                TRACE_LEVEL_ERROR, 
                TRACE_FLAG_WDFLOADING,
                "Error creating WDFDEVICE - %!STATUS!", 
                status);

            goto exit;
        }

        pDevice = GetDeviceContext(fxDevice);
        NT_ASSERT(pDevice != nullptr);

        pDevice->FxDevice = fxDevice;
    }

    //
    // Ensure device is disable-able
    //
    
    {
        WDF_DEVICE_STATE deviceState;
        WDF_DEVICE_STATE_INIT(&deviceState);
        
        deviceState.NotDisableable = WdfFalse;
        WdfDeviceSetDeviceState(pDevice->FxDevice, &deviceState);
    }

    //
    // Create queues to handle IO
    //

    {
        WDF_IO_QUEUE_CONFIG queueConfig;
        WDFQUEUE queue;

        //
        // Top-level queue
        //

        WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
            &queueConfig, 
            WdfIoQueueDispatchParallel);

        queueConfig.EvtIoDefault = OnTopLevelIoDefault;
        queueConfig.PowerManaged = WdfFalse;

        status = WdfIoQueueCreate(
            pDevice->FxDevice,
            &queueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
            &queue
            );

        if (!NT_SUCCESS(status))
        {
			CyapaPrint(
                TRACE_LEVEL_ERROR, 
                TRACE_FLAG_WDFLOADING,
                "Error creating top-level IO queue - %!STATUS!", 
                status);

            goto exit;
        }

        //
        // Sequential SPB queue
        //

        WDF_IO_QUEUE_CONFIG_INIT(
            &queueConfig, 
			WdfIoQueueDispatchSequential);

		queueConfig.EvtIoInternalDeviceControl = OnIoDeviceControl;
        queueConfig.PowerManaged = WdfFalse;

        status = WdfIoQueueCreate(
            fxDevice,
            &queueConfig,
            WDF_NO_OBJECT_ATTRIBUTES,
			&pDevice->SpbQueue
            );

        if (!NT_SUCCESS(status))
        {
			CyapaPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
				"WdfIoQueueCreate failed 0x%x\n", status);

            goto exit;
        }
    }

	WDF_IO_QUEUE_CONFIG           queueConfig;

	WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual);

	queueConfig.PowerManaged = WdfFalse;

	status = WdfIoQueueCreate(pDevice->FxDevice,
		&queueConfig,
		WDF_NO_OBJECT_ATTRIBUTES,
		&pDevice->ReportQueue
		);

	if (!NT_SUCCESS(status))
	{
		CyapaPrint(DEBUG_LEVEL_ERROR, DBG_PNP, "Queue 2!\n");
		CyapaPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
			"WdfIoQueueCreate failed 0x%x\n", status);

		return status;
	}

	//
	// Create an interrupt object for hardware notifications
	//
	WDF_INTERRUPT_CONFIG_INIT(
		&interruptConfig,
		OnInterruptIsr,
		NULL);
	interruptConfig.PassiveHandling = TRUE;

	status = WdfInterruptCreate(
		fxDevice,
		&interruptConfig,
		WDF_NO_OBJECT_ATTRIBUTES,
		&pDevice->Interrupt);

	if (!NT_SUCCESS(status))
	{
		CyapaPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
			"Error creating WDF interrupt object - %!STATUS!",
			status);

		goto exit;
	}

	WDF_TIMER_CONFIG              timerConfig;
	WDFTIMER                      hTimer;
	WDF_OBJECT_ATTRIBUTES         attributes;

	WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, CyapaTimerFunc, 10);

	WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
	attributes.ParentObject = fxDevice;
	status = WdfTimerCreate(&timerConfig, &attributes, &hTimer);
	pDevice->Timer = hTimer;
	if (!NT_SUCCESS(status))
	{
		CyapaPrint(DEBUG_LEVEL_ERROR, DBG_PNP, "(%!FUNC!) WdfTimerCreate failed status:%!STATUS!\n", status);
		return status;
	}

	CyapaPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
		"Success! 0x%x\n", status);

	pDevice->DeviceMode = DEVICE_MODE_MOUSE;

exit:

    FuncExit(TRACE_FLAG_WDFLOADING);

    return status;
}
Esempio n. 5
0
void TrackpadRawInput(PDEVICE_CONTEXT pDevice, struct cyapa_softc *sc, struct cyapa_regs *regs, int tickinc){
	int nfingers;
	int afingers;	/* actual fingers after culling */
	int i;

	nfingers = CYAPA_FNGR_NUMFINGERS(regs->fngr);
	afingers = nfingers;

	//	system("cls");
#ifdef DEBUG
	CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "stat %02x\tTracking ID: %d\tbuttons %c%c%c\tnfngrs=%d\n",
		regs->stat,
		regs->touch->id,
		((regs->fngr & CYAPA_FNGR_LEFT) ? 'L' : '-'),
		((regs->fngr & CYAPA_FNGR_MIDDLE) ? 'M' : '-'),
		((regs->fngr & CYAPA_FNGR_RIGHT) ? 'R' : '-'),
		nfingers
		);
#endif

	for (i = 0; i < nfingers; ++i) {
#ifdef DEBUG
		CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, " [x=%04d y=%04d p=%d]\n",
			CYAPA_TOUCH_X(regs, i),
			CYAPA_TOUCH_Y(regs, i),
			CYAPA_TOUCH_P(regs, i));
		if (CYAPA_TOUCH_Y(regs, i) > 400){
			if (CYAPA_TOUCH_X(regs, i) < 400){
				CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "Simulate left hardware button! %d\n", CYAPA_TOUCH_Y(regs, i));
			}
			else {
				CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "Simulate right hardware button! %d\n", CYAPA_TOUCH_Y(regs, i));
			}
		}
#endif
		if (CYAPA_TOUCH_P(regs, i) < cyapa_minpressure)
			--afingers;
	}

	if (regs->touch->id != sc->lastid){
		sc->x = 0;
		sc->y = 0;
		sc->lastid = regs->touch->id;
	}

	int x = sc->x;
	int y = sc->y;

	bool overrideDeltas = false;

	if (afingers > 0){
#ifdef DEBUG
		CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "Tick inc.\n");
#endif
		sc->tick += tickinc;
		x = CYAPA_TOUCH_X(regs, 0);
		y = CYAPA_TOUCH_Y(regs, 0);
		if (afingers > 1){
			int x1 = CYAPA_TOUCH_X(regs, 0);
			int y1 = CYAPA_TOUCH_Y(regs, 0);
			int x2 = CYAPA_TOUCH_X(regs, 1);
			int y2 = CYAPA_TOUCH_Y(regs, 1);

			int d1 = distancesq(x1 - sc->x, y1 - sc->y);
			int d2 = distancesq(x1 - sc->x, y1 - sc->y);
			if (d2 < d1 || (y > 400 && y2 < 400)){
				x = x2;
				y = y2;
			}
#ifdef DEBUG
			CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "%d %d\t%d %d\t%d %d\n", x, y, x1, y1, x2, y2);
#endif
		}
		else {
			if (sc->mousedown && y > 400){
				overrideDeltas = true;
				sc->x = 0;
				sc->y = 0;
			}
		}
		if ((overrideDeltas != true) && (sc->x == 0 && sc->y == 0)){
			sc->x = x;
			sc->y = y;
		}
	}
	else {
		if (sc->tick < 10 && sc->tick != 0){
			INPUT input;
			if (sc->lastnfingers == 1){
				sc->mousebutton = 0;
			}
			else if (sc->lastnfingers == 2){
				sc->mousebutton = 1;
			}
			else if (sc->lastnfingers == 3){
				sc->mousebutton = 2;
			}
			else if (sc->lastnfingers == 4){
				sc->mousebutton = 3;
			}
			input.mi.dx = 0;
			input.mi.dy = 0;
			input.mi.mouseData = 0;
			sc->mousedown = true;
			MySendInput(pDevice, &input, sc);
			sc->tickssincelastclick = 0;
#ifdef DEBUG
			CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "Tap to Click!\n");
#endif
		}
		sc->tick = 0;
		sc->hasmoved = false;
		sc->mousedownfromtap = false;
		sc->tickssincelastclick+=tickinc;
#ifdef DEBUG
		CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "Move Reset!\n");
#endif
	}

	int delta_x = x - sc->x, delta_y = y - sc->y;
	if (abs(delta_x) + abs(delta_y) > 10 && !sc->hasmoved){
		sc->hasmoved = true;
		if (sc->tickssincelastclick < 10 && sc->tickssincelastclick >= 0){
			INPUT input;
			input.mi.dx = 0;
			input.mi.dy = 0;
			input.mi.mouseData = 0;
			MySendInput(pDevice, &input, sc);
			sc->mousebutton = 0;
			sc->mousedown = true;
			sc->mousedownfromtap = true;
			sc->tickssincelastclick = 0;
#ifdef DEBUG
			CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "Move from tap!\n");
#endif
		}
#ifdef DEBUG
		CyapaPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "Has moved!\n");
#endif
	}
	if (overrideDeltas){
		delta_x = 0;
		delta_y = 0;
	}

	sc->lastnfingers = nfingers;

	if (CYAPA_TOUCH_P(regs, 0) < 20)
		sc->tick -= tickinc;
	if (CYAPA_TOUCH_P(regs, 0) < 10)
		sc->tick = 0;
	else if (sc->hasmoved)
		sc->tick = 0;
	if (sc->tick < 0)
		sc->tick = 0;

	INPUT input;
	if (afingers < 2 || sc->mousedown){
		input.mi.dx = (BYTE)delta_x;
		input.mi.dy = (BYTE)delta_y;
		input.mi.dwFlags = MOUSEEVENTF_MOVE;
		if (delta_x != 0 && delta_y != 0)
			MySendInput(pDevice, &input, sc);
	}
	else if (afingers == 2){
		if (abs(delta_x) > abs(delta_y)){
			input.mi.dwFlags = MOUSEEVENTF_HWHEEL;
			input.mi.mouseData = (BYTE)-delta_x;
			MySendInput(pDevice, &input, sc);
		}
		else if (abs(delta_y) > abs(delta_x)){
			input.mi.dwFlags = MOUSEEVENTF_WHEEL;
			input.mi.mouseData = (BYTE)delta_y;
			MySendInput(pDevice, &input, sc);
		}
	}
	else if (afingers == 3){
		if (sc->hasmoved){
			sc->multitaskingx += delta_x;
			sc->multitaskingy += delta_y;
			if (sc->multitaskinggesturetick > 5 && !sc->multitaskingdone){
				if (abs(sc->multitaskingx) > abs(sc->multitaskingy)){
					BYTE shiftKeys = KBD_LGUI_BIT | KBD_LCONTROL_BIT;
					BYTE keyCodes[KBD_KEY_CODES] = { 0, 0, 0, 0, 0, 0 };
					if (sc->multitaskingx > 0)
						keyCodes[0] = 0x50;
					else
						keyCodes[0] = 0x4F;
					update_keyboard(pDevice, shiftKeys, keyCodes);
					shiftKeys = 0;
					keyCodes[0] = 0x0;
					update_keyboard(pDevice, shiftKeys, keyCodes);
				}
				else if (abs(sc->multitaskingy) > abs(sc->multitaskingx)){
					BYTE shiftKeys = KBD_LGUI_BIT;
					BYTE keyCodes[KBD_KEY_CODES] = { 0, 0, 0, 0, 0, 0 };
					if (sc->multitaskingy < 0)
						keyCodes[0] = 0x2B;
					else
						keyCodes[0] = 0x07;
					update_keyboard(pDevice, shiftKeys, keyCodes);
					shiftKeys = 0;
					keyCodes[0] = 0x0;
					update_keyboard(pDevice, shiftKeys, keyCodes);
				}
				sc->multitaskingdone = true;
				sc->multitaskinggesturetick = -1;
			}
			else if (sc->multitaskingdone){
				if (sc->multitaskinggesturetick > 25){
					sc->multitaskinggesturetick = -1;
					sc->multitaskingx = 0;
					sc->multitaskingy = 0;
					sc->multitaskingdone = false;
				}
			}
#ifdef DEBUG
			CyapaPrint(DEBUG_LEVEL_INFO,DBG_IOCTL,"Multitasking Gestures!\n");
#endif
			sc->multitaskinggesturetick++;
		}
	}

	if (afingers != 3){
		sc->multitaskinggesturetick = 0;
		sc->multitaskingx = 0;
		sc->multitaskingy = 0;
		sc->multitaskingdone = false;
	}


	if (afingers > 0){
		if ((regs->fngr & CYAPA_FNGR_LEFT) != 0 && sc->mousedown == false){
			sc->mousedown = true;

			if (afingers == 1){
				if (sc->y < 400 || sc->x < 400){
					sc->mousebutton = 0;
				}
				else {
					sc->mousebutton = 1;
				}
			}
			else if (afingers == 2){
				sc->mousebutton = 1;
			}
			else if (afingers == 3){
				sc->mousebutton = 2;
			}
			else if (afingers == 4){
				sc->mousebutton = 3;
			}

			input.mi.dx = 0;
			input.mi.dy = 0;
			input.mi.mouseData = 0;
			MySendInput(pDevice, &input, sc);
		}
		if (!overrideDeltas){
			sc->x = x;
			sc->y = y;
		}
	}
	else {
		if (!overrideDeltas){
			sc->x = 0;
			sc->y = 0;
		}
	}

	if ((regs->fngr & CYAPA_FNGR_LEFT) == 0 && sc->mousedown == true && sc->mousedownfromtap != true){
		sc->mousedown = false;

		if (sc->mousebutton == 3){
			BYTE shiftKeys = KBD_LGUI_BIT;
			BYTE keyCodes[KBD_KEY_CODES] = { 0x04, 0, 0, 0, 0, 0 };
			update_keyboard(pDevice, shiftKeys, keyCodes);
			shiftKeys = 0;
			keyCodes[0] = 0x0;
			update_keyboard(pDevice, shiftKeys, keyCodes);

		}
		sc->mousebutton = 0;
		input.mi.dx = 0;
		input.mi.dy = 0;
		input.mi.mouseData = 0;
		MySendInput(pDevice, &input, sc);
	}
}