4
1
2022
9

从 getmail6 到 offlineimap

起因

上个月收到这样一封邮件:

自 5 月 30 日起,您可能会无法再访问那些采用安全性较低的登录技术的应用

意思就是说,Google 觉得把密码直接交给邮件客户端,权限太大,不够安全。所以要用户改用基于 OAuth2 的认证方式,只给程序邮件相关的权限。哦,你说应用专属密码?要用那个必须得启用两步验证——也就是意味着遇到灾难的话,我无法从一无所有的状态开始恢复。

从 POP 到 IMAP

getmail6 只支持使用 XOAUTH2 认证的 IMAP 协议,并不在 POP 协议上支持这个(不知道是否有可能)。所以我得换 IMAP 协议了。

具体操作步骤在 getmail6 的示例配置中有写。简单来说就是自己去申请个桌面软件的 app 信息,然后给自己的用户添加试用权限,再通过 OAuth2 获取 refresh token 和 access token,就能登录了。getmail6 自带了个 getmail-gmail-xoauth-tokens 程序用来走 OAuth2 流程,不需要另外安装程序来处理的同时也可以给其它程序使用。

所以我的 msmtp 配置就不用麻烦了,改两行配置就好:

auth oauthbearer
passwordeval getmail-gmail-xoauth-tokens ~/.getmail/gmail/lilydjwg@gmail.com.json

但是呢,虽然邮件是收回来了,IMAP 和 POP 还是挺不一样的。POP 没有「文件夹」的概念,所有收到的邮件,不管我有没有在 Gmail 网页或者客户端上阅读、归档,不管它进了哪个标签(文件夹)(「垃圾邮件」除外),我都会收到,并且把收过的邮件标记为已读。

而通过 getmail6 使用 IMAP 收取,我能做的选择就是,要不要把收过的邮件标记为已读或者删掉(可在 Gmail 中设置为归档)。不管如何,getmail6 只会收到它运行时位于收件箱中的邮件。如果我选择标记为已读的话,那么已读邮件也不会被 getmail6 收到。所以标已读的话,我在别的地方看过的邮件不会被收到。删除的话会好点,收过的邮件归档了,还省得我手动去归档,但是在别的地方,收过的邮件和已处理的邮件没了区分。

所以不如上 offlineimap,完全同步好了。

从 getmail6 到 offlineimap

offlineimap 的配置就比较复杂了,一是要对文件夹名进行转码,二是我要设定只同步指定的文件夹:收件箱、Maillist 和垃圾邮件。要同步垃圾邮件的原因是,Gmail 经常把有用的邮件往里边扔。

[general]
accounts = gmail
maxsyncaccounts = 10
socktimeout = 60
pythonfile = ~/.offlineimap/offlineimap.py

[Account gmail]
localrepository = gmail-local
remoterepository = gmail-remote

[Repository gmail-local]
type = GmailMaildir
localfolders = ~/.Maildir
filename_use_mail_timestamp = no
nametrans = gmail_nametrans_local

[Repository gmail-remote]
type = Gmail
remoteuser = lilydjwg@gmail.com

sslcacertfile = /etc/ssl/cert.pem
ssl = yes
starttls = no

oauth2_client_id_eval = get_client_id("lilydjwg@gmail.com")
oauth2_client_secret_eval = get_client_secret("lilydjwg@gmail.com")
oauth2_access_token_eval = get_access_token("lilydjwg@gmail.com")

nametrans = gmail_nametrans_remote
folderfilter = gmail_folderfilter
import os
import json
import subprocess

_LOADED_DATA = {}

def _load_data(account):
  with open(os.path.expanduser(f'~/.getmail/gmail/{account}.json')) as f:
    _LOADED_DATA[account] = json.load(f)

def get_client_id(account):
  if account not in _LOADED_DATA:
    _load_data(account)
  return _LOADED_DATA[account]['client_id']

def get_client_secret(account):
  if account not in _LOADED_DATA:
    _load_data(account)
  return _LOADED_DATA[account]['client_secret']

def get_access_token(account):
  cmd = [
    'getmail-gmail-xoauth-tokens',
    os.path.expanduser(f'~/.getmail/gmail/{account}.json'),
  ]
  out = subprocess.check_output(cmd, text=True)
  return out

def gmail_nametrans_remote(foldername):
  foldername = foldername.removeprefix('[Gmail]/').encode('ascii').decode('imap4-utf-7')
  if foldername == '垃圾邮件':
    foldername = 'Spam'
  elif foldername == '草稿':
    foldername = 'Drafts'
  return foldername

def gmail_nametrans_local(foldername):
  if foldername == 'Spam':
    foldername = '[Gmail]/垃圾邮件'
  elif foldername == 'Drafts':
    foldername = '[Gmail]/草稿'
  return foldername.encode('imap4-utf-7').decode('ascii')

def gmail_folderfilter(foldername):
  foldername = foldername.encode('ascii').decode('imap4-utf-7')
  return foldername in [
    'INBOX', '[Gmail]/垃圾邮件', '[Gmail]/草稿',
    'Maillist',
  ]

然后在 Gmail 那边创建个过滤器,把来自邮件列表的邮件扔到「Maillist」文件夹里去。搜索「 (to:@googlegroups.com OR from:vim-dev-github@256bit.org OR to:@zsh.org)」并创建过滤器,选择操作「跳过收件箱、 应用标签“Maillist”」即可。注意以后在修改的时候直接修改「包含字词」字段即可,并且记得「OR」「AND」「NOT」之类的操作符需要改回大写。

这样做完之后还有个问题:一封邮件同步到 offlineimap 后,我在 mutt 里阅读并删掉了它。offlineimap 一看,哟,邮件没了,得在服务器上删掉。Gmail 根据我的设置,把从 IMAP 删除的邮件归档,但是它并没有选项来标记为已读。所以这封邮件最终会以未读的状态躺在「所有邮件」里。

于是我去 App Script 里写了个脚本,把这些邮件标记为已读:

function mark_as_read() {
  const threads = GmailApp.search('is:unread AND NOT (label:Maillist OR in:inbox)', 0, 30)
  for(const thread of threads) {
    Logger.log('Marking as read: %s', thread.getFirstMessageSubject())
    thread.markRead()
  }
}

手动运行一遍之后,就可以在左侧栏里给它设置个触发器定时跑啦。

新邮件提示

使用 offlineimap 之后,最大的问题变成了邮件散落在不同的账号下的不同文件夹,一个个过去翻看太低效了。所以我就给 zsh 设置了提醒:

mailpath=(
  ~/.Maildir/INBOX/new'?GMail has a new message.'
  ~/.Maildir/Spam/new'?GMail has a new spam.'
  ~/.Mail/inbox'?New local mails.'
)

问号前边是邮箱的路径,后边是提示信息。之前那个 mbox 格式的邮箱我还留着,用来收取来自本地 cron 的邮件。

一个小问题是,procmail 用不成了。不过现在各种无用的网站消息也少了,所以不需要通过 procmail 处理垃圾邮件了(新浪微博我没有使用邮件注册、LinkedIn 和 Twitter 消停了、网易和QQ邮箱不用了)。现在中文邮件列表也几乎没人用了,我也不用让程序去重写「回复:RE:回复:」这类糟糕的邮件标题和过滤掉自动回复了。

后记

然后今天我又试了一下 POP + 账号密码登录,还是可以用的。难怪通知里要写个「可能」……而反倒是 OAuth2 的 token 经常失效,需要重新走浏览器点几下来授权。

Category: Linux | Tags: 电子邮件 Linux IMAP
3
26
2022
11

微信消息通知的困扰

一直以来,不得不用的微信以其糟糕的通知体验让我十分不爽。

在手机上,我使用的是 Google Play 商店里的微信。在电脑上,我使用的是通过 Wine 运行的 Windows 版本微信([archlinuxcn] 仓库里的 wine-wechat-setup 脚本可用于安装)。

消息通知不及时

这个问题是最近我的手机日渐陈旧之后我才注意到的。表现是,在一段时间(比如一两天)不使用微信之后,收到新的微信消息或者视频通话,可能会延迟几个小时收到通知。在 Android 通知日志中可以确认,收到通知的时间和消息在微信中展示的时间有数小时之差,并不是因为我没有及时看手机。

我的 Telegram 从来不这样丢消息,即使因为后台进程过多 Telegram 被杀之后,通知只会不能在其它端阅读之后被清除,而不会延迟那么久。而微信,即使它还在后台运行着,却经常占着资源不干活,何况消息通知本应走 FCM。

打开微信即清除所有通知

我有一条微信消息,但是我现在不方便立即回复(比如需要使用电脑而我正出门在外),所以我会让那条通知一直留着。其实以前版本的 Android 系统更方便,可以将通知延后一段时间,只是不能自动指定延后的时间比较遗憾,后来被移除真的太可惜了。

然后呢,比如我要进个超市,或者测个核酸,付个钱啥的,只好打开微信扫码呗。结果所有还未处理的通知全部不见了!等忙完当时的事情,回到家里的时候,我就不一定还记得我还有几条微信消息还没处理了。

多端不同步容易错过

我在用电脑的时候,如果不登录电脑版微信的话,那么我将不会注意到手机上的微信有新消息。那就登录电脑版吧,然后我去上个厕所吃个饭,不用电脑的时候又会错过消息。在电脑端登录的时候让手机上也显示消息通知?那样所有消息都要看两遍,而且消息多的时候还得仔细回想某条消息到底是不是已经处理了。

Telegram 的消息同步做得真好啊。你在哪端用,哪端先给你发通知。其它端的消息会晚几秒出现。一旦在任意端读取了消息,另外的端上的相应消息全部都会被取消掉,不会有消息重复的问题(不过最近好像 Android 上的通知取消变得不那么可靠了)。

手机不可离远

现在电脑版微信终于不需要天天在手机上确认登录了。只要我每两三天登录一次,就可以避免把手机扔去充电了、来到电脑前、又去找手机的麻烦事。——我一开始是这么以为的。直到我发现,我刚刚看到通知、正要处理的消息,并没有在电脑版微信里同步出来。

不知道为什么,微信跟电脑用户有仇似的。明明我有电脑了,不需要凑合于手机的小屏幕和戳戳戳的屏幕键盘了,微信还非得把手机给拉过来。

语音通话不支持耳机接听

某天,我因为沉迷于放置型游戏,把手机扔桌子上充上电让它自个儿玩,自己去睡觉了。第二天早上睡正酣的时候,来了个电话,我拿耳机给接了。然后需要通话的另一方不知道怎么想的,没有商量就发起了微信语音通话,这个耳机根本接不到……

我也尝试过让 Google 助理回拨电话,不过使用不熟练,并没有成功。你说我为什么不起床去接?我睡着被电话惊醒了,还没回过神来啊 QAQ。

3
8
2022
6

Qt 的字体渲染问题

GUI 程序我现在依然倾向于 GTK,因为虽然 Qt 拥有良好的跨平台性,但可能是太注重跨平台性了,在 Linux 平台上反而有一些水土不服的问题。

字体太多,支持太少

你可能觉得,系统上字体太少,所以经常会遇到不常见的字符无法显示的情况。然而对于 Qt 来说,字体越多,反而越容易遇到个别字符不能显示的情况。

这是我的 /etc/fonts/conf.d/66-qt.conf 中的一段。因为顺序的原因,我只能放到 /etc 下。除了针对 sans-serif 配置外,我也有同样的配置应用于 serif 和 monospace。

