8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-08-25

概要

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

経緯

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

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

仕方なく日本語の資料をあきらめて、英語の記事やMercurialにあるソースコードを漁ってみた結果、
SDL_CreateShapedWindowSDL_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公式からアーカイブを取得・展開後、上記の通りに配置する

ソースコード

こちらを参考にさせて頂きました。
「プログラムでシダを描画する」をD言語+SDL2+dubで描画する
ありがとうございました。

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);
}
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

[after]
ss.png

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

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

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

感想

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?