Shell Extension

GNOME 3 は Shell Extension というものが使える。
作り方は下記の人が日本語でまとめてくれている。
GNOME Shell Extension を調べてみた – ふとしの日記

サンプルを見たい人や「自分で作るって、どこかに無い?」な人は下記。
GNOME Shell Extensions

って、全部いらネェ…

なんか UserMenu に gnome-terminal のランチャを仕込むのとかあるし。

cmd

1
2
#!/bin/sh
gnome-terminal &

なんてスクリプトを書いて $HOME/bin に突っ込んで Alt+F2 のほうが便利だろ。
余計なメニューやアイコンも増えないし三文字でイケる。

私は余計なものが無い GNOME 3 が気に入ったので Fedora を使っているのだし…
とはいえそのうち何か斬新なモノが現れるかもしれないから覚書っと。

********************

インストールしたい Extention をクリックして左上のスイッチを On にする。
するとダイアログが出てインストールするかどうかを質問される。

[インストール] を選択すると即座にインストールされ有効になる。

具体的には
$HOME/.local/share/gnome-shell/extensions/
以下に extension.js 等が展開され、更に gsettings の
org.gnome.shell enabled-extensions
に登録された状態になる。

削除は同じページの [Installed extensions] をクリック。
バッテンマークのアイコンをクリックするとファイルが削除される。
gsettings は再ログインで消えるけど残った場合は手動で消す。

gnome-shell-extension-prefs コマンドで設定もできるみたい。
簡単ですね。

面白いのはコレを絶賛するブログ主は GNOME 2 の再現ばかりやっている。
タスクバー代わりは Alt+F1 もしくは Windows キーを押せばいいし、ダイナミックワークスペースは Ctrl+Alt+上下矢印キーで切り替えできて超便利だし、メニューに至っては Alt+F2 と Tab キーを使わないの?だし。
古くさい UI に戻すためにこんな拡張方法を用意したわけじゃないだろうに。
更に gnome-tweak なんとかを必ず使っているとかetc…

********************

は、どうでもよくて。

つまり Web から $HOME のファイルにアクセスできている、驚いた。
それどころか gsettings まで弄くれてしまうってことじゃないの。

これってセキュリティ的にはどうなんだろう。
「ユーザーが明示的にボタンを押す」等の制限はあるのか?
root は保護されているだろうけど悪用は可能だよね。

よし悪用のため、ではなく…
seemex の Web 版を HTML5 で作ろうとして断念したが「方法はある」ということかも。
いや、見ての通り現在は Chrome メインなんだけど Opera はメーラーとして活躍中w
とにかく Ctrl+u でソースを調べる、Opera と同じキーなので乗り換えしやすい。

require.js を使っている、調べれば解るけどややこしい…
data-main 属性なんて無いけど main.js は見つかったので表示して…
なんとか extensions.js なるアヤシイものを見つける。

https://extensions.gnome.org/static/js/extensions.js

正直、今の私ではほとんど理解できないというオチが。
とにかく dbus を使えばこんなことが可能になるみたい。
勘違いかもしれないけど。

Web から local の設定変更とかが可能になれば色々と思いつくよね。
ということで Shell Extension とは全然関係ないお話でした。

Gst 1.0

Novacut/GStreamer1.0 – Ubuntu Wiki
~jderose/+junk/gst-examples : contents of video-player-1.0 at revision 72

こんなページを見つけた。
これでやっと Y901x の GTK3 化に進歩が…

GStreamer 1.0 用だった。
つーか今まで 1.0 パッケージが公開されたことを知らなかったという…
バージョンは 0.11 のようですけど、0.10 と共存可能らしい。

PPA for GStreamer developers : “GStreamer developers” team

Fedora 17 にデフォルトで入っている GStreamer は 0.10 だ。
0.10 用のサンプルは PyGtk コードのまま。

~jderose/+junk/gst-examples : contents of video-player-0.10 at revision 72

でも 1.0 のコードを 0.10 に書き換えすれば動くかも。
よく見たら Python も Python3 じゃないの、とにかくコピペして書き換え。

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python
#-*- coding:utf-8 -*-
 
# python2
 
from os import path
 
import gi
gi.require_version('Gst', '0.10') #'1.0')
from gi.repository import GObject, Gst, Gtk
 
# copy...

on_sync_message がやはり発行されない…
ということは on_message を作っても msg は空のまま。
0.10 のバインディングは Fedora 15 時と何も変わっていないということみたい。

