西西图吧

触摸按键怎么实现 Android触摸屏虚拟按键实现方法

时间:2019-05-15 18:44:08作者:小琦关注:1栏目:科技

1.在实现虚拟按键时,内核必须有映射名为virtualkeys.的虚拟按键映射文件,如触摸屏设备驱动程序里的设备名为touchyfeely,则虚拟按键映文件的路径必须为:

/sys/board_properties/virtualkeys.touchyfeely

虚拟按键映射文件描述了触摸屏上虚拟按键的坐标和Linux按键代码,该文件是一个纯文本文件,由一系列换行符或冒号分隔的虚拟按键布局描述组成。相应的语法要求如下:

注释行以“#”开头,只对本行有效。

每个虚拟按键由6个冒号分隔的数据进行描述:

0x01:版本代码。必须始终为 0x01。

:虚拟按键的 Linux 按键代码。

:虚拟按键中心的 X 轴坐标(以像素为单位)。

:虚拟按键中心的 Y 轴坐标(以像素为单位)。

:虚拟按键的宽度(以像素为单位)。

:虚拟按键的高度(以像素为单位)。

所有的坐标和尺寸都是根据显示坐标系指定的。

下面是虚拟按键映射文件的两种格式:

a.一行

# All on one line

0x01:158:55:835:90:55:0x01:139:172:835:125:55:0x01:102:298:835:115:55:0x01:217:412:835:95:55

b.多行

# One key per line 0x01:158:55:835:90:55 0x01:139:172:835:125:55 0x01:102:298:835:115:55 0x01:217:412:835:95:55

这两种格式的示例数据是在一款480*800分辨率的触摸屏上,虚拟按键的centerY坐标为835,比触摸屏的可见区域略低的位置。

2.按键布局及字符映射

有了上面的虚拟按键映射文件在内核的实现后,还需要对应的kl和kcm文件,下面是针对上面例子对应的配置文件:

a.按键布局文件(/system/usr/keylayout/touchyfeely.kl)

key 158 BACK

key 139 MENU

key 102 HOME

key 217 SEARCH

b.按键字符映射文件(/system/usr/keychars/touchyfeely.kcm)

type SPECIAL_FUNCTION

3.为什么是virtualkeys.

在frameworks/native/services/inputflinger/EventHub.cpp文件中,我们可以看到如下内容:

status_t EventHub::loadVirtualKeyMapLocked(Device* device) {

// The virtual key map is supplied by the kernel as a system board property file.

String8 path;

path.append("/sys/board_properties/virtualkeys.");

path.append(device->identifier.name);

if (access(path.string(), R_OK)) {

return NAME_NOT_FOUND;

}

return VirtualKeyMap::load(path, &device->virtualKeyMap);

}

从上面两句path.append可以看出最终的path值就是/sys/board_properties/virtualkeys.了。

4.文件内容为何是一行或多行以“:”分隔的文本内容?

上面代码继续往下跟,可以追溯到frameworks/native/libs/input/VirtualKeyMap.cpp文件,

status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) {

*outMap = NULL;

Tokenizer* tokenizer;

status_t status = Tokenizer::open(filename, &tokenizer);

if (status) {

ALOGE("Error %d opening virtual key map file %s.", status, filename.string());

} else {

VirtualKeyMap* map = new VirtualKeyMap();

if (!map) {

ALOGE("Error allocating virtual key map.");

status = NO_MEMORY;

} else {

#if DEBUG_PARSER_PERFORMANCE

nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);

#endif

Parser parser(map, tokenizer);

status = parser.parse();

#if DEBUG_PARSER_PERFORMANCE

nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;

ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",

tokenizer->getFilename().string(), tokenizer->getLineNumber(),

elapsedTime / 1000000.0);

#endif

if (status) {

delete map;

} else {

*outMap = map;

}

}

delete tokenizer;

}

return status;

}

从上面代码可以看到重点语句status = parser.parse();,即会调用到同一文件的parse函数,其

status_t VirtualKeyMap::Parser::parse() {

while (!mTokenizer->isEof()) {

#if DEBUG_PARSER

ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),

mTokenizer->peekRemainderOfLine().string());

#endif

mTokenizer->skipDelimiters(WHITESPACE);

