luaの実行環境でcairoグラフィックスを実行する際の自分宛てメモ

 

バックグラウンドで更新ルーチンを走らせておいて、メインのluaからは実際のウィジェット(グラフィクスが表示されるトップレベルウィンドウ)更新は管理しない、という仕掛けを作る。

 

ウィジェットのコンストラクタで次のように更新ルーチンをセットする。
g_timeout_add(33, (GSourceFunc)timer_test, cv);


一応(不確実ではあるが)33ミリ秒毎にtimer_testが呼び出される。
ここでタイミングを1000ミリ秒とかにすると、空き時間にluaが全力疾走するのでCPU負荷は上がる(※)。1ミリ秒とかにするとバックグラウンド側の処理が頻発するのでluaの実行は適当に間引かれる(はず)。いっぽう、バックグラウンド側の処理そのものは軽いせいか、全体でのCPU負荷は軽くなる。プログラム全体の動作としては当然遅くなる。
33ミリ秒というのは意外と長いようで、lua側の表示ルーチンだと更新される前に描画が複数回実行されてしまいチラつきが生じる場合が多い。(特にcls()を実行してから全域を書き直している場合)
 

以下はウィジェット更新をセットする関数。なお、ウィジェットが破壊(実行中、手動で閉じた。あるいは実行後luaのガベコレに回収された)される際、cv->windowはNULLされるようにしてある。
static gboolean timer_test(Luacanvas *cv)
{
    gint x, y;
    if (cv->window == NULL ) {
        return FALSE;
    }
    gtk_widget_get_size_request(cv->window, &x, &y);
    gtk_widget_queue_draw_area(cv->window, 0, 0, x, y);
    return TRUE;
}

 

 

次の関数はexpose_eventのコールバック関数から呼び出される。コールバック関数に直接書かなかったのは、当初更新を手動で行っていた時の名残り。
timer_testが動いている限り、ウィンドウが隠れていても呼び出される。
static void _show_update(Luacanvas *cv)
{
    cairo_t     *cr;
    //
    // このルーチンはウィンドウが生きている限り、timerで間接的に呼び出される
    //
    cr = gdk_cairo_create(cv->drawing_area->window);
    cairo_set_source_surface(cr, cv->surface, 0.0, 0.0);
    cairo_paint(cr);
    cairo_destroy(cr);
}

 

 

---

更新間隔内に描画処理を完結させ(終わらない場合は一旦止めて)更新完了を待って再開すべきと思うが、lua側とのタイミングのやりとりをどうするか。

 

 

※luaそのものは1スレッド分(core2duoだと1コア分)しか消費しないようだが、cairoのstorkeを繰り返し実行するとCPU負荷が跳ね上がる。負荷が上がるだけでOS上の他のタスクの切り替えは正常なので、デスクトップが重くなるような事はほとんど感じない。

 

以前、このように  gtk の国際化対応をディスりましたが、gtkはちゃんと対応していました。

 

ここにお詫び申し上げます。


メニュー構築部分を書き直さなくては、と思って関数を確認しようとgtkのリファレンスマニュアルを再読していて見つけました。
日頃の斜め読みのツケがこんなところに出てきました。


さて、その対応ですが。

 

gettextを使っている時は、次の関数を使います。

void
gtk_action_group_set_translation_domain
                               (GtkActionGroup *action_group,
                                const gchar *domain);

                               
例えばこんな感じ。

actions = gtk_action_group_new("Actions");
gtk_action_group_set_translation_domain(actions, GETTEXT_PACKAGE);

GETTEXT_PACKAGEはconfigure.ac内での定義(多分)。普通はGETTEXT_PACKAGEになっているはず。

 

 


gettextによらない場合、次の関数で定義できる。

void
gtk_action_group_set_translate_func (GtkActionGroup *action_group,
                                     GtkTranslateFunc func,
                                     gpointer data,
                                     GDestroyNotify notify);                                   

 

無事、メニューも日本語で表示されるようになりました。

 

ちょっとしたデータ処理に便利なsqlite3をluaから使うためのライブラリがありました。

 

http://lua.sqlite.org/index.cgi/home

 

lua5.xでtest済のようです。結構昔からあるらしい。

 


早速、室内サーバ上のデータにアクセスしてみました。これはwifi付きのセンサーから3分毎に飛んでくるデータを日々記録しているものです。サーバ上ではpythonで書いたcgiが管理しています。


 

 

実行中の様子。db:nrows()はSQLで問い合わせた結果をイテレータで返します。内容はluaのテーブルですので、デバッガで一目瞭然です。

 

便利だな(自画自賛)。

 

慣れないライブラリを使いこなすには相当時間がかかるものですが、デバッガが使えると少しは早くなるかも知れません。

NHKラジオのインターネット配信らじるらじるが、いつの間にかプロトコル変更されていて、以前からあったスクリプトで再生できなくなっていました。

 

