MJHD

エモさ駆動開発

HSPにおいてモジュール内部でbuttonを配置して分岐先でthismodを参照する

よくわからないタイトルになってしまった。

例えば

#module mainScreen id, text

#modinit int screenId
  screen screenId, 640, 480
  button gosub "テスト", *button_click@mainScreen

  text = "Hello World!!"
return

#modcfunc get_text
return text

*button_click
  // mes text とはできない
  mes get_text(thismod) // しかし、これも当然エラー
return

#global

こんな感じのコードを動かしたい。
そこで、若干無理矢理ではあるがマクロで真っ黒な黒魔術を使うことにした。

#module mainScreen id, text

#define global new_mainScreen(%1, %2) %tnew_mainScreen \
    if 0 : *%i {\
        _thismod@mainScreen = %1 :\
        gosub *button_click@mainScreen :\
        return } :\
    %i@mainScreen = *%p1 :\
    newmod %1, mainScreen, %2, %o %o0

#modinit int screenId, var dispatcher
  screen screenId, 640, 480
  button gosub "テスト", dispatcher

  text = "Hello World!!"
return

#modcfunc get_text
return text

*button_click
  mes get_text(_thismod@mainScreen) // 参照できる
return

#global

new_mainScreenマクロにより、button命令で分岐すると自動的に_thismod変数へモジュール変数が代入されるようになる。
ただこのままだと、全てのボタンの処理が同じになってしまうため、それぞれのボタンに対して別々の処理を割り当てられるよう、以下のように書き換えた。

#module mainScreen id, text, events

#define global new_mainScreen(%1, %2) %tnew_mainScreen \
    if 0 : *%i {\
        _thismod@mainScreen = %1 :\
        gosub *button_click@mainScreen :\
        return } :\
    %i@mainScreen = *%p1 :\
    newmod %1, mainScreen, %2, %o@mainScreen %o0

#define ctype register_event(%1) %tregister_event \
    events(%1) = *%i :\
    if 0 : *%o


#modinit int screenId, var dispatcher

    dimtype events, vartype("label"), 64

    screen screenId, 640, 480

    button gosub "mes", dispatcher
    register_event(stat) {
        mes get_text(_thismod@mainScreen)
        
        return
    }

    button gosub "dialog", dispatcher
    register_event(stat) {
        dialog get_text(_thismod@mainScreen)

        return
    }

    text = "Hello World!!"
return

#modcfunc get_text
return text

#modfunc dispatch int objid
    gosub events(objid)
return

*button_click
    dispatch _thismod@mainScreen, stat
return

#global

new_mainScreen main, 0

events変数にオブジェクトIDに対応する処理を登録していく。そして、button_clickラベルにおいてそれぞれの処理を呼び出す関数dispatchを実行する。
何かもっといい方法があったらコメントしてください。

簡単な正規表現エンジンを書いた

学校の課題が「検索,置換を行うプログラムを作成しなさい.オプションとして,独自の機能を盛り込んでもよし」とあったので,簡単な正規表現のエンジンを書いてみた.

対応する文法は以下の3つ.

記号 意味
* 直前の文字の0回以上の繰り返し
x|y xまたはy
xy xとyの連結

具体的な文法規則は以下のサイトにある,「文脈自由文法」(CFG: Context-free Grammer)による定義を使用した.
正規表現エンジンを作ろう (3) (2/3):CodeZine(コードジン)(要会員登録)

CFGはその変数名に対応する関数を作り,それぞれの関数の中で再帰的に他のヘンス名に対応する関数を呼び出すことで,構文木を作成することができる.

a -> 'A' b | c

という定義があったら,


Node* parse(const char* text) {
  char* it = text;
  return a(&it);
}

Node* a(char** it) {
  Node* node;

  if (**it == 'A') {
    node = b();
    (*it)++;
  } else {
    node = c();
  }

  return node;
}

のように書いていけば良い.多分.

先ほど示したサイトではこの後,構文木からNFAを,NFAからDFAを作成することになると思うのだが,今回は以下のサイトを参考にしてVM正規表現エンジンにした.

Mokkosu - VM型の正規表現エンジンを実装する - Qiita

Regular Expression Matching: the Virtual Machine Approach

VM型とは,正規表現を一度専用のバイトコードコンパイルし,そのバイトコードVM(仮想マシン)によって実行することで,判定を行う方法.
以下に今回のVMの構造を示す.