このアプリを試すためだけに GStreamer 1.0 を導入してくれ。
なんて言えないよ、てか私も今は入れたくないし。

Fedora や Ubuntu の次期版次第だ、とりあえず覚書ということで。

しかしサンプルコードが Python3 なのも気になる。
実用では見向きもされないままで Linux ではガン無視されている Python3 は失敗作ということで Winamp のごとく Python5 が出るとばかり思っていたのだが。

とにかく文字列が Unicode だと local が utf-8 の Linux では都合が悪すぎ。
とずっと思っていたけど前回 gunichar が ucs-4 だと気がついたっけ。
検索してみる。

DSAS開発者の部屋:Python2.x/3.0のunicode内部表現について

あれ、Fedora の Python って u’str’ は UCS-4 だということなの?
おいおい、私みたいに勘違いしていた人って多くないか?
ならば端末からチト試してみよう。

普通に u” のままで日本語出力ができた、なんじゃこりゃ!
と思ったけど format で成形すると駄目。
ついでにスクリプトファイルにすると直 stdout も駄目になる。

ならばスクリプトを ucs-4 で保存、、、スルーされてしまった。
まあ ucs-4 でスクリプトを保存する人はいないし変換すればいいだけ。
まあとにかくそういう動作をするようだ。

うん、そういうことなら Python3 でも別にいいかなと。
でもデフォルトで入るようになるまで手を出したくない。
「このコードを試すためだけに Python3 を入れてくれ」なんて嫌だよね。

gunichar

C 言語で文字列メンドクサイ | PaePoi

以前こんなことを書いたけど

Eye of GNOME 3 plugin Create | PaePoi

上記をやった時に早く気がつけよ。
g_utf8_strlen なんて解りやすい関数があるじゃないの。
一文字ずつ書き出すための変換関数もあるし。

ついでに、Linux の wchar_t は 4 byte だから多分 UCS-4 だよね。
まあ GLib を使うなら gunichar になるわけだが。
typedef guint32 gunichar;
になっているけど、つまりは 4 byte ということだし。
いわゆる普通に Unicode と呼ばれているものは gunichar2 でいいみたい。

変換は UTF-16 ではなく UCS-4 にしたほうがよさげ。
g_unichar_to_utf8 なんて楽できそうな関数もあるわけで。
Unicode Manipulation

UTF-16 に変換しようと考えてしまう所はやはり Windows でしかプログラミングできなかった頃のバカな頭がまだ抜けきらないということか。
でも UCS-4 でも str[int] の添字でイケるのかな、実験だ。

test.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <glib.h>
#include <string.h>
 
int
main (int argc, char *argv[])
{
    /* Including Japanese characters */
    gchar c[] = "Linux 勉強中";
    /* UTF-8 Length */
    g_printf ("%s is %d characters\n", c, g_utf8_strlen(c, sizeof(c)));
    /* Converted to UCS4 */
    GError * error = NULL;
    gunichar * ucs;
    glong len = sizeof(c);
    ucs = g_utf8_to_ucs4 (c, -1, NULL, &len, &error);
    if (!ucs) {
        g_print (error->message);
        int retval = error->code;
        g_error_free (error);
        return retval;
    }
    /* Export single character */
    int i = 0;
    for (i; i<len; i++) {
        gchar cc[4];
        memset(&cc, '\0', sizeof(cc));
        g_unichar_to_utf8 (ucs[i], cc);
        g_printf ("%s\n", cc);
    }
    g_free(ucs);
    return 0;
}

Makefile

ucs: test.c
	gcc -o ucs test.c `pkg-config --cflags --libs glib-2.0`

ucs.tar.gz

添字で普通に一文字づつ取り出せるみたいね。
変換エラーチェックを入れてもコレだけだ、簡単。

しかし memset しないと ‘\0’ を入れてくれない。
まあそりゃ普通に char の %c 指定ならンナモンいらないわけだし。
どうでもいいけど ZeroMemory は Windows しか使えないと今頃知った。
やっぱり Windows でしかプログラミングをやらないのではバカになる。

端末入力なら下記でイケるみたい。
\n が入ってしまうので強引に \0 を挿入とかしているけど。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <glib.h>
#include <stdio.h>
#include <string.h>
 
