2016年9月2日金曜日

組み込みJavaScriptエンジン Duktape 非公式日本語訳(途中)









Cアプリケーションに組み込める、フリーのJavaScriptエンジンを探していて、Duktapeというプロジェクトが見つかり、興味があったのでドキュメントを読んでいました。
以下は、途中までですが、読みながら翻訳していたものを公開します。



 == 以下、翻訳版注意書き ==





この文章は、アプリケーション組み込み向けEcmascript(Javascrip)エンジンであるDuktapeのドキュメントの非公式日本語訳です。
この翻訳は、自分がDuktapeについて把握することを目的として行ったものです。全体を通して翻訳が雑です。というか誤訳を含みます。翻訳ミスしても実際のサンプルコードと自作コードで確認すれば気づくだろう、だからいいや、というレベルです。訳しづらかったところを飛ばしていたりもします。翻訳ミス前提で読んでください。
日本語としてメチャクチャなので、頭の中で再度訳しながら読むことになるかと思います。
ライセンスは原文に準じます。原文が許す限り、新しい翻訳のベースなど自由に使っていただいて構いません。とはいえ、新規に訳したほうが早いでしょう。
指摘・修正はメール(michinari.nukazawa@gmail.com)またはtwitterに頂けると嬉しく思います。

原文
http://duktape.org/guide.html


== 以下、翻訳文 ==

version 1.4.0 (2016-01-10)

## このドキュメントの範囲

このガイドの内容は、あなたのプログラムでDuktapeを使いはじめるためのイントロダクションです。あなたが基礎知識をすでに持っているならば、わかりやすい<a href>API reference</a>にてAPI構成を一覧できます。<a href>Duktape Wiki</a>の内容は、さらなる概要と使用例、ベストプラクティスです。

このドキュメントはDuktapeの内部構造について扱いません。(もしあなたが我々と共にDuktapeを弄ることに興味があるようならば、Duktape repoをご覧ください。)

## Duktapeとは何か?

Duktapeは、移植性とコンパクトなフットプリントに注力した、組み込み可能なEcmascript E5/E5.1エンジンです。Duktapeにより、あなたのC/C++プログラムへ、スクリプティング機能を簡単に拡張することができます。あなたはEcmascriptの中へ、プログラムの中心的な制御フローを作ることができ、同時に高速動作するC関数を使い、重い処理を取り扱うことができます。

(訳注:適当訳)EcmascriptとJavascriptは、大まかに等価ですが、ブラウザ向け拡張などが付いている場合もあって等しくはありません。Duktapeも例外ではなく、よく使われるprint(),alart()を持っています。なので、我々はEcmascriptの全体を実装したと言い表しています。

## Conformance
 (略)
## Features
 (略)
## Goals
 (略)

## ドキュメント構成

<a href>Getting started</a>は、あなたがダウンロード、コンパイル、そしてDuktapeをあなたのプログラムに導入するまでを取り扱っています。これの内容は確固とした例によって、あなたがどのようにあなたのプログラムにスクリプティング機能を導入するかを含んでいます。
<Programming Model><Stack types><C types>で語られているのは、Duktapeヒープのコアコンセプト, context, value stacks, Duktape API, Duktape C関数 です。Dukatapeのスタック構造とC型システムのラッパーについて概要を語っています。

Duktapeが実装しているEcmascript機能についていくつかのセクションで語っています: Type algorithms (for custom types), Duktape built-ins (additional built-ins), Ecmascript E6 features (features borrowed from ES6), Custom behavior (behavior differing from standard), Custom JSON formats, Custom directives, Buffer objects, Error objects (properties and traceback support), Function objects (properties), Debugger, Modules, Logging, Finalization, Coroutines, Virtual properties, Internal properties, Bytecode dump/load, Threading, Sandboxing.

<Performance>では(略)

<Comparison to Lua>では(略)

## Getting started
(略)
## Downloading
(略)
## Command line tool
(略)

## Integrating Duktape into your program

コマンドラインツールは、簡単なサンプルプログラムでDuktapeの組み込み方法を示しています。
組み込んでDuktapeをあなたのプログラムで使う方法はシンプルです:duktape.c, duktape.h, and duk_config.hを追加してビルドし、Duktape APIをあなたのプログラムのどこかから呼ぶ。

Duktapeの配布物に含まれるとても単純なサンプルプログラムの、hello.cがそれを示します。コンパイルとテストをこのプログラムで試すには():

The distributable contains a very simple example program, hello.c, which illustrates this process. Compile the test program e.g. as (<Compiling>の compiler optionも参照してください):
(サンプル略)
テストプログラムは、Duktape contextを作り、任意のEcmascriptコードを実行します:
(サンプル略)

Duktapeは組み込み可能なエンジンであるため、あなたのプログラムの基本のコントロールフローを変更する必要はありません。一般的な使い方は:
・プログラムを起動した際に、Duktape contextを作る(または、スクリプティングが必要となったタイミングで作る)。(訳注:適当訳)通常は、あなたのスクリプトがロードされる間に初期化が終わり、必要なタイミングでそれを実行することができます。
・一意な箇所があなたのプログラムの中にあり、そこでscriptを実行する、またscript内の関数をそこに挿入して呼ぶ。
・script内関数を呼ぶ。最初にpushして呼び出した引数はDuktapeコンテキストのvalue stacksに入れられてDuktape APIから使われる。これは、Duktape APIが実際に呼び出された際に使われる。
・ひとつのscriptを最後まで実行する。戻り値を渡す方法を制御でき、あなたのプログラムへ(Duktape API呼び出しの)戻り値として渡すか、Duktapeコンテキストのvalue stackに置くことができる。Cコードはこれらの戻り値をDuktape APIを通して使うことができる。

単純なサンプルプログラムを見てみましょう。プログラムはstdinを使った行の読み込みをCのmainloop内で行い、呼び出したEcmascriptの助けにより行文字列を変換して、結果を出力します。

