[译]输入系统如何工作——指针输入

翻译自 How input works – pointer input

上一篇博文中,我讨论了键盘输入。这篇博文将会讨论指针设备——多被称为鼠标。就像本系列文章一样,本文仅仅讨论了KWin/Wayland上面的情形。

不同的硬件类型

有好几种不同的设备被识别为指针设备。有轨迹球这样的传统鼠标,也有在笔记本上的触摸板。此外,还有可绝对定位的设备,比如触摸屏。

在libinput中,对于不同的设备,有很多不同的选项可供配置。比如指针加速等其他选项。我们正在开发Wayland的触摸板配置模块,看起来会合并到Plasma 5.9中。像第一篇博文描述的,一旦设备创建,一系列配置选项会被立马设置。

指针运动

指针设备生成不同种类的事件,其中一种就是指针运动事件。通常来说,有两种指针运动事件:绝对运动和相对运动。大多数设备,比如鼠标,产生相对运动事件,所以我们在本文中只关注相对运动事件。

确定新的位置

相对运动是一组带x和y坐标的距离向量,它描述了指针位置该如何移动。

所以,一旦事件从KWin队列中读取出来,需要确定新的指针位置。这并不单单是最后的指针位置再加上一个运动向量。这会导致指针离开可视区域。

相反,指针运动得验证和约束。我们确保光标不会离开可视区域,并且还要遵守应用程序窗口对指针的约束。

这是KWin在Plasma 5.9中支持的新协议。它允许Wayland窗口将指针锁定到某个位置或将指针限制在某个区域。在第一种情况下,指针根本不移动,在第二种情况下,只允许指针在窗口的特定区域移动。

处理新的位置

即使在光标被限定的情况下,不需要移动鼠标指针,事件也会得到进一步处理。即使光标不移动,应用程序也可能对相对运动感兴趣,并对其做出反应。

像键盘的情况一样,为了进一步处理,会生成QMouseEvent并发送到KWin的输入过滤器。指针运动可能在KWin内部处理,例如:指针位置超出了当前的屏幕边缘。或者通过KWayland将指针运动转发到窗口。

更新获取焦点的窗口

如果指针移动,光标可能从一个窗口移动到另一个窗口,或从窗口移动到Wayland服务器端的窗口装饰上。这意味着对于每个指针位置的变化,KWin需要计算当前位置的窗口。

与键盘输入相比,KWin只需要考虑活动窗口,但这也是一个相当复杂的任务。我们需要考虑将输入变换应用于屏幕还是窗口,我们需要在窗口上应用输入掩码,考虑窗口装饰,检查屏幕是否被锁定,Xwayland 1.18之前的临时解决方案等一大堆问题。

最后,该方法可能已经确定了一个获得指针焦点的新平面。KWin使用KWayland来更新聚焦的指针表面,确保离开旧表面和进入新平面的事件触发。

当然,当移动鼠标时,应该更新焦点。如果拖放(按下指针按钮并移动),则不会更新焦点。

更新指针图案

如果指针移动到新的位置,指针图标有可能会改变。不幸的是,这可能需要多次往返消息才能确定。窗口管理器不知道窗口想要使用哪个指针图标,直到动作被发送到窗口。一个窗口可以通过两种方式做出反应:它可以更新光标图像,也可以不更新。在第一种情况下,KWin通过KWayland通知图像发生变化,在第二种情况下根本没有通知。

这意味着移动光标时,KWin不知道光标图标是否是正确的。所以在指针运动时,KWin更新光标到新的位置,但是光标图标仍然是旧的,但可能在一帧之后,客户端就会更新光标图案了。不得不说,在Wayland的显示下,并不是每一帧都是完美的,这是我遇到的唯一一个此类情况:光标可能显示错误的图标。

进入窗口时,光标尚未定义。直到窗口设置光标,否则光标还是未设置的状态。此时,KWin不会渲染光标;这意味着当进入冻结窗口(程序无响应的窗口)时,我们没有光标。我们以后必须改进这一点。目前,我们没有检测挂起的应用程序,因为我认为我们无法检测到它们:客户端使用专用的Wayland事件线程,因此程序可以永远给一切事件回复OK,即使事件并没有处理。

