device.cpp 5.12 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*******************************************************************************
 *
 * X testing environment - Google Test environment feat. dummy x server
 *
 * Copyright (C) 2012 Canonical Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 ******************************************************************************/

28
#include "xorg/gtest/evemu/xorg-gtest-device.h"
29

30
#include <linux/input.h>
31
#include <fcntl.h>
32
#include <dirent.h>
33
34
35
36
37

#include <stdexcept>

#include <gtest/gtest.h>

38
39
40
#define SYS_INPUT_DIR "/sys/class/input"
#define DEV_INPUT_DIR "/dev/input/"

41
struct xorg::testing::evemu::Device::Private {
42
  Private() : fd(-1), device(NULL), device_node() {}
43
44
45

  int fd;
  struct evemu_device* device;
46
  std::string device_node;
47
48
};

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
static int _event_device_compare(const struct dirent **a,
                                 const struct dirent **b) {
  int na, nb;

  sscanf((*a)->d_name, "event%d", &na);
  sscanf((*b)->d_name, "event%d", &nb);

  return (na > nb) ? 1 : (na < nb) ? -1 : 0;

}

static int _event_device_filter(const struct dirent *d) {
  return (strncmp("event", d->d_name, sizeof("event") - 1) == 0);
}

void xorg::testing::evemu::Device::GuessDeviceNode(time_t ctime) {
  struct dirent **event_devices;
  int n_event_devices;

  n_event_devices = scandir(SYS_INPUT_DIR, &event_devices,
                            _event_device_filter, _event_device_compare);

  if (n_event_devices < 0) {
    std::cerr << "Failed to guess device node." << std::endl;
    return;
  }

  bool found = false;
  for (int i = 0; i < n_event_devices && !found; i++) {
    std::stringstream s;
    s << DEV_INPUT_DIR << event_devices[i]->d_name;

    int fd = open(s.str().c_str(), O_RDONLY);
    char device_name[256];

    ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name);
    if (strcmp(device_name, evemu_get_name(d_->device)) == 0) {
      struct stat buf;
      if (fstat(fd, &buf) == 0) {
        if (buf.st_ctime >= ctime) {
          d_->device_node = s.str();
          found = true;
        }
      }
    }
    close(fd);
  }

  for (int i = 0; i < n_event_devices; i++)
    free(event_devices[i]);
  free(event_devices);
}

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
xorg::testing::evemu::Device::Device(const std::string& path)
    : d_(new Private) {
  static const char UINPUT_NODE[] = "/dev/uinput";

  d_->device = evemu_new(NULL);
  if (!d_->device)
    throw std::runtime_error("Failed to create evemu record");

  FILE* fp = fopen(path.c_str(), "r");
  if (fp == NULL) {
    evemu_delete(d_->device);
    throw std::runtime_error("Failed to open device file");
  }

  if (evemu_read(d_->device, fp) <= 0) {
    fclose(fp);
    evemu_delete(d_->device);
    throw std::runtime_error("Failed to read device file");
  }

  fclose(fp);

  d_->fd = open(UINPUT_NODE, O_WRONLY);
  if (d_->fd < 0) {
    evemu_delete(d_->device);
    throw std::runtime_error("Failed to open uinput node");
  }

130
  time_t ctime = time(NULL);
131
132
133
134
135
  if (evemu_create(d_->device, d_->fd) < 0) {
    close(d_->fd);
    evemu_delete(d_->device);
    throw std::runtime_error("Failed to create evemu device");
  }
136
137

  GuessDeviceNode(ctime);
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
}

void xorg::testing::evemu::Device::Play(const std::string& path) const {
  FILE* file = fopen(path.c_str(), "r");
  if (!file)
    throw std::runtime_error("Failed to open recording file");

  if (evemu_play(file, d_->fd) != 0) {
    fclose(file);
    throw std::runtime_error("Failed to play evemu recording");
  }

  fclose(file);
}

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
void xorg::testing::evemu::Device::PlayOne(int type, int code, int value, bool sync)
{
  struct input_event ev;
  if (evemu_create_event(&ev, type, code, value))
    throw std::runtime_error("Failed to create event");

  if (evemu_play_one(d_->fd, &ev))
    throw std::runtime_error("Failed to play event");

  if (sync) {
    if (evemu_create_event(&ev, EV_SYN, SYN_REPORT, 0))
      throw std::runtime_error("Failed to create EV_SYN event");

    if (evemu_play_one(d_->fd, &ev))
      throw std::runtime_error("Failed to play EV_SYN event");
  }
}

171
172
173
174
const std::string& xorg::testing::evemu::Device::GetDeviceNode(void) {
  return d_->device_node;
}

175
176
177
178
xorg::testing::evemu::Device::~Device() {
  close(d_->fd);
  evemu_delete(d_->device);
}