スクリプトコードは"process.js"です。サンプルの文字列変換関数は、プレーンテキストの行文字列をHTMLに変換し、アスタリスク'*'で囲まれた文字を太字(bold)にします。
(サンプルコード略)
Cコードは"processlines.c"で、Duktapeコンテキストを初期化し、scriptを実行し、スクリプトが変換する処理対象の行文字列をstdinから読み込み、processLine()関数を行文字列ごとに実行します。
(サンプルコード略)

サンプルコード内のDuktape機能呼び出し箇所を、部分ごとに見て行きましょう。(適当訳)さらなる情報が欲しいならば、<Programming model>に書いてあります。
(サンプルコード略)
まずDuktapeコンテキストを生成します。コンテキストはEcmascriptの変数を変換し、value staskに入出力(pushing,popping)します。以降のDuktapeAPI動作の呼び出しではvalue stackが付き、入出力(pushing,popping)と実行がスタック上で行われます。製品コードには、望むならば、duk_create_heap()によってfatal error handlerをセットすることができます。<Error handling>を参照することで、エラーハンドリングのベストプラクティスについての議論を見ることができます。
(サンプルコード略)
評価(eval)呼び出しで"process.js"ファイルを読み込み、それがスクリプトをコンパイルし実行します。scriptに登録されているprocessLine()関数はEcmascriptグローバルオブジェクトへ、後で使うために格納されます。実行の際、スクリプトエラー・シンタックスエラーはハンドリングされ、fatal errorはハンドリングされません。もしエラーが起これば、エラーメッセージは強制的に安全に使うduk_safe_to_string()を通して提供され、それ以上に深刻なエラーにはなりません。実行結果の文字列は強制的にconst char * (ポインタ)に変換されるためリードオンリで、NULL終端のUTF-8エンコーディング文字列であるため、printfで直接使うことができます。
(サンプルコード略)
1行目は、Ecmascriptグローバルオブジェクトをvalue staskにpushしています。2行目はグローバルオブジェクトからprocessLineプロパティを探索しています("process.js"スクリプトファイル内に定義されている)。引数の-1はvalue stackのインデックスで、マイナス値はスタックを先頭から始め、よって-1はグローバルオブジェクトを一番先頭にスタックします。
(サンプルコード略)
Pushして行文字列が入っている文字列ポインタをvalue stackに入れます。文字列長は自動で、NULL終端で測られます(strlen()のように)。Duktapeは文字列のコピーを作成してstackにpushしますので、この関数の呼び出し後に文字列バッファを開放・書き込みしても構いません。
(サンプルコード略)
ここでvalue stackに入っているものは:グローバルオブジェクト,processLine関数,行文字列。
duk_pcall()メソッドは関数を実行し、指定された数の引数をvalue stack上から与え、それらを関数,引数,戻り値に置き換えます。ここで実行後のvalue stackに入っているものは:グローバルオブジェクト,実行結果 です。実行時のエラーは保護されており、取得してprint出力できます。duk_safe_to_string() APIを呼び出して安全エラーを表示することができます。最終的に、結果(またはエラー)がvalue stackからpopされます。
(サンプルコード略)
Duktapeコンテキストを破棄し、コンテキストのすべてのリソースを開放します。この呼び出しはvalue stackとその上の参照をすべて開放します。このサンプルではvalue stackの左端にglobal objectが入っているというくわだてです。これは問題ありません:メモリリークは起こりません、もしheapを破棄する際にvalue stackが空でも。

コンパイルは次のように。
(サンプルコード略)
テスト実行(process.jsがカレントディレクトリに必要)
(サンプルコード略)

## EcmascriptからCコードを呼び出す(Duktape/C bindings)

この導入サンプルでは、どのようにEcmascriptからCコードを呼び出して実行するか、難しくないCコードをEcmascriptから簡単に呼び出す方法を示します。

Ecmascriptは例にもれずしばしば、逆のシチュエーションとして中からCコードを呼び出したくなります。事例としては、スクリプト内で何らかの処理をとても多く実行する場合や、最適化されない低レベルbyte操作や文字列操作です。最適化されたCヘルパが呼び出し可能であり、あなたはすばらしいEcmascript言語の中で、あなたのスクリプトのロジックを記述して、その中で性能要求がクリティカルな部分をCコード呼び出しにすることができます。別の推測では、ネイティブなライブラリへのアクセスに使われるでしょう。

あなたが書くネイティブC関数の実装は標準的なC関数を、特殊な呼び出し規約の Duktape/C バインディングに適合させたものです。Duktape/C 関数は1つの引数を持ち、Duktapeコンテキストと1つの戻り値としてエラーまたは数値戻り値を持ちます。関数が呼び出し時の引数を得たり戻り値をセットする際にはDuktapeコンテキストのvalue stackに対して、Duktape APIを用いてそれを操作します。我々がさらに深くDuktape/CバインディングとDuktapeAPIを見るのはもう少し後にしましょう。サンプルは:
(サンプルコード略)
行ごとに見ていきます:
(サンプルコード略)
value stackのインデックス0番(スタックの先頭から、関数呼び出し時に指定された最初の引数)の内容が数値(number)であるか確認しています、この場合はこれは数値(number)です;もしそうでなかったなら、errorを投げて、値を返しません。もし内容が数値(number)であるならば、その数値をdouble型で返します。
(サンプルコード略)
引数を2乗して、value stackにプッシュします。
(サンプルコード略)
関数呼び出しからreturnして、これ(訳注:return 1)が指し示すものは(1つの)戻り値がvalue stackの先頭に存在することです。あなたはreturn 0を返すこともでき、これは戻り値が無いことを示します(DuktapeのデフォルトではEcmascriptのundefinedを返す)。負の値の戻り値は、errorが発生し、自動的に投げられたことを示す:これはエラーの投げ方としては単純なものです。なお、あなたがstackから値をポップして返すことを必要としないならば、Duktapeはあなたに関数からのreturnを自動で提供することができます。Programming modelにて詳細を参照ください。

