| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <cstdarg> |
| |
| #include "libevdev_mock.h" |
| #include "replay_device.h" |
| #include "util.h" |
| |
| using replay::EvemuDevice; |
| using replay::ReplayDevice; |
| using replay::ReplayDeviceConfig; |
| using replay::TestBit; |
| |
| extern "C" { |
| |
| // Implements the log method used by libevdev. Passes all logs to ReplayDevice. |
| static void EvdevLog(void* udata, int level, const char* format, ...) { |
| ReplayDevice* device = reinterpret_cast<ReplayDevice*>(udata); |
| va_list args; |
| va_start(args, format); |
| device->VLog(ReplayDeviceConfig::kLogEvdev, format, args); |
| va_end(args); |
| } |
| |
| // Wrapper callback to pass the syn callback to ReplayDevice |
| static void EvdevSynCallback(void* udata, EventStatePtr evstate, |
| struct timeval* timeval) { |
| ReplayDevice* device = reinterpret_cast<ReplayDevice*>(udata); |
| device->SynCallback(evstate, timeval); |
| } |
| |
| // Do nothing, there is no kernel device. |
| int EvdevOpen(EvdevPtr evdev, const char* device) { |
| return Success; |
| } |
| |
| // Do nothing, there is no kernel device. |
| int EvdevClose(EvdevPtr evdev) { |
| return Success; |
| } |
| |
| // Do nothing, there is no kernel device. |
| int EvdevRead(EvdevPtr evdev) { |
| return Success; |
| } |
| |
| // This method usually queries the kernel for device information using ioctls. |
| // We fake this by copying data from the evemu device. |
| int EvdevProbe(EvdevPtr device) { |
| // find replay device instance by file descriptor |
| EvemuDevice* evemu = ReplayDevice::GetDevice(device->fd)->evdev_file(); |
| |
| // copy device information |
| strcpy(device->info.name, evemu->GetName().c_str()); |
| memcpy(device->info.prop_bitmask, evemu->GetPropMask(), |
| sizeof(device->info.prop_bitmask)); |
| |
| memcpy(device->info.bitmask, evemu->GetMask(0), |
| sizeof(device->info.bitmask)); |
| memcpy(device->info.key_bitmask, evemu->GetMask(EV_KEY), |
| sizeof(device->info.key_bitmask)); |
| memcpy(device->info.led_bitmask, evemu->GetMask(EV_LED), |
| sizeof(device->info.led_bitmask)); |
| memcpy(device->info.rel_bitmask, evemu->GetMask(EV_REL), |
| sizeof(device->info.rel_bitmask)); |
| memcpy(device->info.abs_bitmask, evemu->GetMask(EV_ABS), |
| sizeof(device->info.abs_bitmask)); |
| |
| for (size_t i = 0; i < ABS_CNT; ++i) { |
| memcpy(&device->info.absinfo[i], &evemu->GetAbsinfo(i), |
| sizeof(device->info.absinfo[i])); |
| } |
| const EvdevInfo& info = device->info; |
| if ((info.absinfo[ABS_X].maximum > 0 && |
| info.absinfo[ABS_X].resolution <= 0) || |
| (info.absinfo[ABS_Y].maximum > 0 && |
| info.absinfo[ABS_Y].resolution <= 0) || |
| (info.absinfo[ABS_MT_POSITION_X].maximum > 0 && |
| info.absinfo[ABS_MT_POSITION_X].resolution <= 0) || |
| (info.absinfo[ABS_MT_POSITION_Y].maximum > 0 && |
| info.absinfo[ABS_MT_POSITION_Y].resolution <= 0)) { |
| std::cerr << "Missing resolution field in device file" << std::endl; |
| exit(1); |
| } |
| |
| return Success; |
| } |
| |
| // Only used during resync. Not supported by this mock. |
| int EvdevProbeAbsinfo(EvdevPtr device, size_t key) { |
| return Success; |
| } |
| |
| int EvdevIsSinglePressureDevice(EvdevPtr device) { |
| EvdevInfoPtr info = &device->info; |
| |
| return (!TestBit(ABS_MT_PRESSURE, info->abs_bitmask) && |
| TestBit(ABS_PRESSURE, info->abs_bitmask)); |
| } |
| |
| // This method usually queries the kernel for the current MTSlot state. |
| // We fake this by returning 'no fingers' as the current state. |
| int EvdevProbeMTSlot(EvdevPtr device, MTSlotInfoPtr req) { |
| for (int i = 0; i < ABS_CNT; ++i) { |
| if (req->code == ABS_MT_TRACKING_ID) |
| req->values[i] = -1; |
| else |
| req->values[i] = 0; |
| } |
| return Success; |
| } |
| |
| // Only used during resync. Not supported by this mock. |
| int EvdevProbeKeyState(EvdevPtr device) { |
| return Success; |
| } |
| |
| // This mock device always allows monotonic timers. |
| int EvdevEnableMonotonic(EvdevPtr device) { |
| return Success; |
| } |
| |
| } // extern "C" |
| |
| namespace replay { |
| |
| Evdev* NewEvdevMock(int id) { |
| // create new evdev instance. Store the ReplayDevice ID as file descriptor |
| Evdev* evdev = new Evdev(); |
| evdev->fd = id; |
| |
| // setup event state |
| EventStatePtr evstate = new EventStateRec(); |
| evstate->debug_buf_tail = 0; |
| evdev->evstate = evstate; |
| |
| // setup method callbacks |
| evdev->log = &EvdevLog; |
| evdev->log_udata = ReplayDevice::GetDevice(evdev->fd); |
| evdev->syn_report = &EvdevSynCallback; |
| evdev->syn_report_udata = ReplayDevice::GetDevice(evdev->fd); |
| |
| // allow libevdev to init. |
| if (Event_Init(evdev) != Success) { |
| DeleteEvdevMock(evdev); |
| return NULL; |
| } |
| |
| // Per default we start with the MT slot 0 |
| MT_Slot_Set(evdev, 0); |
| |
| return evdev; |
| } |
| |
| void DeleteEvdevMock(Evdev* evdev) { |
| delete evdev->evstate; |
| evdev->evstate = NULL; |
| |
| delete evdev; |
| evdev = NULL; |
| } |
| |
| } // namespace replay |