AIが変える未来 ― そしてFVWMが再び輝く理由

2026年1月——私たちは今、新しい時代の入り口に立っています。

胸の奥で静かに、しかし確かに興奮が湧き上がってくるのを感じます。

私はインターネット黎明期を体験した世代です。まだ「ホームページ」という言葉すら珍しかった頃、世界中のコンピュータが繋がり始めた、あの不思議な高揚感を覚えています。「これは何か大きなことが始まる」——理屈ではなく、肌で感じた予感でした。

今、あの時と同じ感覚が蘇っています。いや、それ以上かもしれません。

インターネットは「情報の流れ」を変えました。しかしAIは「思考の流れ」そのものを変えようとしています。これは単なる技術革新ではありません。私たちの哲学を、価値観を、世界の見方を根底から揺さぶる——そんな巨大な渦の中に、今まさに私たちは飲み込まれようとしているのです。

AIがもたらす創造の革命

かつてAIといえば「計算が速いコンピュータ」程度のイメージでした。しかし今や、考え方そのものをAIと対話しながら練り上げることができます。

哲学的な問いかけに応え、アイデアを膨らませ、それを形にする。

思想から価値の創造、表現、そして実現まで。

AIはその全てのプロセスに寄り添えるパートナーになりました。

これは単なる「便利なツール」の話ではありません。人間の創造性を拡張する存在としてのAIが、今まさに私たちの目の前に現れているのです。

ウィンドウマネージャとAIの融合

私はこの流れの中で、ひとつの確信を持っています。

ウィンドウマネージャとAIは融合していく。

なぜなら、AIとの協働作業において最も重要なのは「効率的な操作環境」だからです。マウスでメニューを辿る時代は終わりつつあります。キーボードから手を離さず、思考の速度でコマンドを発し、AIと対話し、結果を即座に反映する。そんなワークフローが求められています。

FVWMの復活

そう考えた時、FVWMというウィンドウマネージャが再び脚光を浴びる日が来ると私は確信しています。

「FVWM?あの古いやつ?」と思われるかもしれません。しかし、FVWMには現代のAI時代にこそ活きる特性が揃っています。

1. 究極のキーバインディング

FVWMでは、あらゆる操作をキーボードショートカットに割り当てられます。ウィンドウの移動、リサイズ、アプリの起動——全てがキーひとつ。AIとのチャットを開きながら、コードエディタを呼び出し、ブラウザで調べ物をする。この一連の流れを、指がキーボードから離れることなく実行できます。

2. 無限のカスタマイズ性

FVWMの設定ファイルはテキストベース。つまり、AIに設定を書いてもらうことも可能です。「こういう動作をさせたい」と伝えれば、AIが設定コードを生成してくれる。人間とAIの協働でデスクトップ環境を構築する——これは他のウィンドウマネージャでは難しい体験です。

3. 圧倒的な軽量さ

FVWMは驚くほど軽量です。システムリソースをほとんど消費しないため、その分のパワーをAI処理やブラウザ、開発ツールに回せます。重厚なデスクトップ環境に疲れた人にこそ、この軽快さは新鮮に映るでしょう。

4. コードとの親和性

設定がテキストファイルである利点はもうひとつあります。Gitで管理できるのです。設定の変更履歴を追跡し、複数のマシンで同じ環境を再現する。プログラマにとって、これほど心強いことはありません。

5. シンプルなランチャー機能

FVWMでは、簡単なコマンドひとつでランチャーを作れます。「このキーを押したらこのアプリが起動する」——それだけのことが、設定ファイルに1行書くだけで実現できます。複雑なGUIツールは不要です。

6. Webサーバとの連携

FVWMはシェルコマンドを自由に呼び出せるため、Webサーバの操作やAPIの実行もデスクトップから直接行えます。ローカル環境とクラウドの境界がシームレスになる——これもAI時代の働き方にマッチしています。

新しい時代のデスクトップ

FVWMは一部のマニアの間で、再び注目されるでしょう。

華やかなアニメーションも、透過効果もありません。しかしそこには、思考を妨げない純粋な作業空間があります。AIと向き合い、アイデアを形にする。そのための道具として、FVWMはこれ以上なく適しています。