但是,KWin也可能需要设置一个光标图像。例如,当鼠标在窗户装饰上悬停,或进入特殊模式以选择窗口时,就使用KWin提供的光标图像。KWin从主题中加载这个光标图案。在内部,KWin记录光标图案应该使用的源图像。无论是Wayland窗口还是窗口选择界面,还是使用特定的光标图像。

更新实际光标

通过KWin的内部平台API实现光标位置和图标的实际更新。每个平台以不同的方式设置光标图像。对于我们的主要平台,我们使用DRM API来更新位置并更新图标——如果有新图标的话。

对于Wayland中嵌套的X11这样的嵌套平台,这通过窗口系统内的特定的调用处理。嵌套平台不允许更新光标位置——这是由窗口系统之间通过指针运动间接更新的。但是,光标图案可以更新。

虚拟出来的平台只知道软件指针的概念。这是在合成器渲染过程中渲染出来的光标。目前这只有在QPainter合成器中实现,在OpenGL合成器中还没有实现。

按钮事件

libinput支持指针设备产生的指针按钮的按下、释放事件。这些事件携带着它们触发的指针按钮。

和指针运动事件相比,按钮事件的处理更加直接。指针事件被事件过滤器拦截(比如Alt+Tab)或者被转发到当前获取焦点的窗口。

这相比于X11,是一个巨大的进步。在X11下,如果KWin希望独占的处理指针事件,则需要锁定指针。这意味着屏幕无法锁定。如果当前窗口处于活动状态,屏幕不锁定。在Wayland,这并不重要。如果当前窗口处于活动状态,屏幕可以锁定,也可以获取所有指针事件。一旦解锁当前的窗口仍然是活动窗口。“我”很轻松地为这种情况添加了自动测试。

轴事件

许多指针设备有一个或者两个轴。在X11的核心协议中,轴事件被实现为指针按钮。在Wayland和libinput的情况下,我们有了自己的轴事件,能告诉我们哪个轴被移动了,移动了多少。这是一个很大的提升,相比于X11,这意味着“平滑滚动”是标准的一部分了,在滚动事件的处理过程中,额外的事件处理部分也不会执行。

其他事件的处理也非常简单。KWin创建了一个QWheelEvent并将其传递给许多事件过滤器,如果过滤器没有拦截事件,它会被转发到KWayland::Server,KWayland::Server再把事件发送到获得焦点的指针平面。

触摸板手势

对于触摸板,我们还有别的事件。对于触摸板,Libinput不仅仅识别运动事件和按压事件,还能识别多指手势。它支持两种手势:滑动手势,捏合/旋转手势。KWin获取手势事件,转发给正常的事件系统。我们在Wayland协议中添加了特殊的支持。这允许转发鼠标手势Wayland应用程序,如视频所见。

很不幸,我们还没有真正地使用这些手势。QtWayland没有实现这个协议,所以转发的消息并不会到达应用程序,我们还没有使用它,比如全局手势就没有使用,我们仍在努力界定我们要支持的手势。我希望我们在Plasma 5.9能够改进这点,但是这也不确定。

假期愉快

这是“我”今年的最后一篇博客。下一年我将继续这一系列博客,会谈谈触摸屏事件,或许会包括数位版。

“我”希望每个人都有一个愉快的假期,在2017年有一个不错的开始。

如果你想支持“我”的工作,请考虑捐款。让世界变得更好!——KDE 2016年末募捐

[译]为了更具有创造力,还是不要那么高效吧

本文为 To Get More Creative, Become Less Productive 的翻译。

高效和创造力之间存在着根本性的矛盾,在意识到这一点之前,管理者无法获取更多的创意。

高效的人们以一种系统化的方式来完成一系列必须完成的任务。他们以稳定的、可衡量的方式来完成目标。他们非常有效率、并且能够有效地利用自己的时间。

创造力——可不是这样的。

创造性的发展需要时间和空间。虽然我们可以系统地从事与创造力有关的活动,但是很难系统化创造力本身。特别是,创造力是知识的基础。 几乎所有的创意想法都涉及人们寻找现有知识的新用途——一些旧想法的新组合。James Dyson通过类比锯木厂发明了他的真空吸尘器。 Fiona Fairhurst通过研究鲨鱼皮肤设计了更快的泳装。 George de Mestral通过了解苍耳发明了魔术贴。

