systemdで起動しているGentooでfcitxを使う

一応ブログとしてまとめておく


環境

  • Gentoo Linux (Kernel 5.10.22)
  • init: systemd (247.2-r4)
    OpenRCの方は多分関係ない
  • DM: LightDM (1.30.0-r2)
    .xprofileを読むことに関係する(多分それだけ)
  • DE: i3-gaps (4.18.2)
    たぶん結果的にはあんまり関係ない

結論

  • .xprofiledbusを起動するな
  • USEフラグuser-sessionsを有効にしろ

どっちかだけだとダメだったので多分両方

以下は曖昧な記憶を辿って(状況再現をサボりつつ)書いただけなので参考にならんかも 文章としても崩れてるし

経緯

Fcitx使って日本語を入力したいのでとりあえずWikiを見る

https://wiki.gentoo.org/wiki/Fcitx/ja

  1. app-i18n/fcitxをemergeする
    ついでにapp-i18n/fcitx-configtoolとかapp-i18n/fcitx-skk(skkユーザなので)とか入れる
  2. (LightDMなので).xprofileに以下を記載
    USEフラグは特にいじっていない=デフォルトでついてるgtk3ありでビルドしてるのでximではなくfcitx

    export XMODIFIERS="@im=fcitx"
    export QT_IM_MODULE=fcitx
    export GTK_IM_MODULE=fcitx
    
  3. Gentoo wikiにこんなことが書いてある

    Fcitx が起動する前に D-Bus セッションバスを起動することをお勧めします。これらを X のスタートアップファイル (~/.xprofile または ~/.xinitrc) の最初に追加してください: (gentoo wikiより)

    なるほど(脳死 これ多分OpenRCの人だけなんですよね) 追加する

    eval "$(dbus-launch --sh-syntax --exit-with-session)"
    
  4. 動かんやんけ

という感じだった


fcitx-diagnoseすると「正しくfcitxに接続できません。」とか出る

fcitxコマンドを直接入力してみるとdbusがどうのこうの言っている
Connection Error (Failed to connect to socket /tmp/dbus-XXXXXXXXXX: Connection refused) とかも出てた気がする
これでggるとWSLの記事が大量に出てそうじゃねえよとなったり

私自身dbusに全然詳しくなかったしあんまり勉強する気にもなれなかったので適当に調べまくっているとArchのwikiにこんな記述が

プログラムによっては D-Bus ユーザーメッセージバスを必要とすることがあります。D-Bus は伝統的に dbus-launch によってデスクトップ環境の起動時に実行されていましたが、バージョン226から、systemd がユーザーのメッセージバスを管理するようになりました。 https://wiki.archlinux.jp/index.php/Systemd/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC#D-Bus

そこで.xprofileからdbus-launchを消してみる

ついでにGentoo Wikidbusのページ見てるとuser-sessionsとかいうそれっぽい(Connection refusedと関係がありそうな)USEフラグがあることに気付く そこでそいつを加えてリビルドしてみる

→動いた 一気にやったのでどっちが原因かは明確にはわからなかったけど後で片方ずつやってみたところどちらかが欠けるとダメだったので両方必要だったらしい

ちなみにこれでfcitx5もちゃんと動いた

おまけ

fcitx-skkでAlacritty使ってるとBackspace押したときに文字が入力できなくなります。私の環境ではそうなりました。これはEscとかCtrlとかShiftを(単)押すと戻ります。
UXTermとかだと問題ないし、直接入力にすると起きなくなるので、Fcitx-skk+Alacrittyの複合問題っぽい?

原因は不明ですが調査するのが面倒だったのでTilixにしました。ebuildそのうち書くかも

react-scripts+TypeScript環境でESLint効かせつつaliasする

メモ。ここでいうaliasってのは@/hogeとかでimportするやつ(Webpackの言う"alias")

↓とかあるけどcreate-react-appで作成してreact-scripts使う時は一工夫要る

qiita.com

TypeScript