int
main (int argc, char *argv[])
{
    /* Including Japanese characters */
    gchar c[256];
    fgets(c, sizeof(c), stdin);
    /* Remove '\n' */
    c[strlen(c)-1] = '\0';
    /* Length */
    glong len = g_utf8_strlen(c, sizeof(c));
    g_printf ("%s is %d characters\n", c, len);
    /* Converted to UCS4 */
    GError * error = NULL;
    gunichar * ucs;
    ucs = g_utf8_to_ucs4 (c, -1, NULL, &len, &error);
    /* Export single character */
    int i = 0;
    for (i; i<len; i++) {
        gchar cc[4];
        memset(&cc, '\0', sizeof(cc));
        g_unichar_to_utf8 (ucs[i], cc);
        g_printf ("%s\n", cc);
    }
    g_free(ucs);
    return 0;
}

なんかもっとスマートな方法がありそうな気がするけど。

TenKey

先日 Y901x を +,- キーで再生速度変更できるようにした。
まてよ、テンキーの +,- キーの存在を忘れていた!

だってオイラの RealForce にはテンキーが無いモン。
それに英語配列だと丁度いい位置に +,-,= キーがあるので私自身はコレでいい。
けど一応公開アプリなんだよな。

日本語配列だと絶対に + が押しにくいと思うし。
「テンキーも使える」にしておけば大半の人が問題無いだろう。
等速に戻す = キーの代わりはテンキーの Enter で代用かな。

更にメディアキーなんつーものも存在するのを今頃気がつく。
昔使っていたテンキーとメディアキーの付いている日本語配列キーボードを引っ張り出して実験と対応処理をすることにした。

テンキー超邪魔、スペースキーちっさ、いらないキー多すぎ!
せめてテンキーは左側に付けてくれと、マウス置き場に困る。
最近のノートパソコンとか何を考えて無理やりテンキーを載せているの?
ついでに記号を間違えまくる、慣れって怖い。

とにかく xev を使ってテンキーの割り当てを調べる。

gtk.keysyms.KP_Add
gtk.keysyms.KP_Subtract
gtk.keysyms.KP_Enter

なのね、コレさえ解れば処理は簡単。
KP_Plus とかが gtkkeysyms で見つからなかったけどこんな名前なのね。
NumLock off でもコレらのキーコードは同じだと今頃知った。

次はメディアキー。
だが、xev で確認できない。

totem のソースを見る。
totem-media-player-keys.c を見ると DBus を使っている。
DBus って 0.3.3 の時にマルチスレッド処理に影響が出たので外したんだが…
できれば避けたい。

というか今頃気がついたけど Rhythmbox や Totem はアクティブでない場合でもメディアキーに反応するのね。
BGM を流した状態で作業を行っている最中に再生アプリをアクティブ化せずとも Play/Pause や次を再生にできるということ。
Global Hotkey というみたい。

検索してみるとこのキーを使っている人って結構多いのね。
WinAmp とか有名どころは上記動作ができたらしい。
パソコンで音楽なんて聴かないから知らなかったよ。
というか音楽自体を全然聴かなくなったような…

つか動画では無意味な機能だ。
バックグラウンドムービーなんて相当痛々しい奴しかやらネェってば。

よし、Y901x では無視しようw
ということで変更はソレだけなのだけど 0.3.9 の公開。

それよりキーボードのストレスが凄い、RealForce に戻す。

とはいえ Global Hotkey というのは少し興味ある。
常駐している秀丸をホットキーで呼び出すみたいな動作を Linux でやりたいとか思う時があるかもしれない。
GNOME なら Alt+F2 を使えで終わるけどさ。
「gnome-ter」まで打ち込んで Tab キーを叩くとあら不思議みたいな。
…やっぱりいらないな。

Eye of GNOME 3 plugin Create

Rename Dialog Eye of GNOME 3.* plugin 公開。

EyeOfGnome/Plugins – GNOME Live!

あれ、一番下に
~/.config/eog/plugins/ に入れろって書いてある。
3.0 の時からそう書いていたかな?記憶では v2 と同じ位置に置けと…
つか confug ディレクトリなのか、開発者によって色々思う所あるんだろうなと…

普通にサンプルコードが動いてしまったのだけど。
ちなみに v2 時は ~/.gnome2/eog/plugins に入れろであった。

Eye of GNOME プラグインは本当に面倒 | PaePoi

v2 の時には Gedit Plugin と同様に activate で window ポインタをアトリビュートにして保持すると終了できないという「ワケガワカラナイヨ」な動作をしやがったたので強引な奇策を使う必要があった。

だけど v3 はソレで全然問題ないみたい。

