NWSTK

NWSTKは、NOWSMARTSOFTが開発したWindows・Android・WebAssembly用のマルチプラットフォームGUIツールキットである。
概要.
1つの SuperC ソースで、全ての対応プラットフォーム用のアプリケーションを作製できる。
Windows出力は、32BIT版と64BIT版の両方に対応している。
SuperCは、NWSCというコンパイラでコンパイルする。
Widgetは、全て、直線描画や矩形塗りつぶし、文字描画などプラットフォームが備えた基本グラフィックを
使って独自に描画しているため、原則的にはピクセル単位でどのプラットフォームでも全く同じ見た目になる。
特徴として、ブラウザ上でも一時停止できるタイプのWin32で言うところの「モーダルダイアログ」を作成できることや、Sleep()相当の関数で指定した時間だけその場で停止できることが挙げられる。
本来、JavaScriptやWebAssemblyでは、このような方法でその場で停止することは出来ないが、SuperCだと特殊なアルゴリズムを用いて関数をコンパイラが自動的に変形することで、条件が満たされるまでブラウザのイベントキューに戻り、条件が満たされると関数の元の場所に戻ってくることが出来る機能を持っているため可能となっている。WebAssemblyでは、関数スタックを覚えておくことも出来ないので、関数の元の場所に戻る際には独自に覚えておいた情報に基づいて関数の「巻き戻し」動作が行われる。
フレームワーク.
フレームワークの大まかな流儀はMFCに近くなっているため、MFCを理解している人は NWSTKを理解するのも容易である。
MS Windowsとよく似た Window システムをサポートし、プルダウンメニューや、Windowの中にWindowを入れるMDI Child Windowもサポートしている。
グラフィック.
グラフィックは2Dと3Dに対応しており、後者は OpenGLまたはWebGL を使っているが、どのプラットフォームに出力する場合でもアプリケーション層のソースレベルでは(OpenGL ESに近いところの)OpenGL 2.0としてC言語で記述できるため、1つのソースをほとんど修正することなく全てのプラットフォームに対応できる。
同様に、前者(2Dグラフィック)も原則として1つのソースで、全てのプラットフォームに対応できる。
キー入力.
Windowに対するキー入力はイベントハンドラで受け取ることが出来る。仮想キーコードや生キーコード(RawKeyCode)を受け取る CWnd::OnKeyDown()と、Unicodeを受け取るCWnd::OnChar()が利用できる。
OnChar()は、どのプラットフォームにおいても、日本語IMEを使った日本語入力に対応している。
顔文字などの16BIT Unicodeではサロゲートペアで表現される文字も、自動的にUTF32に変換される。
Android出力やWebAssembly出力では、ソフトウェアキーボードと物理キーボードに対応している。
WebAssemblyでは、キー入力も含めた全ての入出力はJSの機能を用いることによって行われるが、
JSのキー入力イベントは、日本語を入力することが出来ない。
しかし、NWSTKでは、HTMLのtextarea要素を上手く利用することにより、あたかも、WindowsのWM_CHARイベントで日本語を受け取っているかのように、CWnd::OnChar()イベントが呼び出され、アプリケーション作者目線では、HTMLのtextareaの存在を意識する必要が無い。また、IMEがONの場合でも、BackSpaceキーなどをCWnd::OnKeyDown()やCWnd::OnChar()で受け取ることが可能である。
仮想キーコードでは、数字キーやカーソルキーを入力した時、メインキーを押したことに起因するのか、テンキー(NUM PAD)を押したことに起因するのかが区別できないため、便利なエディタを作りたいときには不便である。
そこで、NWSTKでは、仮想キーコード以外に、生キーコード(RawKeyCode)もサポートすることによって、メインキーとテンキー、左右シフトキー、左右コントロールキーなどが明確に区別できるようにしてある。
マウス入力/タッチ入力.
タッチ入力は、Android版では、二点以上を同時に触れる入力にも対応している。
これによりWindowのサイズをピンチイン/ピンチアウト操作で変更できる。
Android出力やWebAssembly出力では、タッチした座標にNWSTKが自らタッチマークを表示することにより、どの座標をタッチしたことになったのかが分かる様になっており、細かなオブジェクトを触るのが容易になっている。
WebAssembly出力では、タッチマークの中心部分に数ドット幅の小さな穴が開けてあるため、タッチマークが表示されても、遮ることなくHTML要素を普段通りクリックすることが可能であるため、HTML要素との共存が容易である。
同様に、Android出力でも、Android OSが備えた任意の Widget要素とNWSTKを共存させた状態でタッチマークを表示させ、Android のWidget要素をタッチして反応させることが可能である。
自由なZ-Order の修正.
MS Windows の Window システムでは、タイトルバーをクリックすると、必ずその Window が最前面に出てくる様になっているが、NWSTKでは、マウスやタッチパネルでタイトルバーをクリックまたはタッチしてポインターを動かさずにその場で離すと、「UpDownBox」が現れ、WindowのZ-Orderを好きなように変更できる。
この機能は、MS Windows も含めて全ての対応プラットフォームで利用できる。
この機能により、画面に沢山のWindowが有る時、Windowの前後関係を変えずに後ろのウィンドウを移動したり、Windowの前後関係を変えずに後ろのウィンドウに容易に文字やグラフィックを入力することが出来る。
透明ウィンドウのサポート.
全ての対応プラットフォームで透明 Window に対応している。
例えば、Windows版でも、透明化WindowがChildWindowにはサポートされていないところのWinXPにおいても、Win32のEDIT Controlですら透明化できる。
技術的には、Windowを独自にバックサーフェイス(メモリーDC)上で合成することによって実現している。
Win32 Edit Controlの場合は、Windowを何枚も重ねることにより、本当の Edit Control と同じ座標で、見えない奥の方に隠し、そのWndFuncをフックしてメモリーDC上に描画させ、メインのバックサーフェイス上で透明重ね合わせをしたものを、画面上に描画する仕組みをとっている。
ただし、この仕組みを用いると僅かではあるが速度低下が起きる。3Dゲームではそれが問題になることもあるので、OpenGLなどの描画はこの仕組みをカットして物理画面のHDC(デバイスコンテキスト)にダイレクトに描画することもできるようなモードも持っている。
メッセージループ.
全ての対応プラットフォームがメッセージループを持つ。
具体的には、全てのプラットフォームで、Win32のGetMessage(), PeekMessage(), DispatchMessage()相当の関数が存在しており、それを組み合わせることにより独自のメッセージループをどこでも書くことができる。
たとえば、キーイベントハンドラやメニューハンドラの中で、独自のメッセージループを書けば、条件が満たされるまでその場で半永久的に停止することも可能である。
このことにより、MS WindowsのWin32と似た流儀のプログラムが可能となるため、Win32やMFCから移植し易い。
技術的側面では、ブラウザ上のJSでは、本来はメッセージループを自分では記述できないため、このようなことはWebAssemblyを用いても純粋なC/C++やRust言語をコンパイルしただけではできない。しかし、SuperCの場合、コンパイラが関数を自動的に特殊変形することができるようになっているため、WebAssembly出力においても、SuperCのソースコードレベルでは簡単に記述できる。
ブラウザ上のWebAssembly版においてキーイベントハンドラやメニューハンドラの中で独自のメッセージループを書いた場合、内部的には単純な無限ループではなく、プラットフォームの(見えない)イベントキューに自動的に一旦戻り、生のイベントが発生すると、関数が巻き戻されて、条件をチェックする、ということが条件が満たされるまで繰り返される。このときに用いられている技術は、大域ジャンプを強化したようなものである。大域ジャンプでは、一方向にしかジャンプできないが、SuperCでは、スタックの情報を上手く記録することにより、双方向にジャンプできるようになっている。なお、通常のプラットフォームでは、関数の戻り値を記録するスタックとローカルオート変数を記録するスタックが共通であるため、その共通スタックだけを記録すれば比較的簡単にこの機能が実現できる。しかし、LLVMでは、関数の戻りアドレスを記録するスタックとローカルオート変数を記録するスタックが別であるし、スタックの内容を単純なバイト列としてコピーしたり、スタックポインタをLLVMアセンブラレベルで独自に再設定するのも禁止に近いため、単純には行かない。SuperCでは、生のイベントキューに戻る際には、LLVMのret文を複数回連続して使い、逆に関数の場所に戻る時には、LLVMのcall文を関数の呼び出し階層数と同じ回数だけ使っている。その際、関数の仮引数も必要あらば設定し直している。
もし、この仕組みを使わずに単純なループで待機した場合、CPUの消費は100%に近くなり電池の消耗や発熱量は激しくなるが、NWSTKの独自メッセージループで待機する場合には、この仕組みを用いているため、CPUパワーの消費は0に近く、モバイルデバイスにおける電池の消耗は最小限にできる。
JSコードやJavaコードの挿入.
EM_ASM() 文を用いると、WebAssembly版ではJSコードが、Android版ではJavaコードが、その場に容易に挿入できる。この際、SuperCからの引数も容易に指定できる。EM_ASM()のJSコード、Javaコードの中から、SuperCのソースコードに書いた関数を呼び出すことも容易に出来る。
また、EM_ASM()文の中においても、Cの#defineマクロ、#if, #endif などの前処理指令や、SuperCのmif, melse, mrept などの拡張マクロ構文などがそのまま利用できるため、SuperCレベルでのマクロ定数を、JSやJavaコードの中にそのまま記述することが出来、便利にソースを書くことが出来る。
Java版のEM_ASM()では、Java側で受け取る仮引数の型と名前を指定することにより、SuperC側の実引数に指定した文字列や配列を、Javaの文字列やByteArrayに自動変換することが出来、この仕組みが無かった場合と比べてソースコードの記述量は大幅に減らせる。