[ C++で開発 ]
X Window Systemの相当低レベルAPIであるXlibを使って文字列を表示します。また、日本語文字を表示する方法として、XlibおよびXftの2種類を使用します。
国際化対応(Internationalization: I18N)される前のX Window System(X11R4まで)のXlibでは、XLoadFont()、XDrawString()を使って文字を描画していましたが、これらはASCII文字用で、日本語を出すことができません。XDrawString16()という、パッチ的な(?)関数で日本語を出すこともできるようですが、国際化対応版のX Window System/Xlib(X11R5以降)では推奨されません。
国際化対応版Xlibで日本語文字列を表示するには、ロケール設定とXCreateFontSet()、XmbDrawString()などを使います。
Xlibが扱う文字描画は、Xコアフォントシステムを使うため、最近のスケーラブルフォントを扱うことができません。そこで、Xftフォントシステムを使ってスケーラブルフォントで日本語文字列を表示する方法も調べてみました。
文字表示のサンプルを3種類示します。いずれも、C++言語で記述しています。
Xlibで文字列を表示する非国際化対応コードの基本形です。
#include <X11/Xlib.h> #include <unistd.h> namespace { // 定数定義 const unsigned int WIN_WIDTH = 640; const unsigned int WIN_HEIGHT = 480; const unsigned int WIN_BORDER_WIDTH = 6; const char* MESSAGE = "Welcom to Xlib Programming World!"; unsigned long getColor(Display* display, const char* colorName) { Colormap cmap = DefaultColormap(display, DefaultScreen(display)); XColor color, exact; ::XAllocNamedColor(display, cmap, colorName, &color, &exact); return color.pixel; } } int main(int argc, char* argv[]) { Display* display = ::XOpenDisplay(0); Window root = RootWindow(display, 0); unsigned int rootWidth = DisplayWidth(display, 0); unsigned int rootHeight = DisplayHeight(display, 0); // 使用するカラーを設定 unsigned long black = BlackPixel(display, 0); unsigned long white = WhitePixel(display, 0); unsigned long green = getColor(display, "green"); Window toplevel = ::XCreateSimpleWindow( display, root, (rootWidth - WIN_WIDTH)/2, (rootHeight - WIN_HEIGHT)/2, WIN_WIDTH, WIN_HEIGHT, WIN_BORDER_WIDTH, black, white ); // フォント読み込み Font font = ::XLoadFont(display, "r24"); // 文字描画用GC GC gc = XCreateGC(display, toplevel, 0, 0); ::XSetBackground(display, gc, black); ::XSetForeground(display, gc, green); ::XSetFont(display, gc, font); ::XMapWindow(display, toplevel); ::XFlush(display); ::XSelectInput(display, toplevel, ExposureMask | ButtonPressMask); while (true) { XEvent event; ::XNextEvent(display, &event); if (event.type == Expose) { ::XDrawString( display, toplevel, gc, 100, 100, MESSAGE, strlen(MESSAGE) ); } else if (event.type == ButtonPress) { break; } } ::XCloseDisplay(display); } |
フォントの指定に、XLoadFont()関数、文字列の表示にXDrawString()関数を使用しています。
ビルドは、以下の指定です。
$ g++ textdisplay.cpp -o textdisplay -lX11
国際化対応関数を使った日本語の表示です。
#include <X11/Xlib.h> #include <unistd.h> #include <iostream> namespace { // 定数定義 const unsigned int WIN_WIDTH = 640; const unsigned int WIN_HEIGHT = 480; const unsigned int WIN_BORDER_WIDTH = 6; const char* MESSAGE = "Xlibプログラミングの世界へようこそ"; unsigned long getColor(Display* display, const char* colorName) { Colormap cmap = DefaultColormap(display, DefaultScreen(display)); XColor color, exact; ::XAllocNamedColor(display, cmap, colorName, &color, &exact); return color.pixel; } } int main(int argc, char* argv[]) { if (setlocale(LC_CTYPE, "") == 0) { std::cerr << "Can't set locale" << std::endl; return 2; } if (! ::XSupportsLocale()) { std::cerr << "Current locale is not supported" << std::endl; return 3; } Display* display = ::XOpenDisplay(0); Window root = RootWindow(display, 0); unsigned int rootWidth = DisplayWidth(display, 0); unsigned int rootHeight = DisplayHeight(display, 0); // 使用するカラーを設定 unsigned long black = BlackPixel(display, 0); unsigned long white = WhitePixel(display, 0); unsigned long green = getColor(display, "green"); Window toplevel = ::XCreateSimpleWindow( display, root, (rootWidth - WIN_WIDTH)/2, (rootHeight - WIN_HEIGHT)/2, WIN_WIDTH, WIN_HEIGHT, WIN_BORDER_WIDTH, black, white ); // 文字描画用GC GC gc = XCreateGC(display, toplevel, 0, 0); ::XSetBackground(display, gc, black); ::XSetForeground(display, gc, green); int missingCount; char** missingList; char* defString; XFontSet fontSet = ::XCreateFontSet( display, "-*-fixed-medium-r-normal--16-*-*-*", &missingList, &missingCount, &defString ); if (fontSet == 0) { std::cerr << "Failed to create fontset" << std::endl; return 1; } ::XFreeStringList(missingList); ::XMapWindow(display, toplevel); ::XFlush(display); ::XSelectInput(display, toplevel, ExposureMask | ButtonPressMask); while (true) { XEvent event; ::XNextEvent(display, &event); if (event.type == Expose) { ::XmbDrawImageString( display, toplevel, fontSet, gc, 100, 100, MESSAGE, strlen(MESSAGE)); } else if (event.type == ButtonPress) { break; } } ::XCloseDisplay(display); } |
main関数の最初の部分で、ロケール指定をしています。国際化(I18N)対応では、アプリケーションのロケールを指定します。setlocale()でLC_CTYPEカテゴリを、環境変数で指定したロケールに設定します。なお、決め打ちする場合、setlocaleの第2引数に直接ロケール文字を指定します。(決め打ちしてしまうとI18N対応とは言えませんが、このサンプルのようにプログラム内に特定ロケールの文字列をハードコーディングしている場合、環境変数で他のロケールを指定しても対応できません)
次にX Window Systemがsetlocaleで指定したロケールに対応できるかをXSupportsLocale()関数で検査しています。
フォントの読み込みは、XCreateFontSet()関数で行います。
文字の表示は、XmbDrawString()関数で行います。なお、XwcDrawString()関数もXlibで提供されています。こちらは表示したい文字がwchar_t型で格納されている場合に使用します。
ビルドは、以下の指定です。
$ g++ textdisplay.cpp -o i18ntextdisplay -lX11
スケーラブルフォントを使用する場合、Xlibのみでは対応できないので、freetypeをXから利用できるようにしたXftを用います。
#include <X11/Xlib.h> #include <X11/Xft/Xft.h> #include <unistd.h> #include <iostream> namespace { // 定数定義 const unsigned int WIN_WIDTH = 640; const unsigned int WIN_HEIGHT = 480; const unsigned int WIN_BORDER_WIDTH = 6; const char* MESSAGE = "XlibとXftプログラミングの世界へようこそ"; } int main(int argc, char* argv[]) { Display* display = ::XOpenDisplay(0); Window root = RootWindow(display, 0); unsigned int rootWidth = DisplayWidth(display, 0); unsigned int rootHeight = DisplayHeight(display, 0); // 使用するカラーを設定 unsigned long black = BlackPixel(display, 0); unsigned long white = WhitePixel(display, 0); Window toplevel = ::XCreateSimpleWindow( display, root, (rootWidth - WIN_WIDTH)/2, (rootHeight - WIN_HEIGHT)/2, WIN_WIDTH, WIN_HEIGHT, WIN_BORDER_WIDTH, black, white ); XftFont* xftFont = XftFontOpen( display, 0, XFT_FAMILY, XftTypeString, "VL ゴシック", XFT_SIZE, XftTypeDouble, 24.0, 0 ); Colormap cmap = DefaultColormap(display, 0); XftColor color; ::XftColorAllocName(display, DefaultVisual(display, 0), cmap, "green", &color); XftDraw* draw = ::XftDrawCreate( display, toplevel, DefaultVisual(display, 0), cmap ); ::XMapWindow(display, toplevel); ::XFlush(display); ::XSelectInput(display, toplevel, ExposureMask | ButtonPressMask); while (true) { XEvent event; ::XNextEvent(display, &event); if (event.type == Expose) { ::XftDrawStringUtf8( draw, &color, xftFont, 10, 100, (FcChar8*)MESSAGE, strlen(MESSAGE) ); } else if (event.type == ButtonPress) { break; } } ::XftDrawDestroy(draw); ::XCloseDisplay(display); } |
フォントの指定は、Xftの関数XftFontOpen()を使用します。
色の指定も、XftColorAllocName()などを使用します。
文字の描画はXftDrawStringUtf8()を使用しますが、描画対象領域はXftDraw型で指定する必要があるため、XlibのWindow型をラップしてXftDraw型を生成するXftDrawCreate()関数を使用します。
ビルドは、以下の指定です。
なお、ソースファイルはUTF-8でエンコーディングされている必要があります。
$ g++ xfttextdisplay.cpp -o i18ntextdisplay -I/usr/include/freetype2 -lXft -lX11
CentOS 5.xの場合、libXft-develおよびfreetype-develパッケージで提供されるヘッダーファイルが必要となります。