我々の素数判定コードはネイティブコードによりEcmascriptで記述してあるアルゴリズムを高速化することができます。さらにテストプログラム側では1000000以下から素数を探すことに加えて値の下位が'9999'で終わる数値を探します。Ecmascript版プログラムは以下の通り:
(サンプルコード略)
雑記として、このプログラムはネイティブ関数を呼びますが、もしそれが使えない場合にはEcmascript版にフォールバックします。これにより、Ecmascriptコードは(訳注:今回のサンプルプログラム以外の)他のプログラムからも使用可能になります。一方で、もしこの素数判定プログラムを他のプラットフォームへ移植する場合でもネイティブコードは再コンパイル以外の変更は必要なく、ヘルパ関数を移植するまで遅いだけです。このプログラムの場合、ネイティブなヘルパ関数を検出するのはスクリプトを読み込む際です。あなたは一方でヘルパ関数の検出を任意のタイミングにフレキシブルに変更することができます。

ネイティブヘルパ関数には機能としてprimeCheckEcmascriptと等価なものを素直に実装しました。そこにmainを追加した"primecheck.c"は以下の通りです:
(サンプルコード略)
新しい関数呼び出しがあるので、行毎に見ていきます:
(サンプルコード略)
この2行の呼び出しは2引数をネイティブヘルパ関数が要求するためです。もし値がEcmascriptの数値型ではなかった場合、エラーを投げます。もしこれらが数値であった場合、数値を整数に変換し、それぞれローカル変数のval,limに格納します。index 0が第一引数でindex 1が第二引数です。

技術的には、duk_require_int()が返すのはduk_int_t型です;これは間接的にintへ変換されますがそれはintの長さが16bitまでしかない環境では問題が発生します。一般的なアプリケーションであれば、あなたはこれを心配する必要はありません。さらなる詳細はC typesを参照ください。
(サンプルコード略)
value stackにEcmascript falseをプッシュします。Cコードからreturn 1した場合、呼び出し元のEcmascript側ではこれを戻り値falseが返されたと解釈します。
(サンプルコード略)
1行目は、この後に、Ecmascriptグローバルオブジェクトをvalue stackにpushしています。2行目は、Ecmascript関数オブジェクトを作成し、それをvalue stackにpushしています。関数オブジェクトは、Duktype/C関数であるnative_prime_checkをバインドしてます:この時にEcmascript関数を、Ecmascript側から呼ぶために作成することで、C関数を呼び出せるようになります。第二引数の(2)は、このC関数が呼び出される際にvalue stackに必要な引数の数を示しています。もし呼び出し元がこれより少ない引数で関数を呼び出した場合、足りない引数はundefinedで埋められます; もし呼び出し元がこれより多い引数で関数を呼び出した場合、余った引数は自動で無視されます。最後の3行目で実行されていることは、関数オブジェクトをグローバルオブジェクトに格納しつつ、"primeCheckNative"という名前を付けながら関数オブジェクトをスタックからpopして取り出しています。
(サンプルコード略)
このときvalue stackにはすでにglobal objectがスタックの先頭に格納されています。1行目はprimeTest関数をglobal objectから探索しています(ロード済みのスクリプト内で定義されています)。2-4行目は、primeTest関数を引数無しで呼び出し、保護されたエラーが起こっていないかチェックしています。5行目は、実行結果をpopしてスタックから取り除いています; ここで戻り値は必要でないため。

コンパイル方法は以下:
(サンプルコード略)
実行します("prime.js"がカレントディレクトリに必要です):
(サンプルコード略)
さらに素数チェックに必要な時間を測って、スピードアップがEcmascriptと比べてどの程度有効か見てみましょう。あなたがこれを試すには"prime.js"を編集してネイティブヘルパ関数を無効にします。
(サンプルコード略)
再コンパイルして再実行すると:
(サンプルコード略)

## プログラミング・モデル

## 概観

Duktapeによるプログラミングは直裁的には以下の通りです。
・Duktapeソースコード(duktape.c)とヘッダ(duktape.hとduk_config.h)をあなたのビルドに追加
・Duktapeのheap(ガベージコレクション領域)を作成し、context(スレッドハンドラ的なもの)を初期化することを、あなたのプログラム内で行う
・必要なEcmascriptスクリプトファイルを読み込み、Duktape/C関数を準備しておく。Duktape/C関数はC関数をEcmascriptコード側から呼び出すことで、妥当な性能を出すことやネイティブライブラリへのアクセスを提供する。
・Duktape APIを用いて、Ecmasccript関数を妥当なタイミングで呼び出す。Duktape APIは関数と変数へのアクセスを提供する。変数はC変数型とDuktape内部形式(Ecmascript互換)を相互変換することができる。
・Duktape APIは一方で、Duktape/C関数(をEcmascriptから呼び出した)から、関数呼び出し時の引数および、戻り値へアクセスする方法を提供する。

これらすべてのステップを見ることが、我々の提供するさらなる情報にアクセスするのに必要です。

Heapとコンテキスト
Duktape heapは、ガベージコレクションを提供する単一の領域です。heapは文字列のために確保される領域、Ecmascriptオブジェクトやその他の変数に必要な領域、ガベージコレクション情報などに使われます。heap上のオブジェクトは、heapの先頭に持っている管理に必要な情報により、リファレンスカウント、mark-and-sweepガベージコレクション、オブジェクトのfinalizationなどを行います。Heapオブジェクトはリファレンスなどにより、ガーベージコレクションから見た到達可能性グラフ(reachability graph from a garbace collection perspective)を作ります。インスタンスに対しては、Ecmascriptオブジェクトへの参照を、キーと値によるオブジェクトとプロパティのセットにより提供します。あなたは複数のheapを持つことができ、オブジェクトは異なるheap上に直接存在することができません; (適当訳)あなたは異なるheap間では値をserializationしなければなりません。

Duktapeコンテキストは、Ecmascriptの"実行スレッド"としてDuktape heap内に生成されます。コンテキストはduk_context *によりDuktape APIから参照可能で、Duktape内部のコルーチン(a form of a co-operation thread)により組織化されます。いくつかのコンテキストを、共通のglobal objectを(訳注:ベースの?)環境として組織化することができます; コンテキストは任意のglobal objectを共有することができる一方で、異なる環境を持つことができます。コンテキストハンドルはほとんどのDuktape API呼び出しが要求し、そこからDuktapeコルーチンでvalue stackへアクセスします: 値の挿入と探索、関数の呼び出し、など。