何はともあれtsconfig.json
react-scriptsが起動するたびにこいつを書き変えてしまうので、extendsを使ってpathsを切り出す

tsconfig.json

{
  "extends": "./tsconfig.paths.json"
}

tsconfig.paths.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
  }
}

Webpack

react-scriptsの後ろにいるWebpackの設定をいじる方法がない
かといってejectはしたくないので、react-app-rewiredを使う

設定はconfig-overrides.jsに記述する

const path = require('path');

module.exports = function override(config) {
  config.resolve = {
    ...config.resolve,
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  };
  return config;
};

ESLint

importまわりのLintをやってくれるeslint-plugin-importsというのがあるが、そのままではaliasをよしなにしてくれない

Webpackの設定を使ってくれるeslint-import-resolver-webpackがあるが、webpack.config.jsが必要となる
config-overrides.jsの内容をコピペしても良いが、うまいこと対応する方法があり、これをやると保守性が高くてよさそう

webpack.config.js

const { paths } = require('react-app-rewired');
const overrides = require('react-app-rewired/config-overrides');
const config = require(paths.scriptVersion + '/config/webpack.config.js');

module.exports = overrides.webpack(config, process.env.NODE_ENV);

TypeScriptプロジェクトの場合は.ts(x)ファイルもESLintに認識してもらう必要がある(1敗)が、eslint-import-resolver-typescriptを使うとその辺をよしなにしてくれるので使うと良い

.eslintrc.json

{
  "extends": [
    "plugin:import/errors",
    "plugin:import/typescript"
  ],
  "settings": {
    "import/resolver": {
      "webpack": {},
      "typescript": {}
    }
  },
  "plugins": [
    "import"
  ]
}

メモ(諸々含めた自分用コピペテンプレート) Prettier用の設定とかも入ってるので自分以外の人はよしなに

↓はreact-app-rewiredとESLint関係とPrettier関係(適宜更新する)

yarn add -D react-app-rewired @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-import-resolver-typescript eslint-import-resolver-webpack eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-unused-imports prettier

eslintrc.json

{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:import/errors",
    "plugin:import/typescript",
    "prettier"
  ],
  "root": true,
  "env": {
    "node": true
  },
  "settings": {
    "react": {
      "version": "detect"
    },
    "import/resolver": {
      "webpack": {},
      "typescript": {}
    }
  },
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "react",
    "react-hooks",
    "@typescript-eslint",
    "unused-imports",
    "import"
  ],
  "rules": {
    "no-var": "error",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "sort-imports": "off",
    "unused-imports/no-unused-imports": "error",
    "unused-imports/no-unused-vars": [
      "warn",
      {
        "argsIgnorePattern": "^_"
      }
    ],
    "import/order": [
      "error",
      {
        "alphabetize": {
          "order": "asc"
        }
      }
    ]
  },
  "overrides": [
    {
      "files": [
        "**/*.tsx",
        "**/*.ts"
      ],
      "rules": {
        "react/prop-types": "off",
        "react/display-name": "off",
        "sort-imports": "off",
        "@typescript-eslint/no-unused-vars": "off",
        "@typescript-eslint/no-non-null-assertion": "warn"
      }
    }
  ]
}

.prettierrc

{
  "singleQuote": true,
  "jsxBracketSameLine": true
}

config-overrides.js

/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');

module.exports = function override(config) {
  config.resolve = {
    ...config.resolve,
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  };
  return config;
};

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "baseUrl": "./",
    "noFallthroughCasesInSwitch": true
  },
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules"
  ],
  "extends": "./tsconfig.paths.json"
}

tsconfig.paths.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
  }
}

webpack.config.js

/* eslint-disable @typescript-eslint/no-var-requires */
const { paths } = require('react-app-rewired');
const overrides = require('react-app-rewired/config-overrides');
const config = require(paths.scriptVersion + '/config/webpack.config.js');

module.exports = overrides.webpack(config, process.env.NODE_ENV);

PC組んだ

