weston input 概述

news2025/1/12 9:38:40

weston input 概述

零、前言

本文描述了有关于 weston (基于 wayland 协议一种显示服务器的实现) 中有关于输入设备管理的部分;为了聚焦于此,本文不会对 weston 整体或 wayland 协议进行过多的阐述.

考虑到读者可能存在不同的需求,采用分层次的描述方式,主要面向以下两类人群:

  • 想了解通用显示服务器有关于输入设备管理
  • 想了解 weston 有关于输入设备管理

对于前者,将会站在一个较为抽象的层次,以感性的方式讲述一个通用显示服务器的设计; 对于后者,则更多地会参考 weston 的实现细节,结合一些代码示例,帮助读者更好地理解 weston, 为将来阅读 weston 源码做好一定的准备.

本文基于 weston 10.0.2 分支.

一、显示服务器与输入设备

不知道有没有人会与我一样疑惑,为什么 显示服务器 会与 输入设备 扯上关系呢?

有关于这点的答案实际上很多显示服务器官方都给出了自己的答案,目前主流的答案是: 现代 GUI 程序跟以往的(后台)程序不同,面向的是用户,强调的是交互;因此对于交互的反馈速度(50ms以内)十分看中,为此 GUI 程序一般采取的都是异步编程的方式进行,更为具体的描述是采用事件驱动的异步编程模式.

因此, GUI 程序的 UI 发生改变一定是受到了某些信号源驱动,信号驱动源从广义上可以分为硬件和软件两类:

  • 硬件 : 鼠标、 键盘、 触摸 、蓝牙等
  • 软件 : 定时器 、HTTP/IOT/WS 接口等

其中对于软件类的更多GUI设计者会更加熟悉,例如我们会使用一个定时器去实现一些动态效果(比如说 gif 的播放),利用 IOT 去操作设备以及可能暴露一些 HTTP 接口去进行接口测试.

显示服务器管理的是硬件相关的信号源,而非软件信号源, 理解这点是至关重要的.对于软件信号源,其针对的是 GUI 程序这个进程本身;对于硬件信号源,其针对的是整个系统;两者的影响范围或者说作用域是不同的.

而对于一个正常的 GUI 程序,肯定是不希望其在最小化或者被其他 GUI程序遮挡时仍然接收到诸如鼠标、触摸等事件,毫无疑问如果是这样的话,那将是一件相当奇怪的事情.

所以显示服务器才会代替 GUI 程序去接管系统设备的输入,并针对不同的 GUI 程序进行合适的滤波以及转换 (比如在 GUI 程序隐藏时就输入事件分发给它们),使得在 GUI 程序侧硬件输入的处理逻辑与软件输入没有差异.

二、输入设备种类

根据 weston 的实现,将 weston 的输入设备分为了三大类,具体如下图所示:

在这里插入图片描述

其中 seat 代表的是输入设备的抽象形式,而 pointertouchkeyboard 则代表的是不同种类的输入设备; 这么划分实际上很大程度上是由输入设备硬件本身的特性,几种不同输入设备的特性如下:

  • pointer : 常见的设备类型为鼠标,其特点是输入一般以相对坐标的方式给出,即给出是一个矢量
  • keyboard : 常见的设备类型为键盘,其特点是输入一般以键值给出,例如 ABCD
  • touch : 常见的设备类型为触摸屏,其特点是输入一般以绝对坐标的方式给出

上面各个输入设备的特性从日常使用角度理解起来应该是不难的; 值得一提的是 seat 这个作为其抽象的命名,其具体命名的意图我也不是特别理解,但 seat 并非是 weston 创造的名词,其命名依据来自于一个专门负责输入设备管理的库libinput.

三、显示服务器输入管理设备总体设计

在描述 weston 整体输入管理之前,有必要先停下来,捋一捋weston输入设备管理需要实现的目标:

  • 获取(linux)系统输入事件捕获,以及监听输入设备
  • GUI程序提供统一的输入事件逻辑
  • 按照GUI程序的需求派发不同的输入事件