<fontconfig>
  <!-- Adjust font order for Qt applications -->
  <alias>
    <family>sans-serif</family>
    <prefer>
      <!-- 格拉哥里字母:Ⰽⱁⱀⱄⱅⰰⱀⱅⰹⱀ Ⰹⱍⰹⰳⱁⰲ -->
      <family>Noto Sans Glagolitic</family>
      <!-- 爪哇文:꧁   ꧂ -->
      <family>Noto Sans Javanese</family>
      <!-- 西夏文:𗷲𗒅 -->
      <family>Noto Serif Tangut</family>
      <!-- 埃及象形文字:𓁹 -->
      <family>Noto Sans Egyptian Hieroglyphs</family>
      <!-- 苏美尔楔形文字:𒆠𒂗𒂠 -->
      <family>Noto Sans Cuneiform</family>
      <!-- 中日韩统一表意文字扩展 C:𫚥 -->
      <family>HanaMinB</family>
      <!-- 拉让文:ꥃ -->
      <family>Noto Sans Rejang</family>
      <!-- 越南傣文:ꪀꪑ -->
      <family>Noto Sans Tai Viet</family>
      <!-- 切罗基文:ꮳꮧꮢ ᨣ -->
      <family>Noto Sans Cherokee</family>
      <!-- 老傣仂文:ᨣ -->
      <family>Noto Sans Tai Tham</family>
      <!-- 安纳托利亚象形文字:𔘓 -->
      <family>Noto Sans Anatolian Hieroglyphs</family>
      <!-- 马姆穆文补充:𖤍  -->
      <family>Noto Sans Bamum</family>
      <!-- 图标字体(PUA): -->
      <family>OperatorMonoSSmLig Nerd Font</family>
      <!-- 巴塔克文:ᯤ -->
      <family>Noto Sans Batak</family>
      <!-- 古北欧文:ᛋᛖᚱᚣᚨᛚᚳᚨᚾᛞᛚᛖ -->
      <family>Noto Sans Runic</family>
    </prefer>
  </alias>
</fontconfig>

这个配置的意思是,把这些字体的优先级提高一些。当使用 fontconfig 的程序要显示字符的时候,它会指定一个模式,匹配到一个字体列表。渲染文字的时候,就可以遍历这个列表,直到找到可以显示这个字符的字体,所以一般来说,只要系统上装了对应字符的字体,它就能显示出来。

但是 Qt 额外地需要这个配置,因为 Qt 只会检查列表中的前255项。而世界上的不同文字那么多,所以想要能够显示它们,就得有一堆字体。比如 noto-fonts 这个包里就有614个字体文件,远超 Qt 支持的数量。总有些奇奇怪怪的文字被网友用来当颜文字,或者挂在名字上彰显个性。不这么调整一下,Qt 遇到了就只能「吃豆腐」了。

空心豆腐

当一个字符显示不出来的时候,那么怎么显示好呢?一般会显示成某种方框。Pango火狐会将该字符的 Unicode 码点以十六进制的形式显示在方框里边,这样虽然不知道这个字符长什么样子,但至少知道它是哪个字符,也知道多块豆腐是不是同一字符,在不能复制字符本身的时候很有用。比如当它出现在求助者的截图里的时候,比如当它出现在不能复制的地方的时候。

然而 Qt 不这样做。管你什么字符,Qt 统一显示为空心方框。从视觉上完全无法知晓它到底是什么字符,要是复制不到的话,就别想弄明白你缺什么字体了。

PS: Matrix 客户端 fluffychat 的 Web 版,使用的是 Fluffy 图形界面库,即使在 Web 版,文字渲染依然完全是自己做的。不管浏览器的设置不管系统的设置,豆腐块是带叉号的方框,还不能选中,十分讨厌。

非 BMP 字符

所有使用 UTF-16 的平台(Java、JavaScript、Windows、Qt),外加 MySQL 容易遇到的一个问题:非 BMP 字符(也就是那些 U+FFFF 之后的字符)会被当作是两个字符处理。随着 emoji 的流行,大家应该都修了不少。然而,Qt 在展示非 BMP 字符的时候,你可以选中半个字符。如果不小心漏掉半个的话,复制出来的半个字符就会变成问号(还好不是 GBK 时代那样弄乱后续所有字符)。

font features

一些字体可以通过 fontconfig 设置 fontfeatures 属性来启用(或者禁用)一些特性,比如连字,带斜杠的 0,小型大写字母,居中的中文标点,等等。Pango 很早就支持了,火狐最近也支持了,但 Qt 那边依旧没啥动静。(感谢 Coelacanthus 的评论。)

Category: Linux | Tags: linux 字体 Qt
2
2
2022
18

Wayfire 迁移进展(四):不那么 high 的 DPI

使用24寸4k屏幕作为主屏的时候很简单,设置 scale 为 2 就好了。但是,当 2 嫌太大、1 嫌太小的时候,问题就来了。比如我希望使用 120dpi,把 scale 设置为 1.25 可好?

scale=1.25 text

而这才是理想的效果:

120dpi text

看不出来差别?放大八倍,你看差别多明显:

8x compare

正常 120dpi 渲染出来的文字边缘清晰犀利,次像素平滑左红右蓝。再看看 scale=1.25 的文字,线条经常糊掉,次像素平滑效果几乎完全被抹掉。实际看上去的效果就是跟透明麿沙玻璃看屏幕似的,线条边缘总是有点糊糊的感觉,1080p 的屏幕被降级成了 720p 似的。

之所以出现这样的情况,是因为 Wayland 只支持整数倍缩放。因为,Wayland 混成器不能告诉客户端你得把窗口给画成 1.25 倍的,而客户端也无法告诉混成器我这个图像画的是 1.25 倍。所以,混成器只好告诉客户端你给我画个 2 倍的图像吧。混成器拿到图像之后再缩小 0.625 倍,自然有些逻辑像素就不能对应到单个的物理像素上去了。

所以,我还是设置 scale=1,不要混成器帮我去缩放。我自己通过另外的办法告诉客户端把字写大点儿。图标之类的就顾不上啦,反而大点小点都还能看。比如我要 1.25 倍大小的文字,就这样做:

  • GTK 3:在 dconf 里设置org.gnome.desktop.interface.text-scaling-factor=1.25就好了。最开始的截图就是 dconf-editor 里这一项配置。
  • Qt:设置环境变量 QT_WAYLAND_FORCE_DPI=120
  • Telegram:除了上边这个环境变量外,额外地在它自己的设置里设置 150% 的缩放(Telegram 的字偏小所以要设置得大一些)。设置环境变量是为了 fcitx5。
  • waybar:config 文件中设置 heightstyle.css 中设置 font-size
  • Xwayland:和 X11 下的 HiDPI 设置差不多的。比如 GTK 2 设置 Xresources Xft.dpi: 120 就好了。

