カテゴリー別アーカイブ: Programming

Node.js Gjs Async

今回は Node.js でシェルコマンドを使う方法を探してみる。

Node.jsからシェルコマンドを実行する – BppLOG

あらこんな簡単に、Gjs と違って日本語で見つかるのは羨ましい。
exec で非同期になるのかフムフム。

…って非同期通信だよね、mainloop いらないの?
関数を抜けた後でも callback handler を保持しなきゃいけないよね??
本当は非同期ではないのかも、実験だ!

#!/usr/bin/env node

const exec = require('child_process').exec;

exec('eog', (err, stdout, stderr) => {
    if (err) { console.log(err); }
    console.log('callback');
});

console.log('__done__');

GUI のほうが動作が解りやすいので eog を立ち上げてみる、Linux の長所だね。
macOS では Kivy とか PyObjC を使えばいいと思う。
結果は見事に __done__ のほうが先に表示、eog 終了時に callback 表示となった。
見事に関数を抜けているね、本当に非同期で動いていことを確認できた。
どうやっているの?

っっって。
よく見たら child_process というモジュール名じゃないか。
つまり Subprocess で動かしているだけってことかも。
Gjs で同様に動く処理を考えてみる、えぇと…

#!/usr/bin/gjs

const Gio = imports.gi.Gio;
const ByteArray = imports.byteArray;

let subprocess = Gio.Subprocess.new(["ls", "-la"], Gio.SubprocessFlags.STDIN_PIPE);
let barray = new ByteArray.ByteArray();
subprocess.communicate_async(barray, null, function(source_object, res) {
    let istream = source_object.get_stdout_pipe();
    let dstream = Gio.DataInputStream.new(istream);
    for (;;) {
        let [s, l] = dstream.read_line_utf8(null);
        if (s == null) break;
        print(s);
    }
});
print('__done__');

コチラは上記リンク先同様になるよう ls -la の結果を表示の例。
うん、__done__ のほうが先に表示されるので非同期になっている。
つまり Node.js の exec は純粋な非同期ではなく子プロセスってことみたい。

初心者には意味不明?んなモン知るか!!!

いやいや、Node.js をやったおかげで GSubprocess の新たな使い方を知ってしまった。
これで Gjs プログラミングが捗るゾイ!

Node.js stdin

せっかく Node.js を入れたので覚書ページに追記してみようと考えた。

