9
21
2011
2

使用Xtest模拟鼠标点击

最近做一个小工具,需要模拟鼠标点击事件。当然,我可不想去调用 xdotool 或者 xmacro,效率什么不说,光是添加这么个罕见的依赖就不喜欢。顺便也好练习下 C 编程。

Xtest 的函数名长参数列表也长,不过用起来很简单。我所需要调用的函数就两个:

  • XTestFakeMotionEvent:把鼠标光标移动到指定坐标;
  • XTestFakeButtonEvent:模拟鼠标键

Xtest 的函数手册都在一个 manpage 里。看一下就知道用法了。

XTestFakeMotionEvent有五个参数,第一个是Display指针,然后依次是屏幕号、坐标和延时。屏幕号写-1就是默认了。延时我用0就好了。XTestFakeButtonEvent有四个参数,第一个依旧是Display指针,然后是按键号、是不是按下(还是放开按键)、延时。左键是1其它依次递加。不知道为什么这些函数要有个延时的参数。

#include<X11/Xlib.h>
#include<X11/extensions/XTest.h>

/* ... */

int clickAt(int x, int y){
  Display *dpy = XOpenDisplay(NULL);
  if(dpy == NULL){
    return 0;
  }

  XEvent event;

  /* get info about current pointer position */
  XQueryPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)),
      &event.xbutton.root, &event.xbutton.window,
      &event.xbutton.x_root, &event.xbutton.y_root,
      &event.xbutton.x, &event.xbutton.y,
      &event.xbutton.state);

  XTestFakeMotionEvent(dpy, -1, x, y, 0);
  XTestFakeButtonEvent(dpy, 1, 1, 0);
  XTestFakeButtonEvent(dpy, 1, 0, 0);
  /* place the mouse where it was */
  XTestFakeMotionEvent(dpy, -1, event.xbutton.x, event.xbutton.y, 0);
  XCloseDisplay(dpy);
  return 1;
}

这个函数实现了点击指定的屏幕坐标,完事之后再把鼠标光标移回去。最开始是没有移回去的,然后测试的时候我经常找不到鼠标光标了。。。。

C 语言用起来挺不爽的,所以后来做了个 Python 模块。不过功能很不完整,以后有需要时再慢慢加啦。要是谁有兴趣也可以自己加了后给我发 pull request 就更好了。代码地址:https://github.com/lilydjwg/winterpy/blob/master/pyso/X.c,编译命令:

gcc -O2 -shared -lX11 -lXtst `pkg-config --cflags --libs python3` X.c -o X.so

编译后import X,然后help(X)就知道用法了。

Category: Linux | Tags: python C代码 X Window
9
16
2011
11

有一天...

有一天,电子和正电子相爱了……

Category: 文学 | Tags: 爱情 物理
9
7
2011
19

生成 Awesome 的“应用程序”菜单

Ubuntu 下,Awesome 有个叫 debian_menu 的模块,用于向 Awesome 菜单中添加一个类似于 GNOME 的“应用程序”菜单的项。然而到了 ArchLinux 下,却没这么个模块了。本来我并不太在意,但看到别人折腾后,自己又开始手痒了。

本来是准备自己用 Python 写个程序来生成的,用 pkgfile 一查,却发现有archlinux-xdg-menu这么个软件包,遂装了。原来主要是两个 Perl 脚本。其一生成各种格式的菜单配置,其二根据配置文件为指定的窗口管理器生成菜单配置文件。虽然看示例配置文件似乎不支持 Awesome,但xdg_menu --help一看却是支持 Awesome 的。

xdg_menu --format awesome > ~/.config/awesome/menu.lua

然后改下rc.lua,把这个大菜单加上就可以了:

require("menu")