コルーチンたちはcall stackにより実行を制御され、関数呼び出しの追跡、ネイティブまたはEcmascriptの分類、などをEcmascriptエンジン内で行います。(適当訳)コルーチンたちは一方でvalue stackを持ち、すべてのコルーチンのアクティブなコールスタックをstoreします。


== 以下、翻訳未終了 ==


2016年8月6日土曜日

テザリング状態のUbuntuでaptできない原因と対処

結論としては、『テザリングで使うIPS+Proxy接続(また仮想マシンのNATなど)』の組み合わせにより、Ubuntu環境でaptが使えなくなる場合があるようです。








対処方法としては、Proxy接続を通さないで直接インターネット接続させることで、aptを使えるようになりました。
Ubuntu,aptの設定変更で済ませたかったところですが、仕方ありません。

現象


apt-get update が、以下のエラーメッセージを表示して失敗します。
===
InRelease: クリアサインされたファイルが有効ではなく、'NODATA' を得ました (認証にネットワークが必要?)
===

英語だと以下になります。
===
Get:1 http://extras.ubuntu.com trusty InRelease                               
100% [1 InRelease gpgv 3650 B]Splitting up /var/lib/apt/lists/partial/extras.ubuIgn http://extras.ubuntu.com trusty InReleaseta and signature failed
E: GPG error: http://extras.ubuntu.com trusty InRelease: Clearsigned file isn't valid, got 'NODATA' (does the network require authentication?)

===

fix-missingも何も効かないので焦りました。

対処


ppaリポジトリを追加しようとしてこの現象が起こったので、最初はaptの設定に原因があるかとも考えました。

http://askubuntu.com/questions/474549/got-nodata-issue-nodata-does-the-network-require-authentication

最初は、このページに書かれている、『/var/lib/apt/lists以下を再作成』などの設定作業による回避を模索していました。
過去の経験からaptが壊れた場合、aptの設定(/var/apt/source.list.d/ 以下など)をクリアすれば解決することを知っていたからですが、余計な寄り道になってしまいました。

=====
Your mobile broadband provider is running a faulty transparent proxy. The URL http://extras.ubuntu.com/ubuntu/dists/trusty/InRelease should return "404 Not Found". Instead it returns "200 OK" but sends 404 content. Since the URL is "OK",
(以下略)
=====

とのことでしたので、仮想マシン(VirtualBox)のNAT経由ではなく、ゲストへのUSBブリッジ接続により、直接ゲストからUSBテザリングしました。
これで、aptが使えるようになったことを確認しました。

また、これも経験的にそうだろうと思ってアタリだったのですが、Ubuntuゲスト側でNAT接続を切らないとネットに繋がらないようです。
また、繋がらない場合にpingを打つと(ex. ping www.google.com など)繋がりはじめる場合がある気がします。こちらは検証していませんが。

2016年3月2日水曜日

Gtk3でダイアログにWidgetを追加する



Gtk3で書いているアプリケーションで、カスタムダイアログが必要となった際の記録です。

最初にGtkDialogを使ったときに、 ダイアログ内に追加したWidgetが表示されなかったため、少し時間を取られたので記事にしています。

SpinButtonを追加したGtkDialog

Gtk3でダイアログボックスを使用するGtkDialogにWidgetを追加する場合、まずダイアログから、ダイアログ内のコンテナウィジェットへのポインタを取り出します。
GtkWidget *    gtk_dialog_get_content_area ()
によって取得したコンテナにGtkBoxなどのWidgetを追加していくことで、ダイアログをカスタマイズします。

また、
GtkWidget *    gtk_dialog_get_action_area ()
もありますが、こちらはdeprecated (廃止予定に非推奨)となっています。

そして、ダイアログ内へ追加したWidgetを、gtk_dialog_run() したときに表示するには、
gtk_widget_show_all(dialog);
を実行しておきます。
GtkWindowに対して使うことが多いと思いますが、dialogにも有効でした。

ソースコード

作成中のプログラムからそのままコピーしてきたコードなので、オリジナル関数(et_error())などが入っていますが上手くやってください。



 
GtkWidget *dialog;
 
GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
 
dialog = gtk_dialog_new_with_buttons ("New Document",
 
 
 
NULL,
 
 
 
flags,
 
 
 
"_OK",
 
 
 
GTK_RESPONSE_ACCEPT,
 
 
 
"_Cancel",
 
 
 
GTK_RESPONSE_REJECT,
 
 
 
NULL);

 
GtkWidget *hbox_w = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
 
GtkWidget *hbox_h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
 
GtkWidget *spin_w = gtk_spin_button_new_with_range(0, 20000, 100);
 
GtkWidget *spin_h = gtk_spin_button_new_with_range(0, 20000, 100);
 
GtkWidget *label_w = gtk_label_new_with_mnemonic("width ");
 
GtkWidget *label_h = gtk_label_new_with_mnemonic("height");
 
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_w), 1200);
 
gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_h), 600);
 
GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 
gtk_box_pack_start(GTK_BOX(hbox_w), label_w, true, true, 0);
 
gtk_box_pack_start(GTK_BOX(hbox_h), label_h, true, true, 1);
 
gtk_box_pack_end(GTK_BOX(hbox_w), spin_w, true, true, 0);
 
gtk_box_pack_end(GTK_BOX(hbox_h), spin_h, true, true, 1);
 
gtk_box_pack_start(GTK_BOX(content), hbox_w, true, true, 0);
 
gtk_box_pack_start(GTK_BOX(content), hbox_h, true, true, 1);
 
gtk_widget_show_all(dialog);

 
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
 
switch (result)
 
{
 
 
case GTK_RESPONSE_ACCEPT:
 
 
 
{
 
 
 
 
vg->rect.w = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_w));
 
 
 
 
vg->rect.h = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_h));
 
 
 
 
et_debug("size:%f,%f,%f,%f\n",
 
 
 
 
 
vg->rect.x, vg->rect.y, vg->rect.w, vg->rect.h);
 
 
 
}
 
 
 
