700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > android系统触摸屏虚拟按键

android系统触摸屏虚拟按键

时间:2023-12-04 13:15:12

相关推荐

android系统触摸屏虚拟按键

(一)虚拟按键的描述可以见 韩超和梁泉的 《Android系统级深入开发——移植与调试》的第八章:

虚拟按键(VirtualKey)是Eclair版本开始增加的新特性。VirtualKey的功能是利用触摸屏,模拟按键发生的事件,这样就可以利用触摸屏的边缘,实现一些可以自定义的按键效果。

虚拟按键的实现效果如图8-5所示。

图8-5虚拟按键的实现效果

在Android系统中,触摸屏设备发送的是RawInputEvent(原始输入事件),而按键发送的是KeyEvent(按键事件)。KeyEvent直接发送给应用程序层,RawInputEvent在Android的Java框架中被转换成MotionEvent发送给应用程序层。

在Android系统中虚拟按键的实现方法是:在某种情况下,将RawInputEvent转换成KeyEvent。

frameworks/base/services/Java/com/android/server目录中的InputDevice.Java文件负责处理虚拟按键的主要文件。

虚拟按键的处理相对简单,需要根据以下文件对虚拟按键的内容进行配置:

/sys/board_properties/virtualkeys.{devicename}

在InputDevice.Java文件中通过readVirtualKeys,对进行消息的转化。根据配置文件将RawInputEvent转换成按键相关的内容。

virtualkeys.{devicename}是虚拟按键的适配文件,需要在目标文件系统的/sys/board_properties/目录中。

虚拟按键配置文件的格式如下所示:

0x1:扫描码:X:Y:W:H:0x1:……

例如,在MSM的mahimahi平台上查看虚拟按键的配置文件如下所示:

#cat/sys/board_properties/virtualkeys.synaptics-rmi-touchscreen

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

由此可见,其中定义了4个区域的虚拟按键,它们的Y坐标相同,可见4个按键的矩形区域位于水平的一排。其转换的扫描码分别为158,139,102,217,分别对应于BACK(返回),MENU(菜单),HOME(主界面),SEARCH(搜索)这4个按键。

另外一个系统的虚拟按键的配置文件如下所示:

$cat/sys/board_properties/virtualkeys.qtouch-touchscreen

0x01:139:90:936:116:104:0x01:102:252:936:116:104:0x01:158:402:936:116:104

其转换的扫描码分别为:139,102,158,分别对应于MENU(菜单),HOME(主界面),BACK(返回)这3个按键。

提示:使用虚拟按键转换成为的是按键的扫描码,不是按键码,因此依然需要经过按键布局文件的转化才能得到按键码。

(二)如果按照韩超和梁泉的《Android系统级深入开发——移植与调试》的第八章描述虚拟按键的实现过程如下:

1.硬件分析我所使用的触摸屏分辨率是1158*768,可视区域大小是1024*768(这也是LCD屏的大小),在触摸屏两侧总有5个按键。2.触摸屏按键驱动的修改既然可视区域在触摸屏中间部位,因此需要在触摸屏驱动中修改input_set_abs_params()函数中的参数 input_set_abs_params(input_dev, ABS_MT_POSITION_X, 61, 1085, 0, 0);

input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);

其中61是可视区域左侧x轴左边坐标,1085是右侧的。

3.Android框架层

Android上层通过读取触摸屏坐标并经过转算后来识别定义好的虚拟按键,代码位于frameworks/base/services/java/com/android/server/KeyInputQueue.java中:

static class Virtualkey{}是负责按键定位的方法;

private void readVirtualKeys(String deviceName)负责读取sys文件,这是最重要的代码,也是与底层sys文件系统沟通的桥梁,它会读取/sys/board_properties/virtualkeys.{deviceName}文件,deviceName一定要与触摸屏设备名称一致,不然会找不到指定的sys文件。

4.sys文件系统

前面讲到了readVirtualKeys会读取sys文件,这个sys文件就是定义虚拟按键的坐标以及键值,它的协议格式是一段字符串,每个按键有六项分别用冒号分割,按键按键之间也是用冒号分割,标准格式是:

键类型:键值:按键区域中心x坐标:按键区域中心y坐标:按键区域宽:按键区域高

加载触摸屏以及创建sys文件的代码:

#ifdef VIRTUAL_KEYSstatic ssize_t virtual_keys_show(struct kobject *kobj,struct kobj_attribute *attr, char *buf){if (1) {return sprintf(buf,__stringify(EV_KEY) ":" __stringify(KEY_VOLUMEUP) ":1030:370:30:30"":" __stringify(EV_KEY) ":" __stringify(KEY_VOLUMEDOWN) ":1030:470:30:30"":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":1030:18:30:30"":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":1030:136:30:30"":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":1030:236:30:30""\n");} else {}}static struct kobj_attribute virtual_keys_attr = {.attr = {.name = "virtualkeys.xxxx",.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 void 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_properties\n"); }#endif

其中.name = "virtualkeys.xxxx"的xxxx就是触摸屏设备名称,也就是前面说到的{deviceName},virtual_keys_init()函数可以在触摸屏probe函数中调用。另外,我将可视区域左侧的两侧按键移到了右侧实现,因此五个按键区域中心x坐标都是一样,这部分要在触摸屏驱动增加x轴坐标调整,这部分我就不再说明。以上步骤完成后可以使用 cat /sys/board_properties/virtualkeys.{deviceName}查看虚拟按键的配置文件,并试试按下触摸屏上按键是否有反应,如果坐标不正确还要进行耐心地校准。

具体的可以去HTC网站(/devcenter/downloads)上下载HTC手机的linux源码,HTC很多款手机的BACK,MENU,HOME,SEARCH电容屏虚拟按键都是采用这种方式实现的。(三)但是我用的是四线电阻触摸屏,用上面的方式好像没有任何反应,所以我采用最直接的方式用input_event发送按键消息,就是在触摸屏处理芯片TSC驱动中当读到一定范围内的触摸事件就发送按键消息:这种方式只要修改TSC驱动的几个地方:1.定义全局局部变量用于记录是否有虚拟按键按下

static int backkeydown=0;static int homekeydown=0;static int menukeydown=0;

2..在prope函数中添加

set_bit(EV_SYN, input_dev->evbit);set_bit(KEY_HOME, input_dev->keybit);//set_bit(KEY_SEARCH, input_dev->keybit);set_bit(KEY_BACK, input_dev->keybit);set_bit(KEY_MENU, input_dev->keybit);

3.在中断处理work里面添加按键按下消息

