Windows
Dlanguage
SDL
dlang
dub

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

More than 3 years have passed since last update.


概要



  • 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言語の標準ライブラリに欲しい。