break;
 
 
default:
 
 
 
et_debug("Cancel\n");
 
 
 
break;
 
}
 
gtk_widget_destroy (dialog);

 
return false;


以上です。

2016年2月28日日曜日

JavaScript enginたちのコンパイル(2015)

2015年版の情報ですが、SpiderMonkeyとv8のビルド手順を確認した際のメモです。
勿体無いので一応公開しておきます。


 SpiderMonkeyの取得とビルド



公式の手順にあるビルドシステムのブートストラップを実行します。
wget -O bootstrap.py https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py && python bootstrap.py

下記の返答を 要求されたので「1」を答えました。
その後で依存パッケージ導入のために管理者権限を要求されます。
Please choose the version of Firefox you want to build:
1. Firefox for Desktop
2. Firefox for Android
Your choice:

SpiderMonkeyの環境構築はV8と違って、ブートストラップの最後にgitリポジトリの案内まで表示してくれる親切さです。(公式ダウンロードリポジトリ案内を読むまでもなかった。)


Gitリポジトリ取得は、履歴の長さ故かとても時間がかかります。
git clone https://git.mozilla.org/integration/gecko-dev.git

わたしは一晩放置しました。
最新ソースだけ欲しい場合は、単に最新ソースツリーを指定して取得したほうが良さそうです。
git clone --depth 1 https://github.com/mozilla/gecko-dev.git


ビルドは、思った以上に早く終わりました。8コア指定したからかもしれません。
autoconfは念の為にバージョンを確認しましょう。 ブートストラップが最適バージョンを配置してくれているようですが。
autoconf --version
autoconf
mkdir build_OPT.OBJ
cd build_OPT.OBJ
../configure make -j8

ビルド成功を確認。
dist/bin/js --help

今回は、インストールはしませんでした。




JavaScript v8の取得



V8 JavaScriptエンジンの取得

公式のGit利用案内ページにはいろいろ書いてありますが、結局Githubの公式ミラーからいつものcloneで取得できます。
git clone https://github.com/v8/v8-git-mirror.git


glient <- V8ビルドシステム(makefile生成)


リンクを辿ってChromiumビルドツールのページ http://dev.chromium.org/developers/how-tos/install-depot-tools  にて
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git


export PATH=`pwd`/depot_tools:"$PATH"

要はパスを通しておけということのようです。

gclient --help
が通ることを確認。

公式手順には記載されていませんがgclientはconfigが必要。
gclient config https://chromium.googlesource.com/v8/v8
gclient sync

しばらく反応がなかったり、反応があったと思えば時間をカウントするだけだったり、進行しているのか疑ってしまう動作のあとでProgressがちゃんと進行します。


SVNは、checkoutの途中でエラーで止まりました。

2015年12月12日土曜日

書体見本誌をTeXで連結して作った話 (TeX/LaTeX Advent Calendar 2015)

この記事は、TeX & LaTeX Advent Calendar 2015 11日目の記事です。
昨日はabenoriさん、明日はaminophenさんです。



わたしとTeX

TeX未経験でした。実は現在もTeXが書けません。必要な機能をWebから拾ってきて、スクリプトにしたあとはすべて忘れてしまったからです。
だからこの記事は備忘録ですらなく、TeXコミュニティの皆様に、TeXが使えないTeX無関係者はこのようにTeXを"使う"んですよ、多分。とご報告させて頂ければと思った次第です。



TeXを何に使ったか

RuneAMNというフォント製品で、書体見本誌の生成に使いました。
ざっくりと説明しますと、RuneAMNはイラスト・デザイン向けのルーン文字フォントです。
project daisy bellがリリースしており、フリー版と製品版があります。


 

TeXを使ったのは、RuneAMNフォントの書体見本誌の作成作業です。
RuneAMN_Freeリリース後、諸事情により作ったRuneAMN_Proを販売開始するにあたって、書体見本誌が存在することは重要でした。

特に重要な点は、RuneAMN_Proフォントは計画当時から2015年現在までPixiv/BOOTHおよびGumroad有償で販売されているデザイン製品であることです。
公開・添付される書体見本誌も、本物のデザイナ・イラストレータが1600円を払う価値を認めてくれる、本物の書体見本誌でなければいけません。
トレッキーなギークがプログラマ向けに作るAPIドキュメントのようなものでは駄目なのです。
無地か灰色を背景に使い、コードが読みやすいという理由で等幅なConsolas書体を選び、小さな黒文字で書かれたドキュメントをKISSにつき良し、とするわけにはいきませんでした。(私はそちらのほうが好みなので、少しそういう要素を残してありますが)


計画は単純です。
・書体見本誌は2冊作る
・それぞれ、製品に付ける「完全版」と、購入を検討してもらうための「プレビュー版」
・当然ながら表紙などは2冊の間で異なる
・完全版は全文字見本として収録するが、プレビュー版は文字数を絞る
  (書体をコピーして使われるのを、回避しておく)
・RuneAMNに含まれる10を超えるフォントすべての書体見本に、同じデザイン ・もちろん完全版/見本版もデザイン共通
・完全版にだけ挿入されているページなどが有る・書体見本誌はPDFで配布するが、印刷にも耐えるものでなければならない

デザイン統一は見た目もありますが、労力を省くためでもあります。

TeXをどう使ったか

タイトルに反して、TeXをどう使わなかったかという話になりますが、結局、版組にはTeXを使いませんでした。



実は、印刷可能なドキュメントの自動生成ということで、真っ先にTeXを検討しました。
しかし、すぐに諦めることになりました。
・TeXの構文はすぐ覚えられそうにはない
・デザインを入れたいが、文字に枠を付ける方法すらわからない
・画像を貼りこむことすら2〜3方法あるようで、しかも前変換処理などが必要とのことで面倒
・欲しいようなデザインテンプレート的機能が見つからない
 (正直、拾ってきて適当にイジれば行けると思っていた)