if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {

// Multiple keys can appear on one line or they can be broken up across multiple lines.

do {

String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);

if (token != "0x01") {

ALOGE("%s: Unknown virtual key type, expected 0x01.",

mTokenizer->getLocation().string());

return BAD_VALUE;

}

VirtualKeyDefinition defn;

bool success = parseNextIntField(&defn.scanCode)

&& parseNextIntField(&defn.centerX)

&& parseNextIntField(&defn.centerY)

&& parseNextIntField(&defn.width)

&& parseNextIntField(&defn.height);

if (!success) {

ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",

mTokenizer->getLocation().string());

return BAD_VALUE;

}

#if DEBUG_PARSER

ALOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, "

"width=%d, height=%d",

defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height);

#endif

mMap->mVirtualKeys.push(defn);

} while (consumeFieldDelimiterAndSkipWhitespace());

if (!mTokenizer->isEol()) {

ALOGE("%s: Expected end of line, got '%s'.",

mTokenizer->getLocation().string(),

mTokenizer->peekRemainderOfLine().string());

return BAD_VALUE;

}

}

mTokenizer->nextLine();

}

return NO_ERROR;

}

从上面可以看到为何以"0x01"开头了吧,接下来再调用parseNextIntField函数来解析剩下的5个数据,该函数

bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) {

if (!consumeFieldDelimiterAndSkipWhitespace()) {

return false;

}

String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);

char* end;

*outValue = strtol(token.string(), &end, 0);

if (token.isEmpty() || *end != '') {

ALOGE("Expected an integer, got '%s'.", token.string());

return false;

}

return true;

}

从上面可以看出这些数据都会转为整型,其中分隔符WHITESPACE_OR_FIELD_DELIMITER定义如下:

static const char* WHITESPACE_OR_FIELD_DELIMITER = " tr:";

5.上面分析了驱动的虚拟按键映射文件对应的解析需求,那么我们实际的驱动文件要如何在原有的触摸屏驱动基础上添加相应的文件结点生成处理呢?(当然也可以独立为一个驱动来实现)

下面是抽象出来的映射文件生成及数据处理需要的代码:

#defineTOUCHYFEELY_KEY_HOME 102

#defineTOUCHYFEELY_KEY_MENU 139

#defineTOUCHYFEELY_KEY_BACK 158

#defineTOUCHYFEELY_KEY_SEARCH 217

static ssize_t virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)

{

return sprintf(buf,

__stringify(EV_KEY) ":" __stringify(TOUCHYFEELY_KEY_HOME)":298:835:115:55"

":" __stringify(EV_KEY) ":" __stringify(TOUCHYFEELY_KEY_MENU)":172:835:125:55"

":" __stringify(EV_KEY) ":" __stringify(TOUCHYFEELY_KEY_BACK)":55:835:90:55"

":" __stringify(EV_KEY) ":" __stringify(TOUCHYFEELY_KEY_SEARCH)":412:835:95:55"

"n");

}

static struct kobj_attribute virtual_keys_attr = {

.attr = {

.name = "virtualkeys.touchyfeely",

.mode = S_IRUGO,

},

.show = &virtual_keys_show,

};

static struct attribute *properties_attrs[] = {

&virtual_keys_attr.attr,

NULL

};

static struct attribute_group properties_attr_group = {

.attrs = properties_attrs,

};

static voidtouchyfeely_virtual_keys_init(void)

{

int ret;

struct kobject *properties_kobj;

properties_kobj = kobject_create_and_add("board_properties", NULL);

if (properties_kobj)

ret = sysfs_create_group(properties_kobj, &properties_attr_group);

if (!properties_kobj || ret)

pr_err("failed to create board_propertiesn");

}

接下来,我们需要在probe函数调用上面的init函数,

static inttouchyfeely_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

/*...*/

touchyfeely_virtual_keys_init();

   /*...*/

set_bit(KEY_HOME, input_dev->keybit);

set_bit(KEY_MENU, input_dev->keybit);

set_bit(KEY_BACK, input_dev->keybit);

set_bit(KEY_SEARCH, input_dev->keybit);

/*...*/

}

至此,我们对虚拟按键需要实现的内容和配置文件有了清晰的了解。

本文标签:科技

西西图吧 | 关于我们 | 联系我们 | 合作加盟 | 服务协议 | 法律声明 | 工作机会 | 网站地图

提示:本站信息仅供参考,不能作为任何事项的依据;请谨慎参阅,本站不承担由此引起的法律责任。

Copyright © 2013-2018 www.chinamaho.com All Rights Reserved

蜀ICP备17021134号-4