if((tc.y>=81)&&(tc.y<=181)){if((tc.x>=2711)&&(tc.x<=2832)){if(backkeydown==0){input_event(input, EV_KEY,KEY_BACK, 1);backkeydown=1;//printk("back key down\n");}}else if((tc.x>=1891)&&(tc.x<=1933)){if(homekeydown==0){input_event(input, EV_KEY,KEY_HOME, 1);homekeydown=1;//printk("home key down\n");}}else if((tc.x>=1088)&&(tc.x<=1143)){if(menukeydown==0){input_event(input, EV_KEY,KEY_MENU, 1);menukeydown=1;//printk("menu key down\n");}}}

4.在tsc_send_up_event函数中添加按键释放消息

if(backkeydown==1){backkeydown=0;input_event(input, EV_KEY, KEY_BACK, 0);//printk("back key up\n");}if(homekeydown==1){homekeydown=0;input_event(input, EV_KEY, KEY_HOME, 0);//printk("home key up\n");}if(menukeydown==1){menukeydown=0;input_event(input, EV_KEY, KEY_MENU, 0);//printk("menu key up\n");}

整个修改后的TSC驱动如下:

/** drivers/input/touchscreen/tsc.c** Copyright (c) MtekVision Co., Ltd.*Kwangwoo Lee <kwlee@>** Using code from:* - ads7846.c*Copyright (c) David Brownell*Copyright (c) Nokia Corporation* - corgi_ts.c*Copyright (C) - Richard Purdie* - omap_ts.[hc], ads7846.h, ts_osk.c*Copyright (C) 2002 MontaVista Software*Copyright (C) Texas Instruments*Copyright (C) Dirk Behme** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.*/#include <linux/module.h>#include <linux/slab.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/i2c.h>#include <linux/i2c/tsc.h>#define TS_POLL_DELAY1 /* ms delay between samples */#define TS_POLL_PERIOD1 /* ms delay between samples */#define TSC_MEASURE_TEMP0(0x0 << 4)#define TSC_MEASURE_AUX(0x2 << 4)#define TSC_MEASURE_TEMP1(0x4 << 4)#define TSC_ACTIVATE_XN(0x8 << 4)#define TSC_ACTIVATE_YN(0x9 << 4)#define TSC_ACTIVATE_YP_XN(0xa << 4)#define TSC_SETUP(0xb << 4)#define TSC_MEASURE_X(0xc << 4)#define TSC_MEASURE_Y(0xd << 4)#define TSC_MEASURE_Z1(0xe << 4)#define TSC_MEASURE_Z2(0xf << 4)#define TSC_POWER_OFF_IRQ_EN(0x0 << 2)#define TSC_ADC_ON_IRQ_DIS0(0x1 << 2)#define TSC_ADC_OFF_IRQ_EN(0x2 << 2)#define TSC_ADC_ON_IRQ_DIS1(0x3 << 2)#define TSC_12BIT(0x0 << 1)#define TSC_8BIT(0x1 << 1)#defineMAX_12BIT((1 << 12) - 1)#define ADC_ON_12BIT(TSC_12BIT | TSC_ADC_ON_IRQ_DIS0)#define READ_Y(ADC_ON_12BIT | TSC_MEASURE_Y)#define READ_Z1(ADC_ON_12BIT | TSC_MEASURE_Z1)#define READ_Z2(ADC_ON_12BIT | TSC_MEASURE_Z2)#define READ_X(ADC_ON_12BIT | TSC_MEASURE_X)#define PWRDOWN(TSC_12BIT | TSC_POWER_OFF_IRQ_EN)static int backkeydown=0;static int homekeydown=0;static int menukeydown=0;struct ts_event {u16x;u16y;u16z1, z2;};struct tsc {struct input_dev*input;charphys[32];struct delayed_workwork;struct i2c_client*client;u16model;u16x_plate_ohms;boolpendown;intirq;int(*get_pendown_state)(void);void(*clear_penirq)(void);};static inline int tsc_xfer(struct tsc *tsc, u8 cmd){s32 data;u16 val;data = i2c_smbus_read_word_data(tsc->client, cmd);if (data < 0) {dev_err(&tsc->client->dev, "i2c io error: %d\n", data);return data;}/* The protocol and raw data format from i2c interface:* S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P* Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].*/val = swab16(data) >> 4;dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);return val;}static void tsc_read_values(struct tsc *tsc, struct ts_event *tc){/* y- still on; turn on only y+ (and ADC) */tc->y = tsc_xfer(tsc, READ_Y);/* turn y- off, x+ on, then leave in lowpower */tc->x = tsc_xfer(tsc, READ_X);/* turn y+ off, x- on; we'll use formula #1 */tc->z1 = tsc_xfer(tsc, READ_Z1);tc->z2 = tsc_xfer(tsc, READ_Z2);//printk("x=%d,y=%d\n",tc->x,tc->y);/* Prepare for next touch reading - power down ADC, enable PENIRQ */tsc_xfer(tsc, PWRDOWN);}static u32 tsc_calculate_pressure(struct tsc *tsc, struct ts_event *tc){u32 rt = 0;/* range filtering */if (tc->x == MAX_12BIT)tc->x = 0;if (likely(tc->x && tc->z1)) {/* compute touch pressure resistance using equation #1 */rt = tc->z2 - tc->z1;rt *= tc->x;rt *= tsc->x_plate_ohms;rt /= tc->z1;rt = (rt + 2047) >> 12;}return rt;}static void tsc_send_up_event(struct tsc *tsc){struct input_dev *input = tsc->input;dev_dbg(&tsc->client->dev, "UP\n");if(backkeydown==1){backkeydown=0;input_event(input, EV_KEY, KEY_BACK, 0);//printk("back key up\n");}if(homekeydown==1){homekeydown=0;input_event(input, EV_KEY, KEY_HOME, 0);//printk("home key up\n");}if(menukeydown==1){menukeydown=0;input_event(input, EV_KEY, KEY_MENU, 0);//printk("menu key up\n");}input_report_key(input, BTN_TOUCH, 0);input_report_abs(input, ABS_PRESSURE, 0);input_sync(input);}static void tsc_work(struct work_struct *work){struct tsc *ts =container_of(to_delayed_work(work), struct tsc, work);struct ts_event tc;u32 rt;/** NOTE: We can't rely on the pressure to determine the pen down* state, even though this controller has a pressure sensor.* The pressure value can fluctuate for quite a while after* lifting the pen and in some cases may not even settle at the* expected value.** The only safe way to check for the pen up condition is in the* work function by reading the pen signal state (it's a GPIO* and IRQ). Unfortunately such callback is not always available,* in that case we have rely on the pressure anyway.*/if (ts->get_pendown_state) {if (unlikely(!ts->get_pendown_state())) {tsc_send_up_event(ts);ts->pendown = false;goto out;}dev_dbg(&ts->client->dev, "pen is still down\n");}tsc_read_values(ts, &tc);rt = tsc_calculate_pressure(ts, &tc);if (rt > MAX_12BIT) {/** Sample found inconsistent by debouncing or pressure is* beyond the maximum. Don't report it to user space,* repeat at least once more the measurement.*/dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);goto out;}if (rt) {struct input_dev *input = ts->input;if((tc.y>=81)&&(tc.y<=181)){if((tc.x>=2711)&&(tc.x<=2832)){if(backkeydown==0){input_event(input, EV_KEY,KEY_BACK, 1);backkeydown=1;//printk("back key down\n");}}else if((tc.x>=1891)&&(tc.x<=1933)){if(homekeydown==0){input_event(input, EV_KEY,KEY_HOME, 1);homekeydown=1;//printk("home key down\n");}}else if((tc.x>=1088)&&(tc.x<=1143)){if(menukeydown==0){input_event(input, EV_KEY,KEY_MENU, 1);menukeydown=1;//printk("menu key down\n");}}}if (!ts->pendown) {dev_dbg(&ts->client->dev, "DOWN\n");input_report_key(input, BTN_TOUCH, 1);ts->pendown = true;}input_report_abs(input, ABS_X, tc.x);input_report_abs(input, ABS_Y, tc.y);input_report_abs(input, ABS_PRESSURE, rt);input_sync(input);dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",tc.x, tc.y, rt);} else if (!ts->get_pendown_state && ts->pendown) {/** We don't have callback to check pendown state, so we* have to assume that since pressure reported is 0 the* pen was lifted up.*/tsc_send_up_event(ts);ts->pendown = false;}out:if (ts->pendown)schedule_delayed_work(&ts->work,msecs_to_jiffies(TS_POLL_PERIOD));elseenable_irq(ts->irq);}static irqreturn_t tsc_irq(int irq, void *handle){struct tsc *ts = handle;if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {disable_irq_nosync(ts->irq);schedule_delayed_work(&ts->work,msecs_to_jiffies(TS_POLL_DELAY));}if (ts->clear_penirq)ts->clear_penirq();return IRQ_HANDLED;}static void tsc_free_irq(struct tsc *ts){free_irq(ts->irq, ts);if (cancel_delayed_work_sync(&ts->work)) {/** Work was pending, therefore we need to enable* IRQ here to balance the disable_irq() done in the* interrupt handler.*/enable_irq(ts->irq);}}static int __devinit tsc_probe(struct i2c_client *client,const struct i2c_device_id *id){struct tsc *ts;struct tsc_platform_data *pdata = pdata = client->dev.platform_data;struct input_dev *input_dev;int err;if (!pdata) {dev_err(&client->dev, "platform data is required!\n");return -EINVAL;}if (!i2c_check_functionality(client->adapter,I2C_FUNC_SMBUS_READ_WORD_DATA))return -EIO;ts = kzalloc(sizeof(struct tsc), GFP_KERNEL);input_dev = input_allocate_device();if (!ts || !input_dev) {err = -ENOMEM;goto err_free_mem;}ts->client = client;ts->irq = client->irq;ts->input = input_dev;INIT_DELAYED_WORK(&ts->work, tsc_work);ts->model = pdata->model;ts->x_plate_ohms= pdata->x_plate_ohms;ts->get_pendown_state = pdata->get_pendown_state;ts->clear_penirq= pdata->clear_penirq;snprintf(ts->phys, sizeof(ts->phys),"%s/input0", dev_name(&client->dev));input_dev->name = "TSC";input_dev->phys = ts->phys;input_dev->id.vendor = 0x0001;input_dev->id.product = 0x0001;input_dev->id.version = 0x0100;input_dev->id.bustype = BUS_I2C;input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);set_bit(EV_SYN, input_dev->evbit);set_bit(KEY_HOME, input_dev->keybit);//set_bit(KEY_SEARCH, input_dev->keybit);set_bit(KEY_BACK, input_dev->keybit);set_bit(KEY_MENU, input_dev->keybit);input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);if (pdata->init_platform_hw)pdata->init_platform_hw();err = request_irq(ts->irq, tsc_irq, 0,client->dev.driver->name, ts);if (err < 0) {dev_err(&client->dev, "irq %d busy?\n", ts->irq);goto err_free_mem;}/* Prepare for touch readings - power down ADC and enable PENIRQ */err = tsc_xfer(ts, PWRDOWN);if (err < 0)goto err_free_irq;err = input_register_device(input_dev);if (err)goto err_free_irq;i2c_set_clientdata(client, ts);return 0;err_free_irq:tsc_free_irq(ts);if (pdata->exit_platform_hw)pdata->exit_platform_hw();err_free_mem:input_free_device(input_dev);kfree(ts);return err;}static int __devexit tsc_remove(struct i2c_client *client){struct tsc*ts = i2c_get_clientdata(client);struct tsc_platform_data *pdata = client->dev.platform_data;tsc_free_irq(ts);if (pdata->exit_platform_hw)pdata->exit_platform_hw();input_unregister_device(ts->input);kfree(ts);return 0;}static struct i2c_device_id tsc_idtable[] = {{ "tsc", 0 },{ }};MODULE_DEVICE_TABLE(i2c, tsc_idtable);static struct i2c_driver tsc_driver = {.driver = {.owner= THIS_MODULE,.name= "tsc"},.id_table= tsc_idtable,.probe= tsc_probe,.remove= __devexit_p(tsc_remove),};static int __init tsc_init(void){return i2c_add_driver(&tsc_driver);}static void __exit tsc_exit(void){i2c_del_driver(&tsc_driver);}module_init(tsc_init);module_exit(tsc_exit);MODULE_AUTHOR("Kwangwoo Lee <kwlee@>");MODULE_DESCRIPTION("TSC TouchScreen Driver");MODULE_LICENSE("GPL");

实践证明上述方式完全可行,这里我没有指定TSC的kl和kcm文件,android系统会自动选择默认的\android\sdk\emulator\keymaps\目录下的qwerty.kl和qwerty.kcm文件,如果你的系统不能正常收到按键消息,你首先可以用android的getevent看看驱动是否能正常发送按键消息给上层,如果能正常收到触摸屏驱动发送的按键消息,上层android系统不能收到的原因就是kl和kcm文件不对。5.另外附上加载TSC驱动的代码

/* TouchScreen */#define IRQ1 1unsigned int tsc_int;unsigned int tsc_irq_no;static int ts_get_pendown_state(void){int result=!(imapx_gpio_getpin(tsc_int, IG_NORMAL));//printk("%d\n",result);return result;}static int ts_init(void){//printk("====ts_init===\n");tsc_int = __imapx_name_to_gpio(CONFIG_TP_TSC_INT);if(tsc_int == IMAPX_GPIO_ERROR) {printk(KERN_ERR "failed to get tsc_int pin.\n");return -1;}tsc_irq_no = imapx_gpio_to_irq(tsc_int);imapx_gpio_setcfg(tsc_int, IG_INPUT, IG_NORMAL);imapx_gpio_setirq(tsc_int, FILTER_MAX, IG_FALL, 1);return 0;}struct tsc_platform_data tsc_info = {.model= ,.x_plate_ohms= 180,.get_pendown_state= ts_get_pendown_state,.init_platform_hw= ts_init,};static struct i2c_board_info ts_i2c_clients = {I2C_BOARD_INFO("tsc", 0x48),.type= "tsc",.platform_data= &tsc_info,.irq= IRQ1,};static int __init imap_arch_init(void){int ret;// do the correct init for cpuif (cpu == NULL)panic("imap_arch_init: NULL cpu\n");ret = (cpu->init)();if (ret != 0)return ret;ret = platform_add_devices(imap_uart_devs, nr_uarts);if (ret != 0)return ret;/*瑙︽懜灞?/i2c_register_board_info(1, &ts_i2c_clients, 1);/*瑙︽懜灞忚櫄鎷熸寜閿?///virtual_keys_init();printk(KERN_INFO "leaving imap_arch_init\n");return ret;}

---------------------------------------------------------------------------------------------------------------------------------------- QQ:229425962 phone:15820435302 欢迎交流和转载,文章中(一)(二)部分内容来自网络。 ----------------------------------------------------------------------------------------------------------------------------------------

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。