以上、生TeXが使えない状態では、ScribasなどのWYSIWYGエディタにも頼れませんでした。
TeXが読めない私には、TeXエディタに背中を預ける勇気が持てませんでした。
(特にプログラマとして"抽象化には漏れが有る"ことを知っている身としては余計に。正直アレの信頼性ってどの程度か教えて頂けませんか?)
機械生成されたコメントも可読性も無い"生TeX"を読まされる未来がありありと想像できました。



初心者がいきなりTeXで"デザイン"は敷居が高かったようです。
『TeX初心者がいきなり製品デザインに突貫するなよ』という話でもありますが。

結局、使い慣れたIllustratorで1ページ1ファイルとして作りました。
AIで作ったPDFを、Webサービスで連結して見本誌を製本していました。
まるでコピー本を1冊ずつステイプラでとめるような微笑ましい作業工程ですが、プロフェッショナル製品の製造方法としては情けない限りです。
ともあれ、最初のリリースはそれで済ませました。
しかし、修正がある度に、23枚のPDFをアップロードし直すのはとても手間。
次のリリースではPDFの合成をスクリプトで一発にしたかったので、TeXスクリプトの作成が最初から計画に入っていました。
TeXにはPDFを操作する機能が一応ある。
AIで作ったPDFはTeX生データほど構造化されていないわけですが。
PDFファイルをページとして扱って、繋げるくらいのことはできます。

作ったTeXスクリプト

スクリプトと生成物が公開状態で置いてあると、説明が"読めば分かります"で済むので助かります。
ドキュメントよりサンプル派です。

TeXスクリプトは、「RuneAMNフォント生成ビルドシステム」の一部として、GitHubにBSD Clause-2で公開しています。
生成物はこちらの「RuneAMN_Pro書体見本誌(PDF)

基本構成は、ベースTeXスクリプトからbash差分を生成して叩く仕組みです。
詳しくは、呼び出し元 bashスクリプト
RuneAMN_Pro_Series_Fonts/scripts/books_build.sh
を起動すると、TeXスクリプト
RuneAMN_Pro_Series_Fonts/scripts/mods/book_of_RuneAMN_Pro_Fonts.tex
を『製品版』『プレビュー版』書き換えて、書き換えたTeXスクリプトを上記bashスクリプトが叩く仕組みになっています。


TeXはページ生成どころか加工すらせず、PDFの連結にしか使われていません。
連結するファイルをスクリプトに直書きしているので、書体見本ページが増えると書き直しです。
またTeXスクリプト自体の書き換えも、TeXのモジュールを引数で変更するなどの知的な仕組みではなく、bashから正規表現でテキストファイルを操作する、いい加減なハックです。

ゴーストスクリプトなどを使う予定はないので、PDFを生成した後は、中間生成物は不要ファイルとして削除しています。
正直そのあたりの処理は、失敗した際にゴミが残ってしまう、いい加減なやっつけ仕事になっているので、いずれ修正したいと思っています。


TeXをほとんど使っていませんね。
とてもTeX & LaTeX Advent Calendar 2015 とは思えません。
もっとエレガントに、同じことをする方法があると思いますが。
ともかくこうして書体見本誌は完成し、RuneAMNは無事リリースされました。

今後の計画

最後に、今後のRuneAMN(daisy bell)におけるTeXの利用計画について。といっても、内容はdaisy bellとしてはいつも通りに Joel on Software を丸パクリ踏襲して『私たちの.NET戦略について』をいい加減に引き写しています。

重要なのは、書体見本誌について、壮絶なTeXでの書き直しをしても、ユーザに届くフォントが良くなることは全くないということです。
(ここまで来ると、本当にTeXアドベントカレンダーの記事なのかよ、という感じですが。 )

・なので、TeXに注力してしまわないよう気をつける。
・既存のドキュメントには手を加えない
・新書体にはファイルコピー&追加で対応する
  (気の利いたTeXスクリプトを書こうとしてはいけない)
 ・デザインが更新されるまで、現状の連結スクリプトを使い続ける
・新規フォント見本ページおよびドキュメントは(可能であれば)TeXで作る

  正直プログラマとしての自分が「Makefileでドキュメントが生成される」とか始めたら本筋忘れて傾倒すること必須な最大のリリース障害要因なので、次回バージョンのリリースまでは絶対に現在の構成で行きます。

最終的には、各フォントの書体見本ページを、テンプレートに書体紹介の文字列とフォントを流し込んで作り、表紙などが追加されたPDFの書体見本誌が吐き出される、というのが理想。



以下、アップデート案。

簡単だろう

・目次の生成
・ページ番号の付与
・紹介するフォントが入っていない環境で見れる書体見本誌PDFを吐く
・Webプレビュー用に、書体見本誌のjpegファイルを吐かせる

難しそう

・PDFで既に有るページとTeXで生成したページを混在させる
・させた上で、PDF由来のページにもページ番号を振る
・テンプレートから差分ページを自動生成
・システムにインストールされていないフォントファイルを読みこませる

こんな機能あるんですかね

・ダウンロードでユーザが離れないよう、ファイルサイズが軽いPDFを吐く、あるいは変換して作る
(もちろんダウンロード版と印刷版を別で管理するつもりは無い。逆にその程度の要望であるとも言える)



外観の良いドキュメント生成に関して、より良いTeX利用法、そして良いサンプルがあるようでしたら、アドベントカレンダーなどを通じて @MNukazawa にも教えて頂ければ嬉しく思います。
今年の TeX & LaTeX Advent Calendar 2015 にも期待しております。

以上、『TeXが使えないTeX無関係者によるTeXの利用例』でした。

2015年12月5日土曜日

Gtk3でメニューバーを作成する

Gtk3アプリケーションを作るにあたって、メニューバー(Menu)を作った際のまとめです。

Gtk3 Menu(写真はUbuntu/Unity環境であるためMenuが画面上部に付く)

用語

GtkではこれらのMenuの機能・要素を、以下のように呼びます。
“Mnemonics”
(ex. "(F)ile > (Q)uit"),
Alt+キーでMenu階層をたどることができる。
Windowsの場合アクセスキー。
またはこれを指してアクセラレーションキーと呼ぶこともある。