古いものが新しい価値を持つ。テクノロジーの世界では、そういうことが時々起こります。

FVWMの復活は、その好例になるかもしれません。

お役立ちホームページのリンク整理・スタイル改善

作業概要

お役立ちホームページ(/oyakudachihomepage)の古いリンクを整理し、新規リンクを追加。CSSスタイルも改善しました。

作業前の状態

4つのリンクが掲載されていた:

  • Web Shufu – 内容が別サイトに変更されていた
  • DESIGN IT! w/LOVE – リダイレクトで不安定
  • クロの思考ノート – 正常稼働
  • WordPress Codex 日本語版 – 文字化けで使用不可

実施した作業

  1. データベースバックアップ取得
  2. 古いリンク3件を削除(クロの思考ノートは残す)
  3. 新規リンク8件を追加(計9リンク)
  4. カテゴリ分けして整理(AI、Linux、Python、API)
  5. CSSスタイル改善(蛍光グリーン → GitHub風ブルー)
  6. リスト形式を番号から点に変更

追加したリンク

【AI・対話型アシスタント】

  • ChatGPT – OpenAIの対話AI
  • Claude – Anthropicの思慮深いAI

【Linux・デスクトップ環境】

  • FVWM3 公式リポジトリ
  • クロの思考ノート(既存)
  • Ubuntu Packages

【Python・データ分析】

  • DEAP Documentation
  • Pandas公式ドキュメント

【API・開発リソース】

  • Bybit API Documentation
  • GNOME Developer

技術メモ

自動リンク化の仕組み:script.php内のJavaScriptが、class=elk内のURLを自動的にリンク化している。

結果

お役立ちホームページが見やすくなりました!

fvwm3 1.1.2 (1.1.1-2-gefef84cf)のインストール

FVWM3のアップデートがありましたね。(2024-11-30)
変更点は以下に
https://github.com/fvwmorg/fvwm3/blob/main/CHANGELOG.md

インストール方法が変わりました。

fvwm3 has traditionally been using autotools. However, this is now deprecated in favour of meson. It is suggested that all systems which support meson use this instead as it is now the preferred build system to use.

autotoolsからmeson推奨になりました。

gitからダウンロードして
mesonとninjaをpipでインストールしました。ubuntuのパッケージでインストールmesonだとバージョンが古いとのエラーを出すためです。

~/.local/bin/meson setup build -Dhtmldoc=true -Dmandoc=true
~/.local/bin/ninja -C build
sudo ~/.local/bin/ninja -C build install

でインストールを行った

FVWM3のインストール

FVWM3の最新をGitHubよりインストールする。
https://github.com/fvwmorg/fvwm3

#!/bin/bash

# ユーザーのキー入力を待つ関数
pause() {
  read -n1 -r -p "$1" key
  echo
}

echo "FVWM3のインストールを開始します。"

# 必要なパッケージをインストール
echo "必要なパッケージをインストールしています..."
sudo apt update
sudo apt install -y libevent-dev libx11-dev libxrandr-dev libxrender-dev libxt-dev libxft-dev asciidoctor \
libfontconfig-dev libfreetype6-dev libfribidi-dev libncurses5-dev libpng-dev libreadline-dev \
librsvg2-dev libsm-dev libxcursor-dev libxext-dev libxi-dev libxpm-dev sharutils

pause "パッケージのインストールが完了しました。続行するにはキーを押してください..."

# 'fvwm3'ディレクトリが存在するか確認
if [ -d "fvwm3" ]; then
  echo "'fvwm3'ディレクトリが既に存在します。"

  # ディレクトリがGitリポジトリか確認
  if [ -d "fvwm3/.git" ]; then
    echo "既存のディレクトリはGitリポジトリです。"
    while true; do
      read -p "最新の変更を取得するために'git pull'を実行しますか? (y/n): " yn
      case $yn in
          [Yy]* ) cd fvwm3; git pull; break;;
          [Nn]* ) echo "既存の状態で続行します。"; cd fvwm3; break;;
          * ) echo "はい(y)かいいえ(n)でお答えください。";;
      esac
    done
  else
    echo "既存のディレクトリはGitリポジトリではありません。"
    while true; do
      read -p "ディレクトリを削除して再クローンしますか? (y/n): " yn
      case $yn in
          [Yy]* ) rm -rf fvwm3; echo "ディレクトリを削除しました。"; break;;
          [Nn]* ) echo "処理を中止します。スクリプトを終了します。"; exit 1;;
          * ) echo "はい(y)かいいえ(n)でお答えください。";;
      esac
    done

    # GitからFVWM3をクローン
    echo "FVWM3のリポジトリをクローンしています..."
    git clone https://github.com/fvwmorg/fvwm3.git
    pause "リポジトリのクローンが完了しました。続行するにはキーを押してください..."
    cd fvwm3
  fi