というか。
class 直下にポインタ保持変数を書くのは初期化に限られていると思う、うっかり self を忘れがちなどころか継承を行うと場合によっては知らぬ間に書き換えらている可能性があるので正直勧められない。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
#-*- coding:utf-8 -*-
 
class Cls(object):
    def __init__(self):
        self.first = "3rd"
 
class Test(Cls):
    first = "1st"
    def __init__(self):
        Cls.__init__(self)
        print "2nd"
        print self.first
 
if __name__ == "__main__":
    Test()
 
""" output
2nd
3rd
"""

継承元を熟知していないと Python の場合こういうことが起こるのよ。
__init__ の下に書きたくない場合は絶対に継承元が利用していないと断言できる変数名にするしかない。
とはいえ公式が公開しているサンプルコードなので同様に書くのが一番初心者が迷わないよな。
まあソレは余談で。

*.plugin の IAge が 2 指定のままなのが気になるけど。
IAge=3 に書き換えてもどっちでも動くや、意味ネェ。
多分このキーを完全無視しているだけなのだろう。

とにかく Gedit とほぼ同一に書いて問題なくなったぞと。
Gedit Plugin の解説が知らぬ間にスゲェ細かくなてるね。
書き方が解らない場合は Gedit のを参考にすればいいかなと。

Gedit/PythonPluginHowTo – GNOME Live!

次は Unicode へ変換して日本語を含む文字数を算出する方法に困った。
ダイアログ表示時に拡張子を除いた部分のみを選択するのに必要な処理。
gtk_entry_get_text() 戻り値から 普通に unicode(str) でイケなかったのよ。

v2 時はイケた、というか PyGtk がそうバインドされていた。
どうやら PyGI ではあの親切極まりない全自動変換は諦めるしか無いようだ。
ならば GLib を使ってドット位置の long 値を見つければいいと考えた。

Unicode Manipulation

上記で strstr から for 文のような処理が使える方法を探すも上手くいかない。
GLib.utf8_substring(s, 0, 3) とかで日本語も一文字扱いなのは解ったけどドット位置を日本語も一文字扱いで探す方法が見つからない。
というか UCS4 への変換すらできない、Python で扱えないということか。
やはり Python の強力な文字列変換機能に頼りたい。

Character Set Conversion

g_filename_display_name () というのを見つけて実験。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python
#-*- coding:utf-8 -*-
 
from gi.repository import Gtk, GLib
 
class Win(Gtk.Window):
    """
        Unicode Test
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_title("Gtk3")
        entry = Gtk.Entry()
        button = Gtk.Button("Button")
        button.connect("clicked", self.on_clicked, entry)
        vbox = Gtk.VBox()
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(entry, False, False, 0)
        self.add(vbox)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        self.set_focus(entry)
 
    def on_clicked(self, widget, entry):
        s = entry.get_text()
        print s
        #unicode(s) #=> UnicodeDecodeError
        displayname = GLib.filename_display_name(s)
        #print displayname #=> UnicodeDecodeError
        print len(unicode(displayname))
 
if __name__ == "__main__":
    Win()
    Gtk.main()

display_name 変換後であれば Unicode 変換が可能になる。
しかし display_name を標準出力するとエラー、ややこしいよ。
GTK3 は ucs4 を utf-8 として扱っているみたいだからえっと…
つまりメモリ内での扱いが少々違っていることだけ解ればいい、と思う。

Activate 時の Action とかを本家が全然解説していないけど Gedit とまったく同じ。
下記から PyGI コードに自力変換する、そんなに難しくはない。

Eye of GNOME Reference Manual

他色々と v2 時とは違っていて。
画像表示中に EogListStore から削除すると EogThumbView が死ぬとかリネーム前のファイルを取り除くのは最後にしないとサムネイル更新が上手くいかないとかets…
キリがないので後はコードを見てで済ませる。

とにかくこんなコードでやっと成功した。
多分外国人のほうが興味があると思うのでヘタクソな英語にしました。
日本人って自分で作らず探しているだけの奴ばっかだもんなぁ…

renamedlg.plugin

[Plugin]
Loader=python
Module=renamedlg
IAge=2
Name=rename
Name[ja]=リネーム
Description=view of file rename
Description[ja]=観覧中のファイルをリネーム
Authors=sasakima-nao <m6579ne998z@gmail.com>
Copyright=Copyright © 2012 sasakima-nao <m6579ne998z@gmail.com>
Website=http://palepoli.skr.jp/

renamedlg.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#-*- coding:utf-8 -*-
 
#    Eye of GNOME renamedlg plugin version 3.0.0
#    Copyright © 2012 sasakima-nao <m6579ne998z@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    Eye of GNOME Plugins
#    Eye of GNOME Reference Manual
#
#   2012.08.28 3.0.0
 
from gi.repository import GObject, Gtk, Eog, Gio, GLib
import os
 
ui_str = """<ui>
    <menubar name="MainMenu">
        <menu action="Edit">
            <separator/>
            <menuitem action="rename"/>
        </menu>
    </menubar>
