自作OSに挑戦する日記 10日目

 「30日でできる!OS自作入門」を読んで分かったことや、とりあえず書いておきたいことなどを書いていきます。
 この本はChapterが1から30まであるので、各チャプター毎に1記事書いていきます。

Chapter 10 「重ね合わせ処理」

今回やった内容

  • ウィンドウの重ね合わせができるようにする

環境

作業記録

 前回でメモリ管理ができるようになったのですが、1バイト単位で確保・解放しているので空きが断片的になってしまう可能性があります…。ということで4KB単位で確保・解放ができるような関数を作ってあげます。

// メモリ確保(4KB単位)
unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size){
    size = (size + 0xfff) & 0xfffff000;
    return memman_alloc(man, size);
}

// メモリ解放(4KB単位)
int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size){
    size = (size + 0xfff) & 0xfffff000;
    return memman_free(man, addr, size);
}

 AND演算を使ってサイズを4KB単位に切り上げます。これでヨシ!


 今まではマウスカーソルを動かすたびにVRAMを書き換えていて、それまでに描画していた内容などはさっぱり上書きしていたのですが、流石にそれはだめということで重ね合わせができるようにします。

 ウィンドウ(カーソル)ごとにレイヤを持たせてそれに描画を行ってもらい、後でレイヤを重ね合わせるようにして重ね合わせ処理を行うみたいです。絵描きツールのレイヤ的なイメージっぽい感じで…。

 それぞれのレイヤを「シート」と名付けて構造体を作り、また「シート」を管理する構造体を作ることで重ね合わせを実現します。描画などはこの構造体を使って行います。実装は難しくもなく本の説明を見れば理解できるようになっていました(説明が分かりやすい)。

 ということで実装できたコードが以下になります。全部載せるのは長すぎるので描画を行う部分だけを…。

// sheet.c

struct SHEET{       // シート情報
    unsigned char *buf;
    int buf_width, buf_height, vram_x, vram_y, col_inv, layer_height, flags;
};

struct SHEET_CTL{   // シート管理
    unsigned char *vram;
    int width, height, top;
    struct SHEET *sheets[MAX_SHEETS];
    struct SHEET sheet_data[MAX_SHEETS];
};

// シート描画(特定シートの指定範囲)
void sheet_refresh(struct SHEET_CTL *sheet_ctl, struct SHEET *sheet, int buf_x0, int buf_y0, int buf_x1, int buf_y1){
    if(sheet->layer_height >= 0){
        int base_x = sheet->vram_x, base_y = sheet->vram_y;
        sheet_refresh_with_range(sheet_ctl, base_x + buf_x0, base_y + buf_y0, base_x + buf_x1, base_y + buf_y1);
    }
}

// シート描画(VRAM範囲指定)
void  sheet_refresh_with_range(struct SHEET_CTL *sheet_ctl, int vram_x0, int vram_y0, int vram_x1, int vram_y1){
    unsigned char *vram = sheet_ctl->vram;

    // シート全てで…
    for(int h_val = 0; h_val <= sheet_ctl->top; h_val ++){
        struct SHEET *sheet = sheet_ctl->sheets[h_val];
        unsigned char *buf = sheet->buf;

        // 各シートのバッファを基準とした座標を逆算する
        int buf_x0 = vram_x0 - sheet->vram_x;
        int buf_y0 = vram_y0 - sheet->vram_y;
        int buf_x1 = vram_x1 - sheet->vram_x;
        int buf_y1 = vram_y1 - sheet->vram_y;

        // シートの外に出てしまった場合は修正する
        if(buf_x0 < 0) buf_x0 = 0;
        if(buf_y0 < 0) buf_y0 = 0;
        if(buf_x1 > sheet->buf_width) buf_x1 = sheet->buf_width;
        if(buf_y1 > sheet->buf_height) buf_y1 = sheet->buf_height;

        // 指定範囲だけ再描画
        for(int y = buf_y0; y < buf_y1; y ++){
            for(int x = buf_x0; x < buf_x1; x ++){
                int vram_x = sheet->vram_x + x;
                int vram_y = sheet->vram_y + y;

                char color = buf[y * sheet->buf_width + x];
                if(color != sheet->col_inv){
                    vram[vram_y * sheet_ctl->width + vram_x] = color;
                }
            }
        }
    }
}

// シート移動
void sheet_slide(struct SHEET_CTL *sheet_ctl, struct SHEET *sheet, int vram_x, int vram_y){
    int old_vram_x = sheet->vram_x, old_vram_y = sheet->vram_y;
    int width = sheet->buf_width, height = sheet->buf_height;
    sheet->vram_x = vram_x;
    sheet->vram_y = vram_y;
    if(sheet->layer_height >= 0){
        sheet_refresh_with_range(sheet_ctl, old_vram_x, old_vram_y, old_vram_x + width, old_vram_y + height);
        sheet_refresh_with_range(sheet_ctl, vram_x, vram_y, vram_x + width, vram_y + height);
    }
}

 これを実行すると…

 マウスがタスクバーを破壊しないようになりました〜!マウスだけでなくマルチウィンドウになった時にもこれは使えますね。「シート」にはこれからずっとお世話になりそうです。

まとめ

 重ね合わせ処理完成しました〜!やはりマウスがタスクバーを破壊しないようになったのは大進歩ですね…。
 「30日で自作OS」なのでこれで1/3到達です。間にテスト期間挟んだのでぴったり30日とは行かないですが順調にできています。この勢いのまま完成まで頑張りたいと思います〜。