はらがみが楽しいです。楽しいですがiPadなので色々とキツい。無反応とか。
あとなんかBlue Protocolっていうゲームが出るらしい。PC専用で。
あと黄昏ニ眠ル街っていうゲーム。まだ製品化はされてないけど昨年の冬コミで体験版を手に入れているので遊びたい。

そんな感じで強めのデスクトップPCは以前から欲しかったんですが最近周囲の既存勢が組み直していることもあり爆発したので組むことにしました。

どんなPCにするか

とりあえず上記3ゲームの推奨スペックを見ます。

はらがみ

Blue Protocol

黄昏ニ眠ル街


ということでざっくり

  • CPU: Intel Core i7-7700以上のスペック
  • RAM: 16GB以上
  • GPU: GTX 1060以上

という感じのやつがあればいいんだな〜となりました。
多分やりたいゲームをベースにするのがわかりやすくて良いんだと思います。基準が低すぎるのであんまり選択肢は狭まらないんですが。

RAMはなんとなく倍の32GBにします。

CPUは最近Intelさんの調子も悪そうなのと、周りみんな使ってるRyzenで組みたい感じがしたのでRyzenにします。
あとなんか最近新しいやつ(5000番台)が出たらしいですね。ということでそれにしたくなります。
Core i7相当というとRyzen 7って感じで5800Xかな?と思ったんですが色々見ると微妙そうな感じ。
5600XはRyzen 5なのでCore i5相当かな?って感じで微妙だと思っていたんですが、調べてみると悪くなさそうな感じがしたのでこれにしました。
ファンがついて40kと大変お得。逆に不安になりそうな気もしますが。

GPUはなんか1660Tiとかがコスパいいらしいって聞いたのでそれにします(雑)。