mymainmenu = awful.menu({ items = { { "Awesome", myawesomemenu, beautiful.awesome_icon },
          -- ...
          { "应用程序 (&A)", xdgmenu },
          -- ...

截图如下:

效果不错,只可惜没图标。

又:从 wiki 上看到,原来这菜单可以添加快捷键的,只要在相应字母前加上&符号即可。

2011年12月3日更新dlin帮忙修改了archlinux-xdg-menu,现在有图标了 ;-)

2012年5月28日更新:现在 Arch 下需要稍微修改下命令参数了:

xdg_menu --format awesome --root-menu /etc/xdg/menus/arch-applications.menu > ~/.config/awesome/menu.lua
Category: Linux | Tags: arch awesome
9
7
2011
6

Awesome 中 GIMP 窗口的处理

GIMP一启动就有三个窗口,一个显示图像的,一个工具箱,一个图层什么的。工具箱和图层这些虽然被Awesome自动判为浮动窗口了,但因为显示图像的主窗口是最大化,所以它们经常被图像窗口遮住。将这两个窗口置顶是最简单的办法,但是不太完美。这样它们也会遮住诸如我的浮动终端之类的窗口。

既然是高可配置的Awesome,当然不是没有办法让它们乖乖听话。于是翻翻手册,在我的 rc.lua 里又加了如下代码:

-- {{{2 for GIMP
client.add_signal("focus", function(c)
  if c.class and c.class == 'Gimp-2.6' then
    for _, i in ipairs(c:tags()) do
      for _, j in ipairs(i:clients()) do
        if j.role and (j.role == 'gimp-toolbox' or j.role == 'gimp-dock') then
          j.above = true
        end
      end
    end
  end
end)
client.add_signal("unfocus", function(c)
  if c.class and c.class == 'Gimp-2.6' then
    for _, i in ipairs(c:tags()) do
      for _, j in ipairs(i:clients()) do
        if j.role and (j.role == 'gimp-toolbox' or j.role == 'gimp-dock') then
          j.above = false
        end
      end
    end
  end
end)

这样在 GIMP 的窗口获得焦点时就把那两个窗口置顶,失去焦点时再取消置顶。不过令我有些不解的是,不能给单个的client对象添加信号处理。

8
24
2011
22

改变终端下的光标颜色,包括 screen 和 tmux!

曾经在Ubuntu中文论坛里看到一个改变光标颜色的方法,用光标颜色来指示是在 Vim 的普通模式还是插入模式下(因为 gnome-terminal 不支持使用转义序列改变光标形状)。Vim Wiki 上的 tip

if &term =~ "xterm\|rxvt"
  silent !echo -ne "\e]12;HotPink\007"
  let &t_SI="\e]12;RoyalBlue1\007"
  let &t_EI="\e]12;HotPink\007"
  autocmd VimLeave * :!echo -ne "\e]12;green\007"
endif

可惜它不适用于当时我正在使用的 screen。现在我改用 tmux 了,偶然改变TERM变量测试的时候,发现光标颜色竟然改变了——虽然还附带一些“不良反应”。我想到:一定有办法来正确地改变光标颜色的!

于是求助于 Google,很快找到了这个,有了用于 screen 的转义序列。不过依旧不适用于 tmux。把“tmux”也加到关键词里再搜,终于找到了这个。根据这个帖子,screen 和 tmux 比 xterm 多出来的那些字符序列是告诉 screen 或者 tmux 把其中的字符序列直接发送到终端模拟器处理。

于是,我的 vimrc 又可以更新了:

let color_normal = 'HotPink'
let color_insert = 'RoyalBlue1'
let color_exit = 'green'
if &term =~ 'xterm\|rxvt'
  exe 'silent !echo -ne "\e]12;"' . shellescape(color_normal, 1) . '"\007"'
  let &t_SI="\e]12;" . color_insert . "\007"
  let &t_EI="\e]12;" . color_normal . "\007"
  exe 'autocmd VimLeave * :!echo -ne "\e]12;"' . shellescape(color_exit, 1) . '"\007"'
elseif &term =~ "screen"
  if !exists('$SUDO_UID')
    if exists('$TMUX')
      exe 'silent !echo -ne "\033Ptmux;\033\e]12;"' . shellescape(color_normal, 1) . '"\007\033\\"'
      let &t_SI="\033Ptmux;\033\e]12;" . color_insert . "\007\033\\"
      let &t_EI="\033Ptmux;\033\e]12;" . color_normal . "\007\033\\"
      exe 'autocmd VimLeave * :!echo -ne "\033Ptmux;\033\e]12;"' . shellescape(color_exit, 1) . '"\007\033\\"'
    else
      exe 'silent !echo -ne "\033P\e]12;"' . shellescape(color_normal, 1) . '"\007\033\\"'
      let &t_SI="\033P\e]12;" . color_insert . "\007\033\\"
      let &t_EI="\033P\e]12;" . color_normal . "\007\033\\"
      exe 'autocmd VimLeave * :!echo -ne "\033P\e]12;"' . shellescape(color_exit, 1) . '"\007\033\\"'
    endif
  endif
endif
unlet color_normal
unlet color_insert
unlet color_exit

因为 tmux 的TERM变量和 screen 的一致,所以得使用TMUX变量来判断是在 tmux 里还是在 screen 里。

最后,说下指定颜色的方法。可以使用和 HTML 中一样的#rrggbb甚至简写#rgb,也可以使用颜色名。这里有个 xterm 的颜色名表。

2011年8月25日更新:

写了个 zsh 函数:

if [[ $TERM == xterm* ]] || [[ $TERM == *rxvt* ]]; then # {{{2 设置光标颜色
  cursorcolor () { echo -ne "\e]12;$*\007" }
elif [[ $TERM == screen* ]]; then
  if [[ -n "$TMUX" ]]; then
    cursorcolor () { echo -ne "\ePtmux;\e\e]12;$*\007\e\\" }
  else
    cursorcolor () { echo -ne "\eP\e]12;$*\007\e\\" }
  fi
fi
Category: Linux | Tags: vim 终端 screen zsh tmux
8
15
2011
14

userChrome 脚本:中键粘贴并搜索

Google 字典没了,但单词还是要查的。目前用的是沪江小D。实际上之前也一直在用它的划词查询书签。释义简单了点,但速度是足够快了。可是再也不能之前选中单词后点几下就能查到了(划词有时候会很烦,特别是与FireGuesture的搜索冲突的时候)。过了几天使用Ctrl-C来复制或者Ctrl-W来清除上一个词或者Ctrl-T打开新标签使用关键字搜索的日子,最终还是忍不住折腾了起来,最后终于写成以下脚本:

if(location == "chrome://browser/content/browser.xul"){
  var s = document.getElementById('searchbar');
  s.addEventListener('mousedown', function(e){
    if(e.button == 1){
      this.value = '';
    }
    return true;
  }, false);
  s.addEventListener('mouseup', function(e){
    if(e.button == 1){
      var where = e.altKey ? 'current' : 'tab';
    }
    setTimeout(function(){
      if(s.value){
        s.doSearch(s.value, where);
      }
    }, 100);
    return true;
  }, false);
}

使用方法:

先吐槽下:这使用方法可比脚本难找得多。我是边 Google 边摸索才弄明白的。

首先安装userChromeJS插件。火狐现在已经不再读取userChrome.js文件,所以需要这个插件。另外火狐5的话目前需要Add-on Compatibility Reporter这个插件。然后就是把代码粘贴到<firefox_profile>/chrome/userChrome.js文件里了。行家的话应该知道怎么单独文件之类的,我暂时就懒得研究了。

一切就绪后重启火狐。这时在搜索框点击鼠标中键就会将 X 剪贴板的内容粘贴到搜索框中并在新标签页中打开搜索。如果点击的时候按住Alt键的话,会在当时页面中打开。

最后,既然用到的 X 剪贴板,当然是不支持 Windows 了。(我还不会用Javascript操纵剪贴板。。。)

Category: 火狐 | Tags: 火狐 userChrome
8
8
2011
9

再见了,Google 字典!

今天,当我又遇到一个不太认识的单词时,我像往常那样选中它,然后切换到火狐标签栏的第一个已固定的标签页,正准备连续在输入框点击左键和中键来查询的时候,蓦然发现,Google 字典不告而别了——

Google Dictionary Are Gone

曾经有一次,Google 字典变成了 Google 搜索。当时我就很失落——这么好用的一个工具没了。第二天一看,它又回来了。我以为是用户的反对使 Google 反悔了。可是现在,即使明天它再回来,我也不敢再信任了。已经开了好几个月的固定标签页关掉吧,删除相关书签和搜索关键字。下个星期再去删未完成的抓取 Google 字典的 Python 脚本。至于那个我自己修改过的 GreaseMonkey 脚本,算了吧,留着作为纪念好了。

Category: 网络 | Tags: google 词典
7
31
2011
5

在 ncurses 中使用 readline

有一天,我发现了一个很好用的 Python shell——bpython。它使用了 ncurses 来做界面,使用了 pygments 来高亮代码,怎么看都比 ipython 漂亮,更不用说 Python 自己的了。不过既然它使用了 ncurses,麻烦也就来了——ncurses 不支持 readline!虽然有些模拟,但终究是不好用,M-f M-b不起使用,M-数字也不能用。于是我再次去 google 同时使用 ncurses 和 readline 这两个库的办法。

功夫不负有心人,这次终于 google 到了点有用的东西

The basic idea is to use call rl_callback_read_char() when input from the user is available (determined with select, or similar), then print 'rl_line_buffer' as you would any other string in ncurses, and optionally set A_REVERSE on the position indicated by rl_point. (or just reposition the cursor I guess, either works...)

不过可惜的是,这封邮件给出的代码在我这里并没有跑起来。其实跑起来了也用处不大,因为我需要的那部分代码独立性太差了,还是得重写。

花了一个下午,我终于弄出了一个雏形。

首先,这个rl_callback_read_char()是这么用的(文档):

#include<stdio.h>
#include<readline/readline.h>

int main(void){
  int cont = 1;

  void callback(char *text){
    if(text == NULL){
      rl_callback_handler_remove();
      putchar('\n');
      cont = 0;
    }else{
      printf("%s.\n", text);
    }
  }

  rl_callback_handler_install(">> ", callback);
  while(cont){
    rl_callback_read_char();
  }
  return 0;
}
      

首先安装个回调函数,它将在 readline 读取到一行内容时调用。当标准输入可用的时候,调用rl_callback_read_char()来读取字符。另外注意,这里我用了 gcc 的嵌套函数支持,免得弄出不少全局变量。

知道怎么用rl_callback_read_char()之后,就可以按那封邮件所说的,把它和 ncurses 联合起来了。代码修改自NCURSES Programming HOWTO。思路很简单,readline 负责读取并处理用户输入,显示是自己处理的。不过作为中文用户,纠结了半天的中文问题。最开始是有了 ncurses 之后,中文显示异常。这个是通过setlocale解决的。然后又是光标放的位置不对。于是又用上了我同样不熟悉的 wchar,自己计算光标应该放在哪里。

#define _XOPEN_SOURCE 700       /* for wcswidth and 700 is for mbsnrtowcs */
#include<wchar.h>
#include<ncurses.h>		/* ncurses.h includes stdio.h */
#include<stdlib.h>
#include<string.h>
#include<readline/readline.h>
#include<locale.h>
 
int mygetstr(char *str, int y, int x){
  WINDOW *win;
  int size, col;
  int ok = 0;
  int width;
  wchar_t wstr[80];
  char *p;
 
  getmaxyx(stdscr, size, col);
 
  void getaline(char *s){
    str = s;
    rl_callback_handler_remove();
    ok = 1;
  }
 
  rl_callback_handler_install("", getaline);
  win = newwin(1, col-x, y, x);
  while(1){
    rl_callback_read_char();
    if(ok)
      break;
    werase(win);
    strncpy(str, rl_line_buffer, 80);
    p = str;
    /* how many column chars before cursor occupies? */
    size = mbsnrtowcs(wstr, (const char**)&p, rl_point, 80, NULL);
    width = wcswidth(wstr, size);
    mvwprintw(win, 0, 0, "%s", str);
    /* put the cursor at right column */
    wmove(win, 0, width);
    wrefresh(win);
  }
  delwin(win);
  return 0;
}
 
int main(){
  char mesg[] = "Enter a string: ";
  char str[80];
  int row, col;
 
  setlocale(LC_ALL, "");        /* make ncurses handle Chinese correctly */
  initscr();
  getmaxyx(stdscr, row, col);
  mvprintw(row / 2, (col - strlen(mesg)) / 2, "%s", mesg);
  refresh();
  mygetstr(str, row / 2, (col + strlen(mesg)) / 2);
  mvprintw(LINES - 2, 0, "You Entered: %s", str);
  getch();
  endwin();
 
  return 0;
}
      

注意:此代码只是演示用,缓冲区溢出什么的我都没处理。

终于搞定了 C 下结合两者的使用,接下来 Python 版思路是有了,但因为其标准库 readline 中没有提供rl_callback_read_char()函数,所以只能用 ctypes 了。下面是在 Python 里使用rl_callback_read_char()的示例,ncurses 部分我暂时不想折腾了。

#!/usr/bin/env python3
# vim:fileencoding=utf-8

import sys
import readline
import ctypes
import ctypes.util

rllib_path = ctypes.util.find_library('readline')
rllib = ctypes.CDLL(rllib_path)

def callback(s):
  if s is None:
    rllib.rl_callback_handler_remove()
    sys.stdout.write('\n')
    sys.exit()
  elif not s:
    pass
  else:
    print('%s.' % s.decode())
    # 这样也可以
    # print(readline.get_line_buffer())

cbfunc = ctypes.CFUNCTYPE(None, ctypes.c_char_p)

rllib.rl_callback_handler_install.restype = None
rllib.rl_callback_handler_install(ctypes.c_char_p(b">> "), cbfunc(callback))

while True:
  rllib.rl_callback_read_char()
      

2011年8月4日更新:今天终于完成了个 quick and dirty 的 Python 版:

#!/usr/bin/env python3
# vim:fileencoding=utf-8

import sys
import readline
import ctypes
import ctypes.util
import curses
import struct
from 字符集 import width
 
rllib_path = ctypes.util.find_library('readline')
rllib = ctypes.CDLL(rllib_path)
 
def getstr(win, y, x):
  _, col = win.getmaxyx()
  inputbox = curses.newwin(1, col-x, y, x)
  ret = ''
  ok = False
  def callback(s):
    nonlocal ok, ret
    if s is None:
      rllib.rl_callback_handler_remove()
      raise EOFError
    elif not s:
      ok = True
    else:
      ret = s.decode()
      ok = True
  
  cbfunc = ctypes.CFUNCTYPE(None, ctypes.c_char_p)
  
  rllib.rl_callback_handler_install.restype = None
  rllib.rl_callback_handler_install(ctypes.c_char_p(b""), cbfunc(callback))
  
  while True:
    rllib.rl_callback_read_char()
    if ok:
      break
    inputbox.erase()
    # 这样获取的值不对。。。
    # bbuf = ctypes.string_at(rllib.rl_line_buffer)
    buf = readline.get_line_buffer()
    bbuf = buf.encode()
    inputbox.addstr(0, 0, buf)
    rl_point = struct.unpack('I', ctypes.string_at(rllib.rl_point, 4))[0]
    w = width(bbuf[:rl_point].decode())
    inputbox.move(0, w)
    inputbox.refresh()

  del inputbox

  return ret

msg = '输入字符串:'
win = curses.initscr()
curses.noecho()
row, col = win.getmaxyx()
win.addstr(row // 2, (col - width(msg)) // 2, msg)
win.refresh()
s = getstr(win, row // 2, (col + width(msg)) // 2)
win.addstr(row - 2, 0, '你输入了: ' + s)
win.getch()
curses.endwin()
      
Category: Linux | Tags: C代码 ncurses python readline
7
26
2011
0

GM脚本:维基百科语言链接中,中英文优先

每次在一大堆语言列表中找“中文”或者“English”实在太累,所以想了这么个办法。虽然维基百科的页面已经使用了jQuery,但我还是执着地没有使用它。不过也用到了点新东西——XPath

// ==UserScript==
// @name           Wikipedia 语言链接顺序调整
// @description	   将维基百科中的中英文语言链接放到最前面
// @namespace      http://lilydjwg.is-programmer.com/
// @include        http://*.wikipedia.org/*
// @include        http://*.wiktionary.org/*
// @include        https://*.wikipedia.org/*
// @include        https://*.wiktionary.org/*
// ==/UserScript==

var links = document.evaluate('//*[@id="p-lang"]//a[text()="中文" or text()="English"]', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
var ul;
for(var i=0, len=links.snapshotLength; i<len; i++){
  var link = links.snapshotItem(i);
  ul = ul || link.parentNode.parentNode;
  ul.insertBefore(link.parentNode, ul.firstChild);
}

我第一次、也是唯一一次看到在火狐中使用XPath的示例在这里这里是MDC文档。


点击安装。


2011年8月12日更新:加入了维基词典的支持。

2011年11月7日更新:加入对 HTTPS 的支持。

7
1
2011
10

GM 脚本:Google顶栏还是白色的好看

月光都说了,Google的大多数页面还是白色顶栏比较合适,黑的看着实在是不爽。所以还是改回来好了:

// ==UserScript==
// @name          White Google topbar
// @namespace     http://lilydjwg.is-programmer.com/
// @description	  bring back google's white topbar
// @include       http://*.google.com*
// @include       https://*.google.com*
// ==/UserScript==

var css = "#gbx3, #gbx4 { background-color: #ffffff; border-bottom: 1px solid #3366cc; } #gbz .gbzt, #gbz .gbgt, #gbg .gbgt, .gbts, .gbz0l { color: #3366cc !important; } .gbzt-hvr, .gbzt:focus, .gbgt-hvr, .gbgt:focus { background-color: #eff3fb; } .gbz0l .gbtb2 { border-top-color: #3d7af5 !important; } #gbi5 { background-position: -6px -22px; }";
if (typeof GM_addStyle != "undefined") {
  GM_addStyle(css);
} else if (typeof PRO_addStyle != "undefined") {
  PRO_addStyle(css);
} else if (typeof addStyle != "undefined") {
  addStyle(css);
} else {
  var heads = document.getElementsByTagName("head");
  if (heads.length > 0) {
    var node = document.createElement("style");
    node.type = "text/css";
    node.appendChild(document.createTextNode(css));
    heads[0].appendChild(node); 
  }
}

点击安装

Category: 火狐 | Tags: google GreaseMonkey

| Theme: Aeros 2.0 by TheBuckmaker.com