else
  # GitからFVWM3をクローン
  echo "FVWM3のリポジトリをクローンしています..."
  git clone https://github.com/fvwmorg/fvwm3.git
  pause "リポジトリのクローンが完了しました。続行するにはキーを押してください..."
  cd fvwm3
fi

# autogen.shを実行
echo "autogen.shを実行しています..."
./autogen.sh

pause "autogen.shの実行が完了しました。続行するにはキーを押してください..."

# コンパイルオプションを環境変数で設定
echo "コンパイルオプションを設定しています..."
export CPPFLAGS="-march=native -O2"
export CFLAGS="-march=native -O2"

# configureを実行
echo "configureを実行しています..."
./configure --enable-mandoc --enable-golang --enable-htmldoc

pause "configureが完了しました。続行するにはキーを押してください..."

# ビルドを実行
echo "FVWM3をビルドしています..."
make clean
make -j$(nproc)

pause "ビルドが完了しました。続行するにはキーを押してください..."

# インストールを実行
echo "FVWM3をインストールしています..."
sudo make install

echo "FVWM3のインストールが完了しました。"
    1. 使用方法
      新しいファイルを作成し、例えばinstall_fvwm3.shという名前を付けます。
    2. 上記のスクリプトをそのファイルにコピーします。
    3. スクリプトに実行権限を与えます。
      chmod +x install_fvwm3.sh
    4. スクリプトを実行します。
      ./install_fvwm3.sh

    各ステップで進捗が表示され、続行する前にキー入力を求められます。これにより、各段階でプロセスを確認できます。

    注意事項:

    • スクリプト内でsudoを使用しているため、実行時にパスワードの入力が求められる場合があります。
    • スクリプトは現在のディレクトリにfvwm3というディレクトリを作成します。
    • 必要に応じて、スクリプトの内容を変更して環境に合わせてください。

Ubuntuのapt upgradeでUbuntu Proの通知がうざい

以下のメッセージがapt upgradeで表示される。肝心の必要な情報は見づらい。
以下のファイルをコメントアウトすると消える。

Get more security updates through Ubuntu Pro with 'esm-apps' enabled:
  python2.7-minimal imagemagick libjs-jquery-ui libopenexr25 libpostproc55
  libmagickcore-6.q16-6-extra libavcodec58 libmagickwand-6.q16-6 libavutil56
  imagemagick-6.q16 libswscale5 libmagickcore-6.q16-6 libswresample3
  imagemagick-6-common libavformat58 python2.7 libpython2.7-minimal
  libpython2.7-stdlib libavfilter7
sudo vi /etc/apt/apt.conf.d/20apt-esm-hook.conf
#APT::Update::Pre-Invoke {
#       "[ ! -e /run/systemd/system ] || [ $(id -u) -ne 0 ] || systemctl start --no-block apt-news.service esm-cache.service || true";
#};

#APT::Update::Post-Invoke-Stats {
#       "[ ! -f /usr/lib/ubuntu-advantage/apt-esm-hook ] || /usr/lib/ubuntu-advantage/apt-esm-hook || true";
#};

#binary::apt::AptCli::Hooks::Upgrade {
#       "[ ! -f /usr/lib/ubuntu-advantage/apt-esm-json-hook ] || /usr/lib/ubuntu-advantage/apt-esm-json-hook || true";
#};

Deapの遺伝子の1の出現数を固定する

from deap import tools,base,creator
import random

toolbox = base.Toolbox()
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)

saidai=30  ##1にする数

def makegene(container):
    hako=[0]*200
    sel=random.sample(range(len(hako)),saidai)
    for h in sel:
        hako[h]=1
    return container(hako)