我遇到的差不多就这些了。没办法,Linux 就是这么乱 QAQ。不过虽然 Wayland 协议不支持,好歹还有绕过的办法。

Category: Linux | Tags: Wayland screen 显示器 linux
12
4
2021
5

Wayfire 迁移进展(三):taskmaid, waybar 以及 mako 等

我又来更新我的 Wayfire 迁移进展啦~

我写了一个 taskmaid 工具,使用 wlr foreign toplevel management 扩展来提供窗口管理相关功能。程序自己作为 daemon 随 wayfire 启动运行,通过 D-Bus 提供接口供别的程序使用。你问我为什么不直接每个需要的程序直接使用 Wayland 协议?因为用起来麻烦呀。Wayland 提供信息的方式是一组一组的事件,也并没有高层次的库,处理起来只能一堆回调怼上去,相当不顺手。

它最主要的功能就是在 waybar 上标题当前窗口的标题啦。顺便加上了中键关闭和显示 app-id 的功能。应用程序图标因为没有办法准确匹配(比如火狐 nightly 版本的 app-id 也是 firefox),所以没有做。

其次是获取当前活动窗口所在的显示器接口名称。我使用这个名称来判断我是不是位于 E-ink 屏幕上,并为终端、Vim、skim、mutt 等工具使用专门适配过的亮色主题。这个方案比之前在 X11 下使用当前鼠标坐标来判断要灵活一些。当然更灵活的方案是匹配显示器的名称啦,这个数据 Wayfire 的 Wayland 协议里是有提供的,有需要的话我再做。Wayland 并没有一个协议来获取当前鼠标或者键盘焦点所在的显示器信息,所以我只好用窗口管理协议来跟踪活动的窗口了。

顺便还做了个 lswin 工具,列出打开的窗口信息。工作区太多啦,我又喜欢最大化,有时候会有窗口忘了关。甚至偶尔我还会不小心把窗口最小化了,然后没有办法给恢复回来……如果以后还经常出现这种情况,我再给 taskmaid 加一个恢复最小化窗口的功能好了。

除了使用 taskmaid 显示标题之外,我还加了个显示 AQI 的小脚本。以及之前忘记加网速指示器了,现在也加回来了。不得不说 waybar 比 Awesome 的那个顶栏要好配置得多。不仅不限语言,而且不是非得用定时器,可以在信息变动的时候及时更新信息,没变动就不浪费资源获取重复的信息了。我的 waybar 配置都在这里

我给 Wayfire 发了一个 pull request,添加了最基础的快捷键禁制器的支持,可以在 spicy 等软件中屏蔽 Wayfire 自己的快捷键了,大大方便了我对 Wayfire 和 Sway 的测试。当然也可以用于 VNC 啦。不过并没有像 Sway 那样加选项、支持主动禁用快捷键等功能,短期内我也不太可能会去加这个。原本我是想着在 Wayfire 里再跑一个 Wayfire 或者 Sway 啥的,但考虑到配置方面的问题,还是拿虚拟机隔离了比较好。

最新的 Wayfire 版本已经支持切换到之前的工作区啦~

桌面通知程序 mako,之前遇到两个问题。一是通知经常是糊的,没有适配 HiDPI 屏幕,而鼠标指针则一直都是又糊又大。我给修了,虽然我其实有点不知道我是怎么修好的……总之是整理了一下代码,为了减少调试日志而减少了与 Wayland 混成器的通讯,也变得更高效了。会记住上次显示使用的缩放倍率,所以不会像之前那样一出来是糊的、下一刻才会变清晰了。在 Wayland 协议里,客户端可以获知有哪些显示器、大小和缩放倍率如何,但是客户端并不能提前知道自己将会显示在哪个显示器上(倒是能够指定显示在哪个显示器上),只有显示出来之后才知道,然后做调整后再更新一下……

另一个问题是,在 Wayfire 下 mako 在全屏时不会显示通知,会被盖住。设置 layer=overlay 之后倒是能在全屏时显示通知了,但是它也会在 swaylock 锁屏界面上显示……后来了解到 mako 有模式这么个特性,我就在锁屏的时候切换到专为锁屏设置的模式下,解锁之后再切回来。反正锁屏是一句命令,自己拿脚本包一下就好了。

最近新的桌面环境稳定下来了,倒是没有再遇到更多的 bug 了。Spicy 会在里边的虚拟机跑特效动画时经常报个「Gdk-Message: Error flushing display: 资源暂时不可用」错误然后退出,我给它用 try_until_success 包了一下倒是问题不大。我也发现了不光是 Wireshark,也有 GTK 程序弹出菜单会显示在错误的位置。

那么就酱~

Category: Linux | Tags: Wayland wayfire
11
20
2021
11

Wayfire 迁移进展(二):Xwayland HiDPI 以及 waybar

这几天完成了一个很重要的功能:我让 Xwayland 支持 HiDPI 了!

实际上让 Xwayland 支持 HiDPI 的补丁早就有了,但是我当时尝试的时候补丁并不能很好地应用,我手动修了修,不明不白地应用上之后,并没有能够正常使用。现象是,DPI 是对了,但是窗口大小会不断地缩为原来的四分之一(长宽都减半),全屏时也只占左上角的四分之一。

这几天我给 xorg-xwayland 打上了新版补丁,然后在一番理解之后,给 wlroots 写好了相应的补丁,现在 Xwayland 终于也可以看清晰了!

这两个补丁,xwayland 那边是通过一个 X 窗口属性来设置缩放倍数,然后 xwayland 会告诉混成器自己的窗口使用了对应的缩放倍数,这样混成器就不会当它不支持缩放、强行给拉伸一下了。当然还有输入坐标的转换之类的。缩放倍数为 2 时,X 客户端会看到之前两倍的显示大小,并且 X 使用的坐标是 Wayland 这边的两倍,所以 Wayland 的输入事件从 X 服务器传给 X 客户端的时候需要乘以 2。