名前 意味
mem メモリ.コンパイルしたバイトコードを格納.
pc プログラムカウンタ.これから実行するメモリ上の命令の位置を表す.
sp 文字列ポインタ.判定を行う文字を示す.

今回はスレッドを分ける必要があったため,pcとspをまとめてContextという構造体に入れた.
その他詳しい定義は先ほど示したリンクを参照してください.


#include <stdio.h>
#include <stdlib.h>

/****************************************
 * 木構造
 ****************************************/

typedef enum {
    ND_Union,
    ND_Star,
    ND_Char,
    ND_Concat,
    ND_Empty
} NodeType;

typedef struct Node {
    NodeType type;
    char value;
    struct Node** chd;
    int chd_count;
} Node;

Node* new_node();
void add_chd(Node*, Node*);
void free_tree(Node*);
void print_tree(Node*, int);

Node* new_node() {
    Node* node = (Node*)malloc(sizeof(Node));
    node->value = 0;
    node->chd = NULL;
    node->chd_count = 0;
    return node;
}

void add_chd(Node* t, Node* p) {
    if (t->chd == NULL) t->chd = (Node* *)malloc(0);
    t->chd = (Node* *)realloc(t->chd, 1 * sizeof(Node*));
    t->chd[t->chd_count++] = p;
}

void print_tree(Node* t, int level) {
    char indent[256];
    int  cnt = -1;

    sprintf(indent, "%% %ds", level);
    printf(indent, "");
    printf("Type:");
    switch(t->type) {
        case ND_Concat:
            printf("Concat");
            break;
        case ND_Union:
            printf("Union");
            break;
        case ND_Star:
            printf("Star");
            break;
        case ND_Char:
            printf("Char");
            break;
        case ND_Empty:
            printf("Epsilon");
            break;
    }
    printf(",Val:%c, Chd:%d\n", t->value, t->chd_count);

    while (++cnt < t->chd_count)
        print_tree(t->chd[cnt], level+1);
}

void free_tree(Node* t) {
    int cnt = -1;

     while (++cnt < t->chd_count)
         free_tree(t->chd[cnt]);
    free(t);
}

/****************************************
 * コンパイラ
 ****************************************/
typedef enum {
    OP_Char,
    OP_Match,
    OP_Jump,
    OP_Split
} Opecode;

typedef struct {
    char opecode;
    char operand1;
    char operand2;
    char dummy;
}__attribute__((packed)) Inst;

void print_code(Inst* code, int len) {
    int pc;
    for (pc = 0; pc < len; pc++) {
        printf("%4d | %02x %02x %02x %02x | ", pc, code[pc].opecode, code[pc].operand1, code[pc].operand2, code[pc].dummy);

        switch (code[pc].opecode) {
            case OP_Jump:
                printf("JMP   %4d", code[pc].operand1);
                break;
            case OP_Split:
                printf("SPLIT %4d, %4d", code[pc].operand1, code[pc].operand2);
                break;
            case OP_Char:
                printf("CHAR  %4c", code[pc].operand1);
                break;
            case OP_Match:
                printf("MATCH");
                break;
        }
        printf("\n");
    }
}

int compile(Node*, Inst**);
void compile_node(Node*, Inst**, int*);

int compile(Node* ast, Inst** out) {
    int pc = 0;

    if (*out != NULL) free(*out);
    *out = (Inst*)malloc(0);

    compile_node(ast, out, &pc);

    *out = (Inst*)realloc(*out, sizeof(Inst) * (pc + 1));
    (*out)[pc].opecode = OP_Match;

    pc++;

    return pc;
}

void compile_node(Node* node, Inst** out, int* pc) {
    int tmp1, tmp2;
    int cnt = -1;

    switch(node->type) {
        case ND_Char:
            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Char;
            (*out)[*pc].operand1 = node->value;
            (*out)[*pc].operand2 = 0;
            (*pc)++;
            break;
        case ND_Union:
            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Split;
            (*out)[*pc].operand1 = *pc + 1;

            tmp1 = *pc;
            (*pc)++;

            compile_node(node->chd[0], out, pc);

            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Jump;
            (*out)[*pc].operand2 = 0;

            tmp2 = *pc;
            (*pc)++;

            compile_node(node->chd[1], out, pc);

            (*out)[tmp1].operand2 = tmp2 + 1;
            (*out)[tmp2].operand1 = *pc;

            break;
        case ND_Star:
            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode = OP_Split;
            (*out)[*pc].operand1 = *pc + 1;

            tmp1 = *pc;
            (*pc)++;

            compile_node(node->chd[0], out, pc);

            *out = (Inst*)realloc(*out, sizeof(Inst) * (*pc + 1));
            (*out)[*pc].opecode  = OP_Jump;
            (*out)[*pc].operand1 = tmp1;
            (*out)[*pc].operand2 = 0;

            (*pc)++;

            (*out)[tmp1].operand2 = *pc;

            break;
        case ND_Concat:
            compile_node(node->chd[0], out, pc);
            compile_node(node->chd[1], out, pc);
            break;
        case ND_Empty:
            break;
    }
}