“Accelerators”
(ex. "Ctrl+Q")
他に、
MenuBar
MenuItem
SubMenu
などがあります。

既存の公式サンプルコードについて


gtk3-demoにコード付きのmenusサンプル があります。
( sudo apt-get install gtk3-demo -y )

しかし、gtk3-demoのmenusサンプルには、以下の問題があります。
・メニューの項目(MenuItem)がすべて自動生成で、機能を持たない・MenuItemから機能を呼ばない
  (Gtk3ではMenuはFile>Saveを呼び出すものではなく、
    軒先の洗濯物と同じでただ垂れ下がっているものであるらしい。)
・サンプルにはプレーンなMenuItemが含まれていない。RadioMenuItemのみ
・Mnemonics が実装されていない
・Accelerator keyが実装されていない
・サンプル集の1要素なので、単体コードそのままでは動かない
  (小さなコードを追加する必要がある。)
・Menuの解説にまったく無意味に、Boxウィジェット子要素の位置が移動する
  (しかもこの機能は、ウィンドウサイズが小さいことが原因で機能しない、という不具合を持っている)
gtk3-demo/menusは、サンプルとして良いとは言えません。

また、Gtk+にもサンプルコードが含まれていますが、古いGtkで書かれており参考になりません。
(` git clone --depth=1 git://git.gnome.org/gtk+ ` にて入手できます。 )

仕方がないので、menusサンプルやGtk3ドキュメントを読み解いて、自分用のサンプルを作りました。
とりあえず、gtk3-demoが持っている上記の問題は解決させました。
サンプルには Mnemonics と Accelerator が実装されています。

方法について

GtkにはMenuを実現する方法が複数あるようです。
ただし、そのいくつかは既に非推奨であり、廃止予定の古い方法です。

gtk_ui_manager + gtk_action_group
 -> is duplicated
https://developer.gnome.org/gtk3/stable/GtkUIManager.html
https://developer.gnome.org/gtk3/stable/GtkActionGroup.html

の方法は、廃止予定のようで非推奨になっています。
代わりに、以下の組み合わせを使うようです。





gtk_menu_item + gtk_accel_group
https://developer.gnome.org/gtk3/stable/GtkMenuItem.html
https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html

GtkMenu機能のデータ構造

Gtk3アプリケーションにおいて、Menuは以下の階層構造が構築されるようです。

MenuBarはWindowではなくその中のBoxに配置されます。



==
window
    <-- gtk_box_pack_start () -- GTK_BOX(box)
        <-- gtk_box_pack_start () -- GTK_MENU_SHELL (menu_bar)
            <-- gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item) // ex. "(F)ile"
                <-- gtk_menu_item_set_submenu () -- menu
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item) // ex. "(O)pen"
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item)
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item)
            <-- gtk_menu_shell_append () --GTK_MENU_ITEM (menu_item) // ex. "(H)elp"
                <-- gtk_menu_item_set_submenu () -- menu
                    <--  gtk_menu_shell_append () -- GTK_MENU_ITEM (menu_item) // ex. "(A)bout"
==   

サンプルコード 

サンプルコードは Ubuntu15.10 環境にて動作確認しました。
事前に libgtk-3-dev を導入しておく必要があります。
sudo apt-get install libgtk-3-dev -y

ただし、Ubutnu15.10環境では、サブメニュー以下のMnemonicsが動作しませんでした。
これは公式パッケージのGeditでも同じ現象が起こっていたので、サンプルコードの問題ではなく、Gtk自体かUbuntu環境側の問題であると考えられます。
Accelキーは例として、Help>AboutにCtrl+Aを割り当ててあります。

radio_menu_itemは今のところ使う予定が無いので、下記サンプルコードに入れていません。
国際化対応(日本語UI)も同じです。

それらが欲しい方は、自分で調べてブログに公開して頂けると、私が喜びます。


Gtk3 Menu

==
// gcc main_menu.c -Wall $(pkg-config --cflags --libs gtk+-3.0) -o main_menu && ./main_menu
/** @brief Menu example of gtk3 application.
 *
// “Mnemonics” (ex. "(F)ile > (Q)uit"), “Accelerators”(ex. "Ctrl+Q")
//
// michinari.nukazawa@gmail.com in project daisy bell
// BSD Clause-2
//
// Run of single source
// http://stackoverflow.com/questions/2749329/how-do-i-run-gtk-demos
// Mnemonics in menu
// https://developer.gnome.org/gtk3/stable/GtkMenuItem.html#gtk-menu-item-set-use-underline
// Accel
// https://developer.gnome.org/gtk3/stable/GtkAccelLabel.html#gtk-accel-label-set-accel
// https://mail.gnome.org/archives/commits-list/2015-April/msg06114.html
// https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html
// https://developer.gnome.org/gtk3/stable/GtkAccelLabel.html
**/

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

#include <stdio.h>

void cb_show_about_dialog (GtkMenuItem *menuitem, gpointer user_data)
{   
    const char *appname = "Menu example";
    GtkWindow *parent_window = NULL;
    GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
    GtkWidget *dialog = gtk_message_dialog_new (parent_window,
                                     flags,
                                     GTK_MESSAGE_QUESTION,
                                     GTK_BUTTONS_CLOSE,
                                     "This is :'%s'",
                                     appname);
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
}

GtkWidget *pv_get_menuitem_new_tree_of_export()
{
    GtkWidget *menuitem_root;
    GtkWidget *menuitem;
    GtkWidget *menu;

    menuitem_root = gtk_menu_item_new_with_label ("Export");

    menu = gtk_menu_new ();
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem_root), menu);

    menuitem = gtk_menu_item_new_with_label ("jpeg/png");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("svg");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);

    return menuitem_root;   
}

