将 SDL 集成到 GLib 主循环中
Integrate SDL into GLib mainloop
我正在计划一个使用 SDL 的小型 Vala 游戏项目,我想知道如何将 SDL 正确集成到 GLib 主循环中。上次我用 Vala 和 SDL 做一些事情时,我使用了标准的 SDL 事件循环,但老实说,这是一堆废话,它破坏了整个漂亮的 Vala 或更确切地说是 GLib 信号系统。
我发现 an integration for Cogl 并且我正在寻找与 SDL 相同的东西。
GLib 源由三个回调组成:
- 在轮询之前检查源是否准备就绪(并避免
poll
调用)
- 一个检查轮询后源是否仍然准备就绪
- 一个发送附加的回调
您可以相当简单地进行源检查和调度事件。
public delegate bool SDLSourceFunc (SDL.Event event);
public class SDLSource : Source
{
public SDL.Event event;
public bool prepare (out uint timeout)
{
timeout = 0;
return true;
}
public bool check ()
{
return SDL.Event.poll (out event) > 0;
}
public bool dispatch (SourceFunc callback)
{
return ((SDLSourceFunc) callback) (event);
}
public void add_callback (SDLSourceFunc callback)
{
base.add_callback ((SourceFunc) callback);
}
}
然后,您将使用 Source.CONTINUE
:
循环
var source = new SDLSource ();
source.add_callback ((event) => {
// handle event here
return Source.CONTINUE;
});
source.attach (MainContext.@default ());
这是非常基本的:您的来源可以使用 SDL.EventMask
和 SDL.peep
过滤特定事件。为单个源分派多个事件并附加相关文件描述符也更有效。
如果你使用一些异步代码,你可以直接从 Source
调度中唤醒协程:
public async void next_event_async ()
{
var source = new SDLSource ();
source.attach (MainContext.@default ());
source.add_callback (handle_event_async.callback);
yield;
return source.event;
}
遗憾的是,SDL 使这变得非常困难。理想情况下,SDL API 会提供一个文件描述符来侦听,只要有事件可用,就会发出信号,这样您就可以将它添加到 glib 主循环中,但它没有这个。
虽然不太理想,但我认为最简单和最便携的方法就是安装一个定时器来定期检查 SDL 事件。您可以使用 g_timeout_add 定期唤醒主循环,然后在回调中调用 SDL_PollEvent。
这类似于 ,除了要注意那里给出的方法会有效地导致忙等待(准备函数 returns 如果没有可用的事件则为零超时)所以它最终将始终使用 100% CPU.
Clutter 曾经有一个 SDL backend,您仍然可以在 Git 历史中看到它。它基本上采用这种定期唤醒主循环的方法,除了它创建自定义源而不是使用 g_timeout_add。
考虑到您正在编写游戏,如果您希望无论如何都要不断重绘并让进程定期阻塞 SDL_GL_SwapWindow,那么只安装一个带有 [=13= 的空闲处理程序可能更有意义] 然后在回调中执行所有 SDL 操作。您可以在空闲回调开始时检查事件,然后在结束时调用 SDL_GL_SwapWindow,这会阻止等待重绘完成,这意味着它不会占用 100% CPU.
我正在计划一个使用 SDL 的小型 Vala 游戏项目,我想知道如何将 SDL 正确集成到 GLib 主循环中。上次我用 Vala 和 SDL 做一些事情时,我使用了标准的 SDL 事件循环,但老实说,这是一堆废话,它破坏了整个漂亮的 Vala 或更确切地说是 GLib 信号系统。
我发现 an integration for Cogl 并且我正在寻找与 SDL 相同的东西。
GLib 源由三个回调组成:
- 在轮询之前检查源是否准备就绪(并避免
poll
调用) - 一个检查轮询后源是否仍然准备就绪
- 一个发送附加的回调
您可以相当简单地进行源检查和调度事件。
public delegate bool SDLSourceFunc (SDL.Event event);
public class SDLSource : Source
{
public SDL.Event event;
public bool prepare (out uint timeout)
{
timeout = 0;
return true;
}
public bool check ()
{
return SDL.Event.poll (out event) > 0;
}
public bool dispatch (SourceFunc callback)
{
return ((SDLSourceFunc) callback) (event);
}
public void add_callback (SDLSourceFunc callback)
{
base.add_callback ((SourceFunc) callback);
}
}
然后,您将使用 Source.CONTINUE
:
var source = new SDLSource ();
source.add_callback ((event) => {
// handle event here
return Source.CONTINUE;
});
source.attach (MainContext.@default ());
这是非常基本的:您的来源可以使用 SDL.EventMask
和 SDL.peep
过滤特定事件。为单个源分派多个事件并附加相关文件描述符也更有效。
如果你使用一些异步代码,你可以直接从 Source
调度中唤醒协程:
public async void next_event_async ()
{
var source = new SDLSource ();
source.attach (MainContext.@default ());
source.add_callback (handle_event_async.callback);
yield;
return source.event;
}
遗憾的是,SDL 使这变得非常困难。理想情况下,SDL API 会提供一个文件描述符来侦听,只要有事件可用,就会发出信号,这样您就可以将它添加到 glib 主循环中,但它没有这个。
虽然不太理想,但我认为最简单和最便携的方法就是安装一个定时器来定期检查 SDL 事件。您可以使用 g_timeout_add 定期唤醒主循环,然后在回调中调用 SDL_PollEvent。
这类似于
Clutter 曾经有一个 SDL backend,您仍然可以在 Git 历史中看到它。它基本上采用这种定期唤醒主循环的方法,除了它创建自定义源而不是使用 g_timeout_add。
考虑到您正在编写游戏,如果您希望无论如何都要不断重绘并让进程定期阻塞 SDL_GL_SwapWindow,那么只安装一个带有 [=13= 的空闲处理程序可能更有意义] 然后在回调中执行所有 SDL 操作。您可以在空闲回调开始时检查事件,然后在结束时调用 SDL_GL_SwapWindow,这会阻止等待重绘完成,这意味着它不会占用 100% CPU.