/****************************************
 * 構文解析器
 ****************************************/
void parse(char[], Node**);
Node* parse_exp(char**);
Node* parse_subexp(char**);
Node* parse_factor(char**);
Node* parse_seq(char**);
Node* parse_subseq(char**);
Node* parse_star(char**);

void parse(char regexp[], Node** out) {
    char* it;

    if (*out != NULL) free(*out);

    it = regexp;

    *out = parse_exp(&it);
}

Node* parse_exp(char** it) {
    Node* node;

    node = parse_subexp(it);

    if (**it != '\0') printf("unexpected %c, expected EOF\n", **it);

    return node;
}

Node* parse_subexp(char** it) {
    Node* node;
    Node* node1;
    Node* node2;

    node = parse_subseq(it);

    if (**it == '|') {
        (*it)++;
        node1 = node;
        node2 = parse_subexp(it);
        node  = new_node();
        node->type = ND_Union;
        add_chd(node, node1);
        add_chd(node, node2);
    }

    return node;
}

Node* parse_factor(char** it) {
    Node* node;

    if (**it == '(') {
        (*it)++;
        node = parse_subexp(it);
    } else {
        node = new_node();
        node->type = ND_Char;
        node->value = **it;
    }

    (*it)++;

    return node;
}

Node* parse_seq(char** it) {
    Node* node;

    if (**it == '(' || (**it != '|' && **it != ')' && **it != '*' && **it != '\0')) {
        return parse_subseq(it);
    }

    node = new_node();
    node->type = ND_Empty;

    return node;
}

Node* parse_subseq(char** it) {
   Node* node;
   Node* node1;
   Node* node2;

   node = parse_star(it);
   if (**it == '(' || (**it != '|' && **it != ')' && **it != '*' && **it != '\0')) {
       node1 = node;
       node2 = parse_subseq(it);
       node  = new_node();
       node->type = ND_Concat;
       add_chd(node, node1);
       add_chd(node, node2);
   }

    return node;
}

Node* parse_star(char** it) {
    Node* node;
    Node* tmp;

    node = parse_factor(it);

    if (**it == '*') {
        tmp = node;
        node = new_node();
        node->type = ND_Star;
        add_chd(node, tmp);

        (*it)++;
    }

    return node;
}


/****************************************
 * VM
 ****************************************/

typedef struct {
    int pc;
    char* sp;
} Context;

typedef struct {
    Context ctx;
    Inst* mem;
} VM;

VM* new_vm();
int run(VM*);

VM* new_vm() {
    VM* vm = (VM*)malloc(sizeof(VM));

    vm->ctx.pc = 0;
    vm->ctx.sp = 0;
    vm->mem = NULL;

    return vm;
}

int run(VM* vm) {
    Context ctx;
    Inst* inst = vm->mem + vm->ctx.pc;

    switch(inst->opecode) {
        case OP_Jump:
            vm->ctx.pc = inst->operand1 - 1;
            break;
        case OP_Split:
            ctx = vm->ctx;
            vm->ctx.pc = inst->operand1;
            if (run(vm)) return 1;

            vm->ctx = ctx;
            vm->ctx.pc = inst->operand2;
            if (run(vm)) return 1;
            return 0;
        case OP_Char:
            if (*vm->ctx.sp == inst->operand1) {
                vm->ctx.sp++;
            } else {
                return 0;
            }
            break;
        case OP_Match:
            return 1;
    }

    vm->ctx.pc++;

    return run(vm);
}

/****************************************
 * main関数 
 ****************************************/