混成器这边,需要了解客户端传过来的 X 坐标和 Wayland 坐标不再相同,需要进行相应的转换。没有进行转换的结果就是,客户端告诉混成器说自己是 1024x1024 的窗口大小,然后实际上创建出来是 512x512 的。混成器再告诉客户端你现在只有这么大,然后客户端说好,我调整一下。于是又变小了……

两个补丁打上之后,由于头文件有变化,混成器可能需要重新编译一遍。然后按 X 的方式设置 HiDPI,比如设置 Xft.dpi: 192 或者 winecfg 里设置 dpi 为 192。如果有运行于 Xwayland 的 GTK 3 程序,也要设置 GDK_SCALE=2 GDK_DPI_SCALE=0.5。然后执行以下命令设置 X 属性,让 Xwayland 做相应调整:

xprop -root -format _XWAYLAND_GLOBAL_OUTPUT_SCALE 32c -set _XWAYLAND_GLOBAL_OUTPUT_SCALE 2

接下来就能愉快地 wine 和 gimp 啦~

这几天做的另一个比较大的动作是配置好了 waybar。我也不知道这个组件叫什么好,很多地方都叫面板(panel),i3 / sway 那边直接叫 bar。它现在经常出现在屏幕顶部所以我也有时叫它顶栏。总之就是显示窗口信息、系统托盘和状态指示器啥的那一条。

我是从它自带的配置文件修改的,但是风格给完全改掉了。原本的风格是一块一块的彩色背景的字,我嫌太过显眼,经过一番调整之后,给改成了黑底上的彩色字,跟我原来的 Awesome 差不多,也和我显示器的黑边挺配的。指示器的放置也是差不多的。十分遗憾的是并没有合适的窗口列表小部件可用。它自带的那个 wlr/taskbar 会把所有工作区的窗口全部显示出来,很挤。而且不知道为什么,一旦加上它之后就最小要占用 34px 的高度,太占空间了,所以作罢。我打算以后自己实现一个,现在就拿正在播放的媒体凑一下吧。它现在长这样:

我的 waybar

左边就是使用 playerctl 做的媒体信息。左键可暂停播放,滚轮切歌。字的颜色是和窗口边框匹配的。

中边留着给窗口标题。

右边依次是:

  • idle 禁制器。点一下眼睛亮起来,禁用无活动时自动休眠。
  • CPU 和 load 信息。
  • CPU 温度。太高了会变红。
  • 内存使用率。
  • 电池信息。充满电又插着电源线,它就隐藏起来了。预期充电或者使用的时候图标会出现,并且可以看到剩余时间。如果在放电并且电量低,应该会变红并且闪烁。这设定跟我 Awesome 的那个一样,不知道等到什么时候才能用上,到时候才能看到效果了。
  • 音量。左键单击是切换静音,滚轮调节音量。图标会显示设备类型(我这里有内建、HDMI(实际上是走的 DP)、蓝牙三种)。麦克风静音的时候也会显示图标。
  • 系统托盘。它终于可以在多个屏幕上同时显示啦~
  • 时钟。

我最终还是用上了那个包名为「otf-font-awesome」的图标字体。

waybar 比 Awesome 的 bar 好配置多了。很方便使用外部程序来定义。也不一定要用定时器。程序可以一直跑着,一行一条状态更新,就不需要在「更新不及时」和「更新太频繁消耗资源、干扰调试」之间抉择了。

我把 wayfire、waybar 以及其它一些东西的配置文件上传到 GitHub dotconfig 仓库了。XDG 标准路径挺好的。

还有一些小的更新——

壁纸我使用了 swaybg,因为它支持不同显示器使用不同的壁纸,这样我的 e-ink 墨水屏就可以独享纯白壁纸了~或者什么时候我专门找张合适的黑白壁纸也挺好的。

fcitx5 输入条的文字 padding 太大。鉴于我现在日常使用 Wayland 了,我改了我这个主题,适配 Wayland。X 那边也没有太难看。

fcitx5-paste-primary 已经添加了 Wayland 支持,虽然实现很不优雅……

看图软件,使用 imv(同时支持 Wayland 和 X)取代了 sxiv。许多图片要预览的话,thunar 或者 geeqie 也挺好的。

以及一些已经报告的 bug:

还有一些未报告和未调查清楚的 bug,等事情明了之后我再更新啦~

Category: Linux | Tags: Wayland wayfire
11
15
2021
4

Wayfire 迁移进展

这几天又解决了一些问题,记一下。

Wayfire 部分:

  • 部分按键绑定无效的问题,Super+数字键无效是因为这个是 git 版本才有的。PrintScreen 无效是因为需要写成KEY_SYSRQ……
  • 窗口标题的问题。显示中文的 pr 已经提交。也把 scale 插件的问题一起修了。
  • 要不显示标题栏也很好办,首先 preferred_decoration_mode = server 让窗口们都用 wayfire 画的装饰,然后设置 height = 0 这样就看不到标题栏啦(窗口边框还留着;连边框都不想要的话可以不加载这个插件就好了)。
  • lightdm 启动不了 wayfire 的问题,在 ~/.xprofilesleep 1 就好了。相关 issue:Missing some input devices in wayland session · Issue #63 · canonical/lightdm
  • 嗯,lightdm 是会给 Wayland 会话 source ~/.xprofile 的。所以在里边判断 XDG_SESSION_TYPE 环境变量然后做相应的处理就好了。另外 sddm 是会 source ~/.profile 的。
  • invert 和 zoom 插件只支持一个显示器的问题,没有再次复现。可能是 git 版修了吧。
  • 挂起之后恢复,没键盘鼠标的问题大概也好了?今天我只遇到过一次没键盘,重新插了一下……
  • resize 调整窗口时保持比例(比如用于 scrcpy)。我已经在自己的 fork 中加入这个功能。
  • 锁屏使用 swaylock。不过它只锁屏并不会关显示器,所以我又写了个 xset dpms force off 的等价程序
  • HiDPI 下 Xwayland 窗口是糊的。通过改变插值算法(默认的 GL_LINEAR -> GL_NEAREST)来缓解。sway 默认就支持这个。这个 nearest 算法在整数倍放大时,会不那么糊,不过颗粒感会很明显(就是把显示器分辨率给降回去啦)。
  • 哦,还有个 git 版本的新问题:slurp 或 swaylock 在运行时,会消耗不少 CPU。我发现是由于 wayfire 一直在发送 configure 事件造成不断地重绘,已经给补上并提交 pr 了。