这意味着从他们需要创造力的开始,人们就需要时间来学习与他们的工作无关的东西,以便他们有广泛而深入的知识库。

此外,有创造力的企业很少涉及稳定和可衡量的进步。 相反,创造力包括尝试许多不同的可能性,以及在找到正确的解决方案之前努力尝试几个盲目的岔路。

但这些活动——建立一个知识库并探索它——需要时间。 很难简单地安排几个小时在这里和那里进行创造性的追求。相反,有些时候,需要花时间学习一个新的知识领域,或者与同事进行漫长的谈话,来理清楚一个新的想法。 所以很多创意活动可能看起来像是一个可疑的,直到突破来临。

许多公司想要他们的员工获得更多创造力,这其中的主要原因就是生产力和创造力之间的这种差异。

公司通常基于生产率来评估员工。 更重要的是,他们根据假设他们将雇佣高效的人来制定他们的招聘计划。 他们希望组织中的人员能够取得明显的进步。 他们致力于发展尽责的人——能够完成任务的人。

如果一个组织真正想要创造力,它必须开始招聘更多的人,而不是完成任务、仅仅让公司保持运作所需的那么点人。Google的20%的时间,即鼓励员工将20%的时间花在新想法上的行动,已经执行了很多时间了。对于如何在公司实施这项政策,已经有了很多的讨论,我的观点是,如果你要给每个人一个机会来发展他们的创造力,你需要多雇用10-20%的人。

当要开发一个新的想法的时候,管理者还需要为员工提供一些时间安排上的灵活性。 给予某人10-20%的自由时间来发展他们的创造力,这并不一定意味着他们每周将花费4-8小时来发展创造力。 相反,可能有几个星期,一些人专注于他们需要完成的任务,还有几个星期或者几天来验证一个“兔子洞”的想法。

但是,仅仅为员工提供创造性所需的时间和灵活性是不够的。 管理者必须奖励员工从事的、提供创造性解决方案的任务,如学习新事物,发展新技能,与同事广泛的对话,以及尝试不成功的想法。

发展创造力的管理工作是可行的,但是它需要那些对效率痴迷的管理者们放松对工作时间的约束。

《硅谷钢铁侠:埃隆·马斯克的冒险人生》

“我们想要会飞的汽车,而不是140个字符。”

  • 马斯克有自己的想法

他需要做的是,制定一个目标,然后尽力实现。

在SpaceX,他知道自己要造便宜的火箭,所以员工必须努力优化每个部件;在特斯拉,员工必须有最好的执行力。

“他(马斯克)总是说,让最基本的物理原理说话。”
“我们首先要决定世界上最好的遮阳板是什么样子的,然后做得比那更好。”

  • 勇敢面对未知

虽然之前马斯克不知道如何发射火箭,但是在SpaceX开始之后,马斯克自学相关知识,甚至问员工问题。

但是这些未知都没有阻止马斯克去造火箭、生产电动汽车,相反,智能门把手和触屏操控系统是一次非常完美的反击。

  • 面对压力越挫越勇

三家公司多次遇到危机,财务危机之下,马斯克亲自审核支出,公关危机之中,马斯克自己写新闻稿来辩驳。

当然,马斯克出色的学习能力、完美的记忆力也是马斯克成功的条件,但这一点,我们没法学习到。


坚持自己的想法非常困难,但实现自己的想法对于个人来说非常重要,但是马斯克对于自己想法的固执、对于员工的苛责实在不是一个好的表达。自己的想法可能伴随着来自众人、来自传统的压力,但是新东西要是一开始就得到了旧世界的承认,那还是新东西吗?

“我告诉他,‘不只是那个家伙错了,我再重申一遍,你也错了。’”

即使在社交场合中,马斯克也可能会突然从餐桌前站起来,不做任何解释,径直走到外面去看星星,仅仅因为他不愿意跟傻瓜待在一起闲聊。

Continue reading 《硅谷钢铁侠:埃隆·马斯克的冒险人生》

如何命名二进制PHP扩展?

Python界有自己的文件名约定https://www.python.org/dev/peps/pep-0427/#file-name-convention,比如distribution-1.0-1-py27-none-any.whl一看就知道自己能不能用到这个whl包。

最近需要发布thrift_protocol扩展,故自己定下一个命名规范,在此记录之。

