12
9
2013
5

替换 Awesome 内建的桌面通知

Awesome 用户们,你们有没有觉得 Awesome 那个 naughty 组件的通知很丑?

Awesome notification

这样子是不是好一些?

Xfce notification

Linux 的桌面通知机制是使用 D-Bus 通信的。所以,要换个桌面通知的流程如下:

  1. 让旧的桌面守护进程释放对应的 D-Bus 目的地名;
  2. 运行新的桌面守护进程。

首先安装个新的通知守护进程,比如我安装的xfce4-notifyd

阅读 Awesome 的naughty.lua代码之后,发现 Awesome 其实能够「慷慨」地释放org.freedesktop.Notifications这个通知用的地址的:

$ awesome-client
awesome#return dbus.release_name("session", "org.freedesktop.Notifications")
   boolean true

使用awesome-client告诉 Awesome 执行这条语句,释放这个 D-Bus 目的地址。返回true就表示执行成功了。如果喜欢的话,当然可以把这句写到rc.lua里去。

其实做到这一步就可以了。在需要时 D-Bus 会自动激活 xfce4-notifyd 的进程。当然也可以手动运行:

$ /usr/lib/xfce4/notifyd/xfce4-notifyd

还可以使用xfce4-notifyd-config命令来进行简单的配置哦。当然,这个替换对于从 Awesome 脚本里直接调用 naughty 不起效的。

从上边的图片可以看到,XFCE 的通知支持按钮的,Awesome 不支持这个。不过,它们都支持类似这种<span color="blue">蓝色文字</span>Pango 文本标记语法

要换回使用 Awesome 来显示通知的话,先关掉其它通知守护进程,然后让 Awesome 告诉 D-Bus 它要来处理这个地址上的消息:

awesome# return dbus.request_name("session", "org.freedesktop.Notifications")
   boolean true

最后来吐槽一下 C 公司的notify-osd,就是 Ubuntu 上默认那个看上去不错的黑框框。它不支持 Pango 文本标记也就罢了,不能同时显示多条通知只能一个个地来也就罢了,像 fcitx 这样往通知上放点按钮你猜会怎么着?——

notify-osd

竟然出来个夺取窗口焦点的弹框……

11
27
2013
10

恢复火狐地址栏图标及 HTTPS 站点标识

大约一年前,火狐 14 发布的时候,取消了在地址栏显示网站图标的功能,而且重新设计了 HTTPS 网站的标识。那个白色背景上的绿色标识我很不喜欢,于是通过给 omni.ja 文件打补丁的方法恢复了之前的样式:

  • HTTP 网站图标:

    HTTP favicon

  • HTTPS 域名显示:

    HTTPS domain

  • HTTPS 「经营者」显示:

    HTTPS identity

  • 部分 chrome URL 显示(我自己照着改的):

    Chrome URL

可是,火狐 21 对这部分代码进行了重构,我找不到相关部分的代码了。于是,我一直还在使用火狐 20。直到前几天。

实际上,恢复在地址栏显示网站的扩展有不少,但无一例外,它们都只是恢复网站图标的显示。有个插件也恢复了 HTTPS 网站的浅蓝色域名显示,但是没有已认证的「经营者」的绿色显示。而且,它的代码是通过定时器每隔几百毫秒就执行一次,相比事件响应的方法太没效率了。

于是,使用代码片断速记器(默认快捷键Shift+F4),配合 Firebug 查看 chrome://browser/content/browser.xul,根据之前的样式表,我终于得到了以下解决方案(实际上上边的截图是我这个解决方案应用之后的)——

首先,安装 userChromeJS 扩展。这个扩展使得在火狐启动时加载 userChrome.js 文件。

其次,userChrome.js 文件,使地址栏显示网站图标并在正确的时机切换。它位于火狐配置目录下的 chrome 目录下:(注意已有新版

if(location == "chrome://browser/content/browser.xul"){
  (function(){

  var eTLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]
                    .getService(Components.interfaces.nsIEffectiveTLDService);

  function updateIcon(event){
    var tab = event.target;
    if(tab != gBrowser.selectedTab){
      return;
    }
    var icon = tab.image || 'chrome://mozapps/skin/places/defaultFavicon.png';
    document.getElementById('page-proxy-favicon').src = icon;
    var identity = document.getElementById('identity-box');
    if('verifiedDomain'.indexOf(identity.className) != -1){
      var identityLabel = document.getElementById('identity-icon-labels');
      identityLabel.collapsed = false;

      var domain = eTLDService.getBaseDomain(tab.linkedBrowser.lastURI);
      document.getElementById('identity-icon-label').value = domain;
    }
  }

  var container = gBrowser.tabContainer;
  container.addEventListener("TabSelect", updateIcon, false);
  container.addEventListener("TabAttrModified", updateIcon, false);

  })();
}