我的 Wayfire fork 位于 https://github.com/lilydjwg/wayfire/tree/lilydjwg,里边有什么请自行看提交历史。不过要注意的是,这个分支我可能会 push -f 以清理历史。

应用程序部分:

  • flameshot 需要设置 XDG_CURRENT_DESKTOP=sway 才能工作,然而在多显示器的时候只会给用户编辑左上角的部分,还是没法用。于是我用回传统的「选择+截图」组合了,只不过在 Wayland 下是 slurp + grim 这个组合。
  • wl-paste 和 xsel 不同步的问题,是由于 wlroots Xwayland 在窗口没有焦点时,被禁止与 Wayland 部分同步剪贴板。
  • 我装好 xdg-desktop-portal{,-wlr},设置好 XDG_CURRENT_DESKTOP 环境变量,然后重置了一下 obs-studio 的配置文件之后,它能工作了。不过只能录整个屏幕,不能按窗口录啦(由于更容易意外录到别的内容,反而不那么安全了)。

然后是剩下的问题:

  • spicy 无法捕获键盘是因为 wayfire 没有实现那个协议。有空我去 patch 一下好了。
  • 火狐还是不能录屏。听说是协议有更新火狐还没跟上?
  • wireshark 的菜单会显示在屏幕最右边。
  • mako 的通知文字经常是糊的,reload 一下什么的可能会好。也遇到过它不显示,reload 一下又好了的情况。
  • GTK 3 程序的右键菜单,上下会多出一部分内边距并被加上的圆角。圆角我还能忍,但多这么一部分不能选中就很难看了,然后火狐的多级菜单还没把这个考虑进去,没对齐各级菜单……

解决这各种问题挺累的,我就不仔细核查和整理了。本文只是个记录,把已经完成的事项从我的 TODO 列表转存到博客而已啦=w=

Category: Linux | Tags: Wayland wayfire
11
12
2021
0

不同情况下的图形效果

我在上一篇说到,Wayland 下再也不会遇到撕裂了。后来群友说,这是 Intel 的 modesetting 驱动特有的问题。所以我又重新比较了一下。为了让事实说话,而不是靠很容易有偏见的主观感受,我使用 Sony Xperia XZ2 Compact 手机的「慢动作」功能,以 960fps 录制了不同情况下,火狐滚动同一页面的效果。不过视频我就不放了,上传费时又占地方。

首先是我长期使用的组合:X11 + Intel 集成显卡 + modesetting DDX,窗口管理器是 Awesome 3.5.9,混成器是 picom。这种情况下有非常严重的斜线撕裂,在画面内容变化大的时候(如无过渡切桌面、视频镜头转换、大幅度滚动页面)必现。撕裂的样子如下(「慢动作」的分辨率有限,亮度低是预期现象):

X11 Intel 上的画面撕裂

可以很清楚地看到,截图中的页面是由两帧的内容拼接而成的。而且左下的是后一帧,右上的是前一帧,我不知道为什么会是这样子。我记得以前它不会撕这么大条斜线的啊,是斜-水平-斜的样子。反正都很难受就是了。我以前以为这种撕裂是时不时出现的,但多次「慢动作」慢放表明,它发生的频繁度大大超出了我之前的感受。

这个组合还有个问题是:外接显示器时,鼠标会跟着画面的更新而闪烁。画面更新越多越频繁,它闪烁得越快,而且和更新的区域也有关系。因为眼睛总跟着鼠标跑,所以即使它大部分时候不影响定位,但对主观感受的伤害应该也挺大的。

然后 Wayland + Intel 集成显卡,混成器是 Wayfire。完全观察不到撕裂。鼠标光标也很稳定不闪。

X11 + Nvidia 独立显卡 + 官方闭源驱动。还是 Awesome + picom 的组合,输出还是经过 Intel 显卡。也完全观察不到撕裂。不过有个更严重的问题——我的外接显示器每十来秒会黑屏几秒。听说把显示器关掉重开就可以解决,不过我无意使用这个方案,就不理它了。除了我之前提到的容易崩溃之外(其实我也不知道现在它还崩不崩),这显卡不支持视频硬件加速。这意味着我根本没办法看 4k 视频。PS: 火狐的 WebRender 能正常自行启用。

X11 + Intel 集成显卡 + Nvidia PRIME Offloading。我只是好奇地试一试这个方案啦。以前这个方案里火狐没办法启用图形加速,只能用「basic」渲染器来着。现在火狐有了 Software WebRender,倒是也能比较好地跑起来了。依旧会撕裂,好像比用 Intel 显卡要好那么一点点?

然后对比一下水族馆的图形性能数据:

  • X11 + Intel 显卡,30fps 左右。
  • Wayland + Intel 显卡:接近 60fps。
  • X11 + Nvidia 显卡:30-40ftps。
  • X11 + PRIME,这个比较令我意外,竟然也在 30fps 上下。要知道这个是纯 CPU 实现的,没有 GPU 加速的呀。

其实 WebGL 我用得不多啦(Google 地图更常卡在网络 I/O 上而不是渲染上)。更多的是播放在线(YouTube)视频啦。

  • X11 + Intel 显卡,1080p 60fps,GPU 用满,丢帧三分之一!4k 60fps 也差不多。原来重点不是分辨率(反正 GPU 的解码能力还没用满),重点是视频的帧率啊。
  • Wayland + Intel 显卡,4k 60fps 都不怎么丢帧,更不说其它了。GPU 图形计算用到一半左右。
  • 没有更多方案了。我这 CPU,软件解 4k 会卡成 PPT 的。