toolbox.register("individual", makegene, creator.Individual)
#toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, 200)
ind=toolbox.individual()
print(ind)
[0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
ind.count(1)
30

上記では、1が30個に固定されている。この数をランダムに1から30個までにする場合。

from deap import tools,base,creator
import random

toolbox = base.Toolbox()
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)

saidai=30 ##1から最大数saidaiまでにする

def makegene(container):
    hako=[0]*200
    sel=random.sample(range(len(hako)),random.randint(1,saidai))
    for h in sel:
        hako[h]=1
    return container(hako)

toolbox.register("individual", makegene, creator.Individual)
ind=toolbox.individual()
print(ind)
print(ind.count(1))

DEAP を使う

pythonにて遺伝的アルゴリズムを試してみる。
DEAP is a novel evolutionary computation framework

%%time

import random
import numpy
from deap import algorithms
from deap import base
from deap import creator
from deap import tools

creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)
toolbox = base.Toolbox()
toolbox.register("attr_bool", random.randint, 0, 1)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, 200)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

def evalOneMax(individual):
    total=individual.count(1) ###1の個数を数える
    return(total),

toolbox.register("evaluate", evalOneMax)
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutFlipBit, indpb=0.03)
toolbox.register("select", tools.selTournament, tournsize=3)

def main():
    random.seed(64) ##random Fix
    pop = toolbox.population(n=300)
    hof = tools.HallOfFame(1)
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    #stats.register("avg", numpy.mean)
    #stats.register("std", numpy.std)
    stats.register("min", numpy.min)
    stats.register("max", numpy.max)

    algorithms.eaSimple(pop, toolbox, 0.5, 0.2, 50, stats, halloffame=hof)

    return pop

if __name__ == "__main__":
    pop=main()
    best_ind = tools.selBest(pop, 1)[0]
    print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values))
gen nevals min max
0 300 78 120
1 187 89 122
2 157 96 127
3 176 99 129
.
.
.
49 178 174 189
50 187 172 189
Best individual is [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], (189.0,)
CPU times: user 1.09 s, sys: 688 µs, total: 1.09 s
Wall time: 1.09 s

このパラメーターだと50世代で189になり、200が作られていない。algorithms.eaSimpleの50を300に増やすと200世代と少しで、200の個体が作られる
algorithms.eaSimple(pop, toolbox, 0.5, 0.2, 100, stats, halloffame=hof)

pandas.DataFrameを作る

indexにDatetimeIndexでdtype=’datetime64[ns]の値、Closeで100-190の値のDataFrameを作る

import pandas as pd

df_test=pd.DataFrame({
'Date':[
'2022-08-19 00:30:00',
'2022-08-19 00:45:00',
'2022-08-19 01:00:00',
'2022-08-19 01:15:00',
'2022-08-19 01:30:00',
'2022-08-19 01:45:00',
'2022-08-19 02:00:00',
'2022-08-19 02:15:00',
'2022-08-19 02:30:00',
'2022-08-19 02:45:00'],
'Close':[
100,
110,
100,
120,
140,
150,
120,
100,
180,
190]
}
)
df_i = df_test.set_index('Date')
df_i.index=pd.to_datetime(df_i.index)

中身をチェック。
class ‘pandas.core.frame.DataFrame’
DatetimeIndex: 10 entries, 2022-08-19 00:30:00 to 2022-08-19 02:45:00

df_i.info()

listに代入する
class ‘pandas._libs.tslibs.timestamps.Timestampになってる

list=[]
for da in df_i.index:
    print(type(da))
    list.append(da)

datetime型にする

type(list[0].to_pydatetime())

リストからdatetime型にする

for data in list:
    #print(type(data))
    time = data.to_pydatetime() # datetime型に変換
    #print(type(time))
    print(time)

Pandas Tips

ファイルをPanda DataFrameに読み込む

import pandas as pd

usecols = ['Date','Open','High','Low','Close','Volume']
file="~/sshmnt/candles_15m_bybit_BTC-USDT.csv"
data = pd.read_csv(file,names=usecols,parse_dates=True,index_col='Date')

print(len(data))