ちもろぐさん(https://chimolog.co)にはお世話になりました。

買うぞ!!!!

自作のロマンがあるので ↓で適当に構成してみます。 pcjisaku.com

最近はBTOもいいらしいですが、BTOにするだけで2万円くらい増えたので自作することにしました。

CPUが売り切れていたので、(別の店で買ってもよかったんですが)折角なので秋葉原まで買い出しに行ったら最初に入った店で普通に見つけたので普通に買いました。マザボとセットで買うと割引ができるらしいのでついでにマザボも買いました。
その後他の店に行ったらそちらの方が割引額がでかかったので、いろいろ見て回ってから決めるべきでした。

余談

は? ちなみに5千円くらい安い

やっちまった… 型番はちゃんと確認しよう。

組むぞ!!!!

組みます。

主な流れはだいたい 自作パソコンの組立て方 | パソコン工房【公式通販】 を参考に。
↑は↑のパーツを使っている前提になっているので、自分のパーツとは違うなぁとなった場合はパーツの説明書読んだりそのパーツでggったりすると大体解決する。

初めてであんまり余裕がなかったので写真とかは撮ってません。

で、完成。

大体4時間かかりました。

構成

  • CPU: Ryzen 5 5600X
  • マザーボード: ASUS TUF GAMING B550-PLUS
    なんか光るけど別に光らなくていいので切った。
  • グラフィックボード: MSI GeForce GTX 1660 AERO ITX 6G OC
    1660Tiのはずが1660になっていました。普通に快適なのでこのままでいいや。
  • ケース: Versa H26
    透明なはずのサイドパネルがめっちゃ曇ってるしめっちゃ傷が入ってますが、ただのフィルムなので取れます(両面)。ネジ穴の周りとかすみっこから剥ぐのが良いと思います。
    あとなんかフロントのファンが光ります。
  • 電源: KRPW-BK650W
    なんか人気っぽかったので適当に選んだ
  • OS1: Windows 10
    USBメモリなんですね。かっこいい。 f:id:xecua:20201218033608j:plain
  • OS2: Gentoo Linux
    Minimal Installation CDのカーネルが5.4でオンボードNICのドライバ(RTL8125B)がないのでネットに繋がらない。
    Arch Linuxのインストールディスクはカーネルが(RTL8125Bが動く)5.9なのでそっち経由でインスコ

あとは適当にHDDとSSDとディスクドライブを積みましたが、SATAケーブルがマザボ付属の2本しかなかったのでディスクドライブを諦めました。買わなきゃ。

感想

完走した感想としては、やっぱり最初のハードルが一番高かったので、TLとかお店にいる詳しい人に聞くのが一番いいんだろうなぁというところです。

パーツ揃ってから組む作業は結構楽しかったので、そういうところも自作の魅力なのかなぁと思いました。

はらがみがヌルヌル動いて楽しい。

i3blocksを導入したら時間が溶けたのが悔しいのでひっかかったところを記録しておく

まぁ2時間くらいしか溶けてないけどね


なんか日本語の文章少なくね???? i3使うような人は英語でええわというタイプの人が多いんでしょうか

ということでi3statusからi3blocksに移行したときのよくわからなかった箇所のメモ

インスコ

Archならcommunityリポジトリにある 他のディストリでも簡単に手に入るやろ(無関心)

$ sudo pacman -S i3blocks

i3の設定

/etc/i3/configあたりのデフォの設定だとi3statusに関係のある箇所は2箇所で、

bar {
        status_command i3status
}

ここはstatus_command i3blocksにすればOK

i3blocksを使うだけならこの時点でi3-msg restart(あるいはi3のデフォの設定なら$mod+shift+r($mod+shift+cではない))で表示が変わるはず

あとは

# Use pactl to adjust volume in PulseAudio.
set $refresh_i3status killall -SIGUSR1 i3status
bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ +10% && $refresh_i3status
bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ -10% && $refresh_i3status
bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute @DEFAULT_SINK@ toggle && $refresh_i3status
bindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ toggle && $refresh_i3status

ここは音声の設定を反映しなくていいなら特に何もしなくていいけど、反映したいなら1行目を

set $refresh_i3status killall -SIGRTMIN+1 i3status

とかにすると良さそう

  • シグナルが送れればいいのでpkillでもkillallでも同じ
  • SIGRTMIN+1は自由に使える系のやつらしい
    +1の数字は多分↑のリンク先に書いてある範囲なら何でもいいんだけど、i3blocksの方の設定で合わせる必要がある

i3blocksの設定

色々勉強するのが面倒な我々のために公式がbootstrapのセットを用意してくれている

github.com

これを~/.config/i3blocksとかにcloneしてくる

で、中にconfig.exampleがあるのでcp config.example configconfigファイルを作成

そのままだと設定は有効化されなくて、有効化したいディレクティブの中のcommandを設定してやる必要がある ただ親切なことにhogeディレクティブなら(configファイルがあるディレクトリ)/hoge/hogeに配置されているので、例えば

[wifi]
command=~/.config/i3blocks/wifi/wifi
INTERFACE=wlp15s0
label=󿪨 # ←Cicaのシンボルなので他のフォントだとうまく見えないかも
interval=60
separator=false

みたいにすれば良い

それぞれのコマンドの中身はシェルスクリプトなのでいじりたければ簡単にいじれるし、↑で設定した値は環境変数としてスクリプトに渡されるらしい 例えば↑で設定したINTERFACEwifi/wifiスクリプトの中でNICにアクセスするのに使われてる

音声に関してはpulseaudio使ってるなら多分volumeよりvolume-pulseaudioの方が良い、知らんけど

あと

[volume-pulseaudio]
command=~/.config/i3blocks/volume-pulseaudio/volume-pulseaudio
#label=♪
#label=VOL
interval=once
signal=10 # ←
#STEP=5%
AUDIO_HIGH_SYMBOL=
AUDIO_MED_SYMBOL=
AUDIO_LOW_SYMBOL=
AUDIO_MUTED_SYMBOL=

コメントつけた部分がi3の方で飛ばすようにしてたSIGRTMIN+nnで、デフォで10になってるけどSIGRTMIN+1飛ばしてるならsignal=1にする必要があるはず

完成品

ひっかかりポイントはこれくらいですかね

あと適当にいじると f:id:xecua:20200913025203p:plain こんな感じになって良い

ちなみにオーディオのところはクリックすると出力先切り換えられたりスクロールすると音量調節できるようになってたりする。すごい

Flutter触ってみた

日記です。内容はありません。


繰り返し洗って使えるマスクを買ったんですが、30回洗ったら捨ててねとのことだったので、回数をメモする手段としてカウンターアプリを作りました。 別にその辺に転がってるアプリでもよかったんですが、Flutterが気になってたのでついでという感じで。

環境構築

Arch Linuxなのでyay使ってAURからFlutter SDKAndroid SDKインスコします。

yay -S flutter android-sdk 

Android SDKが入ったら/opt/android-sdkが生えるので、/opt/android-sdk/tools/bin/sdkmanagerを使って各種ツールを入れていきます。
Android SDKのツールはJava 8のランタイムを使うので、JAVA_HOMEがJava8を指すようにする必要があります。ArchでOpenJDK8を入れると多分/usr/lib/jvm/java-8-openjdkとかになるんじゃないでしょうか。
必要なのはplatform-toolsbuild-toolsplatformsで、後2者はOSによっていろいろあるみたいですが、私のスマホAndroid 9(API Level 28)なので、

JAVA_HOME=/usr/lib/jvm/java-8-openjdk sdkmanager 'platform-tools' 'build-tools;28.0.3' 'platforms;android-28'

となりました。

エディタはVSCodeを使いました。以前別の用途でIntelliJ使おうとしてLinux版微妙だなとなったので。 拡張機能も完成度が高く、VSCodeもちゃんとしたIDEだなぁと感動。
裏で動くプロセスのために、環境変数JAVA_HOMEがちゃんとJava 8を指すようにしないといけないところは注意が必要でした(これで数時間溶けました)。
VSCodeでいうところのlaunch.jsonenvJAVA_HOMEを指定するだけだと不十分でした。

また、flutter doctorするとAndroid SDKのライセンスが云々言われ、flutter doctor --android-licensesで同意したのですが、何故か消えませんでした。 この状態でも普通に動いたのでこの現象は無視しても良いんでしょうかね。

永続化

ローカルで完結させたいのでデータの永続化手段を確保。
sqfliteとかmoorとか検討しましたが、前者は生SQLを扱わないといけない点、後者はコード生成が上手く動かなかった点から採用を見送り、Hiveを採用しました。

dependencies:
  hive: ^1.4.4
  hive_flutter: ^0.3.1
  path_provider:
dev_dependencies:
  hive_generator: ^0.7.1
  build_runner: ^1.10.1

公式のQuick Startではbuild_runnerが1.10.2になってて、Dart SDKが2.10じゃないとダメらしい。Stableはまだ2.9ですよ…?

box.getAtとかbox.deleteAtみたいなAtのついたメソッドはKeyじゃなくてindexで持ってくるせいでエラーの原因になるので、box.getみたいなAtのつかない方を使った方が良いでしょうね(今後のためのメモ)。
https://github.com/hivedb/hive/issues/376

ウィジェット間通信

https://qiita.com/agajo/items/50d5d7497d28730de1d3
を参考に、楽そうだったのでChangeNotifierproviderパッケージのChangeNotifierProviderを利用しました。

boxの操作は非同期処理が必要っぽいので、データを取ってくる側はFutureBuilder使って

class Hoge extends StatelessWidget {
  final int id;
  Hoge(this.id);

  Future<string> getData(BuildContext context) async {
    var box = await Provider.of<DatabaseProvider>(context).box;
    return box.get(id);
  }

  @override
  Widget builder(BuildContext context) {
    return FutureBuilder(
        future: getData(context),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (!snapshot.hasData) return Text('');
          return Text(snapshot.data);
        }
  }

みたいな感じで実装。

逆に、Providerのデータ更新処理を呼び出す側は

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      ...
      floatingActionButton: FloatingActionButton(
          onPressed: () async {
            var title = await showTitleInputDialog(context);
            if (title != null) {
              Provider.of<DatabaseProvider>(context, listen: false)
                  .createNewItem(title);
            }
          },
          tooltip: 'New Item',
          child: Icon(Icons.add)),
    );

みたいな感じで。
Provider.oflisten引数をfalseにしないと、ここでいうcreateNewItemみたいなデータを更新するメソッドが使えないのは注意が必要かなぁという感じでした(今後のためのメモ)。
https://github.com/rrousselGit/provider/issues/313
作者さんのコメントにめっちゃ👎がついてて草ですね(まぁ正しくて、エラーメッセージはちゃんと読みましょうという話ですが)。

完成品

↑みたいな内容のなさでどうやって作ったのか謎になりますが、出来たものがこちらになります。

マスク洗った回数以外も記録できます。一応。
あと名前のところも変更できます。一目でわからないクソデザインですが。

完走した感想ですが、Dartはprivateであることを変数名/クラス名の頭の_で表現するのさえなければかなり良いと思いました(こなみ)。

SATySFiで二重角括弧

久しぶりの投稿になっちゃった。増やしていきたいね。

最近SATySFiを触っているんですが、LaTeXだと[\![とかで出せるやつ(〚〛)が標準でないっぽいのでmath.satyh\sqbracketをパクって適当に作りました。

組版の知識とかもないので変数の略語の意味を汲み取るのも苦労しました。

実際のところは\sqbracketを2つ重ねただけなのでかなり単純ですが。

let bracket-metrics fontsize halflen =
  let w0 = fontsize *' 0.1 in
  let w1 = fontsize *' 0.04 +' halflen *' 0.004 in
  let w2 = halflen *' 0.15 in
  let t = fontsize *' 0.04 in
    (w0, w1, w2, t)

let bracket-path x0 x1 x2 t ypos hgtaxis halflen =
  let ytop = ypos +' hgtaxis +' halflen in
  let ybot = ypos +' hgtaxis -' halflen in
    start-path (x2, ytop)
      |> line-to (x0, ytop)
      |> line-to (x0, ybot)
      |> line-to (x2, ybot)
      |> line-to (x2, ybot +' t)
      |> line-to (x1, ybot +' t)
      |> line-to (x1, ytop -' t)
      |> line-to (x2, ytop -' t)
      |> close-with-line

let double-bracket-left pathf hgt dpt hgtaxis fontsize color =
  let halflen = Math.half-length hgt dpt hgtaxis fontsize in
  let (w0, w1, w2, t) = bracket-metrics fontsize halflen in
  let widparen =  w0 +' w1 +' w2 in
  let pathl (xpos, ypos) =
    let x0 = xpos +' w0 in
    let x1 = x0 +' w1 in
    let x2 = x1 +' w2 in
      pathf x0 x1 x2 t ypos hgtaxis halflen
  in
  let pathr (xpos, ypos) =
    let x0 = xpos +' widparen in
    let x1 = x0 +' w1 in
    let x2 = x1 +' w2 in
      pathf x0 x1 x2 t ypos hgtaxis halflen
  in
  let path point = unite-path (pathl point) (pathr point) in
  let graphics point = [ fill color (path point); ] in
    (inline-graphics ( widparen *' 1.5 ) (hgtaxis +' halflen) (halflen -' hgtaxis) graphics, (fun _ -> 0pt))

let double-bracket-right pathf hgt dpt hgtaxis fontsize color =
  let halflen = Math.half-length hgt dpt hgtaxis fontsize in
  let (w0, w1, w2, t) = bracket-metrics fontsize halflen in
  let widparen = w0 +' w1 +' w2 in
  let pathl (xpos, ypos) =
    let x0 = xpos +' widparen -' w0 in
    let x1 = x0 -' w1 in
    let x2 = x1 -' w2 in
      pathf x0 x1 x2 t ypos hgtaxis halflen
  in
  let pathr (xpos, ypos) =
    let x0 = xpos +' widparen -' w0 +' w1 +' w2 in
    let x1 = x0 -' w1 in
    let x2 = x1 -' w2 in pathf x0 x1 x2 t ypos hgtaxis halflen
  in
  let path point = unite-path (pathl point) (pathr point) in
  let graphics point = [ fill color (path point); ] in
    (inline-graphics ( widparen *' 1.5 ) (hgtaxis +' halflen) (halflen -' hgtaxis) graphics, (fun _ -> 0pt))

let bbracket-left = double-bracket-left bracket-path
let bbracket-right = double-bracket-right bracket-path
let-math \bbracket = math-paren bbracket-left bbracket-right

下の画像のように出力されます。LaTeXのもの(そのさらに下)と比較しても遜色ないような気がします。 f:id:xecua:20200609204953p:plain f:id:xecua:20200609204955p:plain

git challenge #12に参加しました

10/26(土)にmixi本社で開催されたgit challenge#12に参加してきました。

git challengeとは、バージョン管理ツールgitを使って、ミクシィの社員さんが作成した問題に2人1組のペアで挑み、その成果を競う大会です。 最近はこの他にBug Shooting ChallengeやTDD Challenge、さらに年末に初開催されるUnity ChallengeなどのChallengeシリーズが開催されていますが、その第一弾であり、人気のイベントです。

事前準備

もともとgitの扱いには多少自信があったので、腕試しという感じが強く、マニュアルを読むなどは特にしませんでした。

後悔することになるんですが...

午前

朝が11時とやや遅いので少し早めに渋谷入りし、ゲーセンへ。特に成果はありませんでした。

今回パートナーとなったのはしるみんさん(https://twitter.com/silmin_)でした。ありがとうございます。

最初に社員さんによるLTとチュートリアルがありました。

LTはmixi社内で利用されているCI/CDの仕組みについてと、Tig(https://jonas.github.io/tig/)についての紹介でした。どちらも良さそうな感じ。

チュートリアルは難しくはありませんでしたが、緊張で少しテンパってしまい不安になってしまいました。

ランチ

おひる #mixi_git#traP1yakudopic.twitter.com/XqyiuqQdli — かふぇいん (@caffe__ine) October 26, 2019

すごそうなお弁当が出てきました。

なか pic.twitter.com/tzJWnwy2ak — かふぇいん (@caffe__ine) October 26, 2019

ランチ中は今やっていることなどを話していたんですが、皆さんすごくて私があの場にいていいのかわからなくなりました。

午後

競技開始です。

私たちのチームは分担して個別にどんどん問題を解きつつ、わからないところがあれば相談する、という戦略をとりました。

最初は調子がよく、一時は1位タイになるも、後半がなかなか伸びず、最後に点数が高めの問題を少し解くことでなんとか4位タイになりました。

あとでわかったんですが、もう1問解くことが(ほぼ)できていて、これができていれば単独4位になっていました。無念。

競技後は解説やgitの内部構造に着目したボツ問クイズなどがあり、大いに勉強になりました。

私はまだ浅瀬でチャプチャプしているだけだったんだなぁと思い知らされました。


私の所属しているサークル(traP/https://trap.jp)からは私の他に2人参加しており、そのうちの一人であるyto(60°)さんのチームが優勝でした。めでたい。

ちなみになんかtraPのメンバーがいるチームの優勝率が7/12とえげつないことになっているらしいです。この中に名を連ねられなかったのが残念。

懇親会

この手のイベントの参加時にはいつものことなんですが私は他の人に話しかけられないタイプです。

いつものように一人で楽しそうに話している人々を眺めていたところ、気を使ってくださったのか社員さんに声をかけていただき、先程解けなかった問題の解説や、社員さんの経歴などについての話をして盛り上がりました。

参考にしてはいけない感じの経歴をお持ちの方もいらっしゃったんですが、その行動力は見習うべきものでした。私も何かしないといけないなぁと思った次第でした。

さいごに

素敵なイベントを開催・運営してくださったmixiの社員の方々、パートナーとして私を支えてくださったしるみんさんに感謝します。

今後もこういったイベントは参加(あわよくば運営)したいものです。