カーソル形状
ここでは、Xlib の関数も含めて、ポインタのカーソル形状について、総合的に解説していきます。
X のカーソル形状 (Cursor 型の XID) は、主に以下の関数を使って、作成することができます。
Xlib の関数では、2色+透明のカーソルしか作れません。
フルカラーにしたり、アルファ値を使いたい場合などは、Render 拡張機能を使う必要があります。
X のカーソル形状 (Cursor 型の XID) は、主に以下の関数を使って、作成することができます。
- [Xlib] XCreateFontCursor() で、X 標準で定義されているカーソルから、指定形状のカーソルを作成。
- [Xlib] XCreatePixmapCursor() で、1bit の Pixmap イメージを元に、2色+透明のカーソルを作成。
- [Render 拡張機能] XRenderCreateCursor() で、Picture イメージからカーソルを作成 (RGBA のイメージを使用可能)。
- [Render 拡張機能] XRenderCreateAnimCursor() で、複数の Cursor から、アニメーションカーソルを作成。
- Xcursor ライブラリを使って、X カーソルファイルやカーソルテーマから、カーソルを読み込む (アニメーションカーソルも含む)。
また、ARGB イメージのバッファから Cursor を作成することもできる。
Xlib の関数では、2色+透明のカーソルしか作れません。
フルカラーにしたり、アルファ値を使いたい場合などは、Render 拡張機能を使う必要があります。
Xcursor ライブラリ
Xcursor 自体は拡張機能ではなく、内部で拡張機能 (Render) を使用した、ライブラリです。
システムにインストールされているカーソルテーマから、カーソルを読み込むことができるので、あらかじめ定義された一般的なカーソルを使いたい場合は、主にこちらを使います。
ARGB イメージを元に、任意のカーソルを作成することもできます。
システムにインストールされているカーソルテーマから、カーソルを読み込むことができるので、あらかじめ定義された一般的なカーソルを使いたい場合は、主にこちらを使います。
ARGB イメージを元に、任意のカーソルを作成することもできます。
カーソル関連の関数 (Xlib)
//カーソルの解放 void XFreeCursor(Display *display, Cursor cursor); //ウィンドウ上のカーソルを変更 void XDefineCursor(Display *display, Window w, Cursor cursor); //デフォルトのカーソルに戻す void XUndefineCursor(Display *display, Window w);
XDefineCursor() でカーソルを設定すると、指定ウィンドウ上にポインタが来た時、自動でそのカーソル形状に変更されます。
XDefineCursor() で cursor を None にするか、XUndefineCursor() を行うと、デフォルトのカーソル形状 (ルートウィンドウのカーソル) に戻すことができます。
2色のカーソル
Pixmap からカーソル作成
2色+透明で、独自のカーソルを作りたい場合は、XCreatePixmapCursor() を使います。
この場合、depth = 1bit の Pixmap で、色のイメージ (前景色か背景色か) と、マスクイメージ (透明か不透明か) の、2つのイメージが必要になります。
ビットが ON で、前景色または不透明として扱われます。
前景色と背景色は、それぞれ XColor 構造体で指定することができます。
pixel 値は使われないので、red, green, blue で色を指定する必要があります。
※値は常に 0〜0xffff の範囲で指定する必要があるので、注意してください。
x, y は、ホットスポット位置です。
カーソルイメージ内で、どの位置を基準位置とするかを指定します。
ポインタの位置 = イメージ内のホットスポット位置となります。
Cursor XCreatePixmapCursor(Display *display, Pixmap source, Pixmap mask, XColor *foreground_color, XColor *background_color, unsigned int x, unsigned int y); typedef struct { unsigned long pixel; unsigned short red, green, blue; char flags; //DoRed, DoGreen, DoBlue char pad; } XColor;
この場合、depth = 1bit の Pixmap で、色のイメージ (前景色か背景色か) と、マスクイメージ (透明か不透明か) の、2つのイメージが必要になります。
ビットが ON で、前景色または不透明として扱われます。
前景色と背景色は、それぞれ XColor 構造体で指定することができます。
pixel 値は使われないので、red, green, blue で色を指定する必要があります。
※値は常に 0〜0xffff の範囲で指定する必要があるので、注意してください。
x, y は、ホットスポット位置です。
カーソルイメージ内で、どの位置を基準位置とするかを指定します。
ポインタの位置 = イメージ内のホットスポット位置となります。
1bit イメージから Pixmap 作成
なお、1bit のイメージデータを元に Pixmap を作成したい時は、XCreateBitmapFromData 関数が使えます。
d は、Pixmap を作成するスクリーンを指定することになります。ルートウィンドウなどで構いません。
data は、1bit のイメージデータです。
Y1列は 8bit 単位にします。余分なバイト余白はありません。
左上から順に、最下位ビットから上位ビットへの順で、各ピクセルが並びます。
※先頭のビットは最下位ビットであることに注意してください。(0,0) のビットが ON であれば、0x01 となります。
Pixmap XCreateBitmapFromData(Display *display, Drawable d, char *data, unsigned int width, unsigned int height);
d は、Pixmap を作成するスクリーンを指定することになります。ルートウィンドウなどで構いません。
data は、1bit のイメージデータです。
Y1列は 8bit 単位にします。余分なバイト余白はありません。
左上から順に、最下位ビットから上位ビットへの順で、各ピクセルが並びます。
※先頭のビットは最下位ビットであることに注意してください。(0,0) のビットが ON であれば、0x01 となります。
Xcursor
Xcursor は、カーソルに関する X ライブラリです。
<X11/Xcursor/Xcursor.h> のインクルードと、-lXcursor のリンクが必要です。
関数について詳しくは、Xcursor をご覧ください。
<X11/Xcursor/Xcursor.h> のインクルードと、-lXcursor のリンクが必要です。
関数について詳しくは、Xcursor をご覧ください。
ARGB カーソル
ARGB イメージで独自のカーソル (非アニメーション) を作りたい場合は、XcursorImageCreate() で XcursorImage 構造体を作成した後、pixels メンバのバッファにイメージデータをセットし、XcursorImageLoadCursor() でそれを Cursor に変換します。
//XcursorImage 構造体を確保 XcursorImage *XcursorImageCreate(int width, int height); //XcursorImage から Cursor を作成 Cursor XcursorImageLoadCursor(Display *dpy, const XcursorImage *image); //XcursorImage の破棄 void XcursorImageDestroy(XcursorImage *image);
XcursorImage 構造体
typedef struct _XcursorImage { XcursorUInt version; XcursorDim size; XcursorDim width; XcursorDim height; XcursorDim xhot; XcursorDim yhot; XcursorUInt delay; XcursorPixel *pixels; } XcursorImage;
version | イメージデータのバージョン (1) |
---|---|
size | マッチさせるカーソルサイズ |
width,height | イメージの幅と高さ |
xhot,yhot | ホットスポット位置 |
delay | アニメーション時、次のフレームまでの時間 (ms) |
pixels | イメージデータ (uint32_t *)。 0xAARRGGBB の形で、左上から順にピクセルを並べる。 |
カーソルテーマ
カーソルテーマから X カーソルファイルを読み込んで、Cursor を作成したい場合は、XcursorLibraryLoadCursor() を使います。
現在のテーマから、指定カーソルファイルを読み込んで、Cursor を返します。
アニメーションカーソルの場合は、Cursor に複数のカーソルイメージが含まれます。
現在のテーマは、XcursorGetTheme(display) で取得できます。
戻り値が NULL の場合、デフォルトのテーマ (default) が読み込まれます。
検索パスは XcursorLibraryPath() で取得できます。複数パスは ':' で区切られています。
カーソルテーマは、各検索パスのディレクトリ上に、指定テーマ名のディレクトリが存在するかどうかで検索されます。
ディレクトリが存在する場合、そのディレクトリ下の cursors ディレクトリ内に、カーソルファイルが置かれています。
なお、カーソルテーマのディレクトリ内に index.theme がある場合、そこで指定されたテーマを継承することができるので、追加として、そのテーマ内のカーソルファイルも検索されます。
複数の形状で同じファイルを使用する場合は、リンクファイルになっている場合もあります。
標準的なカーソルファイル名については、以下を参考にしてください。
>> https://www.freedesktop.org/wiki/Specifications/cursor-spec/
Cursor XcursorLibraryLoadCursor(Display *dpy, const char *file);
現在のテーマから、指定カーソルファイルを読み込んで、Cursor を返します。
アニメーションカーソルの場合は、Cursor に複数のカーソルイメージが含まれます。
現在のテーマは、XcursorGetTheme(display) で取得できます。
戻り値が NULL の場合、デフォルトのテーマ (default) が読み込まれます。
テーマの検索
カーソルテーマのデフォルトの検索パスは、以下のディレクトリです。~/.local/share/icons ~/.icons /usr/share/icons /usr/share/pixmaps
検索パスは XcursorLibraryPath() で取得できます。複数パスは ':' で区切られています。
カーソルテーマは、各検索パスのディレクトリ上に、指定テーマ名のディレクトリが存在するかどうかで検索されます。
ディレクトリが存在する場合、そのディレクトリ下の cursors ディレクトリ内に、カーソルファイルが置かれています。
なお、カーソルテーマのディレクトリ内に index.theme がある場合、そこで指定されたテーマを継承することができるので、追加として、そのテーマ内のカーソルファイルも検索されます。
カーソルファイル名
テーマ内にあるカーソルファイルは、拡張子なしで、arrow や hand などの、各形状を指定する名前になっているので、file 引数には、パスを含まないファイル名を指定します。複数の形状で同じファイルを使用する場合は、リンクファイルになっている場合もあります。
標準的なカーソルファイル名については、以下を参考にしてください。
>> https://www.freedesktop.org/wiki/Specifications/cursor-spec/
プログラム
左ボタンを押すごとに、カーソル形状を変更します。
(2色の十字カーソル → 青のグラデーションカーソル → デフォルトテーマの "wait" カーソル)
閉じるボタンで終了します。
<e06-cursor.c>
(2色の十字カーソル → 青のグラデーションカーソル → デフォルトテーマの "wait" カーソル)
閉じるボタンで終了します。
$ cc -o run e06-cursor.c util.c -lX11 -lXcursor
<e06-cursor.c>
#include <stdio.h> #include <X11/Xlib.h> #include <X11/Xcursor/Xcursor.h> #include "util.h" const unsigned char g_curimg_1bit[] = { 15,15, //width,height 7,7, //xhot, yhot //image 0xc0,0x01,0x40,0x01,0x40,0x01,0x40,0x01,0x40,0x01,0x40,0x01,0x3f,0x7e,0x01,0x40, 0x3f,0x7e,0x40,0x01,0x40,0x01,0x40,0x01,0x40,0x01,0x40,0x01,0xc0,0x01, //mask 0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xff,0x7f,0xff,0x7f, 0xff,0x7f,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01 }; /* 2色カーソル */ static Cursor _create_cursor2(Display *disp,const unsigned char *buf) { Cursor cur; Pixmap img,mask; XColor colf,colb; int w,h; w = buf[0]; h = buf[1]; //Pixmap 作成 img = XCreateBitmapFromData(disp, g_disp.root, (char *)buf + 4, w, h); mask = XCreateBitmapFromData(disp, g_disp.root, (char *)buf + 4 + ((w + 7) / 8) * h, w, h); //Cursor 作成 colf.red = colf.green = colf.blue = 0; colf.flags = DoRed | DoGreen | DoBlue; colb.red = 0xffff; colb.green = colb.blue = 0; colb.flags = DoRed | DoGreen | DoBlue; cur = XCreatePixmapCursor(disp, img, mask, &colf, &colb, buf[2], buf[3]); XFreePixmap(disp, img); XFreePixmap(disp, mask); return cur; } /* ARGB カーソル */ static Cursor _create_cursorARGB(Display *disp) { XcursorImage *img; XcursorPixel *pd; Cursor cur; int ix,iy,a; img = XcursorImageCreate(16, 8); if(!img) return 0; img->xhot = 0; img->yhot = 0; //イメージ pd = img->pixels; for(iy = 0; iy < 8; iy++) { for(ix = 0; ix < 16; ix++) { a = 255 - ix * 255 / 15; *(pd++) = ((uint32_t)a << 24) | 0x0000ff; } } //カーソルに変換 cur = XcursorImageLoadCursor(disp, img); XcursorImageDestroy(img); return cur; } int main(int argc,char **argv) { Display *disp; Window win; XEvent ev; Cursor cur[3]; int current = 0; disp = XOpenDisplay(NULL); if(!disp) return 1; set_display(disp); printf("[path] %s\n", XcursorLibraryPath()); cur[0] = _create_cursor2(disp, g_curimg_1bit); cur[1] = _create_cursorARGB(disp); cur[2] = XcursorLibraryLoadCursor(disp, "wait"); win = create_test_window2(disp, 200, 200, rgb_to_pixel(0xffffff), ButtonPress); XDefineCursor(disp, win, cur[0]); //イベント XMapWindow(disp, win); while(1) { XNextEvent(disp, &ev); if(event_quit(&ev)) break; if(ev.type == ButtonPress && ev.xbutton.button == Button1) { current++; if(current >= 3) current = 0; XDefineCursor(disp, win, cur[current]); } } XCloseDisplay(disp); return 0; }
XFIXES 拡張機能
クリップボードの説明時に XFIXES 拡張機能を使いましたが (セレクションの所有者変更時の通知イベント)、この拡張機能を使うと、ウィンドウでカーソル形状が変更された時の通知を受け取ることができます。
また、現在表示されているカーソルのイメージを取得することも出来るので、スクリーンショットを撮った後に、カーソルイメージを合成させることも出来ます。
さらに、カーソルを非表示にしたり、表示させたりすることも出来ます。
(通常の関数でも、すべて透明なイメージでカーソルを作成すれば、非表示のような状態にすることは可能です)
詳しくは、XFixes をご覧ください。
また、現在表示されているカーソルのイメージを取得することも出来るので、スクリーンショットを撮った後に、カーソルイメージを合成させることも出来ます。
さらに、カーソルを非表示にしたり、表示させたりすることも出来ます。
(通常の関数でも、すべて透明なイメージでカーソルを作成すれば、非表示のような状態にすることは可能です)
詳しくは、XFixes をご覧ください。