gtk3によるマウスカーソルの変更(落とし穴と解決策)

gtk3を導入してアプリケーションを書いています。
アプリケーション上で表示するマウスカーソルのアイコンを変更しようとして、落とし穴にはまったので、原因と解決策を紹介します。
(原因、というほど大げさなものではありませんが)

(画像にアイコンが無いのは単にgnome-screenshotの仕様)


現象としては、gdk_window_set_cursor()関数を呼んでも、マウスカーソルのアイコンが変更されず、機能しません。

出現するエラーメッセージは以下。
コマンドライン実行すると確認できます。
(PhotonVector.exe:19302): Gdk-CRITICAL **: gdk_window_set_cursor: assertion 'GDK_IS_WINDOW (window)' failed

で、原因ですが、
『gtk_widget_show() または gtk_widget_show_all()を呼ぶ前に、
マウスカーソル変更のgdk_window_set_cursor()を呼ぼうとすると、
引数に必要なGdkWindowを正常に取得できていないため、変更に失敗する』
ということのようです。

Sample Source

以下がサンプルソースです。
マウスカーソルのアイコン変更が機能することを確認できます。
落とし穴である不具合は、下のマウスカーソル指定をコメントアウトして、上のコメントアウトを解除すると再現を確認できるはずです。

任意の画像ファイル(透過png矢印画像)によるアイコン

環境はUbuntu15.10-amd64です。
なお、ライブラリ導入はこちら。
sudo apt-get install libgtk-3-dev -y
Makefileにしてしまったので、ビルドコマンドは記載できませんが、
pkg-config --libs --cflags gtk+-3.0
を絡めることになるかと。良いかんじに作ってください。

サンプルソースの動作が確認できれば、あとは任意のGdkCursorを生成することで、マウスカーソルを思い通りの画像に変更することができるはずです。

===
#include <gdk/gdk.h>
#include <gtk/gtk.h>

int main (int argc, char **argv){
    gtk_init(&argc, &argv);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request (window, 300,200);

    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);

/*
    // ** error occurs (not work) use *set_cursor() before gtk_widget_show*()
    GdkWindow* win = gtk_widget_get_window(window);
    GdkCursor* cursor = gdk_cursor_new(GDK_PENCIL);
    gdk_window_set_cursor(win, cursor);
*/
    gtk_widget_show_all(window);

    GdkWindow* win = gtk_widget_get_window(window);
    GdkCursor* cursor = gdk_cursor_new(GDK_PENCIL);
    gdk_window_set_cursor(win, cursor);

    gtk_main();
return 0;
}

===

実際のアプリケーションでは以下のような感じで使っています。
====

 
GdkWindow *window = gtk_widget_get_parent_window(self->widget);
 
if(!window){
 
 
et_debug("GdkWindow not grub");
 
}else{
 
 
GdkCursor* cursor = gdk_cursor_new_from_pixbuf(
 
 
 
 
gdk_display_get_default(),
 
 
 
 
self->tools[tool_id]->icon_unfocus,
 
 
 
 
0,
 
 
 
 
0);
 
 
/*
 
 
GdkCursor* cursor = gdk_cursor_new_from_name(
 
 
 
 
gdk_display_get_default(),
 
 
 
 
"help");
 
 
*/
 
 
if(!cursor){
 
 
 
et_error("%d", tool_id);
 
 
}else{
 
 
 
gdk_window_set_cursor(window, cursor);
 
 
}
 
}

====

アイリスオーヤマ加湿器SHM-100Uレビュー (取り外し写真あり)


アイリスオーヤマ 加湿器 SHM-100U
写真は加湿器を裏側から撮ったものです。
以前から冬場は体調を崩しやすく、今年も冬が近づくにつれて、足の一部で肌荒れが出て痒くてたまりませんでした。薬を塗っても効果が薄かったため、今年は加湿器を購入することにしました。
加湿器は、効果が無かったり壊してしまったりしても良いよう、安価な加湿器の中から、 「アイリスオーヤマ 加湿器 加熱式 アロマ対応 グリーン SHM-100U
を選びました。
なお自室は、小さな鉄筋マンションの一室です。

箱を開けると加湿器は紙製の支持材に入っています。
取り出して確認すると、電源スイッチは裏側にあることがわかります。
表にあるLEDで光っているボタンは「リセットスイッチ」であるようです。

Web上のレビューによると「周辺が濡れる」という話があり、100均で適当な受け皿を買おうかとも思ったのですが、調度良いものが加湿器と一緒に入っていることに思い至りました。
(※火災の危険などあると思います。責任は持ちません。)

購入当初、裏側のリセットスイッチを使う頻度がわからなかったので、裏側の電源スイッチを表に置いて運用したら良いのではないかと思いました。今はまだそうしています。
しかし、一晩運用するなら翌朝には自動停止しており、電源スイッチではなくリセットが必要になるので、正面に置いて電源は付けっぱなしにするのが良さそうです。
推奨する使い方ではないのだと思いますが、動作させながらタンクを外して水を足すこともできます。私は慎重にやらないと周囲に水を撒いてしまいますが。

アイリスオーヤマ 加湿器 SHM-100U タンク
タンクは金属バネとゴム蓋による単純な仕組みです。台座の出っ張りからタンクの蓋を押し上げて開け、そこから重力で自然に給水する仕組みです。

アイリスオーヤマ 加湿器 SHM-100U 本体(蒸気の誘導ほか外した状態)

実際の使用時間ですが、私の環境では公称の8時間よりすこし短い時間で水タンクを使いきっているように思います。
しかし、とりあえず一晩使うには、寝る前にセットしておけば、朝起きた時にはタンクの水をを使いきって止まっているので、それはそれで便利です。
空気が乾いているかどうかは、私には体感ではわからないので、なんとも言えません。
しかし8時間弱動いていれば、9時間睡眠でもほとんどをカバーできるので、加湿器が常に動いていないと困る体質の方ということでなければ、この加湿器は良いと思います。

=======


TIGORA(ティゴラ)のトレッキング シューズ

 TIGORA(ティゴラ)の トレッキング シューズを買いました。 メインの靴がアシックスウォーキングで街歩き用なのですが、これまではこれで高尾山などの軽い山も登っていました。 今回、靴底があまりに摩耗したこともあってアシックスウォーキングを買い替えたのですが、ついでに消耗が激...