int main(int argc, char** argv) {
    Node* ast  = NULL;
    Inst* code = NULL;
    VM vm;
    int size;

    vm.ctx.sp = argv[1];
    
    printf("Text: %s, Pattern: %s\n\n", argv[1], argv[2]);

    parse(argv[2], &ast);

    printf("SYNTAX TREE:\n");
    print_tree(ast, 0);

    size = compile(ast, &code);

    printf("COMPILED CODE:\n");
    print_code(code, size);

    vm.mem = code;

    printf("TEST:\n");
    if (run(&vm)) {
        printf("Match!\n");
    }

    free_tree(ast);
    free(code);
}

Roundcubeでsmtps(またはimaps)接続できない

Roundcubeにて,

SMTP Error: SMTP error: Connection failed: Failed to connect socket: fsockopen(): unable to connect to ~~~ (Unknown error)」

と出て接続できない問題が発生した.

どうやら,PHPのあるバージョンからSSL接続のチェックが厳しくなり,self-signedの証明書で問題が起こるらしい.

解決策として,roundcubeのディレクトリ/config/config.inc.phpを編集し,以下のようにする.

$config['smtp_server'] = 'ssl://ホスト';

~~~省略~~~

$config['smtp_conn_options'] = array(
  'ssl' => array(
    'verify_peer' => false,
    'verify_peer_name' => false,
  ),
);

imapsの場合は,smtp_serverではなくdefault_hostの方にssl://をつけ,smtp_conn_optionsimap_conn_optionsに置き換えればOK.

HSPのデバッグウィンドウにコールスタック一覧を表示させてみた(2015/9/29 更新)

f:id:wait0000:20150929192738p:plain

2015/9/29: 更新ボタンを追加

ダウンロードは以下。
hsp_callstack_v2.zip

32bitの標準ランタイム以外を使おうとするとエラーが出てしまうはずなので、
あくまで実験用。使う際はオリジナルのhsp3.exeとhsp3debug.dllを必ずバックアップすること。


OpenHSPに取り込んでいただけたようです。ありがたいです。
チェンジセット 759 – OpenHSP

r759以降のソースコードに更新ボタンを付けたい人は以下のパッチファイルが必要です。 https://mega.nz/#!LIgjHDoa!AjTKRaoeSdHJjK1xonIH3mlOcifMywe3JEs4MBv5060

TerminatorがSGRプロトコルに対応していない?

以前から、vimを最大化して使っている最中に、vimの右端の方をクリックしてもラップして左端の方がクリックされていた。
なんとなく無視していたのだが、気になったので調べたところ、どうやら以前のxtermの仕様では223列目以降のマウス座標に対応していないらしい。
Mouse support when running in a terminal emulator wider than 223 character cells - Google グループ

端末業界では有名な話らしいが、初めて知った。

解決方法は、まずvimVim 7.3.632以上へ更新すること、次に.vimrcに以下の記述をすること。

if has("mouse_sgr")
    set ttymouse=sgr
else
    set ttymouse=xterm2
endif

これにより解決する…らしいのだが、自分の環境では直らなかった。

試しに他のターミナルエミュレータを使用してみたところ、無事正常に動いたことから、おそらくTerminatorがSGRに対応していない?ということがわかった。

Terminologyへ乗り換え

そこで、代わりのターミナルエミュレータを調べてみたところ、Terminologyが面白そうだったので導入してみた。(Terminology - 動画も再生可能な仮想端末(ターミナル)アプリケーション期待の新星)

動作もこのたぐいのモノの中では割と安定していて、今のところは常用している。

f:id:wait0000:20150905140715p:plainf:id:wait0000:20150905140723p:plainf:id:wait0000:20150905140729p:plainf:id:wait0000:20150905140734p:plain f:id:wait0000:20150905140802p:plainf:id:wait0000:20150905140826p:plain f:id:wait0000:20150905140859p:plain

Golangのbluemondayは注意書きをよく読んで使おう

microcosm-cc/bluemondayという便利なサニタイザーがある。
文章中に含まれるHTMLタグやクォーテーションなど、脆弱性に繋がる文字列を削除してくれるパッケージだ。

よく他のブログでは、

sanitized := bluemonday.UGCPolicy().Sanitize(query)

のようにUGCPolicyを使っているが、このポリシーは少しだけ注意が必要だ。

https://github.com/microcosm-cc/bluemonday/blob/master/helpers.go#L153にもあるように、

// "id" is permitted. This is pretty much as some HTML elements require this
// to work well ("dfn" is an example of a "id" being value)
// This does create a risk that JavaScript and CSS within your web page
// might identify the wrong elements. Ensure that you select things
// accurately

ID属性はそのまま素通しされる。<div id="hoge"></div>などがそのまま通されてしまう。