首先看看php如何判断扩展兼容与否,dl.c:172dl.c:182分别根据zend_api(比如20151012)和build_id(比如API20151012,NTS)来判断是否兼容。

所以我们用build_id+platform就可以表示ABI版本了,加上name和version就得出命名规范:

{distribution}-{version}-{build_id}-{platform}.{so,dll}

比如:

thrift_protocol-1.0-API20151012,NTS-linux_x86_64.so

遗憾的是,github会吞掉英文逗号,所以只能用英文句号替代了。

不过,phpize的时候能看到zend_api,无法看到build_id,算是一个不方便的地方,只能通过php -i | grep 'PHP Extension Build'来变通获取了。

将ngx_brotli编译成动态nginx模块

Brotli是google新出的一个压缩算法,据说比Zopfli的压缩率要高20–26%(Zopfli是google之前发布的一个和Deflate兼容的压缩算法)。

Chrome 49+、Firefox 44+以及Opera 36+都支持Brotli了,所以,是时候尝试一把了。

但是为了支持这个特性,自己编译部署nginx太麻烦了,nginx 1.9.11开始支持动态模块,所以也来尝试一把。

系统需求

sudo apt-get install autoconf libtool nginx-extras

本人测试环境ubuntu 14.04, ppa:nginx/stable, nginx/1.10.1

安装libbrotli

按照 libbrotli 的说明,依次执行如下命令安装

git clone https://github.com/bagder/libbrotli
./autogen.sh
./configure
make
sudo make install

为了让系统中使用到libbrotli的程序能够加载so文件,故创建软链:

sudo ln -s /usr/local/lib/libbrotlienc.so.1 /lib/libbrotlienc.so.1

准备源代码

下载nginx和ngx_brotli的源代码:

wget http://nginx.org/download/nginx-1.10.1.tar.gz
git clone https://github.com/google/ngx_brotli

编辑ngx_brotli/config文件,在顶部添加一行

have=NGX_HTTP_HEADERS . auto/have

configure

在nginx源码目录执行

./configure \
  --add-dynamic-module=../ngx_brotli/ \
  --with-http_dav_module \
  --with-http_realip_module \
  --with-http_v2_module \
  --with-threads \
  --with-http_ssl_module \
  --with-ipv6

并make,得到两个so文件:

$ find -name ngx_http_brotli*.so
./objs/ngx_http_brotli_static_module.so
./objs/ngx_http_brotli_filter_module.so

加载模块

/etc/nginx/nginx.conf文件中添加如下两行,使nginx加载模块:

load_module '/path/to/ngx_http_brotli_static_module.so';
load_module '/path/to/ngx_http_brotli_filter_module.so';

接下来在http段中添加如下两个配置:

brotli on;
brotli_static on;

再执行sudo nginx -t 看看有没有错误,如果没有错误的话那就成功了。

不过,brotli压缩算法只有在https连接中才能生效。

module “/path/to/ngx_http_brotli_static_module.so” is not binary compatible in nginx.conf

这个错误是由于module的signature和nginx的signature不匹配导致的,我的解决办法是,打印出这两个signature,逐位比对,并据此调整configure的参数。

参考 ngx_module.h 比如,第25位表示NGX_HTTP_V2定义与否,此时可以添加--with-http_v2_module来修正signature。

第22位表示NGX_THREADS定义与否,可以添加–with-threads来修正。

第8位和第9位与IPv6有关,只需添加–with-ipv6即可。

至于第30位,只能通过编辑ngx_brotli/config文件,添加have=NGX_HTTP_HEADERS . auto/have来搞定了。

Continue reading 将ngx_brotli编译成动态nginx模块

How to install/upgrade elasticsearch cluster via ansible?

First, we have a playbook:

# es.yml
- hosts: es
  become: yes
  roles:
    - elasticsearch
  serial: "50%"

serial: "50%" means ansbile will run tasks in 50% of hosts (or less).

And, we should add a rule elasticsearch:

# roles/elasticsearch/tasks/main.yml
---
- name: disable shard allocation for the cluster
  uri: url=http://localhost:9200/_cluster/settings method=PUT body='{{ es_allocation.disable }}' body_format=json
  ignore_errors: yes

