D言語＋SDL2で非矩形ウィンドウを作成する

# 概要

* __`SDL_CreateShapedWindow`と`SDL_SetWindowShape`で非矩形のウィンドウを作成する__
    * 作成はできたが、__背景の透過が一部うまくいかなかった (要調査)__
* D言語＋SDL2
    * dmd2.068.0(32bit)＋Dub0.9.23
    * SDL2-2.0.3
* Windows10(64bit)でのみ確認

# 経緯

SDL2では非矩形(キャラクターや図形の形)のウィンドウを作成できると聞いたので、調べてみました。

最初「SDL 非矩形」でググったのですが、「SDL2.0の新しい機能」の所で紹介されているぐらいで、
情報を見つけることができませんでした。(なんでや・・)

仕方なく日本語の資料をあきらめて、英語の記事やMercurialにある[ソースコード](https://hg.libsdl.org/SDL)を漁ってみた結果、
`SDL_CreateShapedWindow`と`SDL_SetWindowShape`という関数がそれらしいことがわかりました。
https://wiki.libsdl.org/SDL-gsoc2010_shaped_windows
https://hg.libsdl.org/SDL/file/704a0bfecf75/src/video/SDL_shape.c
https://hg.libsdl.org/SDL/file/704a0bfecf75/test/testshape.c

ちなみにSDLはC言語のライブラリですが、DerelictSDL2というバインディングでD言語からそのまま利用できます。

# サンプル

## プロジェクトの配置

* shapedwindow/
    * image/
        * background.bmp
    * library/
        * SDL.dll
    * souce/
        * app.d
    * dub.json

## ランタイム

[SDL公式](https://www.libsdl.org/download-2.0.php)からアーカイブを取得・展開後、上記の通りに配置する

## ソースコード

こちらを参考にさせて頂きました。
[「プログラムでシダを描画する」をD言語+SDL2+dubで描画する](http://qiita.com/outlandkarasu@github/items/2d014379858d20651e58)
ありがとうございました。

```d:souce/app.d
import std.exception;
import std.string;
import std.c.string;

import derelict.sdl2.sdl;

/// SDLのロード
static this()
{
    DerelictSDL2.load("./library/SDL2.dll");
}

/// メイン関数
void main()
{
    // SDL初期化
    enforceSdl(SDL_Init(SDL_INIT_EVERYTHING) == 0);
    scope(exit) SDL_Quit();

    // ウィンドウの生成
    auto window = enforceSdl(SDL_CreateShapedWindow(
            toStringz("SDL_CreateShapedWindow test"),
            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
            640, 480,
            SDL_WINDOW_BORDERLESS));
    scope(exit) SDL_DestroyWindow(window);

    // レンダラーの生成
    auto renderer = enforceSdl(SDL_CreateRenderer(window, -1, 0));
    scope(exit) SDL_DestroyRenderer(renderer);

    // 背景画像の読み込み
    auto surface = enforceSdl(SDL_LoadBMP(toStringz("./image/background.bmp")));  //<- 背景が緑色の画像
    scope(exit) SDL_FreeSurface(surface);

    // 透過色の指定
    SDL_WindowShapeMode mode;
    mode.mode = ShapeModeColorKey;
    mode.parameters.colorKey = SDL_Color(0, Uint8.max, 0, Uint8.max);

    auto texture = enforceSdl(SDL_CreateTextureFromSurface(renderer, surface));
    scope(exit) SDL_DestroyTexture(texture);

    // 非矩形ウィンドウに設定
    SDL_Rect srcRect;
    enforceSdl(SDL_QueryTexture(texture, null, null, &srcRect.w, &srcRect.h) == 0);
    SDL_SetWindowSize(window, srcRect.w, srcRect.h);
    SDL_SetWindowShape(window, surface, &mode);
    SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);

    // 描画実行
    enforceSdl(SDL_RenderCopy(renderer, texture, null, null) == 0);
    SDL_RenderPresent(renderer);

    // イベントループ
    mainLoop: for (SDL_Event event; SDL_WaitEvent(&event);)
    {
        switch (event.type)
        {
            // ウィンドウクローズで終了
            case SDL_QUIT:
                break mainLoop;
            // ESCキー押し下げで終了
            case SDL_KEYDOWN:
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    break mainLoop;
                break;
            default:
                break;
        }
    }
}

/**
 *  SDLのエラーメッセージの取得
 *
 *  Returns:
 *      SDLのエラーメッセージ。エラーが無ければnull。
 */
string getSdlMessage() @trusted
{
    if (auto msg = SDL_GetError())
    {
        SDL_ClearError();
        return msg[0 .. strlen(msg)].idup;
    }
    else
    {
        return null;
    }
}

/**
 *  SDL関数のエラーチェック
 *
 *  Params:
 *      value = SDL関数の戻り値か、エラーチェック結果。
 *  Returns:
 *      エラーが発生していなければvalue。
 *  Throws:
 *      Exception エラーが発生していた場合にスローされる。
 */
T enforceSdl(T)(T value, string file = __FILE__, size_t line = __LINE__)
{
    return enforce(value, getSdlMessage(), file, line);
}
```

```json:dub.json
{
    "name": "shapedwindow",
    "targetType": "executable",
    "description": "A non-rectunglar window test written by D language.",
    "dependencies": {
        "derelict-sdl2": "1.2.8"
    }
}
```

## ビルド

```:Console
dub
```

## 結果

[before]
![ss0.png](https://qiita-image-store.s3.amazonaws.com/0/87446/dbf4e3fe-60a1-8206-a68b-12857c507085.png)

[after]
![ss.png](https://qiita-image-store.s3.amazonaws.com/0/87446/13097bf3-cb14-7dd8-ce16-358279fc3d5e.png)

非矩形のウィンドウをは作成できました__が、__背景(緑色)の一部が透過されずに見えてしまっています。
textureからrendererにコピーする際に拡大縮小が起きている(？)のかもしれませんが、わかりませんでした。

また、現在(2015.8.25)SDL公式で配布されているSDL2.0.3では、`SDL_SetWindowHitTest`が未実装のため、
タイトルバーのない非矩形ウィンドウは__マウスドラッグで動かせません。__
(マウスイベントと`SetWindowPosition`で自前で頑張れば出来るのかもしれませんが)
次のSDL2.0.4で`SDL_SetWindowHitTest`が[実装される](http://sdl2referencejp.osdn.jp/SDL_SetWindowHitTest.html)のを待つのが無難な気がしました。
(DerelictSDL2の対応的な意味で)

とりあえず当初知りたかったことは確認できたので、良しとします。。。


# 感想

* DerelictやDubのおかげで、D言語からSDLを扱うのはとても簡単。
* 昔のSDLはゲーム専用という印象だったが、最近は色々できることが増えている様子。
* ファイルを扱うCUIなアプリに、簡単なGUIフロントエンドを被せて配布・・みたいなこともやってみたい。
* 個人的には、SDLが__D言語の標準ライブラリに欲しい。__