[ 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パッケージで提供されるヘッダーファイルが必要となります。