GlibのGRegexをglibcのregex.hで置き換える

X11 Mouse Cursor plug-in for GIMP 2.0.5GIMP 2.4 用のX11 Mouse Cursor plug-in for GIMP 1.1.5をリリースした。細かいバグを潰している。

GIMP 2.4 ではGLibの最小バージョンは2.12だがGRegexはGLib-2.14以降でのみ利用できるので、1.1.5に関してはGRegexをregex.hで置き換えた。その要になる部分を整理してdiff形式でメモしておく。
やっていることは「framename」に格納された文字列に対して「(XXms)」や「(XXpx)」(XXは数字)となる部分文字列を探し、数字を「digits」に、"ms"や"px"を「suffix」に格納することである。

+#include <regex.h>

-  GRegex re;
+  regex_t re;
-  GMatchInfo *info = NULL;
+  regmatch_t info[3];   /* store matched atoms' offsets */
+  const gchar *p;       /* the place of "framename" on which we try to match regex */

-  re = g_regex_new ("[(][ ]*(\\d+)[ ]*(px|ms)[ ]*[)]",
-                    G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
-                    0,
-                    NULL);
+  regcomp (&re, "[(][ ]*([[:digit:]]+)[ ]*(px|ms)[ ]*[)]", REG_ICASE | REG_EXTENDED);

-  for (g_regex_match (re, framename, 0, &info); g_match_info_matches (info); g_match_info_free (info))
+  for (p = framename; regexec(&re, p, 3, info, 0) == 0; p += info[0].rm_eo)
     {
-      digits = g_match_info_fetch (info, 1);
+      digits = g_strndup (p + info[1].rm_so, info[1].rm_eo - info[1].rm_so);
-      suffix = g_match_info_fetch (info, 2);
+      suffix = g_strndup (p + info[2].rm_so, info[2].rm_eo - info[2].rm_so);

-      g_match_info_next (info, NULL);
     }

-  g_regex_unref(re);
+  regfree(&re);

Icon Theme Updater 0.9.0 をリリース

Icon Theme Updater 0.9.0をリリースした。0.8.1からコードをかなり書き直した。
「Gorilla」など一部のアイコンテーマでは異なるサイズのアイコンが同じディレクトリに混在しており、Icon Theme Updaterではこの様なテーマは今のところうまく扱えない。ImageMagickの「identify」コマンドでアイコンのサイズを読み取るコードは既に入っているが、それを使ってアイコンを振り分けるコードを書くのが面倒で投げ出してしまった。

Firefox拡張『ZAQホームページウイルススキャンサービス切り替えボタン』をアップデート

tksmashiw2009-02-08

Firefox拡張『ZAQホームページウイルススキャンサービス切り替えボタン』バージョン1.1をリリースした。変更点はFirefox 3.1対応のみ。
以下のようなアップデート情報を用意したらスクリーンショットの様に表示された。

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<p>ZAQホームページウイルススキャンサービス切り替えボタン 1.1の変更点</p>
  <p>Firefox 3.1.*に対応
  </p>
</html>

Fedora 10上でMinGWを使ってクロスコンパイル

表題のような記事を書こうとしたがすでに先人がおりあまり書くことが無い。以下に重要なページのリンクを上げておく。
MinGW | Minimalist GNU for Windows
MinGW(Wikipedia)
Fedora MinGW project

MinGW: Compile software for Windows without leaving your Fedora machine
Fedora 10で「virt-viewer」をクロスコンパイルする実例。
bitWalk'sの「MinGW クロスコンパイラ」の項目
すぐりふひと氏のブログ。MinGWに関して日本語で非常に多くの記事を書いておられる。
MinGW Cross Compiler Project
同氏によるFedora10用コンパイル済みバイナリが多数置いてある。
Cross-compiling GTK+ apps for Windows
Ubuntu 7.10での環境構築の方法ほか。
GTK+ - Download for Windows
GTK+アプリをビルドするのに必要なファイル群。


ロスコンパイルの実例としてGlibのGRegexを使用した以下のようなプログラムをコンパイルして動作確認してみた。

gregex.c:

#include <stdio.h>

#include <glib.h>
#include <glib/gprintf.h>

int main(int argc, char *argv[])
{

  if (argc == 1)
    return 0;
  int i = 0;
  gint ret;
  gchar *string = NULL;
  GError *error = NULL;
  GMatchInfo *info = NULL;
  const gchar *pat = "[(][ ]*(\\d+)[ ]*ms[ ]*[)]";
  GRegex *reg = g_regex_new (pat,
                             G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
                             0, /* match flag */
                             &error);
  if (error)
  {
    g_error_free (error);
    return 1;
  }
  for (i = 1; i < argc; ++i)
  {
    if (g_regex_match (reg, argv[i], 0, &info))
    {
      string = g_match_info_fetch (info, 1);
      ret = g_ascii_strtod (string, NULL);
      g_free (string);
      g_match_info_free (info);
      g_fprintf (stdout, "%d\n", ret);
    }
  }
  g_regex_unref (reg);
  return 0;
}

