LoginSignup
18
14

More than 5 years have passed since last update.

WindowsでRust環境を作ってGtk3でOpenGLする

Last updated at Posted at 2018-10-19

VSCodeのデバッガでbreakすることができて、GTK3も動いた。そのあたりのメモ

  • 2018/10
  • Windows10(64bit)
  • msys2( mingw-w64-x86_64-gcc )

triangle.jpg

とりあえず動くようになった。

rustのインストールとtoolchainはどれにするか

でインストールする。
選択肢は

  • msvc (default) と gnu
  • stable (default)と nightly

stablenightly では、いろいろなところでnightly版を要求された。

msvc はデバッガに lldb を使う( https://github.com/vadimcn/vscode-lldb )ところがうまくいかなかった。
gnu は gdbを使い、これはうまく動いた。

nightly-gnu で行く。

インストールの確認

>rustup show
Default host: x86_64-pc-windows-msvc

installed toolchains
--------------------

stable-x86_64-pc-windows-gnu
stable-x86_64-pc-windows-msvc
nightly-x86_64-pc-windows-gnu (default) # <= これ
nightly-x86_64-pc-windows-msvc

rustupで最初から nightly-x86_64-pc-windows-gnu を選択するか
下記のようにして変更する。

> rustup install nightly-x86_64-pc-windows-gnu
> rustup default nightly-x86_64-pc-windows-gnu

gccとgdbをインストールして、vscodeから見えるようにする

msys2のインストール

下記のパッケージをインストールする。

  • mingw-w64-x86_64-gcc
  • mingw-w64-x86_64-gdb

vscodegccgdb を見つけられるように、Windowsの環境変数 PATH に追加する。


D:\msys64\mingw64\bin

vscode

動作確認用に rust の新しいプロジェクトを作成する。

# --binがデフォルトになった
> cargo new hello

hello フォルダを vscode で開く。
vscodeの拡張機能をインストールする。

  • Rust(rls)。インストール後、rls, racer, rustfmtなどをインストールするぽいのでstatusバーのアップデートが消えるまで、裏でインストールしている?
  • Native Debug。rustの実行ファイルをgdbでデバッグする

main.rs を開いて ctrl-shift-bcargo build でビルド。
Debug タブで構成を追加して、GDB を選択。launch.jsontaget./target/debug/hello に書き換える。F5で実行。

launch.json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [

        {
            "name": "Debug",
            "type": "gdb",
            "request": "launch",
            "target": "./target/debug/hello",
            "cwd": "${workspaceRoot}"
        }
    ]
}

main.rs にbreakポイントをセットして、実行を停止できることを確認する。

break.jpg

gtk3でGUI

msys2

$ pacman -Sl | grep gtk3
mingw32 mingw-w64-i686-gtk3 3.24.1-1
mingw32 mingw-w64-i686-webkitgtk3 2.4.11-6
mingw64 mingw-w64-x86_64-gtk3 3.24.1-1
mingw64 mingw-w64-x86_64-webkitgtk3 2.4.11-6
$ pacman -Sy mingw-w64-x86_64-gtk3

vscode

のサンプルコードを実行してみる。

main.rs
extern crate gtk;
use gtk::prelude::*;
use gtk::{ButtonsType, DialogFlags, MessageDialog, MessageType, Window};

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }
    MessageDialog::new(
        None::<&Window>,
        DialogFlags::empty(),
        MessageType::Info,
        ButtonsType::Ok,
        "Hello World",
    )
    .run();
}
Cargo.toml
[dependencies.gtk]
version = "0.1.0"
features = ["v3_16"]

ビルドして実行。

gtk.jpg

動いた。

OpgnGLのwidget

GtkGLArea を使う

参考になるコードを探す

これをRustに移植してみたい。
その前に、msysでビルドして実行してみる。

cloneして02_add_openglをvscodeで開いた。

shellをmsys2のbashにする。

.vscode/settings.json
{
    "terminal.integrated.shell.windows": "D:\\msys64\\usr\\bin\\bash.exe",
    "terminal.integrated.env.windows": {
        "MSYSTEM": "MINGW64",
        "CHERE_INVOKING": "1",
        "PATH": "/mingw64/bin:/usr/local/bin:/usr/bin:/bin",
    },
}
.vscode/tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "make",
            "type": "shell",
            "command": "mingw32-make", // <- 上記のbash上で/mingw64/bin/mingw32-makeする意図
            "problemMatcher": "$gcc",
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}
.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug",
            "type": "gdb",
            "request": "launch",
            "target": "./a",
            "cwd": "${workspaceRoot}",
            "preLaunchTask": "make"
        }
    ]
}

MakefileをWindows向けに手直し。

# OpenGL32とGLEW32。glewはpacmanで入れる
all:
    gcc `pkg-config --cflags gtk+-3.0` main.c -lOpenGL32 -lGLEW32 `pkg-config --libs gtk+-3.0`

msys2で動いた。Rust に移植してみよう。

Rust版