其中一点是为了获取完整的来自系统的输入事件;
第二点则更多的是标准化的过程,例如虽然 keyboard 类型的输入设备虽然大多数都是键盘,但是像是红外遥控器、蓝牙遥控器等也算是 keyboard 中的一种,在 GUI程序看起来它们像是行为一致的,但是站在 linux 驱动层与应用层之间则不尽其然, weston 需要将各种输入设备进行分类(pointerkyeboardtouch),然后屏蔽各个输入设备之间具体的细节差异;
第三点而是需要根据应用的需求派发事件,例如有些应用只关注于触摸事件,而对键盘事件不敏感,那么 weston 就不应该将事件分发给它们,当程序不处于焦点状态(选中状态)时,也不应该将事件派发给它们.

基于以上描述, weston 给出的整体设计为:

在这里插入图片描述

其中 weston 处于第二级,向左对接 linux 系统, 向右则对接 wayland 显示服务器协议;然后 weston (display server) 和 GUI 程序 (display client) 通过 wayland (显示服务器)协议连接起来.

第二级中,对于捕获的输入事件 weston 首先将其进行标准化,然后对其进行简单的滤波,最后按照需求进行分发;同时监听系统本身的输入设备,管理输入设备的声明周期.

到此为止,一个较为通用的显示服务器关于输入设备的管理就已经讲解完了,剩下的都是一些比较细节的实现,比如说滤波究竟做了些什么,标准化又做了什么,其实对于大多数应用层开发者都已经是非常的遥远的事情了,所以就此收手也是不错的选择.

如果想要对 weston 有一个整体性的理解,推荐阅读 Wayland与Weston简介,而对于 wayland 协议的架构设计,则可以参考 wayland.

四、显示服务器细节设计

(1) 对接系统输入

在讲解之前,需要审视一下 weston 作为一个显示服务器的职责: 毫无疑问那将是渲染,而非输入.

基于此, weston 在对接系统输入这一块并没有做很多的事情,而是利用现在成熟的开源方案, libinput 以及 udev.

  • libinput : 获取输入事件以及设备状态
  • udev : 管理输入设备

weston 对于 libinput 的封装主要存在于 libweston/libinput-device.c, 而 udev 则是位于 libweston/libinput-seat.c.

对于输入设备的监听,主要是基于 Reactor 模式进行; 通俗地讲就是在每个循环的周期内查询有无设备的移除以及添加相关的事件,具体的细节实现如下:

// file : libweston/libinput-seat.c

// 一次循环周期内调用
static int
udev_input_process_event(struct libinput_event *event)
{
    struct libinput *libinput = libinput_event_get_context(event);
    struct libinput_device *libinput_device =
        libinput_event_get_device(event);
    struct udev_input *input = libinput_get_user_data(libinput);
    int ret = 0;

    switch (libinput_event_get_type(event)) { // 获取输入事件类型类型 (libinput)
    case LIBINPUT_EVENT_DEVICE_ADDED: // 设备添加
        ret = device_added(input, libinput_device);
        break;
    case LIBINPUT_EVENT_DEVICE_REMOVED: // 设备移除
        ret = device_removed(input, libinput_device);
        break;
    default:
        evdev_device_process_event(event);
        break;
    }

return ret;
}

// 设备添加
static int
device_added(struct udev_input *input, struct libinput_device *libinput_device)
{
    // ...
    udev_seat = get_udev_seat(input, libinput_device); // udev, 获取 seat
    if (!udev_seat) {
        weston_log("Failed to get a seat\n");
        return 1;
    }

    seat = &udev_seat->base;
    device = evdev_device_create(libinput_device, seat); // udev, 创建设备节点
    if (device == NULL) {
        weston_log("Failed to create a device\n");
        return 1;
    }
    // ...
    return 0;
}

对于输入事件的监听,同样是位于上面代码片段 udev_input_process_event 中的 evdev_device_process_event,具体实现如下:

// file : libweston/libinput-device.c
int
evdev_device_process_event(struct libinput_event *event)
{
    struct libinput_device *libinput_device =
        libinput_event_get_device(event);
    struct evdev_device *device =
        libinput_device_get_user_data(libinput_device);
    int handled = 1;
    bool need_frame = false;

    if (!device)
        return 0;

    switch (libinput_event_get_type(event)) {
    case LIBINPUT_EVENT_KEYBOARD_KEY:
        handle_keyboard_key(libinput_device,
                    libinput_event_get_keyboard_event(event));
        break;
    case LIBINPUT_EVENT_POINTER_MOTION:
        need_frame = handle_pointer_motion(libinput_device,
                        libinput_event_get_pointer_event(event));
        break;
    case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
        need_frame = handle_pointer_motion_absolute(
                libinput_device,
                libinput_event_get_pointer_event(event));
        break;
    case LIBINPUT_EVENT_POINTER_BUTTON:
        need_frame = handle_pointer_button(libinput_device,
                        libinput_event_get_pointer_event(event));
        break;
    case LIBINPUT_EVENT_POINTER_AXIS:
        need_frame = handle_pointer_axis(
                    libinput_device,
                    libinput_event_get_pointer_event(event));
        break;
    case LIBINPUT_EVENT_TOUCH_DOWN:
        handle_touch_down(libinput_device,
                    libinput_event_get_touch_event(event));
        break;
    case LIBINPUT_EVENT_TOUCH_MOTION:
        handle_touch_motion(libinput_device,
                    libinput_event_get_touch_event(event));
        break;
    case LIBINPUT_EVENT_TOUCH_UP:
        handle_touch_up(libinput_device,
                libinput_event_get_touch_event(event));
        break;
    case LIBINPUT_EVENT_TOUCH_FRAME:
        handle_touch_frame(libinput_device,
                    libinput_event_get_touch_event(event));
        break;
    default:
        handled = 0;
        weston_log("unknown libinput event %d\n",
                libinput_event_get_type(event));
    }

    if (need_frame)
        notify_pointer_frame(device->seat);

    return handled;
}

(2) 对接 wayland 协议

在阅读此之前,应当对 wayland 有足够的了解; 如果还不是很清楚, 可以参考 wayland.

wayland 定义了协议,那么 weston 对接 wayland 协议做的自然就是实现协议; 事实上, waylandfreedesktop 给出的标准规范, 而 weston 则是 freedesktop 给出的
参考实现.

那么回到代码上,又应该如何去做的;如果站在抽象语言的角度,wayland 是定义了一个接口类,而 weston 则是继承其并实现;在 C 语言的世界里,这通常使用函数指针来辅助完成,下面以 keyboard 作为一个示例进行描述:

// file : include/libweston/libweston.h
// 接口定义
struct weston_keyboard_grab;
struct weston_keyboard_grab_interface {
    void (*key)(struct weston_keyboard_grab *grab,
            const struct timespec *time, uint32_t key, uint32_t state);
    void (*modifiers)(struct weston_keyboard_grab *grab, uint32_t serial,
                uint32_t mods_depressed, uint32_t mods_latched,
                uint32_t mods_locked, uint32_t group);
    void (*cancel)(struct weston_keyboard_grab *grab);
};

// file : libweston/input.c
// 接口实现
static const struct weston_keyboard_grab_interface
                default_keyboard_grab_interface = {
    default_grab_keyboard_key,
    default_grab_keyboard_modifiers,
    default_grab_keyboard_cancel,
};

// 接口绑定
// 以 default_grab_keyboard_key 为例
WL_EXPORT void
weston_keyboard_send_key(struct weston_keyboard *keyboard,
                const struct timespec *time, uint32_t key,
                enum wl_keyboard_key_state state)
{
    struct wl_resource *resource;
    struct wl_display *display = keyboard->seat->compositor->wl_display;
    uint32_t serial;
    struct wl_list *resource_list;
    uint32_t msecs;

    if (!weston_keyboard_has_focus_resource(keyboard))
        return;

    resource_list = &keyboard->focus_resource_list;
    serial = wl_display_next_serial(display);
    msecs = timespec_to_msec(time);
    wl_resource_for_each(resource, resource_list) {
        send_timestamps_for_input_resource(resource,
                            &keyboard->timestamps_list,
                            time);
        wl_keyboard_send_key(resource, serial, msecs, key, state); // 注意 wl 是 wayland 的缩写,这里已经完成了对 wayland 协议的对接了
    }
};

static void
default_grab_keyboard_key(struct weston_keyboard_grab *grab,
                const struct timespec *time, uint32_t key,
                uint32_t state)
{
    weston_keyboard_send_key(grab->keyboard, time, key, state);
}

四、后续