综上,Wayland 的表现是最好的!好吧虽然遇到了挺多问题的,不过我大概都能修或者绕过。虽然撕裂不全是 X11 的错,但是确实有客观证据证明它体验不好,不是 Wayfire 的特效太绚烂太怀旧让我偏心了~

Category: Linux | Tags: X window Wayland 显示驱动
11
8
2021
20

Wayland 初体验

前天得知 wayfire 能够直接在 X Window 下运行之后,我就尝试了一下。很好,能跑。虽然不能捕获键盘,从而与外边窗口管理器重复的快捷键无法使用,但至少它的鼠标光标位置是对的,能够用来测试。我在 QEMU 里跑过 KDE Wayland、sway 和 wayfire,无一例外鼠标位置不对。KDE 里是缩放倍数设为 2 之后,鼠标的实际坐标会翻倍。后两者基于 wlroots 的,鼠标的实际坐标位于显示的光标上方几十像素处。总之没法用。

在 X Window 里粗略地配置了一下 wayfire 之后,我就想实际在外边跑跑看。结果被惊艳到了,感觉就像年轻了24岁一样!

优势

尝试几次之后,我终于把 wayfire 跑起来了。

首先是火狐图形性能翻倍!

我打开火狐,跑了一下 WebGL 水族馆(微软的 FishGL 没啦)。接近 60fps,是我在 X Window 下跑的两倍有余!再试试 YouTube 的 4K 视频,竟然几乎不掉帧!我的电脑的性能原来这么好的吗……在 X Window 下,火狐播放这 4K 视频,会占满 GPU,并且 CPU 也几乎用满,丢掉约十分之一的帧。而在 Wayland 下竟然只用了一半的 GPU,CPU 用量也不怎么高。而且操作起来很流畅,右键菜单一点就出来,鼠标划过菜单项,高亮也跟得很紧。

画面再也不撕裂啦!

一直以来,我切换窗口、播放视频、快速滚动窗口内容的时候,时不时能见到画面撕裂。一般是在中间附近,一道水平接着倾斜接着再水平的裂痕会一闪而过,其上和其下是两帧不同的内容。读过 xplain 以及 Wayland Book,我知道这是由于 X Window 服务端与客户端之间并不同步,显示画面时,缓冲区里有啥画啥,并不在乎客户端有没有画完,导致相邻的两帧拼接在了一起。而 Wayland 会使用双重缓冲,客户端在缓冲区里画好了,才告诉混成器这一帧画好了、可以显示出去了。而在画好之前就显示上一次提交的缓冲区内容,所以永远不会出现画一半的情况。

我的光标也不再闪烁啦!

我也不知道为啥,我在 X Window 下,特别是接外接显示器的屋里,鼠标光标会像遥远的星星一样闪啊闪的,有时候闪得甚至影响我操作。我用的 picom 混成器的选项我改来改去,不光没能解决这个问题,反而是搞得它时不时崩溃一下了。我也不知道这是怎么回事,按理说鼠标光标由硬件绘制的,不应该时有时无啊。而在 wayfire 里,光标一直很稳定,从来不闪烁。我是返回到 X Window 下才注意到这一点的。

特效!

尝试 wayfire 的一大原因是我看中了它的特效。当年我初入 Linux 的时候,就挺喜欢 compiz 的特效的,只是后来因为性能问题才转到 Awesome 的。wayfire 有好多我当年常用的特效,果冻、火焰、立方体、展览,还有那个翻页一样的切换效果叫啥来着。没有纸飞机倒是有点可惜。

其它的 Wayland 混成器,KDE 我已经尝试过了,虽然是 X Window 版本,但功能应该是一致的。同样 sway 的对应 X Window 版本 i3 我也尝试过了。结论是,我并不是很在意平铺,但是我很在意键盘操控性。wayfire 也有较为丰富的键盘操作设置,应该也可以通过插件来扩展。

已解决的问题

虽然有上边这些优势,我还是回到 Awesome 来了,因为还有不少问题和需要配置的地方呢。此节记录一下我已经解决的问题。

  • GVim。因为 GVim 使用的是 GTK,所以改一改让它支持 Wayland 并没有很难。我改好的版本在我的 vim fork 的 wayland 分支上。目前还缺少两个重要功能:终端里的剪贴板,以及 +clientserver。
  • mpv 是糊的。研究发现这是因为我加了no-hidpi-window-scale参数。它是为了解决 X Window HiDPI 下窗口过大的问题,但到了 Wayland 下使用它就会导致并不进行 HiDPI 缩放了。
  • 窗口焦点不会跟随鼠标。这是在平铺式窗口管理器里使用很广泛的特性。wayfire-plugins-extra 里有个 follow-mouse 插件可以实现这个。
  • GTK 的鼠标主题、禁用动画设置等不生效。原来在 Wayland 下 GTK 不读取~/.config/gtk-3.0/settings.ini,可以使用 dconf-editor 去编辑 org.gnome.desktop.interface 下的选项
  • xfce4-notifyd 的通知会被当成普通窗口,置于屏幕中间并且有标题栏。有一个 fork 修复了此问题,不过我还是决定使用更轻量的 mako(不是 Python 的那个模板引擎哦)。
  • Xwayland 跑 PRIME offloading 时,Minecraft 的画面会来回抖动。于是只好放弃 NVidia 显卡改成 Intel 显卡了(反正自从我买了 4K 显示器之后就都跑不动了 QAQ)。
  • fcitx5
    • GTK 中,在输入编码的时候,按一次键闪烁一下。csslayer 说是不这样做就无法移动窗口。于是我改了一下代码,选择移不动。反正也只有在屏幕边缘的时候才需要移动。Qt 那边这个问题倒是不大,因为 fcitx5-qt 总是在应用程序的窗口范围内绘制候选词窗口,能够判断什么时候需要移动,所以只是在屏幕边缘的时候闪一闪。
    • Qt 中(就是 Telegram 啦),在窗口下方输入的时候候选词窗口会悬在文本上空。我改了一下代码,现在能够准确定位到文本的上方紧贴着了。代码已经 pr。
    • fcitx5-configtool 不显示部分图标。把GNOME_DESKTOP_SESSION_ID=0环境变量加上就好了。这个环境变量莫名其妙地解决了不少问题呢。