#data2=data[45700:45800]
#data2=data[46000-1000:46000]
#data2=data[0:59000]
data2=data.tail(120+4*24*1) ##1日分+120
print(len(data2))

data3=data2.copy()

close_data=data3["Close"]
close_data.plot()

列を追加

data3['Trend'] = 0
data3
#またはNaNを入れることも
data3['Trend'] = np.nan

列(Trend)を削除

data3.drop('Trend',axis=1)

列を抽出

#カラム1つ
data4["Close"]
#または
data4.Close
#カラムを2つ
data4[["Close","signal"]]

特定の行の表示、インデックスから

data3['2022-09-02 10:0:0' : '2022-09-02']

特定範囲の行(インデックス)の特定列に代入する(1)

data3['2022-09-02 10:0:0' : '2022-09-02']['Trend']=2

特定の範囲のindex行の、特定列に代入する。(2) *上記の(1)が動作しない場合

data3.loc['2022-09-01 09:00:00':'2022-09-01 23:0:0',"Trend"]=-1

atでこのようなスライスでの使い方はエラーがでるので使えない

data3.at['2022-09-01 09:00:00':'2022-09-01 23:0:0',"Trend"]=-1

特定範囲の抽出

ts= "2022-09-01 09:00:00"
ts2="2023-01-01 09:00:00"
data2= (data[ts : ts2])
data2

JupyterでPandasの表示設定

pd.set_option('display.max_columns', 10)
pd.set_option('display.max_rows', 300)

データフレームの作成、日付時間をインデックスに

df_test=pd.DataFrame({
'Date':[
'2022-08-19 00:30:00',
'2022-08-19 00:45:00',
'2022-08-19 01:00:00',
'2022-08-19 01:15:00',
'2022-08-19 01:30:00',
'2022-08-19 01:45:00',
'2022-08-19 02:00:00',
'2022-08-19 02:15:00',
'2022-08-19 02:30:00',
'2022-08-19 02:45:00']
}
)
df_i = df_test.set_index('Date')

上記ではインデックスが作れるが、dtypeがobjectになってる

df_i.index

Index(['2022-08-19 00:30:00', '2022-08-19 00:45:00', '2022-08-19 01:00:00',
'2022-08-19 01:15:00', '2022-08-19 01:30:00', '2022-08-19 01:45:00',
'2022-08-19 02:00:00', '2022-08-19 02:15:00', '2022-08-19 02:30:00',
'2022-08-19 02:45:00'],
dtype='object', name='Date')

上記をデイトタイムにするにはこれ

df_i.index=pd.to_datetime(df_i.index)

インデックスを見るとIndexがDatetimeIndexのdtypeがdatetime64になった

df_i.index

DatetimeIndex(['2022-08-19 00:30:00', '2022-08-19 00:45:00',
'2022-08-19 01:00:00', '2022-08-19 01:15:00',
'2022-08-19 01:30:00', '2022-08-19 01:45:00',
'2022-08-19 02:00:00', '2022-08-19 02:15:00',
'2022-08-19 02:30:00', '2022-08-19 02:45:00'],
dtype='datetime64[ns]', name='Date', freq=None)

インデックスを削除する

data4=data4.reset_index()

インデックスを付ける

data4=data4.set_index("Date", inplace=False)
# inplace=Trueは
data4.set_index("Date", inplace=True) ### data4をイコールで指定しなくても適応される

ipynbのファイルをtxtに変換する。で、特定語を含むファイルを検索する。

Jupyterlabのipynbファイルの内容を検索したい。その為にtxtだけのファイルに変換する。

入出力リダイレクトを使うので、新しくディレクトリを作り、そこに対象のipynbファイルをコピーする。
安全の為にそのディレクトリで操作を行う。

#!/usr/bin/bash

###
### ipynb to txt化
###

find ./ -name "*.ipynb" >list_ipynb

while read LINE
do
echo "$LINE"
LINE2=${LINE/ipynb/txt}

echo "$LINE2 に変換する"
jq -j '.cells | map( select(.cell_type == "code") | .source + ["\n\n"] ) | .[][]' < $LINE > $LINE2
done < list_ipynb
#!/usr/bin/bash

txt化したファイルができたら、検索語をxargs grepでファイルごとに検索する。

find ./ -name "*.txt" | xargs grep $1