在第三大点中显示服务器输入管理设备总体设计,整体设计上整体由四部分组成.除去 display client, 在第四大点中也仅仅只讲述了linux事件捕获和对接wayland,也就是第一节点以及第三节点;而对于第二节点weston自身的实现甚至于只字不提.

在第二节点中描述了 事件标准化事件滤波 以及 事件分发,实际上真实场景远复杂于此;第一部分 weston (libweston/libinput-seat.c 、libweston/libinput-device.c) 合起来也只用来 1300 行左右; 但是第二部分光是 libweston/input.c 都超过了 5000 行,都还没计算 libweston/compositor.c 与此相关的代码.

以上现象说明 weston 对于输入设备的处理本质上是业务代码,维护在大量的上下文状态,根据实际业务场景添加了初看来奇奇怪怪的功能. 对于业务代码,绝非去描述具体的业务逻辑,更多的则是应当于去剖析作者的代码习惯以及风格,帮助读者去阅读理解理解对应的代码,让读者具备独立理解其对应业务的能力.

可能的话,对这部分内容的描述,可能从三个方面去切入;

一方面将会跟随 weston 变更记录(git),描述 weston 在添加某些功能时的具体做法,比如说添加了一个屏幕校准功能的实现,以增量的方式去讲解业务实现;

另一方面则会描述 weston 有关于此最为关键的业务流程,比如说窗口聚焦的实现,以最基础全量的方式去描述其业务实现;

最后则会讲述一下此部分对应代码提交者的代码风格,其编程手法、编程习惯以及对应的编程技巧.

实际上最后一点对于理解代码是非常重要的,理解作者的思维模式,对于代码的理解是具有非常重要的意义,远超于前两点.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/70776.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Studio 导入opencv异常报错紧急救援

Download OpenCV from SourceForge.net 1、下载Android demo之后导入Android Studio 如下图所示 报错信息如下 A problem occurred configuring root project opencv_samples. > Could not resolve all artifacts for configuration :classpath.> Could not find org.j…

校园失物招领毕业设计,学生失物招领系统设计与实现,毕业设计怎么写论文源码开题报告需求分析怎么做

项目背景和意义 目的:本课题主要目标是设计并能够实现一个基于web网页的失物招领网站系统,整个网站项目使用了B/S架构,基于java的springboot框架下开发;管理员通过后台录入信息、管理信息,设置网站信息,管理…

8_4、Java基本语法之线程的通信

一、问题的引入 使用两个线程打印 1-100。线程1, 线程2交替打印? 二、解决问题涉及的方法 涉及到的三个方法: 1.wait():一旦执行此方法,那么调用此方法的线程就会进入阻塞状态,并释放同步监视器。 2.notify():一个线程…

如何使用htmlq提取html文件内容

htmlq能够对 HTML 数据进行 sed 或 grep 操作。我们可以使用 htmlq 搜索、切片和过滤 HTML 数据。让我们看看如何在 Linux 或 Unix 上安装和使用这个方便的工具并处理 HTML 数据。 什么是htmlq? htmlq类似于 jq,但用于 HTML。使用 CSS 选择器从 HTML 文…

[安装] HIVE搭建环境

一、生产环境hive集群架构 参考: hive2.3.7安装记录 hive基础入门与环境的搭建 基础篇七 Hive-2.3.9安装与配置 大数据之Hive 集群搭建 完整使用 数仓(十)hive的Metastore机制 二、前言快读 Hive安装分类 主要是metastore的服务搭建方…

[rsync] 基于rsync的同步

环境 Linux:CentOs7.5 rsync: 3.1.2 rsync安装 一般安装系统时会自带rsync,可通过如下命令查看已经安装的版本信息 rsync --version如果系统未安装,可通过如下方式安装 yum安装【建议】 使用root用户执行yum安装 yum install -y rsync安…

代码随想录刷题记录day37 0-1背包+分割等和子集

代码随想录刷题记录day37 0-1背包分割等和子集 0-1背包 问题:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 例题&#xf…

操作系统实验五 进程间通信-管道通信

实验目的 1.掌握利用管道机制实现进程间的通信的方法 2.了解利用消息缓冲队列机制实现进程间的通信的方法 3..了解利用共享存储区机制实现进程间的通信的方法 五个题目如下 1. 函数int pipe(int fd[2])创建一个管道,管道两端可分别用描述字fd[0]以及fd[1]来描述。需…

