書くためにやった手順(tmux-1.9a)
ソースコードを展開してそのディレクトリ内を~/.tmux.confに書くオプション名で検索する
ここではdefault-terminalを採用
% find ./tmux-1.9a -type f -exec grep -nH "default-terminal" {} +
./tmux-1.9a/examples/tmux.vim:177: \ default-terminal
./tmux-1.9a/tmux.1:2227:.It Ic default-terminal Ar terminal
./tmux-1.9a/options-table.c:133: { .name = "default-terminal",
./tmux-1.9a/server-fn.c:39: term = options_get_string(&s->options, "default-terminal");
./tmux-1.9a/FAQ:183: set -g default-terminal "screen-256color"
./tmux-1.9a/FAQ:378: set -g default-terminal "screen-it"
この中で関係がありそうなものは
./tmux-1.9a/options-table.c:133: { .name = "default-terminal",
./tmux-1.9a/server-fn.c:39: term = options_get_string(&s->options, "default-terminal");
の2つになります。ファイル名から推測するに
./tmux-1.9a/options-table.c:133: { .name = "default-terminal",
これでオプションを定義して
./tmux-1.9a/server-fn.c:39: term = options_get_string(&s->options, "default-terminal");
これで定義しているように見えるのでoptions-table.cから先に見てみることにする
するとこのファイルの中ではオプションがいくつか分けられていて
const struct options_table_entry server_options_table[] = {
const struct options_table_entry session_options_table[] = {
const struct options_table_entry window_options_table[] = {
の3種類のオプションに分かれていることがわかる。この中から追加したいオプションがこのカテゴリに含まれるのかを考えて足す必要がありそう
とりあえずdefault-terminalを見ることにする
{ .name = "default-terminal",
.type = OPTIONS_TABLE_STRING,
.default_str = "screen"
},
なるほど・・・わからんということでこの型を定義しているであろうincludeしているtmux.hを見る
extern const struct options_table_entry session_options_table[];
なのでoptions_table_entry型
struct options_table_entry {
const char *name;
enum options_table_type type;
u_int minimum;
u_int maximum;
const char **choices;
const char *default_str;
long long default_num;
const char *style;
};
に新たに出てきたoptions_table_typeの定義を見る
enum options_table_type {
OPTIONS_TABLE_STRING,
OPTIONS_TABLE_NUMBER,
OPTIONS_TABLE_KEY,
OPTIONS_TABLE_COLOUR,
OPTIONS_TABLE_ATTRIBUTES,
OPTIONS_TABLE_FLAG,
OPTIONS_TABLE_CHOICE,
OPTIONS_TABLE_STYLE
};
なるほど・・・だいたい定義の仕方はわかったので次に読み取る関数の options_get_string(); を使っているところを調べる
% find ./tmux-1.9a -type f -exec grep -nH "options_get_string" {} +
./tmux-1.9a/window-copy.c:580: options_get_string(&sess->options, "word-separators");
./tmux-1.9a/window-copy.c:586: options_get_string(&sess->options, "word-separators");
./tmux-1.9a/window-copy.c:596: options_get_string(&sess->options, "word-separators");
./tmux-1.9a/session.c:247: shell = options_get_string(&s->options, "default-shell");
./tmux-1.9a/server-window.c:208: ptr = options_get_string(&w->options, "monitor-content");
./tmux-1.9a/cmd-attach-session.c:170: update = options_get_string(&s->options, "update-environment");
./tmux-1.9a/cmd-new-session.c:190: cmd = options_get_string(&global_s_options, "default-command");
./tmux-1.9a/cmd-new-session.c:194: update = options_get_string(&global_s_options, "update-environment");
./tmux-1.9a/cmd-split-window.c:83: cmd = options_get_string(&s->options, "default-command");
./tmux-1.9a/cmd-split-window.c:141: shell = options_get_string(&s->options, "default-shell");
./tmux-1.9a/server-client.c:129: overrides = options_get_string(oo, "terminal-overrides");
./tmux-1.9a/server-client.c:777: template = options_get_string(&s->options, "set-titles-string");
./tmux-1.9a/server-client.c:1054: shell = options_get_string(&global_s_options, "default-shell");
./tmux-1.9a/status.c:88: NULL, NULL, options_get_string(&s->options, "status-left"), t, 1);
./tmux-1.9a/status.c:109: NULL, NULL, options_get_string(&s->options, "status-right"), t, 1);
./tmux-1.9a/status.c:211: sep = options_get_string(oo, "window-status-separator");
./tmux-1.9a/status.c:226: sep = options_get_string(oo, "window-status-separator");
./tmux-1.9a/status.c:634: fmt = options_get_string(oo, "window-status-format");
./tmux-1.9a/status.c:637: fmt = options_get_string(oo, "window-status-current-format");
./tmux-1.9a/status.c:1045: wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/status.c:1078: wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/status.c:1101: wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/status.c:1129: wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/cmd-set-option.c:347: oldval = options_get_string(oo, oe->name);
./tmux-1.9a/options.c:124:options_get_string(struct options *oo, const char *name)
./tmux-1.9a/tmux.h:1579:char *options_get_string(struct options *, const char *);
./tmux-1.9a/cmd-new-window.c:86: cmd = options_get_string(&s->options, "default-command");
./tmux-1.9a/names.c:86: fmt = options_get_string(&w->options, "automatic-rename-format");
./tmux-1.9a/server-fn.c:39: term = options_get_string(&s->options, "default-terminal");
./tmux-1.9a/server-fn.c:246: cmd = options_get_string(&c->session->options, "lock-command");
オプションの数に対して関数使っているところが少ないので別の関数もある予感
定義しているっぽい
./tmux-1.9a/options.c:124:options_get_string(struct options *oo, const char *name)
を見に行く
char *
options_get_string(struct options *oo, const char *name)
{
struct options_entry o;
if ((o = options_find(oo, name)) == NULL)
fatalx("missing option");
if (o->type != OPTIONS_STRING)
fatalx("option not a string");
return (o->str);
}
こんな定義になってる
関数名だけで判断するなら最初のif文でそのオプションが存在するのか
次のif文でそのオプションの値は文字列なのかどうかを見ているっぽい
他にも似たやつで
long long
options_get_number(struct options *oo, const char *name)
{
struct options_entry *o;
if ((o = options_find(oo, name)) == NULL)
fatalx("missing option");
if (o->type != OPTIONS_NUMBER)
fatalx("option not a number");
return (o->num);
}
struct grid_cell *
options_get_style(struct options *oo, const char *name)
{
struct options_entry *o;
if ((o = options_find(oo, name)) == NULL)
fatalx("missing option");
if (o->type != OPTIONS_STYLE)
fatalx("option not a style");
return (&o->style);
}
こんなのがある
色々ソースコードを追ってみたけど、変更する箇所は
options-table.cだけで良さげ
この中で追加するために考える必要がありそうなのは
const struct options_table_entry server_options_table[] = {
const struct options_table_entry session_options_table[] = {
const struct options_table_entry window_options_table[] = {
この中のどのジャンルに含まれるかを考える
中のオプションを見たとき
~/.tmux.confに書くとき
Server optionsはset-optionで
Session optionsはset-optionで
Window optionsはset-window-optionで書くことがわかる
じゃあServerとSessionの違いは?ということで調べたらそれぞれ取得するときの第1引数に使うものが異なってくる
Server optionsの場合
options_get_number(&global_options, "buffer-limit");
Session optionsの場合
options_get_string(&s->options, "default-terminal");
というように&global_optionsと&s->optionsで使い分けている
同じようにwindowは&w->options
global_optionsはtmux.cでoptions型のglobal変数として定義されている
s->optionsは
void
server_fill_environ(struct session *s, struct environ *env)
{
char var[MAXPATHLEN], *term;
u_int idx;
long pid;
if (s != NULL) {
term = options_get_string(&s->options, "default-terminal");
environ_set(env, "TERM", term);
idx = s->id;
} else
idx = -1;
pid = getpid();
xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx);
environ_set(env, "TMUX", var);
}
で書いてる通りsession型、定義はtmux.h
struct session {
u_int id;
char *name;
int cwd;
struct timeval creation_time;
struct timeval activity_time;
struct timeval last_activity_time;
u_int sx;
u_int sy;
struct winlink *curw;
struct winlink_stack lastw;
struct winlinks windows;
struct options options;
#define SESSION_UNATTACHED 0x1
int flags;
struct termios *tio;
struct environ environ;
int references;
TAILQ_ENTRY(session) gentry;
RB_ENTRY(session) entry;
};
s->optionsがどうやって作られてるのか調べるために関数を呼び出した関数を呼び出した…と遡っていくと
session.cにて
options_init(&s->options, &global_s_options);
global_s_options変数から生成されているっぽい
これもtmux.cでoptions型のglobal変数として定義されている
同じようにwindow用のglobal_w_options変数も定義されている
これの初期化はtmux.cの
options_init(&global_options, NULL);
options_table_populate_tree(server_options_table, &global_options);
options_set_number(&global_options, "quiet", quiet);
options_init(&global_s_options, NULL);
options_table_populate_tree(session_options_table, &global_s_options);
options_set_string(&global_s_options, "default-shell", "%s", getshell());
options_init(&global_w_options, NULL);
options_table_populate_tree(window_options_table, &global_w_options);
そしてsession.cやwindow.cで新しく作られたときに
options_init(&s->options, &global_s_options);
や
options_init(&w->options, &global_w_options);
のようにそれぞれのセッションやウィンドウのoptionの設定に反映させているのがわかる
では初期化以外でoptionの設定をいじる場合どこでいじるのかということで
options_set_*を使用している初期化以外の関数を見たらcmd-set-option.cに
cmd_set_option_*という法則で関数名が定義されていた
さらにそれを追ったらcmd_set_option_exec関数にたどり着いた
enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
{
struct args *args = self->args;
const struct options_table_entry *table, *oe;
struct session *s;
struct winlink *wl;
struct client *c;
struct options *oo;
struct window *w;
const char *optstr, *valstr;
u_int i;
optstr = args->argv[0];
if (*optstr == '\0') {
cmdq_error(cmdq, "invalid option");
return (CMD_RETURN_ERROR);
}
if (args->argc < 2)
valstr = NULL;
else
valstr = args->argv[1];
if (*optstr == '@')
return (cmd_set_option_user(self, cmdq, optstr, valstr));
table = oe = NULL;
if (options_table_find(optstr, &table, &oe) != 0) {
cmdq_error(cmdq, "ambiguous option: %s", optstr);
return (CMD_RETURN_ERROR);
}
if (oe == NULL) {
cmdq_error(cmdq, "unknown option: %s", optstr);
return (CMD_RETURN_ERROR);
}
if (table == server_options_table)
oo = &global_options;
else if (table == window_options_table) {
if (args_has(self->args, 'g'))
oo = &global_w_options;
else {
wl = cmd_find_window(cmdq, args_get(args, 't'), NULL);
if (wl == NULL) {
cmdq_error(cmdq,
"couldn't set '%s'%s", optstr,
(!args_has(args, 't') && !args_has(args,
'g')) ? " need target window or -g" : "");
return (CMD_RETURN_ERROR);
}
oo = &wl->window->options;
}
} else if (table == session_options_table) {
if (args_has(self->args, 'g'))
oo = &global_s_options;
else {
s = cmd_find_session(cmdq, args_get(args, 't'), 0);
if (s == NULL) {
cmdq_error(cmdq,
"couldn't set '%s'%s", optstr,
(!args_has(args, 't') && !args_has(args,
'g')) ? " need target session or -g" : "");
return (CMD_RETURN_ERROR);
}
oo = &s->options;
}
} else {
cmdq_error(cmdq, "unknown table");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'u')) {
if (cmd_set_option_unset(self, cmdq, oe, oo, valstr) != 0)
return (CMD_RETURN_ERROR);
} else {
if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) {
if (!args_has(args, 'q'))
cmdq_print(cmdq, "already set: %s", optstr);
return (CMD_RETURN_NORMAL);
}
if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0)
return (CMD_RETURN_ERROR);
}
if (strcmp(oe->name, "automatic-rename") == 0) {
for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
if ((w = ARRAY_ITEM(&windows, i)) == NULL)
continue;
if (options_get_number(&w->options, "automatic-rename"))
queue_window_name(w);
else if (event_initialized(&w->name_timer))
evtimer_del(&w->name_timer);
}
}
recalculate_sizes();
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
if (c != NULL && c->session != NULL)
server_redraw_client(c);
}
return (CMD_RETURN_NORMAL);
}
引数に使われるoption型はオプション名でServerかWindowかSessionのオプションかを判別して
WindowまたはSessionの場合は引数に-tがあればその指定した奴だけ変更するようになっている
そこから考えるに
const struct options_table_entry server_options_table[] = {
const struct options_table_entry session_options_table[] = {
const struct options_table_entry window_options_table[] = {
Serverはtmux全体でSessionとかWindowの概念とかそんなもの使わない奴に使って
Sessionはセッションごと
Windowはウィンドウごとの定義に使うみたい (正直最初から薄々気づいてた)
次にこれの使い分けについて調べる
enum options_table_type {
OPTIONS_TABLE_STRING,
OPTIONS_TABLE_NUMBER,
OPTIONS_TABLE_KEY,
OPTIONS_TABLE_COLOUR,
OPTIONS_TABLE_ATTRIBUTES,
OPTIONS_TABLE_FLAG,
OPTIONS_TABLE_CHOICE,
OPTIONS_TABLE_STYLE
};
>||
% find ./tmux-1.9a -type f -exec grep -nH "OPTIONS_TABLE_STRING" {} +
./tmux-1.9a/cmd-set-option.c:299: case OPTIONS_TABLE_STRING:
(中略)
./tmux-1.9a/options-table.c:854: case OPTIONS_TABLE_STRING:
./tmux-1.9a/options-table.c:877: case OPTIONS_TABLE_STRING:
この中で1個ずつ確認して一番怪しい初期化の時に呼び出している
void
options_table_populate_tree(
const struct options_table_entry *table, struct options *oo)
{
const struct options_table_entry *oe;
for (oe = table; oe->name != NULL; oe++) {
switch (oe->type) {
case OPTIONS_TABLE_STRING:
options_set_string(oo, oe->name, "%s", oe->default_str);
break;
case OPTIONS_TABLE_STYLE:
options_set_style(oo, oe->name, oe->default_str, 0);
break;
default:
options_set_number(oo, oe->name, oe->default_num);
break;
}
}
}
を見るに設定を書き込む方法が3種類で実現されている
それぞれの関数はoptions.cで定義されているので見ると
struct options_entry *printflike3
options_set_string(struct options *oo, const char *name, const char *fmt, ...)
がnameの文字列を代入
struct options_entry *
options_set_number(struct options *oo, const char *name, long long value)
がvalueの値を代入
struct options_entry *
options_set_style(struct options *oo, const char *name, const char *value,
int append)
style_parse関数で式を評価して正しければvalueの指定されている色を設定している
次にnameやvalueの数値が入力可能な数値か判定している関数があるので見る
int
cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq,
const struct options_table_entry *oe, struct options *oo, const char *value)
この関数から
cmd_set_option_( typle ) (self, cmdq, oe, oo, value);
みたいな形式の関数を呼び出している
やってることはだいたいこんな感じ
type |
処理 |
string |
引数にaがあれば前の値の後ろに付け足す |
number |
最低値と最大値の値が超えてないか |
key |
キーを指定できてるか |
colour |
blackとかredとか色を指定できているか |
attributes |
brightとかboldとか文字の表現を指定できてるか |
flag |
空エンターならflagを逆に,onかyesかoffかnoになってるか |
choice |
choices変数の構造体にある文字列かどうか |
style |
引数にaがあるか |
colourは評価が終えた後styleに指定している所に色の設定を加えます
style_update_new関数の仕様上name変数は最後が-bgか-fgかじゃないと動きません
同じようにattributesは-attrじゃないと動きません
choiceに入る構造体は
const char *options_table_mode_keys_list[] = {
"emacs", "vi", NULL
};
こんな感じで最後にNULLがあればおk
これで必要な情報が揃ったので実際に実装するとしたら
options-table.cにある
const struct options_table_entry server_options_table[] = {
const struct options_table_entry session_options_table[] = {
const struct options_table_entry window_options_table[] = {
この構造体の中から好きなものを選び
typeの場合によっては.choiceに入れる構造体や.styleを付け足せばオプションが追加できる
処理の中で取得するには
char * options_get_string(struct options *oo, const char *name)
long long options_get_number(struct options *oo, const char *name)
struct grid_cell * options_get_style(struct options *oo, const char *name)
この関数を使って取得できる