ネット配信が始まった当初は興味本位からいろいろ作ったこともあったのですが、最近は使わなくなって放置していました。
というのも、基本的にラジオは自室内でしか聞かないし、その室内には既成品・自作品合わせて8台もあるので聞くなら昔ながらの無線受信で間に合っているからです。

 

しかし、先日ネットを漁っていて偶然にも変更されたプロトコルについての記事を見つけたので、少し弄ってみることにしました。
変更後のプロトコル(正しくは配信形式)はHLS(HTTP Live Stream)というもの。送信側は配信するファイルを細切れにして順序良く送り出し、受信側はその順序をまとめたファイル(M3U,要するにプレイリスト)を見て受信する、というものらしいです。

 

HTTP(勿論、今はHTTPS)なので既存のメディアプレイヤーの多くが対応しているはずですから、片っ端から試してみました。
尚、OSは常用しているLinuxMint17(の最新ポイントリリース。正しい番号を忘れました)です。なのでアプリケーションのバージョンが少し旧いかも知れません。

 

mplayer 1.3.0
LinuxMint17のリポジトリにあるものではなく、かつて自分でビルドしたものです。何故かhttpsを弾いたので今回ビルドしなおして試しました。受信時のバッファが浅いのか、遷移時に音切れします。国会中継は問題ないですが音楽番組を聞こうとすると実用になりません。

 

ビデオ
LinuxMint17にあるアプリケーションで、中身は確かmplayerだったと思います。だからなのか、上記mplayerと同じ症状がでます。こちらはGUIがある分処理が増えて音切れ時間が長くなり、何を聞くにも実用になりません。まあ、OSのオマケなのでこんなものでしょう。

 

VLC 2.1.6 Rincewind
ブラウザで聞くのと同じように聞こえます。流石ですな。

 

Audacious 3.9
M3Uを一度たどって終わりました。ループ再生しません。他にプラグインが必要なのか?(探してないけど)

 

Banshee 2.6.2
LinuxMint17における標準オーディオプレイヤー。音飛び、なんてものではありません。国会中継が夜のニュースでのダイジェスト版のようになって聞こえます。酷すぎる。

 

Mopidy 2.1.0
MPD互換のデーモンです。LinuxMint17のリポジトリにあるものではなく、自分でインストールしたものです。Spotifyにもプラグイン経由でアクセスできるので使っているのですが、直接M3Uを渡すだけではダメのようです。

 

 

というわけで LinuxMint17ではVLC一択という結果になりました。ニュースとかであればmplayerでも辛うじていけるので取り敢えずスクリプトにはしましたが、まあ、使うことは無いでしょうね。

 

自分宛てメモ。

 

Gtk2.0のpopupメニューの使い方について、gnomeのサイト(https://developer.gnome.org/gtk2/stable/gtk-migrating-checklist.html#checklist-popup-menu)には、次のようなサンプルコードが掲げられている。

 

static void
do_popup_menu (GtkWidget *my_widget, GdkEventButton *event)
{
  GtkWidget *menu;
  int button, event_time;

  menu = gtk_menu_new ();
  g_signal_connect (menu, "deactivate",
                    G_CALLBACK (gtk_widget_destroy), NULL);

  /* ... add menu items ... */

  if (event)
    {
      button = event->button;
      event_time = event->time;
    }
  else
    {
      button = 0;
      event_time = gtk_get_current_event_time ();
    }

  gtk_menu_attach_to_widget (GTK_MENU (menu), my_widget, NULL);
  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
                  button, event_time);
}

 

このmenuにmenuitemを追加し、menuitemのactivateに実際の処理を書いたコールバック関数を接続したのだが、動かしてみると実行されない。コールバック関数そのものが呼び出されなかったので、activateされていないと思われる。


試しに、menuの"deactivate"を無効にしたところ動作するようになった。

menuitemの選択と同時にdestroyされたのだろうか。であれば、menuitemがactivateされないのも理解できる。
追加したmenuitemの数が1つだけだったのがいけなかったのだろうか。

 

今回はmenuを動的に生成する必要もなかったので、destroyは実装しないことにした。

leafpadを改変してluaの実行環境を作る話の続き。

 

ようやく上位値を監視できるようになりました。いやはや、長かったなあ。

 

glib,gobjectにおけるオブジェクトの管理(freeやunrefなど)に加えて、luaのスタックの管理もあってプログラムは長く、且つスパゲッティ状態になる一方でした。

 

バグ潜在の可能性はいつまでも残るのですが、しばらく使って様子を見ようと思います(何度目かしらん)。

 

 

監視している変数の値の変更はできない。今までその手の機能を(機械語モニタ以外では)使ったことがほとんどないので。ソースを直した方が早い、と思う。

 

スクリーンショットにバグがあったので差し替えました(2月2日22時28分)

 

leafpadを改変してluaの実行環境を作る話の続き。

 

ブレークポイントで変数を表示できるようになりました。未だに上位値を扱えませんが。

それとgtktreeviewの使いにくさは本当に酷いです。

 

 

 