GLAreaが無い

let gl_area = gtk::GLArea::new(); // GLAreaが無い ?

GLAreaで検索してリポジトリを発見。

こちらは動いた。
プロジェクトを調べる。

Cargo.toml
[dependencies]
gio = "^0"
gtk = { version = "^0", features=["v3_16"] } # featuresでGtkに含まれる内容が変わる?

たぶん v3_16 から GtkGLArea のサポートが入った。

OpenGLの初期化

C++だとglewとかでやるところ。
こちらも gfx-gtk さんに書いてある通りにしたらできた。

c++だとこれだけらしいのだが、

#include <epoxy/gl.h>
#include <epoxy/glx.h>

D:\msys64\mingw64\bin\libepoxy-0.dll から取ってきているぽい?

pub fn load() {
    let loader = Failover(
        DlProcLoader::current_module(),
        Failover(
            DlProcLoader::open(Path::new("libepoxy-0")),
            Failover(
                DlProcLoader::open(Path::new("libepoxy0")),
                DlProcLoader::open(Path::new("libepoxy")),
            ),
        ),
    );
    epoxy::load_with(fn_from(loader));
    gl::load_with(epoxy::get_proc_addr);
}

epoxy::load_with で先に関数ポインタを得て、それを gl::load_with に渡すということらしい。

mainとGTK組み立て

gtk::GLArea::new() して初期化する。
GtkがOpenGLのコンテキストを作成した後の connect_realize で初期化するのが重要。

main.rs
extern crate epoxy;
extern crate gio;
extern crate gl;
extern crate gtk;

use gio::prelude::*;
use gtk::prelude::*;
use std::env::args;

mod gl_renderer;
mod gl_loader;


fn build_ui(application: &gtk::Application) {
    let window = gtk::ApplicationWindow::new(application);

    window.set_title("Window");
    window.set_default_size(640, 480);
    window.set_border_width(10);
    window.set_position(gtk::WindowPosition::Center);
    window.connect_delete_event(move |win, _| {
        win.destroy();
        Inhibit(false)
    });

    let gl = std::rc::Rc::new(gl_renderer::GlRenderer::new());

    let gl_area = gtk::GLArea::new();
    //gl_area.set_vexpand(true);
    //gtk_widget_set_hexpand(gl_area, TRUE);
    let gl_clone = gl.clone();
    gl_area.connect_realize(move |gl_area| {
        gl_area.make_current();
        /*
        if (gtk_gl_area_get_error(area) != NULL) {
            fprintf(stderr, "Unknown error\n");
            return;
        }
        */
        gl_area.set_has_depth_buffer(true);

        gl_loader::load();

        gl_clone.initialize();
    });

    let gl_clone = gl.clone();
    gl_area.connect_render(move |_area, _context| {
        gl_clone.render();

        Inhibit(true)
    });

    window.add(&gl_area);

    window.show_all();
}

fn main() {
    let application = gtk::Application::new("com.github.basic", gio::ApplicationFlags::empty())
        .expect("Initialization failed...");

    application.connect_startup(|app| {
        build_ui(app);
    });

    application.run(&args().collect::<Vec<_>>());
}

libepoxy による初期化のために epoxyshared_library への依存を追加。

Cargo.toml
[dependencies]
gio = "^0"
gtk = { version = "^0", features=["v3_16"] }
gl = "0.10"
epoxy = "0.0.4"
shared_library = "0.1.7"

OpenGL移植メモ

OpenGLそのもののコードに辿り着いた。

unsafe 呼び出しとpointerに不慣れだとちょっと大変。
よく使いそうなところをピックアップしてみる。

primitiveのポインタ

i32 id = 0;

unsafe {
  unsafe_func(&id); // *const i32 が要求されているとして referenceをそのまま渡せる
}

*const u8 から文字列

        let renderer = unsafe {
            let p = gl::GetString(gl::RENDERER);
            CStr::from_ptr(p as *const i8)
        };
        println!("Renderer: {}", renderer.to_string_lossy());

void の nullptr

let p = std::ptr::null();

バッファを渡して文字列を得る

with_capacity でバッファ長を確保して、
後で set_len する。

            let mut infoLog = Vec::<u8>::with_capacity(infoLen as usize);
            //std::vector<char> infoLog(infoLen);
            gl::GetShaderInfoLog(
                target,
                infoLen,
                &mut infoLen as *mut i32,
                infoLog.as_mut_ptr() as *mut i8,
            );
            infoLog.set_len(infoLen as usize); // <- 重要
            let cs = CString::from_vec_unchecked(infoLog);
            println!("Error compiling shader: {}", cs.to_string_lossy());

配列、Vecからポインタを得る

as_ptr, as_mut_ptr

キャストする

// p: *const u8
p as *const i8

RefCell

なんか使い方が違うような気がする。

参考

gtk-rs

libepoxy

GTK3が使っているglew的なライブラリ

opengl

unsafeの使い方が参考になる

18
14
1

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
18
14