VSCodeのデバッガでbreakすることができて、GTK3も動いた。そのあたりのメモ
- 2018/10
- Windows10(64bit)
- msys2(
mingw-w64-x86_64-gcc
)
とりあえず動くようになった。
rustのインストールとtoolchainはどれにするか
でインストールする。
選択肢は
-
msvc
(default) とgnu
-
stable
(default)とnightly
stable
と nightly
では、いろいろなところで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
vscode
が gcc
と gdb
を見つけられるように、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-b
で cargo build
でビルド。
Debug
タブで構成を追加して、GDB
を選択。launch.json
の taget
を ./target/debug/hello
に書き換える。F5で実行。
{
// 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ポイントをセットして、実行を停止できることを確認する。
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
のサンプルコードを実行してみる。
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();
}
[dependencies.gtk]
version = "0.1.0"
features = ["v3_16"]
ビルドして実行。
動いた。
OpgnGLのwidget
GtkGLArea
を使う
参考になるコードを探す
これをRustに移植してみたい。
その前に、msysでビルドして実行してみる。
cloneして02_add_openglをvscodeで開いた。
shellをmsys2のbashにする。
{
"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",
},
}
{
"version": "2.0.0",
"tasks": [
{
"label": "make",
"type": "shell",
"command": "mingw32-make", // <- 上記のbash上で/mingw64/bin/mingw32-makeする意図
"problemMatcher": "$gcc",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
{
"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で検索してリポジトリを発見。
こちらは動いた。
プロジェクトを調べる。
[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
で初期化するのが重要。
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: >k::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 による初期化のために epoxy
と shared_library
への依存を追加。
[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的なライブラリ
- https://github.com/anholt/libepoxy
- https://github.com/mjkoo/epoxy-rs
- https://github.com/itadinanta/gfx-gtk
- https://github.com/antoyo/servo-gtk/blob/master/src/view.rs
opengl
unsafeの使い方が参考になる