関数についてはソースから持ってこようか、とも思ったのですがどうせ1行目くらいしか表示できないし、1行目はほぼ必ず「function XXX(」なので見ても余り意味がないので見送りました。

 

テーブルも各フィールドごとに再帰表示できます。また、luaの標準ライブラリの関数も見ることができます。

 

自分宛てメモ

 

任意のスタック要素をスタックトップに置くには lua_push*() を使う。

但し、luaの関数を直接積むAPIは(多分)無いので、代わりに lua_pushvalue() を使う。

 

専用のAPIが用意されていないのは、「C側でluaの関数を作成して返す」というのが実用上余り意味がない、ことによるのかも。

 

今回はデバッガ作成にあたり、変数やテーブル要素になった関数を調べる必要があり、それにはlua_getinfo()を呼ばなくてはならなかったので。

 

 

lua5.1における、lua_push*()の皆さん。内部で使用するintegerまでそのまま積めるのに、functionだけは積めない。

 

自分宛てメモ

 

1)

lua_getinfo は呼ぶ前に調べる関数をスタックに積まなくてはならないが、呼び出し後はスタックを1レベル消費する。故に呼び出し後の相殺を考慮する必要はない。

 

2)

インデックスを用いてスタックトップに要素を積むとする。
スタック、というくらいなので、任意の要素をトップに複写するのだろう位に考えていた。
しかし、単純に指定したインデックスのところまで、要素を捨てているようだ。

 

スタックのn番目にある要素を調べようとするコード。

 

lua_pushnumber(L,12345);
lua_settop(L,n);
lua_getinfo(L,">S",&ar);
g_debug("%f",lua_tonumber(L,-1));

 

lua_gettop(L)でスタックの長さを測っても、短くなっている。

何か解釈が間違っているかも知れない。

探すと意外に見つからなかったので。

 

余りに初歩的というか基本的なことなので、記事にする人が少ないだけと思いますがハマりどころはちゃんとあります。

 

コンテキストメニューの呼び出し(マウス右クリックでポップアップされる奴)の際、ポップアップ直前に "populate_popup" というイベントが発生します。このイベントにコールバック関数をアタッチして必要な処理を行う訳です。

このコールバック関数はvoidで値を戻しません。このため既定の処理を完全に無くしてしまうことが出来ないと思います(可能な場合はgbooleanを返すようになっている)。

 

コールバック関数cb_populate_popupは引数にオブジェクトとポップアップするメニュー(以下menu)を引きます。

このmenuをgtk_widget_destroyで破壊すれば、デフォルトのコンテキストメニューを抑止できます。破壊してから自前で用意したものに差し替えれば、それを呼び出すことも可能です。マウスのボタンイベントやキーボードのキープレスイベントをチェックするよりスマートかも知れません。


しかしこのイベントは、デフォルトのコンテキストメニューを拡張するのが一番有用な使い方ではなかろうか、と思います。

 

拡張方法は簡単で、引数にあるmenuにmenuitemを付け加えるだけです。勿論、付け加えたmenuitemのコールバック関数(シグナルはactivate)は自分で書かなくてはなりません。

 

そして、ここに冒頭に書いた落とし穴がありました。

 

それは、gtk_widget_show_all(menu) を呼ばないと拡張した分が表示されない、というもの。

 

よく考えれば気づきそうなものなのですが、ここでの処理の後ポップアップの表示へと流れていくので、表示関係の細かいところは当然やってくれるものと思い込んでいたのでした。

 

さて、今回弄ったのはGtkSourceViewのpopulate_popupでした。GtkSourceViewはGtkTextViewの派生widgetであり、populate_popupもtextviewのそれのままです。

 

そのせいか、追加したmenuitemがtextviewのデフォルト部分の直上に置かれており、その上にSourceViewで加わった、undo/redo部分が乗る格好になってしまっています。
回避方法はあるのかも知れませんが、今回は追求しませんでした。GtkSourceView3.0では修正されているのかも知れません。

 

実行中の様子。新規に追加した "Add watch"が中程に挟まってしまっている。

 

 

コールバック関数の例。引数viewがGtkTextViewになっていますが、実際はGtkSourceViewです。


static void cb_populate_popup(GtkTextView *view, GtkMenu *menu,
                                                    gpointer data)
{
    GtkWidget *item;
    item = gtk_menu_item_new_with_mnemonic(_("_Add watch"));
    g_signal_connect(G_OBJECT(item), "activate",
        G_CALLBACK(cb_activate_add_watch), view);
    gtk_menu_shell_prepend(GTK_MENU_SHELL(menu),
                                    gtk_separator_menu_item_new());
    gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item);
    gtk_widget_show_all(menu);
}

 

 

 

コネクトはこんな感じ。

// コンテキストメニューの拡張
g_signal_connect(G_OBJECT(view), "populate-popup",
    G_CALLBACK(cb_populate_popup), NULL );

 


Search

Calendar

S M T W T F S
    123
45678910
11121314151617
18192021222324
25262728   
<< February 2018 >>

Archive

Mobile

qrcode

Selected Entry

Link

Profile

Search

Other

Powered

無料ブログ作成サービス JUGEM