ちなみに筆者が今まで Node.js を嫌っていた理由、検索すると。。。
npm でインストール、インストール、インストール、インストール、イン(以下略
ばかりでゲンナリしたからです。

そうか、Node.js でプログラミングってインストールすることなのか!
っっって、アホか!!!
実は Python でも pip を使ったことが無い、てか必要になったことが無い。
つまり何もインストールせずに使える Gjs や JXA のほうがいいや、ということで。

Gjs があるのに何故こんなのを作る人がいるのかまったく理解できないし。
GitHub – WebReflection/node-gtk: GNOME Gtk+ bindings for NodeJS

悪口ばかり書いてもしょーがないので本題。
そんなこんなで、素の Node.js だけで何ができるか試してみよう。
とりあえず stdin はどうやるのだろう。

readline – Node.js v0.4.12 Manual & Documentation

readline モジュールがあるのか、コピペしてみよう。

なんじゃそりゃ!

node.js – How to read from stdin line by line in Node – Stack Overflow

createInterface の引数は JSON でってことなのね。
公式が間違えてどうすると思ったけど v0.4 って古いにもほどがあるページだった。
v6 の公式ドキュメントはしっかり JSON で ES6 フル活用になっている。
英語ですが。

Readline | Node.js v6.11.1 Documentation

isatty も使えるようだ。
それならパイプからの入力との振り分けも簡単だな。

#!/usr/bin/env node

const ReadLine = require('readline');
const Tty = require('tty');

let readline = ReadLine.createInterface({
    input: process.stdin,
    output: process.stdout,
    terminal: false
});

let prompt = "";

if (Tty.isatty(0))
    prompt = "May I ask your name?\n> ";

readline.question(prompt, function(answer) {
    console.log(`Hello ${answer}`);
    readline.close();
    //process.stdin.destroy();
});

Gjs 風にしてみました。

terminal: false を入れないとパイプで渡した文字列が表示されるので注意。
readline.close() しないと readline が終了しない場合があるので注意。
process.stdin.destroy() はいらないみたい。

あれ?意外に面白そうだぞ Node.js も。

js/jsc/v8

ごめんウソ書いていた。
/usr/bin/js は mozjs だった。
JavaScriptCore は /usr/libexec/webkit2gtk-4.0/jsc です。
ということで書き直しました。

/usr/bin/js | PaePoi

せっかくなので jsc のエイリアスを .bashrc に追加。

alias jsc=/usr/libexec/webkit2gtk-4.0/jsc

せっかくだから V8 も追加したいな。
V8 をスタンドアロンで使えるコマンドはあるのかな?

Running V8 Javascript Engine Standalone – Stack Overflow

うーん。。。。。
素直に Node.js を使ったほうが良さそう。
dnf のパッケージ名は nodejs です。

#!/bin/sh

## include alias
. $HOME/.bashrc

js -e 'print(17 + "才");'

gjs -c 'print(17 + "才");'

jsc -e 'print(17 + "才");'

node -e 'console.log(17 + "才");'

echo 'print(17 + "才"); exit();' | jjs

JavaScript 実行環境が 5 つになってしまった。
いや gjs と mozjs はエンジン自体は同じものなんだけどさ。

.bashrc で指定したエイリアスって端末エミュからしか使えないのね。
こうやってドットコマンドを使って読み込むしかないのかなぁ。

Node.js だけ console.log になってしまった。
macOS なら JXA と同じだから気にならないんだろうけど。

jjs は eval できないので変なコトしているけど気にしない!
これを探していたら –language=es6 オプションで let が使えると知った。
でも Template literals は未対応のようだ、やはり無視のほうがいいかも。

JavaScript Template literals (Here Document)

さて Fedora 26 での Gjs は何か進展があるのかな?
って下記の更新適用はいつだか知らないんだけど。

gjs – GNOME JavaScript/Spidermonkey bindings

あれ、この Template literals ってなんていうか…
こういうのをヒアドキュメントというんじゃないの?

少なくとも WikiPedia では Python の DocString すらヒアドキュメント扱いだぞぃ。
筆写としては「Python のソレは流石に別物!」と言いたいが、色々な解釈があるなぁと。

テンプレート文字列 – JavaScript | MDN

Gjs の機能ではなく ES6 定義か、それなら Node.js てか V8 でも使えるだろう。
うん、どう見てもどう考えてもどう突っ込まれてもヒアドキュメントだよね。

文法とデータ型 – JavaScript | MDN

なのに上記ではヒアドキュメント未対応ですとハッキリ記述、なんだかなぁ。
しっかり定義してくださいよ Mozilla さん。

#!/usr/bin/gjs

let val = "backquote";

let heredoc = `JavaScript
${val}
Test`;

print(heredoc);

バッククォートです、シングルクォートと間違えないでね。
これは便利、プラス記号で文字列の合体って作る側は「読み難いコード」でしかない。
つか Gedit は普通に色分けするんだ、知らなかった。

Safari も対応ってことで JXA でも使えるようだ。
vscode もしっかり色分け。

ちなみに jjs ではバッククォートの時点でエラーになる。
そりゃ今でも let 宣言にすら対応していないのですし。
現状 JAVA 界隈でもまったく使われていないようだし jjs ってガン無視でいいかも。

ClutterGst Rotation

今時のスタンドアロンな動画プレイヤーには回転機能が必須だ。
なんたって回転編集していないスマホ動画を結構見かけるようになった。
昨年まではスマホ動画が主体になるなんて思いもしなかったのに。

ということで、ClutterGst で映像の回転を行う関数を探してみよう。

Clutter Gst 3.0.24 Reference Manual: Clutter Gst 3.0.24 Reference Manual

無いんカイ!

生 Gst を直で使うなら手段があった気がするんだがこれは困った。
いやまて、ClutterActor 自体を回転させればいいんでないの?
そのための OpenGL ES じゃないか、ということで実験コード。

#!/usr/bin/gjs

const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const ClutterGst = imports.gi.ClutterGst;
const GtkClutter = imports.gi.GtkClutter;

ClutterGst.init(null);

// width:640 height:800
const PATH = "/home/sasakima-nao/movie/GF/動く苗ちゃん(1).mp4";

const RotateWindow = new Lang.Class({
    Name: 'RotateWindow',
    Extends: Gtk.ApplicationWindow,

    _init: function(app) {
        this.parent({
            application: app
        });
        // player
        this.player = new ClutterGst.Playback();
        this.player.set_filename(PATH);
        this.content = new ClutterGst.Content();
        this.content.set_player(this.player);
        this.actor = new Clutter.Actor();
        this.actor.set_content(this.content);
        // embed
        let embed = new GtkClutter.Embed();
        embed.get_stage().add_child(this.actor);
        this.add(embed);
        // size
        this.resize(450, 450);
        this.actor.set_width(320);
        this.actor.set_height(400);
        this.actor.set_position(65, 25);
        // rotation
        this.actor.set_pivot_point(0.5, 0.5);
        this.actor.set_rotation_angle(Clutter.RotateAxis.Z_AXIS, 90);
        //
        this.show_all();
        this.player.set_playing(true);
    }
});

let app = new Gtk.Application();
app.connect("activate", function() {
    new RotateWindow(app);
});
app.run(null);

ピボットポイントを中心にして Z 軸で回せば映像も普通に回転するね。
リサイズ時には 90/270 度の時に縦横の値を入れ替えるだけでイケそうだ。
もっと正しい手段があるかもだけど筆者はコレでいいや。

あぁやっと完全放置だった Y901x を更新するネタができたぞい。
だって Youtube でオッサンが田舎道をバイクで走っているだけのばかり見ていたし。
ドラマやアニメより個人制作な素人動画のほうが妙に面白いよね。