よって、javascriptなどでIDを使用して要素を選択する時、間違ってユーザが入力した要素を選択してしまう可能性がある。

そもそもUGCPolicyとは、ブログ記事などに使用することを想定しているため、ID属性は見出しタグに設定する用途などに使えるよう、素通ししていると思われる。

解決方法としては、
1. 新しいPolicyを作る
2. サニタイズした要素以降にID属性を持った要素を配置しない
などがある。

新しいPolicyのサンプルとして、以下を示す。UGCPolicyからAllowStandardAttributesを削除しただけ。

func StandardPolicy() *bluemonday.Policy {

    p := bluemonday.NewPolicy()

    //////////////////////////////
    // Global URL format policy //
    //////////////////////////////

    p.AllowStandardURLs()

    ////////////////////////////////
    // Declarations and structure //
    ////////////////////////////////

    // "xml" "xslt" "DOCTYPE" "html" "head" are not permitted as we are
    // expecting user generated content to be a fragment of HTML and not a full
    // document.

    //////////////////////////
    // Sectioning root tags //
    //////////////////////////

    // "article" and "aside" are permitted and takes no attributes
    p.AllowElements("article", "aside")

    // "body" is not permitted as we are expecting user generated content to be a fragment
    // of HTML and not a full document.

    // "details" is permitted, including the "open" attribute which can either
    // be blank or the value "open".
    p.AllowAttrs(
        "open",
    ).Matching(regexp.MustCompile(`(?i)^(|open)$`)).OnElements("details")

    // "fieldset" is not permitted as we are not allowing forms to be created.

    // "figure" is permitted and takes no attributes
    p.AllowElements("figure")

    // "nav" is not permitted as it is assumed that the site (and not the user)
    // has defined navigation elements

    // "section" is permitted and takes no attributes
    p.AllowElements("section")

    // "summary" is permitted and takes no attributes
    p.AllowElements("summary")

    //////////////////////////
    // Headings and footers //
    //////////////////////////

    // "footer" is not permitted as we expect user content to be a fragment and
    // not structural to this extent

    // "h1" through "h6" are permitted and take no attributes
    p.AllowElements("h1", "h2", "h3", "h4", "h5", "h6")

    // "header" is not permitted as we expect user content to be a fragment and
    // not structural to this extent

    // "hgroup" is permitted and takes no attributes
    p.AllowElements("hgroup")

    /////////////////////////////////////
    // Content grouping and separating //
    /////////////////////////////////////

    // "blockquote" is permitted, including the "cite" attribute which must be
    // a standard URL.
    p.AllowAttrs("cite").OnElements("blockquote")

    // "br" "div" "hr" "p" "span" "wbr" are permitted and take no attributes
    p.AllowElements("br", "div", "hr", "p", "span", "wbr")

    ///////////
    // Links //
    ///////////

    // "a" is permitted
    p.AllowAttrs("href").OnElements("a")

    // "area" is permitted along with the attributes that map image maps work
    p.AllowAttrs("alt").Matching(bluemonday.Paragraph).OnElements("area")
    p.AllowAttrs("coords").Matching(
        regexp.MustCompile(`^([0-9]+,){2}(,[0-9]+)*$`),
    ).OnElements("area")
    p.AllowAttrs("href").OnElements("area")
    p.AllowAttrs("rel").Matching(bluemonday.SpaceSeparatedTokens).OnElements("area")
    p.AllowAttrs("shape").Matching(
        regexp.MustCompile(`(?i)^(default|circle|rect|poly)$`),
    ).OnElements("area")

    // "link" is not permitted

    /////////////////////
    // Phrase elements //
    /////////////////////

    // The following are all inline phrasing elements
    p.AllowElements("abbr", "acronym", "cite", "code", "dfn", "em",
        "figcaption", "mark", "s", "samp", "strong", "sub", "sup", "var")

    // "q" is permitted and "cite" is a URL and handled by URL policies
    p.AllowAttrs("cite").OnElements("q")

    // "time" is permitted
    p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("time")

    ////////////////////
    // Style elements //
    ////////////////////

    // block and inline elements that impart no semantic meaning but style the
    // document
    p.AllowElements("b", "i", "pre", "small", "strike", "tt", "u")

    // "style" is not permitted as we are not yet sanitising CSS and it is an
    // XSS attack vector

    //////////////////////
    // HTML5 Formatting //
    //////////////////////

    // "bdi" "bdo" are permitted
    p.AllowAttrs("dir").Matching(bluemonday.Direction).OnElements("bdi", "bdo")

    // "rp" "rt" "ruby" are permitted
    p.AllowElements("rp", "rt", "ruby")

    ///////////////////////////
    // HTML5 Change tracking //
    ///////////////////////////

    // "del" "ins" are permitted
    p.AllowAttrs("cite").Matching(bluemonday.Paragraph).OnElements("del", "ins")
    p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("del", "ins")

    ///////////
    // Lists //
    ///////////

    p.AllowLists()

    ////////////
    // Tables //
    ////////////

    p.AllowTables()

    ///////////
    // Forms //
    ///////////

    // By and large, forms are not permitted. However there are some form
    // elements that can be used to present data, and we do permit those
    //
    // "button" "fieldset" "input" "keygen" "label" "output" "select" "datalist"
    // "textarea" "optgroup" "option" are all not permitted

    // "meter" is permitted
    p.AllowAttrs(
        "value",
        "min",
        "max",
        "low",
        "high",
        "optimum",
    ).Matching(bluemonday.Number).OnElements("meter")

    // "progress" is permitted
    p.AllowAttrs("value", "max").Matching(bluemonday.Number).OnElements("progress")

    //////////////////////
    // Embedded content //
    //////////////////////

    // Vast majority not permitted
    // "audio" "canvas" "embed" "iframe" "object" "param" "source" "svg" "track"
    // "video" are all not permitted

    p.AllowImages()

    return p
}