- name: download ealsticsearch deb pacakge
  local_action:
    module: get_url
    url: "{{ elasticsearch_url }}"
    dest: "cache/elasticsearch-{{ elasticsearch_version }}.deb"
    checksum: "sha256:{{ elasticsearch_sha256 }}"
  become: no

- name: check if elasticsearch is installed
  command: dpkg-query --showformat='${Version}' -W elasticsearch
  register: elasticsearch_version_check
  failed_when: elasticsearch_version_check.rc > 1
  changed_when: elasticsearch_version_check.rc == 1

- block:
    - name: copy elasticsearch package
      copy: src="cache/elasticsearch-{{ elasticsearch_version }}.deb" dest=/tmp/elasticsearch.deb
    - name: install elasticsearch
      apt: deb=/tmp/elasticsearch.deb
  always:
    - name: delete elasticsearch package
      file: path=/tmp/elasticsearch.deb state=absent
  when: elasticsearch_version_check.stdout != "{{ elasticsearch_version }}"

# configure elasticsearch
# install plugins

- name: restart elasticsearch
  service: name=elasticsearch state=restarted

- name: wait for elasticsearch node to come back up
  wait_for: port=9200 delay=15

- name: enable shard allocation for the cluster
  uri: url=http://localhost:9200/_cluster/settings method=PUT body='{{ es_allocation.enable }}'  body_format=json

- name: wait for cluster health to return to green
  uri: url=http://localhost:9200/_cluster/health method=GET
  register: response
  until: "response.json.status == 'green'"
  retries: 50
  delay: 5
  1. we try to disable allocation for cluster upgrade
  2. download elasticsearch’s deb package to local cache
  3. install and configure, restart elasticsearch
  4. wait 9200 port is available, enable allocation, wait for cluster become green.

some vars:

# roles/elasticsearch/vars/main.yml
---
elasticsearch_version: 2.3.2
elasticsearch_url: "https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/{{ elasticsearch_version }}/elasticsearch-{{ elasticsearch_version }}.deb"
elasticsearch_sha256: 3d474b0123ec8ad4ebfa089f8cde607033e6cbef28a6a0df318bdc3d2a546cd8

es_allocation:
  disable: '{"transient":{"cluster.routing.allocation.enable":"none"}}'
  enable: '{"transient":{"cluster.routing.allocation.enable": "all"}}'

Continue reading How to install/upgrade elasticsearch cluster via ansible?

How to set proxy_next_upstream? and why?

You can add proxy_next_upstream in http, server, location context:

proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;

There is something to notice:

  • You should add error, timeout, invalid_header since the upstream didn’t receive this request.
  • You can add http_502, http_503, http_504 because upstream meet network issue.
  • Don’t add http_500 because there is error in application, retries will cause other server is down.(And developer should fix this error, not load balancer)

Continue reading How to set proxy_next_upstream? and why?

《一日重生》

人生总是有很多不如意。

查理在失意许久之后,决定自杀,来结束这些痛苦。意外之中,他的母亲出现了,他和他母亲度过了一天。

这一天看起来是平常的一天,去给别人理发,给塞尔玛小姐化妆等。在平淡的叙事中,加上查理自己的回忆,查理看到了很多小时候被遗忘、被忽略的事情。

确实,查理一心只想追求父爱,忽略了母爱;等到母亲去世之后才知道母爱的珍贵,此后人生的曲折,只能自己一个人去面对。

遗憾的是,查理面对的,仅仅是母亲的灵魂,唯一能改变的,只能是自己。

那天晚上,母亲带着他去见了父亲的第一任妻子,她说出了“原谅”这个词。

过去的恩怨,我们没有机会去讨回、去复仇,现在能做的,也就只有原谅,要原谅的,不是别人,恰恰是自己。


 

查理是幸运的,他还有机会去重新经历一遍,了解过去、了解自我。

对于我们,就像书中母亲说的一样:

回到过去,比你想象的要难。

Violet?Purple?

这两个单词都是紫色的意思。突然想到这个有什么区别,所以查了下,还真有。

Violet得名于紫罗兰(紫罗兰的英文也是violet),是一种真正的颜色,有自己的波长(380–450 nm)

而Purple则是蓝色和红色的混合色。相比Violet,Purple更偏向于贵族的意味。


这是violet:

 

Violet

这是purple:

purple


references:

Violet (color)

Purple