uv_timer_t timer1;
uv_timer_t timer2;
void exit_cb(void* data) {
uv_close(timer1);
uv_close(timer2);
}
構造体に格納して引きずり回さなければならない。
typedef struct {
uv_timer_t timer1;
uv_timer_t timer2;
} THE_TIMERS;
void exit_cb(void* data) {
THE_TIMERS* timers = (THE_TIMERS*) data;
uv_close(timers->timer1);
uv_close(timers->timer2);
}
どんな時に起きるかというと、libuv を GUI アプリと併用する場合。libuv はメインループを uv_run() で実行する為、GUI のメインループを回す為にアイドルを作る必要がある。アイドルを作るだってぇ!!!
と思ったあなた、ぜひクリスマスは libuv とお過ごし下さい。
例えば、GTK だと以下の様になります。
#include <uv/uv.h>
#include <gtk/gtk.h>
void
idle_cb(uv_idle_t* idle, int status) {
gtk_main_iteration_do(FALSE);
}
int
main(int argc, char* argv[]) {
gtk_init(&argc, &argv);
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show_all(window);
uv_idle_t idle;
uv_idle_init(uv_default_loop(), &idle);
uv_idle_start(&idle, idle_cb);
uv_run(uv_default_loop());
}
この uv_run() のループを止めるには idle を uv_close() する必要があるのですが、ループ自身もハンドルで、かつ uv_run() はそのハンドルのリファレンスがある内しか回らないという仕様なので、こう書く事が出来ます。
#include <uv/uv.h>
#include <gtk/gtk.h>
void
timer_cb(uv_timer_t* timer, int status) {
GtkWidget* label = (GtkWidget*) timer->data;
char buf[64];
time_t t;
time(&t);
strftime(buf, sizeof(buf), "%c", localtime(&t));
gtk_label_set_text(GTK_LABEL(label), buf);
}
void
idle_cb(uv_idle_t* idle, int status) {
gtk_main_iteration_do(FALSE);
}
void
destroy_cb(GtkWidget* w, gpointer data) {
uv_loop_t* loop = (uv_loop_t*) data;
uv_unref(loop); // stop idle
uv_unref(loop); // stop timer
}
int
main(int argc, char* argv[]) {
gtk_init(&argc, &argv);
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "clock");
GtkWidget* vbox = gtk_vbox_new(FALSE, 1);
GtkWidget* label = gtk_label_new("");
gtk_container_add(GTK_CONTAINER(vbox), label);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 20);
gtk_widget_show_all(window);
uv_timer_t timer;
uv_timer_init(uv_default_loop(), &timer);
timer.data = (void*) label;
uv_timer_start(&timer, timer_cb, 1000, 1000);
uv_idle_t idle;
uv_idle_init(uv_default_loop(), &idle);
uv_idle_start(&idle, idle_cb);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(destroy_cb), uv_default_loop());
uv_run(uv_default_loop());
}
もちろん、uv_close() する回数が少なければループは止まらないので、あまりちゃんとした方法では無い気もしますが。GTKの様に iteration 動作出来るツールキットならばいいのですが、iteration 動作出来ないライブラリと併用する場合、libuv 側が回される事になる訳です。つまり libuv に1回だけループを回す手続きが必要な訳で、探しても無かったのでさっきパッチ書いて投げた。取り込まれるかどうかは知らない。
ちなみに、上記の例は libuv と gtk で作る時計なんだけど、これを perl で書くとこうなる
use strict;
use warnings;
use Encode;
use Encode::Locale;
use UV;
use Glib;
use Gtk2 -init;
use Time::Piece;
my $w = Gtk2::Window->new('toplevel');
$w->set_title("timer");
my $v = Gtk2::VBox->new(0, 1);
my $l = Gtk2::Label->new();
$v->add($l);
$w->add($v);
$w->set_default_size(300, 20);
$w->show_all;
my $t = UV::timer_init();
UV::timer_start($t, 1000, 1000, sub {
$l->set_label(decode(locale => localtime->strftime));
});
my $i = UV::idle_init();
UV::idle_start($i, sub {
Gtk2->main_iteration_do(0);
});
$w->signal_connect(destroy => sub {
UV::close($t);
UV::close($i);
});
UV::run;
UV は typester さんが書いた奴。
typester/p5-UV - GitHubあと、最近書いた go言語の libuv バインディング go-uv で書くとこうなる
https://github.com/typester/p5-UV
package main
import (
"fmt"
"github.com/mattn/go-uv"
"github.com/mattn/go-gtk/gtk"
"time"
)
func main() {
gtk.Init(nil)
window := gtk.Window(gtk.GTK_WINDOW_TOPLEVEL)
window.SetTitle("Clock")
vbox := gtk.VBox(false, 1)
label := gtk.Label("")
vbox.Add(label)
window.Add(vbox)
window.SetDefaultSize(300, 20)
window.ShowAll()
timer, _ := uv.TimerInit(nil)
timer.Start(func(h *uv.Handle, status int, 1000, 1000) {
label.SetLabel(fmt.Sprintf("%v", time.Now()))
})
idle, _ := uv.IdleInit(nil)
idle.Start(func(h *uv.Handle, status int) {
gtk.MainIterationDo(false)
})
window.Connect("destroy", func() {
timer.Close(nil)
idle.Close(nil)
})
uv.DefaultLoop().Run()
}
mattn/go-uv - GitHub
Go binding for libuv
https://github.com/mattn/go-uv