XMonadでiPhoneをディスプレイとして使う

f:id:wait0000:20150901143109j:plain

こんな感じで余ってるiPhoneVNCで接続し、3つめのディスプレイにした。
その際の設定のメモ。
(もちろん、PCでもiPhoneでもiPodでもAndroidでも何でも繋げる。)

環境

PC

ArchLinux
X.Org Server 1.17.2
XMonad 0.11.1
XRandR 1.4.3
iPhone4S (640x960)
ディスプレイ1(1920x1080)
ディスプレイ2(1366x768)

準備

iPhone側にRealVNC Viewerアプリをインストールする。無料。

あらかじめRandR#マルチディスプレイ - ArchWikiを参照し、/etc/X11/xorg.conf.d/10-multihead.confファイルを設定しておく。

ちなみに、今回の環境ではこんな感じ。

Section "Monitor"
    Identifier "HDMI3"
    Option     "PreferredMode" "1920x1080"
    Option     "Position"      "0 0"
EndSection

Section "Monitor"
    Identifier "VGA1"
    Option     "PreferredMode" "1360x768"
    Option     "Position"      "1920 0"
EndSection

HDMI接続とVGA接続のディスプレイが設定されている。

この段階で一度再起動しよう。(もしくはDMの再起動)

次に、以下のコマンドを実行し、VNC接続時のパスワードを設定する。(保存先は~/.vnc/passwdとする)

x11vnc -storepasswd`

XRandRとx11vncの設定

~/.xprofileまたは~/.xinitrc(どちらか使用している方)に以下の行を追加する。
おそらく、xmonadを起動する行の前が良い。

xrandr --newmode "640x960_60.00" 50.57 640 680 744 848 960 961 964 994 -hsync +vsync
xrandr --addmode VIRTUAL1 640x960_60.00
xrandr --output VIRTUAL1 --mode 640x960_60.00 --right-of VGA1

x11vnc --clip 640x960+3286+0 -rfbauth ~/.vnc/passwd -ncache 10 -nocursorhape &

xrandr --newmode ~~~の値は、* Modeline Calculatorなどで計算できる。
xrandr --addmode ~~~で、解像度を一覧に追加し、xrandr --output ~~ --mode ~~ --right-of ~~~で、iPhoneの解像度と位置を設定している。

x11vncの行では、--clipオプションによってiPhoneの解像度とiPhoneの位置(今回は一番右端に配置するため、1920+1366=3286)を指定している。

ここで一度ログインし直す。

接続

iPhoneのアプリ側でPCのIPアドレスを入力する。(プライベートIPでよければip addrコマンドによって調べられる。)
設定したパスワードを入力すれば接続完了。

ちなみに

今回は.xprofileなどで設定したが、もちろん10-multihead.confにVIRTUAL1の設定を書くのでも良いと思う。
今回はiPhoneの位置を変更しやすいよう、.xprofileに書いた。

参考

LinuxでiPadをセカンドモニターにする - 技術日記@kiwanami