</ui>"""
 
class RenameDlgPlugin(GObject.Object, Eog.WindowActivatable):
    """
        Rename Dialog Plugin for eog 3.*
        Actibate from Menu select or F2 key
    """
    window = GObject.property(type=Eog.Window)
    def __init__(self):
        GObject.Object.__init__(self)
 
    def do_activate(self):
        uimanager = self.window.get_ui_manager()
        self._action_group = Gtk.ActionGroup("RenameActions")
        actions = [("rename", None, "Rename", "F2", "Rename", self.on_rename)]
        self._action_group.add_actions(actions)
        uimanager.insert_action_group(self._action_group, 0)
        self._ui_id = uimanager.add_ui_from_string(ui_str)
 
    def do_deactivate(self):
        uimanager = self.window.get_ui_manager()
        uimanager.remove_ui(self._ui_id)
        uimanager.remove_action_group(self._action_group)
        uimanager.ensure_update()
 
    def on_rename(self, action, data=None):
        img = self.window.get_image()
        if img == None:
            return
        fullname = img.get_uri_for_display()[7:]
        path, name = os.path.split(fullname)
        label = Gtk.Label(name)
        entry = Gtk.Entry()
        entry.set_text(name)
        d = Gtk.Dialog( "Rename",
                        self.window,
                        Gtk.DialogFlags.MODAL,
                        (Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
                        Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT) )
        vbox = d.get_content_area()
        try:
            vbox.pack_start(label, False, False, 0)
            vbox.pack_start(entry, False, False, 0)
            d.show_all()
            def dlg_ok(self):
                d.response(Gtk.ResponseType.ACCEPT)
            def on_focus_in(self, widget):
                try:
                    # Calculate the number of characters in Unicode
                    # (e.g. Japanese Language)
                    # Converted to a Display Name
                    s = entry.get_text()
                    displayname = GLib.filename_display_name(s)
                    u = unicode(displayname)
                    i = u.rindex(".")
                    entry.select_region(0, i)
                except ValueError:
                    # Full select
                    pass
            def on_focus_out(self, widget):
                entry.select_region(0, 0)
            entry.connect("activate", dlg_ok)
            entry.connect("focus-in-event", on_focus_in)
            entry.connect("focus-out-event", on_focus_out)
            # Loop until success or Cancel
            while 1:
                if d.run() == Gtk.ResponseType.ACCEPT:
                    text = entry.get_text()
                    if text == "":
                        self.messagebox("File name is empty")
                        entry.set_text(name)
                    elif text == name:
                        self.messagebox("Have not changed")
                    elif  text in os.listdir(path):
                        self.messagebox("Found the same file name")
                    else:
                        # Get the EogListStore
                        store = self.window.get_store()
                        # Rename
                        newname = os.path.join(path, text)
                        os.rename(fullname, newname)
                        # Turn the queue
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        # Create EogImage
                        f = Gio.file_new_for_path(newname)
                        newimg = Eog.Image.new_file(f)
                        # Insert EogListStore
                        store.append_image(newimg)
                        # EogThumbView
                        tv = self.window.get_thumb_view()
                        tv.set_current_image(newimg, True)
                        # Turn the queue
                        while Gtk.events_pending():
                            Gtk.main_iteration()
                        # Remove Image from EogListStore
                        store.remove_image(img)
                        break
                else:
                    # Cancel Button
                    break
        finally:
            d.destroy()
 
    def messagebox(self, text):
        dlg = Gtk.MessageDialog(
                self.window,
                Gtk.DialogFlags.MODAL,
                Gtk.MessageType.WARNING,
                Gtk.ButtonsType.OK,
                text)
        dlg.set_title("Eye of GNOME"
        r = dlg.run() 
        dlg.destroy()
        return r

Now, move all of these to ~/.config/eog/plugins/

Pless F2 Key

Enjoy!