多元宇宙算法求解电力系统多目标优化问题(Matlab实现)【电气期刊论文复现与创新】

💥💥💥💞💞💞欢迎来到本博客❤️❤️❤️💥💥💥 🎉作者研究:🏅🏅🏅本科计算机专业,研究生电气学硕…

SpringCloud项目使用Nacos进行服务的注册

本篇介绍Spring cloud项目使用Nacos作为注册中心来进行服务注册及服务发现,并进行简单的测试来验证。 一、简介 nacos是一个集服务发现、服务配置、服务元数据以及流量管理于一体的管理中心,能帮助我们更好的发现、配置和管理微服务。 注意&#xff1…

家政公司网站毕业设计,家政服务系统设计与实现,毕业设计论文源码开题报告需求分析

项目背景和意义 目的:本课题主要目标是设计并能够实现一个基于web网页的家政服务预约系统,整个网站项目使用了B/S架构,基于java的springboot框架下开发;管理员通过后台录入信息、管理信息,设置网站信息,管理…

JS项目打包之ROLLUP.JS入门

一、目的 Rollup是一个用于JavaScript的模块打包器,它将小块代码编译成更大、更复杂的东西,例如库或应用程序。它为JavaScript ES6版本中包含的代码模块使用了新的标准化格式,而不是以前的特殊解决方案,如CommonJS和AMD。ES模块可…

Win10安装Nacos

Win10安装Nacos 文章目录Win10安装Nacos前言下载Nacos安装Nacos验证前言 最近在学微服务的东西,使用的是 Spring Cloud Alibaba 生态,Nacos就是其中关键的一环。 这是 Nacos 的官网地址:https://nacos.io/zh-cn/index.html 官网的文档对于…

Python中用PyTorch机器学习神经网络分类预测银行客户流失模型

分类问题属于机器学习问题的类别,其中给定一组特征,任务是预测离散值。分类问题的一些常见示例是,预测肿瘤是否为癌症,或者学生是否可能通过考试。 最近我们被客户要求撰写关于银行客户流失的研究报告,包括一些图形和…

@Scheduled定时任务搭配Redis防止多实例定时重复调用

有个Redis安装使用教程&#xff0c;可视化界面&#xff0c;有需要的话&#xff0c;可以打开这个链接去看一下 https://blog.csdn.net/weixin_45507672/article/details/105973279 创建个maven项目&#xff0c;在pom.xml文件加上以下依赖 <dependency><groupId>or…

4EVERLAND专用网关公告,免费体验

我们很高兴地宣布发布 4EVERLAND 专用 IPFS 网关&#xff01;与 4EVERLAND 公共网关一起&#xff0c;4EVERLAND 专用网关将为全世界的开发者和用户提供更快、更稳定地访问更能体现其品牌形象的 IPFS 内容。 专用网关的好处&#xff1a; 全球分布的边缘节点提供全球加速无速率…

[附源码]计算机毕业设计快转二手品牌包在线交易系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Spring Boot 入门到精通(二)

文章目录五、SpringBoot整合MyBatis5.1 mapper 配置5.2 mapper映射配置&#xff1a;配置文件方式5.3 注解配置方式六. 自定义部分SpringMvc配置。6.1 SpringBoot整合日期转换器6.1.1 配置原理6.1.2 日期转换器整合6.2 SpringBoot整合拦截器七. Spring Boot 自定义日志配置&…

C++11特性-类的改进

1.构造函数 1.委托构造函数&#xff1a;允许同一个类的构造函数调用其他构造函数&#xff0c;简化变量初始化 class BB { public:BB() {}BB(int max) {this->m_max max > 0 ? max : 100;cout << "max " << this->m_max << endl;}BB(i…

【ATF】bootloader与安全相关启动分析

这个文章的内容不只是指的ATF启动这个部分&#xff0c;其实ATF是TF-A&#xff0c;这个是一个启动框架&#xff0c;所以今天我们来看看bootloader这部分的启动代码。后续继续补充&#xff01;&#xff01;&#xff01; 第一部分参考的内容来自&#xff1a;https://mp.weixin.qq…