MinGW環境を構築する。


$ []sudo yum install mingw32-binutils mingw32-gcc mingw32-w32api mingw32-runtime[]
[]...省略...[]
[]Installed:[]
[] mingw32-binutils.i386 0:2.19-2.fc10 mingw32-gcc.i386 0:4.3.2-12.fc10 []
[] mingw32-runtime.noarch 0:3.15.1-10.fc10 mingw32-w32api.noarch 0:3.12-8.fc10 []

[]Dependency Installed:[]
[] mingw32-cpp.i386 0:4.3.2-12.fc10 mingw32-filesystem.noarch 0:40-3.fc10 []

Complete!
$ []wget 'http://nchc.dl.sourceforge.net/sourceforge/mingw-cross/mingw32-glib2-2.18.1-2.zip'[]
$ []unzip mingw32-glib2-2.18.1-2.zip[]
[]Archive: mingw32-glib2-2.18.1-2.zip[]
[] creating: mingw32-glib2-2.18.1-2/[]
[]...省略...[]
$ []sudo mv mingw32-glib2-2.18.1-2/* /usr/i686-pc-mingw32/[]
$ []rmdir mingw32-glib2-2.18.1-2[]
$ []sed -i 's@^prefix=.*$@prefix=/usr/i686-pc-mingw32@g' lib/pkgconfig/*.pc[]

Makefile.amとconfigure.acを作る。
configure.ac:

#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.63])
AC_INIT([gregex], [0.1], [***@********])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([gregex.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.
PKG_CHECK_MODULES([GREG], [glib-2.0 >= 2.14 ])
AC_SUBST([GREG_CFLAGS])
AC_SUBST([GREG_LIBS])

# Checks for header files.
AC_HEADER_STDC

# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST

# Checks for library functions.

AC_CONFIG_FILES([Makefile])
AC_OUTPUT

Makefile.am:

bin_PROGRAMS = gregex
gregex_SOURCES = gregex.c
gregex_LDADD = @GREG_LIBS@
AM_CFLAGS = @GREG_CFLAGS@

ビルドとWineを使った動作確認。


$ []autoheader && aclocal && autoconf -i && automake -a -c[]
[]...省略...[]
$ []./configure --host=i686-pc-mingw32[]
[]...省略...[]
$ []make[]
[]make all-am[]
[]make[1]: ディレクトリ `/home/tks/unix/gregex' に入ります[]
i686-pc-mingw32-gcc -DHAVE_CONFIG_H -I. -mms-bitfields -I/usr/i686-pc-mingw32/include/glib-2.0 -I/usr/i686-pc-mingw32/lib/glib-2.0/include -g -O2 -MT gregex.o -MD -MP -MF .deps/gregex.Tpo -c -o gregex.o gregex.c
[]mv -f .deps/gregex.Tpo .deps/gregex.Po[]
[]i686-pc-mingw32-gcc -mms-bitfields -I/usr/i686-pc-mingw32/include/glib-2.0 -I/usr/i686-pc-mingw32/lib/glib-2.0/include -g -O2 -o gregex.exe gregex.o -L/usr/i686-pc-mingw32/lib -lglib-2.0 -lintl []
make[1]: ディレクトリ `/home/tks/unix/gregex' から出ます
$ []wine gregex.exe '(59ms)'[]
[]err:module:import_dll Library libglib-2.0-0.dll (which is needed by L"Z:\\home\\tks\\unix\\gregex\\gregex.exe") not found[]
[]err:module:LdrInitializeThunk Main exe initialization for L"Z:\\home\\tks\\unix\\gregex\\gregex.exe" failed, status c0000135[]
$ []cp /usr/i686-pc-mingw32/bin/libglib-2.0-0.dll .[]
$ []wine gregex.exe '(59ms)'[]
59

The Unofficial Widget FactoryにRGBAカラーマップを加える

Cimi’s Official Blogにある通りGTK+アプリケーションにRGBAカラーマップを加えるとGNOME標準のMetacity*1でも半透明のウィンドウを実現することができる。
The Widget FactoryにRGBAカラーマップを加えるパッチがwww.cimitan.comに投稿されているのでこれを元にThe Unofficial Widget Factoryのパッチを作ってみた。
src/main.c:

@@ -41,6 +41,7 @@
 
 void on_scale_value_changed (GtkRange *range, gpointer user_data);
 void on_button_refresh_clicked (GtkWidget *button, gpointer user_data);
+void transparentize_window (GtkWidget *window);
 
 static gchar *
 get_ui_file (void)
@@ -84,6 +85,7 @@
 	glade_xml_signal_autoconnect (d->gxml);
 
 	window = WID(window_main);
+	transparentize_window (window);
 
 	combobox1 = WID(combobox1);
 	combobox2 = WID(combobox2);
@@ -113,7 +115,7 @@
 		gtk_tree_view_column_set_reorderable (column, TRUE);
 		gtk_tree_view_column_set_resizable (column, TRUE);
 		gtk_tree_view_column_set_sort_column_id (column, i);
-		gtk_tree_view_append_column (treeview, column);
+		gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
 		g_free (name);
 	}
@@ -180,6 +182,18 @@
 	theme_refresh ();
 }
 
+void
+transparentize_window (GtkWidget *window)
+{
+  GdkScreen *screen = gtk_widget_get_screen(window);
+  GdkColormap *colormap = gdk_screen_get_rgba_colormap (screen);
+
+  if (colormap && gdk_screen_is_composited (screen))
+    {
+      gtk_widget_set_default_colormap(colormap);
+    }
+}
+
 int
 main (int argc, char *argv[])
 {

「transparentize_window()」という関数を作り、その中でメインウィンドウにカラーマップを加えた。(ちなみに113行目から始まるブロックは型の不整合による警告を消すためのもの。)これで透過できるかと思ったがうまく行かなかったので、UIを構成しているgladeについて調べてみるとEmbedding Libglade Interfacesの最後のブロックに

One thing to note -- if you don't want a widget to be displayed as soon as 
it is constructed with glade_xml_new, you should set the visible property 
on that widget to "no" in Glade. 

と書いてあるのでメインウィンドウの「visible」プロパティを「False」にしたら半透明になった。
src/twf.glade:

@@ -3,7 +3,7 @@
 <!--*- mode: xml -*-->
 <glade-interface>
   <widget class="GtkWindow" id="window_main">
-    <property name="visible">True</property>
+    <property name="visible">False</property>
     <property name="title" translatable="yes">The Widget Factory</property>
     <signal name="destroy" handler="gtk_main_quit"/>
     <child>

メインウィンドウが作られた時点では表示せず、「transparentize_window()」でカラーマップを与えてから「gtk_widget_show_all (window)」で表示するという手順になる。

Fedora 10 用のrpmパッケージを物置にアップした。

*1:「/apps/metacity/general/compositing_manager」を「True」にする必要がある

The Unofficial Widget Factory

tksmashiw2009-01-27

The Widget Factoryhttp://www.gnome-look.org/などでGTK2のテーマのスクリーンショットを撮るためによく使用されているが、本家の開発は2006年で止まっている。対して上記のサイトのスクリーンショットの中にはカレンダーなど本家版には無いウィジェットを持っている物がある。以前からこれが何か分からなかったがようやくThe Unofficial Widget Factoryであることが分かった。
Fedora 10 で上記のサイトからソースをダウンロードし簡単にビルドできた。*1

*1:gtk2-devel, libglade2-devel, libcap-develが必要

xml.parsers.expatのよくわからない挙動 - 解決

xml.parsers.expatのよくわからない挙動で取り上げた内容をPython issue Trackerリポートしてみたら、xml.parsers.expatのバグではなく私のコードのバグであることをkawaiさんという方に指摘していただいた。結局「CharacterDataHandler」は渡されたデータを一回で読み込むとは限らない(SAXの仕様)ので、単にデータを渡すようにすると2回目以降に渡したデータで1回目に渡したデータを上書きてしまうのが原因らしい。(リンク先「The ContentHandler.characters() callback is missing data!」参照。)これを避けるために「CharacterDataHandler」に文字列を渡すときは「+=」で追加していく様にし、「StartElementHandler」で"link"要素の始めに来たときにクリアするようにする。

# -*- coding: utf-8 -*-
from __future__ import with_statement
from xml.parsers.expat import ParserCreate

_cnt = ""
_name = ""
_data = ""
_dict = {}

def make(buf=False):
  """Make database which is accessible by Databese.dict"""
  global _cnt
  global _name
  global _data
  global _dict
  map_file = "/usr/share/icon-naming-utils/legacy-icon-mapping.xml"

  p = ParserCreate()
  p.buffer_text = buf
  p.StartElementHandler = start_element
  p.EndElementHandler = end_element
  p.CharacterDataHandler = char_data

  try:
    with open(map_file, 'rb') as f:
      p.ParseFile(f)
  except IOError,  err:
    print err

  return _dict

def start_element(name, attrs):
  global _cnt
  global _name
  global _data
  if name == 'context':
    _cnt = attrs["dir"]
  if name == 'icon':
    _name = attrs["name"]
    _dict[_name] = (_cnt, _name)
  if name == 'link':
    _data = ''   # _dataをクリアする

def end_element(name):
  global _cnt
  global _name
  global _data
  if name == 'link':
    _dict[_data] = (_cnt, _name)

def char_data(data):
  global _data
  _data += data    # _data に取得した文字列を追加する

kawaiさん有難うございました。


追記2009/01/27
"_data"のクリアをend_element()からstart_element()へ移動。さもないとスペースやタブ、改行が"_data"に入ってしまう。