这部分,特别是标签页事件,参考了 MDN 的相关文档

最后,HTTPS 和 chrome URL 的样式文件,放到同目录下的 userChrome.css 中,有点长:

@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");

/* 地址栏图标 */
#identity-box {
  background-image: linear-gradient(hsl(0,0%,98%), hsl(0,0%,92%));
  box-shadow: 0 1px 0 hsla(0,0%,0%,.05) inset;
  -moz-border-end: 1px solid rgba(0,0,0,.1);
  -moz-margin-end: 3px !important;
}

#identity-box:hover:active,
#identity-box[open="true"] {
  background-image: linear-gradient(hsl(0,0%,92%), hsl(0,0%,82%));
  box-shadow: 0 1px 1px hsla(0,0%,0%,.3) inset,
              0 1px 3px hsla(0,0%,0%,.3) inset;
}

/* Text colors for each:
 *
 * rgb(38, 76, 129) hsl(215, 55, 33)
 * rgb(71, 153, 0)  hsl(92, 100, 30)
 * rgb(229, 115, 0) hsl(30, 100, 45)
 */

#identity-box.verifiedDomain {
  background-image: linear-gradient(hsl(215,60%,92%), hsl(215,58%,88%));
  box-shadow: 0 1px 0 hsla(215,54%,33%,.05) inset;
  -moz-border-end-color: hsla(215,54%,33%,.2);
  color: hsl(215,54%,33%);
}

#identity-box.verifiedDomain:hover:active,
#identity-box.verifiedDomain[open="true"] {
  background-image: linear-gradient(hsl(215,80%,80%), hsl(215,67%,65%));
  box-shadow: 0 1px 1px hsla(215,54%,33%,.7) inset,
              0 1px 3px 1px hsla(215,54%,33%,.5) inset;
}

#identity-box.verifiedIdentity {
  background-image: linear-gradient(hsl(91,70%,90%), hsl(93,60%,81%)) !important;
  box-shadow: 0 1px 0 hsla(92,81%,16%,.05) inset !important;
  -moz-border-end-color: hsla(92,81%,16%,.2) !important;
  background-repeat: inherit !important;
  background-color: transparent !important;
}

#identity-box.verifiedIdentity:hover:active,
#identity-box.verifiedIdentity[open="true"] {
  background-image: linear-gradient(hsl(92,65%,70%), hsl(92,40%,48%)) !important;
  box-shadow: 0 1px 1px hsla(92,81%,16%,.6) inset,
              0 1px 3px 1px hsla(92,81%,16%,.5) inset !important;
}

#identity-box.chromeUI {
  background-image: linear-gradient(hsl(29,70%,95%), hsl(31,60%,90%)) !important;
  box-shadow: 0 1px 0 hsla(30,81%,25%,.05) inset !important;
  -moz-border-end-color: hsla(30,81%,25%,.2) !important;
  background-repeat: inherit !important;
  background-color: transparent !important;
}

#identity-box.chromeUI:hover:active,
#identity-box.chromeUI[open="true"] {
  background-image: linear-gradient(hsl(30,65%,85%), hsl(30,65%,70%)) !important;
  box-shadow: 0 1px 1px hsla(30,81%,25%,.6) inset,
              0 1px 3px 1px hsla(30,81%,25%,.5) inset !important;
}

另外,新版火狐又有一点令我不满了:滚动到页面底部和顶部也会使用平滑滚动,不喜欢。还是这个是可选的,设置 general.smoothScroll.otherfalse 就可以了。

2013年11月29日更新:自火狐 23 起,地址栏搜索会使用搜索栏的默认搜索引擎。对于我这种拿地址栏搜网站、搜索栏查字典的用户来说,这很难以忍受。于是找到 keyword.URL Hack! 这个扩展,新建字符串设置keyword.URL为 Google「手气不错」地址https://www.google.com/search?hl=zh-CN&btnI=1&q=,终于又回到从前二者兼俱的时候。

