| /**************************************************************************** |
| * apps/testing/monkey/monkey_main.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #include <nuttx/video/fb.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <inttypes.h> |
| #include <signal.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include "monkey.h" |
| #include "monkey_utils.h" |
| #include "monkey_log.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define MONKEY_PREFIX "monkey" |
| |
| #define CONSTRAIN(x, low, high) \ |
| ((x) < (low) ? (low) : ((x) > (high) ? (high) : (x))) |
| |
| #define OPTARG_TO_VALUE(value, type, base) \ |
| do \ |
| { \ |
| FAR char *ptr; \ |
| (value) = (type)strtoul(optarg, &ptr, (base)); \ |
| if (*ptr != '\0') \ |
| { \ |
| MONKEY_LOG_ERROR("Parameter error: -%c %s", ch, optarg); \ |
| show_usage(argv[0], EXIT_FAILURE); \ |
| } \ |
| } while (0) |
| |
| #define OPTARG_TO_RANGE(value_1, value_2, delimiter) \ |
| do \ |
| { \ |
| int converted; \ |
| int v1; \ |
| int v2; \ |
| converted = sscanf(optarg, "%d" delimiter "%d", &v1, &v2); \ |
| if (converted == 2 && v1 >= 0 && v2 >= 0) \ |
| { \ |
| value_1 = v1; \ |
| value_2 = v2; \ |
| } \ |
| else \ |
| { \ |
| MONKEY_LOG_ERROR("Error range: %s", optarg); \ |
| show_usage(argv[0], EXIT_FAILURE); \ |
| } \ |
| } while (0) |
| |
| /* Default parameters */ |
| |
| #if defined(CONFIG_VIDEO_FB) |
| # define MONKEY_SCREEN_DEV "/dev/fb0" |
| # define MONKEY_SCREEN_GETVIDEOINFO FBIOGET_VIDEOINFO |
| #elif defined(CONFIG_LCD) |
| # define MONKEY_SCREEN_DEV "/dev/lcd0" |
| # define MONKEY_SCREEN_GETVIDEOINFO LCDDEVIO_GETVIDEOINFO |
| #endif |
| |
| #define MONKEY_SCREEN_HOR_RES_DEFAULT 480 |
| #define MONKEY_SCREEN_VER_RES_DEFAULT 480 |
| |
| #define MONKEY_PERIOD_MIN_DEFAULT 100 |
| #define MONKEY_PERIOD_MAX_DEFAULT 500 |
| #define MONKEY_BUTTON_BIT_DEFAULT 0 |
| |
| #define MONKEY_EVENT_CLICK_WEIGHT_DEFAULT 70 |
| #define MONKEY_EVENT_LONG_PRESS_WEIGHT_DEFAULT 10 |
| #define MONKEY_EVENT_DRAG_WEIGHT_DEFAULT 20 |
| |
| #define MONKEY_EVENT_CLICK_DURATION_MIN_DEFAULT 50 |
| #define MONKEY_EVENT_CLICK_DURATION_MAX_DEFAULT 200 |
| #define MONKEY_EVENT_LONG_PRESS_DURATION_MIN_DEFAULT 400 |
| #define MONKEY_EVENT_LONG_PRESS_DURATION_MAX_DEFAULT 600 |
| #define MONKEY_EVENT_DRAG_DURATION_MIN_DEFAULT 100 |
| #define MONKEY_EVENT_DRAG_DURATION_MAX_DEFAULT 400 |
| |
| #define MONKEY_EVENT_PARAM_INIT(name) \ |
| do \ |
| { \ |
| param->event[MONKEY_EVENT_##name].weight = \ |
| MONKEY_EVENT_##name##_WEIGHT_DEFAULT; \ |
| param->event[MONKEY_EVENT_##name].duration_min = \ |
| MONKEY_EVENT_##name##_DURATION_MIN_DEFAULT; \ |
| param->event[MONKEY_EVENT_##name].duration_max = \ |
| MONKEY_EVENT_##name##_DURATION_MAX_DEFAULT; \ |
| } while (0) |
| |
| /**************************************************************************** |
| * Private Type Declarations |
| ****************************************************************************/ |
| |
| struct monkey_param_s |
| { |
| int dev_type_mask; |
| FAR const char *file_path; |
| int hor_res; |
| int ver_res; |
| int period_min; |
| int period_max; |
| int btn_bit; |
| int log_level; |
| struct monkey_event_config_s event[MONKEY_EVENT_LAST]; |
| }; |
| |
| enum monkey_wait_res_e |
| { |
| MONKEY_WAIT_RES_AGAIN, |
| MONKEY_WAIT_RES_PAUSE, |
| MONKEY_WAIT_RES_STOP, |
| MONKEY_WAIT_RES_ERROR, |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: show_usage |
| ****************************************************************************/ |
| |
| static void show_usage(FAR const char *progname, int exitcode) |
| { |
| printf("\nUsage: %s" |
| " -t <hex-value>" |
| " -f <string>" |
| " -p <string>" |
| " -s <string>" |
| " -b <decimal-value>" |
| " -l <decimal-value>\n" |
| " --weight-click <decimal-value>" |
| " --weight-longpress <decimal-value>" |
| " --weight-drag <decimal-value>\n" |
| " --duration-click <string>" |
| " --duration-longpress <string>" |
| " --duration-drag <string>\n", |
| progname); |
| |
| printf("\nWhere:\n"); |
| |
| printf(" -t <hex-value> Device type mask: uinput = 0x%02X;" |
| " touch = 0x%02X; button = 0x%02X.\n", |
| MONKEY_UINPUT_TYPE_MASK, |
| MONKEY_DEV_TYPE_TOUCH, |
| MONKEY_DEV_TYPE_BUTTON); |
| printf(" -f <string> Recorder playback file path.\n"); |
| printf(" -p <string> Period(ms) range: " |
| "<decimal-value min>-<decimal-value max>\n"); |
| printf(" -s <string> Screen resolution: " |
| "<decimal-value hor_res>x<decimal-value ver_res>\n"); |
| printf(" -b <decimal-value> Button bit: 0 ~ 31\n"); |
| printf(" -l <decimal-value> Log level: 0 ~ 3\n"); |
| |
| printf(" --weight-click <decimal-value> Click event weight.\n"); |
| printf(" --weight-longpress <decimal-value> Long press event weight.\n"); |
| printf(" --weight-drag <decimal-value> Drag event weight.\n"); |
| |
| printf(" --duration-click <string> Click duration(ms) range: " |
| "<decimal-value min>-<decimal-value max>.\n"); |
| printf(" --duration-longpress <string> Long press duration(ms) range: " |
| "<decimal-value min>-<decimal-value max>.\n"); |
| printf(" --duration-drag <string> Drag duration(ms) range: " |
| "<decimal-value min>-<decimal-value max>.\n"); |
| |
| exit(exitcode); |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_get_screen_resolution |
| ****************************************************************************/ |
| |
| static int monkey_get_screen_resolution(FAR int *hor_res, FAR int *ver_res) |
| { |
| #ifdef MONKEY_SCREEN_DEV |
| struct fb_videoinfo_s vinfo; |
| int fd; |
| int ret; |
| FAR const char *dev_path = MONKEY_SCREEN_DEV; |
| *hor_res = MONKEY_SCREEN_HOR_RES_DEFAULT; |
| *ver_res = MONKEY_SCREEN_VER_RES_DEFAULT; |
| fd = open(dev_path, 0); |
| |
| if (fd < 0) |
| { |
| MONKEY_LOG_ERROR("screen dev %s open failed: %d", dev_path, errno); |
| return ERROR; |
| } |
| |
| ret = ioctl(fd, MONKEY_SCREEN_GETVIDEOINFO, |
| (unsigned long)((uintptr_t)&vinfo)); |
| |
| if (ret < 0) |
| { |
| MONKEY_LOG_ERROR("get video info failed: %d", errno); |
| } |
| else |
| { |
| *hor_res = vinfo.xres; |
| *ver_res = vinfo.yres; |
| } |
| |
| close(fd); |
| return ret; |
| #else |
| *hor_res = MONKEY_SCREEN_HOR_RES_DEFAULT; |
| *ver_res = MONKEY_SCREEN_VER_RES_DEFAULT; |
| return OK; |
| #endif /* MONKEY_SCREEN_DEV */ |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_init |
| ****************************************************************************/ |
| |
| static FAR struct monkey_s *monkey_init( |
| FAR const struct monkey_param_s *param) |
| { |
| FAR struct monkey_s *monkey; |
| struct monkey_config_s config; |
| int i; |
| |
| if (!param->dev_type_mask) |
| { |
| show_usage(MONKEY_PREFIX, EXIT_FAILURE); |
| } |
| |
| monkey = monkey_create(param->dev_type_mask); |
| |
| if (!monkey) |
| { |
| goto failed; |
| } |
| |
| monkey_config_default_init(&config); |
| config.screen.hor_res = param->hor_res; |
| config.screen.ver_res = param->ver_res; |
| config.period.min = param->period_min; |
| config.period.max = param->period_max; |
| config.btn_bit = param->btn_bit; |
| memcpy(config.event, param->event, sizeof(config.event)); |
| monkey_set_config(monkey, &config); |
| monkey_log_set_level(param->log_level); |
| |
| MONKEY_LOG_NOTICE("Screen: %dx%d", |
| config.screen.hor_res, |
| config.screen.ver_res); |
| MONKEY_LOG_NOTICE("Period: %" PRIu32 " ~ %" PRIu32 "ms", |
| config.period.min, |
| config.period.max); |
| MONKEY_LOG_NOTICE("Button bit: %d", config.btn_bit); |
| MONKEY_LOG_NOTICE("Log level: %d", monkey_log_get_level()); |
| |
| for (i = 0; i < MONKEY_EVENT_LAST; i++) |
| { |
| MONKEY_LOG_NOTICE("Event %d(%s): weight=%d" |
| " duration %d ~ %dms", |
| i, |
| monkey_event_type2name(i), |
| config.event[i].weight, |
| config.event[i].duration_min, |
| config.event[i].duration_max); |
| } |
| |
| if (MONKEY_IS_UINPUT_TYPE(param->dev_type_mask)) |
| { |
| if (param->file_path) |
| { |
| monkey_set_mode(monkey, MONKEY_MODE_PLAYBACK); |
| if (!monkey_set_recorder_path(monkey, param->file_path)) |
| { |
| goto failed; |
| } |
| } |
| else |
| { |
| monkey_set_mode(monkey, MONKEY_MODE_RANDOM); |
| } |
| } |
| else |
| { |
| monkey_set_mode(monkey, MONKEY_MODE_RECORD); |
| if (!monkey_set_recorder_path(monkey, |
| CONFIG_TESTING_MONKEY_REC_DIR_PATH)) |
| { |
| goto failed; |
| } |
| |
| monkey_set_period(monkey, config.period.min); |
| } |
| |
| return monkey; |
| |
| failed: |
| if (monkey) |
| { |
| monkey_delete(monkey); |
| } |
| |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: parse_long_commandline |
| ****************************************************************************/ |
| |
| static void parse_long_commandline(int argc, FAR char **argv, |
| int ch, |
| FAR const struct option *longopts, |
| int longindex, |
| FAR struct monkey_param_s *param) |
| { |
| int event_index; |
| switch (longindex) |
| { |
| case 0: |
| case 1: |
| case 2: |
| event_index = longindex; |
| OPTARG_TO_VALUE(param->event[event_index].weight, uint8_t, 10); |
| break; |
| |
| case 3: |
| case 4: |
| case 5: |
| event_index = longindex - 3; |
| OPTARG_TO_RANGE(param->event[event_index].duration_min, |
| param->event[event_index].duration_max, |
| "-"); |
| break; |
| |
| default: |
| MONKEY_LOG_WARN("Unknown longindex: %d", longindex); |
| show_usage(argv[0], EXIT_FAILURE); |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: parse_commandline |
| ****************************************************************************/ |
| |
| static void parse_commandline(int argc, FAR char **argv, |
| FAR struct monkey_param_s *param) |
| { |
| int ch; |
| int longindex = 0; |
| FAR const char *optstring = "t:f:p:s:b:l:"; |
| const struct option longopts[] = |
| { |
| {"weight-click", required_argument, NULL, 0 }, |
| {"weight-longpress", required_argument, NULL, 0 }, |
| {"weight-drag", required_argument, NULL, 0 }, |
| {"duration-click", required_argument, NULL, 0 }, |
| {"duration-longpress", required_argument, NULL, 0 }, |
| {"duration-drag", required_argument, NULL, 0 }, |
| {0, 0, NULL, 0 } |
| }; |
| |
| memset(param, 0, sizeof(struct monkey_param_s)); |
| monkey_get_screen_resolution(¶m->hor_res, ¶m->ver_res); |
| param->period_min = MONKEY_PERIOD_MIN_DEFAULT; |
| param->period_max = MONKEY_PERIOD_MAX_DEFAULT; |
| param->btn_bit = MONKEY_BUTTON_BIT_DEFAULT; |
| param->log_level = MONKEY_LOG_LEVEL_NOTICE; |
| MONKEY_EVENT_PARAM_INIT(CLICK); |
| MONKEY_EVENT_PARAM_INIT(LONG_PRESS); |
| MONKEY_EVENT_PARAM_INIT(DRAG); |
| |
| while ((ch = getopt_long(argc, argv, |
| optstring, longopts, &longindex)) != ERROR) |
| { |
| switch (ch) |
| { |
| case 0: |
| parse_long_commandline(argc, argv, ch, |
| longopts, longindex, param); |
| break; |
| |
| case 't': |
| OPTARG_TO_VALUE(param->dev_type_mask, int, 16); |
| break; |
| |
| case 'f': |
| param->file_path = optarg; |
| break; |
| |
| case 'p': |
| OPTARG_TO_RANGE(param->period_min, param->period_max, "-"); |
| break; |
| |
| case 's': |
| OPTARG_TO_RANGE(param->hor_res, param->ver_res, "x"); |
| break; |
| |
| case 'b': |
| OPTARG_TO_VALUE(param->btn_bit, int, 10); |
| param->btn_bit = CONSTRAIN(param->btn_bit, 0, 31); |
| break; |
| |
| case 'l': |
| OPTARG_TO_VALUE(param->log_level, int, 10); |
| param->log_level = CONSTRAIN(param->log_level, 0, |
| MONKEY_LOG_LEVEL_LAST - 1); |
| break; |
| |
| case '?': |
| MONKEY_LOG_WARN("Unknown option: %c", optopt); |
| show_usage(argv[0], EXIT_FAILURE); |
| break; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_pause |
| ****************************************************************************/ |
| |
| static int monkey_pause(void) |
| { |
| sigset_t set; |
| |
| sigemptyset(&set); |
| sigaddset(&set, SIGCONT); |
| |
| return sigwaitinfo(&set, NULL); |
| } |
| |
| /**************************************************************************** |
| * Name: monkey_wait |
| ****************************************************************************/ |
| |
| static enum monkey_wait_res_e monkey_wait(uint32_t ms) |
| { |
| sigset_t set; |
| struct timespec timeout; |
| int ret; |
| |
| enum monkey_wait_res_e res = MONKEY_WAIT_RES_ERROR; |
| |
| if (ms == 0) |
| { |
| return MONKEY_WAIT_RES_AGAIN; |
| } |
| |
| timeout.tv_sec = ms / 1000; |
| timeout.tv_nsec = (ms % 1000) * 1000000; |
| |
| sigemptyset(&set); |
| sigaddset(&set, SIGTSTP); |
| |
| ret = sigtimedwait(&set, NULL, &timeout); |
| |
| if (ret < 0) |
| { |
| int errcode = errno; |
| if (errcode == EINTR) |
| { |
| res = MONKEY_WAIT_RES_STOP; |
| } |
| else if(errcode == EAGAIN) |
| { |
| res = MONKEY_WAIT_RES_AGAIN; |
| } |
| else |
| { |
| MONKEY_LOG_ERROR("Unknow error: %d", errcode); |
| } |
| } |
| else if (ret == SIGTSTP) |
| { |
| res = MONKEY_WAIT_RES_PAUSE; |
| } |
| |
| return res; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: monkey_main |
| * |
| * Description: |
| * |
| * Input Parameters: |
| * Standard argc and argv |
| * |
| * Returned Value: |
| * Zero on success; a positive, non-zero value on failure. |
| * |
| ****************************************************************************/ |
| |
| int main(int argc, FAR char *argv[]) |
| { |
| struct monkey_param_s param; |
| FAR struct monkey_s *monkey; |
| parse_commandline(argc, argv, ¶m); |
| |
| monkey = monkey_init(¶m); |
| |
| if (!monkey) |
| { |
| return ERROR; |
| } |
| |
| while (1) |
| { |
| enum monkey_wait_res_e res; |
| int sleep_ms; |
| |
| sleep_ms = monkey_update(monkey); |
| |
| if (sleep_ms < 0) |
| { |
| break; |
| } |
| |
| res = monkey_wait(sleep_ms); |
| |
| if (res == MONKEY_WAIT_RES_AGAIN) |
| { |
| continue; |
| } |
| else if (res == MONKEY_WAIT_RES_PAUSE) |
| { |
| MONKEY_LOG_NOTICE("pause"); |
| if (monkey_pause() < 0) |
| { |
| break; |
| } |
| |
| MONKEY_LOG_NOTICE("continue..."); |
| } |
| else |
| { |
| /* STOP or ERROR */ |
| |
| break; |
| } |
| } |
| |
| monkey_delete(monkey); |
| |
| return OK; |
| } |