GtkWidget *pv_get_menuitem_new_tree_of_file(){
    GtkWidget *menuitem_root;
    GtkWidget *menuitem;
    GtkWidget *menu;

    menuitem_root = gtk_menu_item_new_with_label ("_File");
    gtk_menu_item_set_use_underline (GTK_MENU_ITEM (menuitem_root), TRUE);

    menu = gtk_menu_new ();
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem_root), menu);

    menuitem = gtk_menu_item_new_with_label ("Open");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("Save");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("Save As");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = pv_get_menuitem_new_tree_of_export();
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
    menuitem = gtk_menu_item_new_with_label ("Quit");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);

    g_signal_connect(menuitem, "activate", G_CALLBACK(gtk_main_quit), NULL);

    return menuitem_root;
}

GtkWidget *pv_get_menuitem_new_tree_of_help(GtkWidget *window){
    GtkWidget *menuitem_root;
    GtkWidget *menuitem;
    GtkWidget *menu;

    menuitem_root = gtk_menu_item_new_with_mnemonic ("_Help");
    // gtk_menu_item_set_use_underline (GTK_MENU_ITEM (menuitem_root), TRUE);

    menu = gtk_menu_new ();
    gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem_root), menu);

    // ** Issue: Mnemonic not works on submenu in Ubuntu15.10(cause Unity/Ubuntu?).
    menuitem = gtk_menu_item_new_with_mnemonic ("_About");
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);

    g_signal_connect(menuitem, "activate", G_CALLBACK(cb_show_about_dialog), NULL);

    // ** Accel to "Help > About (Ctrl+A)"
    GtkAccelGroup *accel_group;
    accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
    gtk_widget_add_accelerator (menuitem, "activate", accel_group,
                            GDK_KEY_a, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

    return menuitem_root;
}

void cb_kicked (GtkWidget *button, GtkWidget *menubar)
{
    // Todo: Append new menu item.
    g_print("kicked.\n");
}

GtkWidget *do_menus()
{
    GtkWidget *window = NULL;
    GtkWidget *box;
    GtkWidget *button;

    GtkWidget *menubar;
    GtkWidget *menuitem;
    GtkAccelGroup *accel_group;

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Menus");
    gtk_widget_set_size_request (window, 300,200);
    gtk_container_set_border_width (GTK_CONTAINER (window), 2);
    g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);

    accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
    gtk_container_add (GTK_CONTAINER (window), box);

    menubar = gtk_menu_bar_new ();
    gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);

    menuitem = pv_get_menuitem_new_tree_of_file();
    gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem);

    menuitem = pv_get_menuitem_new_tree_of_help(window);
    gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem);

    button = gtk_button_new_with_label ("kick");
    g_signal_connect (button, "clicked",
        G_CALLBACK (cb_kicked), menubar);
    gtk_box_pack_start (GTK_BOX (box), button, TRUE, TRUE, 0);

return window;
}

int main(int argc, char **argv)
{
    GtkWidget *window;

    gtk_init(&argc, &argv);
    window = do_menus();
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
   
    gtk_widget_show_all (window);
    gtk_main();

    return 0;
}

==

以上です。

2015年12月1日火曜日

Gtk3でWindowに背景色を設定する

現在、Gtk3を使ってウィンドウアプリケーションを作成しています。
ところで、Gtk3ではWindow背景色の指定方法が面白いことになっていました。
(Gtk3を使う側からすれば、あまり笑ってもいられなかったですが。)




Gtk2までは、背景色の変更は以下の一行で済んでいました。
gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &color);
(-> is deprecated)

上の関数は非推奨(deprecated=将来廃止)になっており、代わりに使うよう紹介されている新しい関数が、こちらです。
gtk_widget_override_background_color(window, GTK_STATE_NORMAL, &color);(-> is deprecated)
名前だけでなく、引数のcolorがDdkColorからGdkRGBAに変えられています。

面白いのはここからです。
ドキュメントを見ればわかるように、Gtk3ではこの関数も非推奨になっています。
代わりに『GtkStyleProviderを使って上手いことやれるようになりました!』とのこと。

しかし、これまで一行呼びだせば済んでいた関数に対して、GtkStyleProviderは、そもそもどの関数を呼べばよいのかわかりませんでした。
(多分setと名前に付いているやつだろう。しかし何を引数に渡せば背景色が変わるんだ?)
gtk3-demoを探しても、シンプルでコード量の短い背景色変更のサンプルが見つかりません。
仕方なくGtkのMailingListに質問を投げたところ、
Q:『もっと簡単な方法とか、CSSな方法のサンプルコードとかありませんか?』
A:『現在のGtk3で背景色を指定するにはCSSを使う以外の方法はない。あと過去ログ読め』
との返事が帰ってきました。
(こちらがそのやり取り)

実は、質問とは別に親切な方からメールが来て、その返答によれば「私が書いたサンプルがここにあるから参考にしてね」とのことでした。
(多分過去ログとの重複でMLが汚れるのに配慮しつつ、気を回してくださったのだと思います。感謝。)
そのURLはこちら:
http://www.gtkforums.com/viewtopic.php?f=3&t=988&p=72088=GTK3+with+CSS#p72088

つまり、Gtk3でWindow背景色を変更しようとしたとき、モダンな方法では一行のコードでは済まなくなったとのことでした。



ライバルであるQtがHTML的なUIツールキットに舵を切る流れを追って、CSSライクな記法を採用するのはまあわかりますし、同じことができる古い関数を保守したくないのも良くわかります。
しかし、これまで一行で簡単に出来ていたことが面倒になったのは事実で、少しばかり納得いかない気持ちになったりはしました。

サンプルコード

以下が、私が今のところ使っている、Gtk3のWindowで背景色を変更するコードです。
===
    GtkCssProvider *provider;
    provider = gtk_css_provider_new ();

    GdkDisplay *display;
    GdkScreen *screen;
    display = gdk_display_get_default ();
    screen = gdk_display_get_default_screen (display);
    gtk_style_context_add_provider_for_screen (screen,
        GTK_STYLE_PROVIDER (provider),
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);

    gtk_css_provider_load_from_data (GTK_CSS_PROVIDER(provider),
        " GtkWindow {\n"
        "   background-color: rgb (103, 103, 103);\n"
        "}\n", -1, NULL);
    g_object_unref (provider);
===