2014年3月25日更新:更新了脚本,在载入过程中不会显示错误的图标了。

Category: 火狐 | Tags: 火狐 userChrome
11
25
2013
10

被冻结的时间

那天中午,照例在网上闲逛时,却得知,那条时间线将永远停留在那时候,永远不会再流动了。不会再有新的消息,也不再会作出回应。

也并不是所有这样的事件都会如此高调地宣告它的发生。

有时,只是突然想起,有个人,很久很久都没有出现过了。

然后发现,询问的消息如石沉大海,有去无回。

甚至都没有办法去搜寻,因为我所知道的信息是那么不完整。

网络世界中,人与人,就像由虚拟的线联系起来。

那些带来温暖和慰藉的线,经常都是那样的纤细。

或者在某个不被注意的瞬间消逝,或者在最后的告别之后断掉。

Mark Pilgram 主动 410 时我只是唏嘘。

当有朋友对我说永别时,我惊讶、不敢置信,亦无限惋惜。

而那些悄然丢失的联系,只能埋藏起来,等待下一次被发掘。虽然明知那几乎不可能。

那些被冻结的时间,那些不会有回应的消息,那些永远的失去。

Category: 未分类 | Tags:
11
24
2013
2

X Window 中的剪贴板

这原本是我在知乎上的一个回答,现在略作修改,放在博客上。


很多 Linux 用户知道,除了通用的Ctrl-C/Ctrl-V剪贴板外,Linux 桌面上还有另一套剪贴板可以用。

首先澄清一下,这个功能不属于 Linux,而是属于它(目前)所广泛使用的显示服务程序——X WindowX Window 的历史比 LinuxVim 都要古老呢。现在所使用的版本 X11 也是 1987 年就已经发布了的。

X Window 目前被广泛使用的用于 X Window 客户端(使用 X Window 的程序)间交换数据的剪贴板有两个:primary selectionclipboard

Primary selection,通常,内容被选择时会被放到这里,按鼠标中键时被获取并粘贴。

例外一火狐浏览器中只有用户主动选择的内容才会被放到 primary selection,由网页代码导致的选择不会修改用户的 primary selection。
例外二Vim / GVim 的「可视」选择默认并不放到 primary selection。有选项可以设置成这样。
例外三:一些网站(如 GitHub)用的 Ace 在线编辑器,在用户「选择」时并不创建真正的选择区,它只在用户按Ctrl-C等键时做一些处理,因此在 Ace 编辑器中选中复制、中键粘贴无效。
例外四Wine 不支持 primary selection。

Clipboard,这就是大家熟悉的剪贴板了,图形界面程序中Ctrl-C复制,Ctrl-V粘贴。终端里因为快捷键会冲突,所以这些图形界面常用的快捷键使用的时候都要按住 Shift 键。

关 于 X Window 剪贴板要注意的地方:以上剪贴板的内容都不是保存在 X 服务器上的,而是客户端程序说,「我请求提供这个剪贴板的数据」(X 服务器通常会允许这样的请求)。另外的程序要粘贴时就会通过 X 服务器向这个程序请求:「请把 XX 剪贴板的数据给我。」所以,X Window 剪贴板上的内容会在拥有它的程序退出后自动被清除。所以一般人会需要用剪贴板管理器来更持久一些地保存剪贴板数据。

关于 X 协议细节可能有些不对,不过大体上是这个样子的啦。

还有没什么程序用到的 secondary selection,以及 Vim 偶尔会用到的 cut buffers(共8个,Vim 和 xterm 会用第一个)。Cut buffers 似乎是由 X 服务器保存数据的。Vim 在挂起时为了避免请求剪贴板数据的程序长时间等待会把自己的选择区内容写到 CUT_BUFFER0。

火狐似乎设置了很短的剪贴板请求超时时间,因此,从远程程序请求剪贴板数据时,可能因为网络延迟导致火狐没有及时得到数据而放弃。

Category: Linux | Tags: linux X Window X window
11
21
2013
8

虾米歌词下载、Python Requests 库,以及 HTTP Keep-Alive

Requests

这是我第二次用 Requests 了。上一次是个下小说的脚本。我已经不记得自己为什么路过了 httplib2,也路过了 urllib3,却最终买了 Requests 的账。也许是不喜欢 httplib2 那个 Google Code 的首页,也许是厌倦了 urllib* 这种名字。不过我想更多的是开门见山的首页,以及开篇那段让人无法拒绝的介绍:

Requests is an Apache2 Licensed HTTP library, written in Python, for human beings.

Python’s standard urllib2 module provides most of the HTTP capabilities you need, but the API is thoroughly broken. It was built for a different time — and a different web. It requires an enormous amount of work (even method overrides) to perform the simplest of tasks.

Things shouldn’t be this way. Not in Python.

相比之下,中文翻译太差了(而且已经陈旧了)。我在这里再译一个版本:

Requests 是使用 Apache2 许可证的 HTTP 库。用 Python 编写,为人类编写。

Python 标准库中的 urllib2 模块提供了你所需要的大多数 HTTP 功能,但是它的 API 烂出翔来了。它是为另一个时代、另一个互联网所创建的。它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务。

事情不应该是那样的,在 Python 世界里。

Requests 使用的是 urllib3,因此继承了它的所有特性。Requests 支持 HTTP 连接保持和连接池,支持使用 cookie 保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。现代、国际化、人性化。相见恨晚

Keep-Alive

以前一直以为 Keep-Alive(连接保持)就是节省了 TCP 连接建立的时间。直到渐渐了解了 TCP 慢启动。直到自己偶然间亲自对比了一次。

使用 httrack 默认参数下载 PostgreSQL 9.3 文档,一千多个页面,29 分钟。后来才注意到 httrack 的 Keep-Alive 支持要写参数手动启用。

使用 wget,默认会使用 Keep-Alive 来复用已有连接。几乎同样的页面,只花了 12 分钟

urllib3 说 它使用 Keep-Alive 单连接从 Google 下载 15 个页面比使用 urllib 每次建立新连接快了一倍。我这里的结果比它的测试还要好呢。

也许,支持 Keep-Alive,是我的 nvchecker 使用 pycurl 要快很多的很重要的一个原因吧。

其它

可惜的是,Requests 不支持 Tornado 的异步调度框架。不过还好,那边我可以用 libcurl,虽然 API 不 Pythonic,至少 cookie 管理和连接管理都很健全。

对了,虾米歌词下载脚本还是在老地方,歌词的获取参考了 you-get 的代码。Requests 的作者 Kenneth Reitz 也有一些其它有意思的东西,包括之前我发现但没意识到是他做的的、用于 HTTP 客户端测试的 Httpbin 网站。

Category: python | Tags: python tcp http
11
15
2013
5

在 Awesome 下对 Wine 运行的 TM.exe 使用 Alt+数字键来切换标签页

现在我一直在使用 Wine 运行 TM2013。这个版本支持一个窗口里以标签页的方式放多个对话了。然后就遇到一个问题——不同平台切换标签页的快捷键是不同的!

Linux 使用Alt+数字,Mac OS X 使用⌘数字,而 Windows 则使用Ctrl+数字。像火狐这种多平台支持得非常好的程序,不仅有一个适合其所运行平台的默认值,而且也可以通过手动修改about:config来使用其它平台上的习惯。但是,TM 显然不可能这么体贴。

不过我是 Linux + Awesome 用户嘛,怎么可能轻易就妥协呢。既然 TM 自己不认,那我让 Awesome 在Alt+数字时给 TM 的窗口发Ctrl+数字就好了嘛。想法是好的,现实却不那么美好,Awesome 不支持直接给窗口发送指定按键。于是只好调用 xdotool 命令了。因为按 Awesome 快捷键的时候,焦点会暂时移出原窗口,所以我指定了窗口 ID。延时的解决方法没这个优雅:

-- {{{ bind_alt_switch_tab_keys
alt_switch_keys = awful.util.table.join(
    -- it's easier for a vimer to manage this than figuring out a nice way to loop and concat
    awful.key({'Mod1'}, 1, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+1') end),
    awful.key({'Mod1'}, 2, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+2') end),
    awful.key({'Mod1'}, 3, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+3') end),
    awful.key({'Mod1'}, 4, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+4') end),
    awful.key({'Mod1'}, 5, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+5') end),
    awful.key({'Mod1'}, 6, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+6') end),
    awful.key({'Mod1'}, 7, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+7') end),
    awful.key({'Mod1'}, 8, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+8') end),
    awful.key({'Mod1'}, 9, function(c) awful.util.spawn('xdotool key --window ' .. c.window .. ' ctrl+9') end)
)
function bind_alt_switch_tab_keys(client)
    client:keys(awful.util.table.join(client:keys(), alt_switch_keys))
end -- }}}

然后在 TM 的窗口上调用这个函数就可以了。唯一有点小遗憾的是,fcitx 的图标在按 Awesome 快捷键时会切换一下。

Category: Linux | Tags: wIne Awesome X Window TM
11
14
2013
4

zsh 按 shell 参数移动

很早以前,我就想,在命令比较长的时候,M-fM-b按单词移动太慢了,特别是遇到长的 URL 或者文件名的时候。用鼠标吧,选择文本又比较麻烦了。所以很希望按 shell 参数来移动的功能,甚至尝试自己写过,但是因为对 zsh 了解太少,终究移动不正常。

昨天夜读 zsh 手册时才发现,原来,我曾见过这个功能的背影。

文档 26.6.1 节(「User Contributions」->「ZLE Functions」->「Widgets」)第一个,讲的是「bash-style word functions」。之前我也在哪里看到过,但是不知道其实这家伙支持好几种风格。使用以下配置就可以把 ZLE 里原来的「单词」概念变成 shell 解析出来的参数了:

autoload -Uz select-word-style
select-word-style shell

但是,我不想替换掉默认的,而是使用另外的键来这样子移动。研究了下代码,最终弄出来了:

# move by shell word {{{2
zsh-word-movement () {
  # see select-word-style for more
  local -a word_functions
  local f

  word_functions=(backward-kill-word backward-word
    capitalize-word down-case-word
    forward-word kill-word
    transpose-words up-case-word)

  if ! zle -l $word_functions[1]; then
    for f in $word_functions; do
      autoload -Uz $f-match
      zle -N zsh-$f $f-match
    done
  fi
  # set the style to shell
  zstyle ':zle:zsh-*' word-style shell
}
zsh-word-movement
unfunction zsh-word-movement
bindkey "\eB" zsh-backward-word
bindkey "\eF" zsh-forward-word
bindkey "\eW" zsh-backward-kill-word

只绑了M-BM-FM-W这三个含大写字母的组合键。其它-match函数的功能以后用到时再加好了。

Category: shell | Tags: zsh shell
11
13
2013
2

GM 脚本:清除 Google Groups 中的翻译提示

终于忍受不了 Google Groups 网页里到处都是的「将本帖翻译成中文」提示了——

// ==UserScript==
// @name           Google Group 清理
// @namespace      http://lilydjwg.is-programmer.com/
// @description    不用将帖子翻译成中文……
// @include        https://groups.google.com/*
// @grant      none
// ==/UserScript==

function doit() {
  console.log('cleaning up...');
  var divs = document.evaluate('//div[@class="gux-confirm-panel-c"]/div/a[@class="GFLL15SM5B"]/parent::div/parent::div', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

  for(var i=0, len=divs.snapshotLength; i<len; i++){
    var div = divs.snapshotItem(i);
    div.parentNode.removeChild(div);
  }
}

var timer;

document.body.addEventListener('overflow', function(e){
  if(timer){
    clearTimeout(timer);
  }
  timer = setTimeout(doit, 500, false);
});

点击安装备用地址

Category: 火狐 | Tags: google 火狐 GreaseMonkey
11
10
2013
5

终止永远等待网络的程序——纠结的 getmail 不再纠结

我收取邮件一直用的是 getmail,然而它有个问题:在网络不好的时候会挂在 recv 系统调用上,等好几个小时都有可能。还好我用的 crond 是 dcron,它知道同一个任务,在上一次任务还没执行完时即使时间到了也不应该再次执行,省了我一堆 flock 锁。不过,这样子导致我收不到邮件也不行啊。

以前也研究过一次,看到 getmail 有设置 socket 的超时时间啊,没整明白。最近网络又老是抽风,而且相当严重,导致我得不断地用 htop 去看、去杀没有反应的 getmail 进程。烦了,于是一边阅读 getmail 源码,一边使用 strace 观察,再配合 iptables 这神器,以及 the Silver searcher,终于找到了问题所在。

原来,由于 Python 的 SSL 对非阻塞套接字的支持问题12,getmail 在使用 SSL 连接时会强制使用阻塞式的套接字(见代码getmailcore/_pop3ssl.py:39以及getmailcore/_retrieverbases.py:187)。也许 Python 2.7 已经解决了这个问题,但是看上去 getmail 还是比较关心 Python 2.3 和 2.4。不过就算是 SSL 支持不好,调用下alarm不要一直待在那里傻傻地等嘛……也许,大部分 getmail 用户很少遇到足够差的网络?

于是考虑 fetchmail。花了两三天的业余时间终于弄明白我的需求该怎么配置了:

set daemon 300
set logfile ~/etc/log/fetchmail.log

defaults proto pop3 timeout 120 uidl
keep fetchsizelimit 0 mda "procmail -f %T"

poll pop.163.com interval 2
username "username" password "password"

poll pop.gmail.com
username "username" password "password"
ssl

poll pop.qq.com interval 2016 # 7 days
username "username" password "password"

但结果就是,除了 GMail 好一点,我让它「对从现在起所收到的邮件启用 POP」就没太大问题之外,腾讯还好,没几封邮件。网易那边,几百封旧邮件全部拖回来了…………

其实这个问题也还好,毕竟是一次性的。可我看它的日志,又发现,它每次收到 GMail 时,都会打印有多少封邮件已读。难道说,它每次去收邮件时都要列出所有可以用 POP3 收取的邮件,然后挑出没有收取过的?想到如果是这样,以后它每次取邮件时都要先取几千上万条已读邮件列表……这不跟 Google App Engine SDK 操作数据库加 offset 时前边所有数据全部读一遍一样扯淡吗……

于是又回来折腾 getmail。其实就这么一个问题,解决了就好。本来是准备去学学ptrace怎么用的,结果忍不住了,直接拿 Python 调 strace 写了这个:

#!/usr/bin/env python3

'''wait and kill subprocess if it doesn't response (from network)'''

import os
import sys
import select
import tempfile
import subprocess

timeout = 60

def new_group():
  os.setpgrp()

def main(args):
  path = os.path.join('/dev/shm', '_'.join(args).replace('/', '-'))
  if not os.path.exists(path):
    os.mkfifo(path, 0o600)
  pipe = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
  p = subprocess.Popen(['strace', '-o', path, '-e', 'trace=network'] + args, preexec_fn=new_group)

  try:
    while True:
      ret = p.poll()
      if ret is not None:
        return ret
      rs, ws, xs = select.select([pipe], (), (), timeout)
      if not rs:
        print('subprocess met network problem, killing...', file=sys.stderr)
        os.kill(-p.pid, 15)
      else:
        os.read(pipe, 1024)
  except KeyboardInterrupt:
    os.kill(-p.pid, 15)

  return -1

if __name__ == '__main__':
  try:
    import setproctitle
    setproctitle.setproctitle('killhung')
    del setproctitle
  except ImportError:
    pass
  sys.exit(main(sys.argv[1:]))

Python 果然快准狠 ^_^

代码在 winterpy 仓库里也有一份

这还是我编程时第一次用到进程组呢。没办法,光杀 strace 进程没效果。嗯,还有非阻塞的命名管道


PS: 去 GMail 设置页看完那个选项的具体名字后离开,结果遇到这个:

你这是让我「确定更改」呢还是「取消取消更改」呢……

11
4
2013
5

GM 脚本:Scrum for Trello

没找到这个家伙的 Firefox 版本。想它只是改改 Trello 的 UI 什么的,于是要来 crx 文件,直接把它的 JavaScript 文件扒出来了(顺便也发现了它的 GitHub 地址,不过已经晚了)。

按这里安装

移植过程中解决的问题:

  • PNG 和 CSS 等外部文件。两个小的 PNG 图片,我直接使用 Data URI 内嵌了。CSS 我用 yuicompressor 压缩之后也通过脚本加入了。
  • jQuery 1.7.1 与 Trello 所使用的 2.0.0 冲突了。改成直接使用页面上已有的 jQuery,顺便还省个不小的依赖。在 GreaseMonkey 中得unsafeWindow.jQuery来访问加上@grant: none指示
  • .live()方法没了……按文档改用.on()。共四处改动。.on()在替代.live()时语法不一样要小心不要弄错了。
  • 匹配 Board 名的正则失效了,修之。

PS: 火狐按住Ctrl键可以创建多个选区,在可视化编辑器里可以同时调整多个地方的样式,真方便呢=w=

Category: 火狐 | Tags: 火狐 GreaseMonkey

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com