剩下的问题

我暂时还是以 Awesome 为主,因为还有很多问题有待解决:

  • Vim 不支持剪贴板、clientserver 特性。我打算通过另外的方式实现。工作量有些大。
  • SPICE 客户端 spicy 无法捕获键盘,造成虚拟机里无法使用与外边相同的快捷键。
  • LightDM 启动 wayfire 会话时会立即退出,而实际上 wayfire 是已经启动了的。各种日志里均没有找到任何有用的信息。
  • wl-paste 获取的剪贴板内容与 Xwayland 的并不同步。似乎大部分程序都支持两种剪贴板协议,但是 wl-paste 和 Xwayland 各支持其中之一?
  • Xwayland 不支持 HiDPI。有个补丁,但是会造成窗口一直缩小再缩小。
  • fcitx5
    • 候选词的内边距有些大。有空再调了。
    • fcitx5-paste-primary 不支持 Wayland。有空再加了。
  • wayfire
    • 窗口标题不支持中文。我倒是打算给 wayfire 加个不显示标题栏的选项。
    • 的 fast switch(真的很快)会导致全屏窗口取消全屏。
    • 似乎不支持触摸板拖拽的时候中断一下。应该很好修补吧?
    • 只能切换到本工作区的上一个窗口,不能跨工作区
    • invert 和 zoom 插件只支持某一个显示器。前者我用不到,但后者应该还有些用处的。
    • 设置的 Super+数字键 切换工作区无效。
    • 我还没想好我那些环境变量去哪里设置比较好。~/.xprofile没啦,~/.pam_enviroment我也不想用。在考虑使用 systemd 那个功能,或者我自己 wrapper 一下 wayfire。

缺憾

  • 使用 libinput 取代了 synpatics,非常好用的画圈滚动(一直画、一直滚)没了。

结语

还有挺多东西要配置的。锁屏啦,壁纸啦,还有未发现的问题啦,等等。慢慢来吧。Wayland 真的挺顺滑的。

对了,有个有意思视频给大家看一下。

这是我还没运行通知守护进程时,火狐发出的通知窗口,不知道为什么被反复创建和销毁。

Category: Linux | Tags: X Window X window Wayland
10
1
2021
3

纯 CSS 实现倒三角箭头

想实现这样的悬停提示框效果:

悬停提示框示例

这个绝对定位的框不是问题,边框的阴影也不是问题。问题是,我怎么弄出来那个倒三角的箭头呢?

在网上搜了一圈,找到的代码是这样的:

.tooltip .tooltiptext::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: black transparent transparent transparent;
}

拿边框给挤出来的。倒三角是有了,但是是纯色的,像这样子:

纯色背景提示框

很多 tooltip 提示框都是这么实现的,反正它们是黑底白字,并没有个边框。而我的是白底灰框,在下边接个纯色的三角块,就太丑啦。可是我又不想在这个被挤的边框上玩出花来,就只能是纯色的一块,所以此路不通了。

于是我就想啊,这个三角箭头怎么能弄出来呢?它不就是个正方形的框被砍了一半,再旋转45°吗?正好 CSS 能单独控制每一边的边框。也不用挤边框那么难以理解的操作了。于是:

#reply-popup::after {
  content: "";
  position: absolute;
  top: calc(100% - 6px);
  left: calc(50% - 6px);
  width: 10px;
  height: 10px;
  background-color: white;
  border-width: 1px;
  border-style: solid;
  border-color: transparent #bfbfbf #bfbfbf transparent;
  transform: rotate(45deg);
}

效果还不错,除了没有阴影,很不搭。那就把阴影加上?

  box-shadow: 0 0 5px gray;

结果嘛,不愧是叫 box-shadow,这阴影真就是个 box,不管你的边框,每一边都有的。我尝试调整 z-index 想让提示框把「无边阴影」给遮挡住,但是没成功。

于是又想办法。我记得 CSS 有个 clip 的功能来着?后来发现现在有个 clip-path,支持多边形,挺好的,我可以弄个三角形给剪裁一下,不用求助了行内嵌 SVG 了。

我尝试了一下,clip-path 是会随着 transform 一起旋转的,这似乎让火狐的 clip-path 多边形编辑功能很困惑,实际效果和显示的控制点之间我没能看出什么关联来。于是放弃可视化编辑,还是老老实实地算坐标点。也不复杂啦,右上、右下、左下,三个点就好了。然后放大两倍来容纳阴影。这样会正好切一半,在 Google Chrome 上没啥问题,但是在火狐上,不会完全遮挡住悬停元素的框,会漏出那么一丝丝出来。往上移一像素又太多,所以我把三角形底边上的两个端点稍微移动了一下,来挡住这一丝丝边框。最终规则是这样的:

  clip-path: polygon(145% -50%, 150% 150%, -50% 145%);

PS: 大家一直说 Google Chrome 更适合开发,但一些细节上火狐还是做得更多。比如火狐虽然没有角度编辑器,但是有多边形编辑器。火狐会在 DOM 树上把伪元素显示出来方便查看。火狐也会给元素标注事件、滚动、溢出、弹性盒,但 Google Chrome 只标注了弹性盒。话说 Google Chrome 最近终于加上了中文翻译了呢。另外 Google Chrome 的字体选择真不听话,难怪大家都喜欢强制指定一大串字体名。

哦对了,火狐可以用右键点击元素,然后按 q 键来检查元素。Google Chrome 没这个快捷键,得肉眼扫右键菜单。不知道有没有什么更方便的办法。

Category: Web前端 | Tags: web css blog

| Theme: Aeros 2.0 by TheBuckmaker.com