summaryrefslogtreecommitdiff
path: root/evloop.lua
blob: 6c12c3a957eb4e7dbc39c08be0ba0e90f8297c4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
local M = {}
M.__index = M

local queue = {}

local function resume_task(task, e)
	if coroutine.status(task.co) == "dead" then
		return false
	end

	if task.filter then
		for _, f in ipairs(task.filter) do
			if f == e[1] then
				goto resume
			end
		end
		return true
	end

	::resume::
	local ok, ret = coroutine.resume(task.co, e)
	if not ok then
		io.stderr:write(debug.traceback(task.co, ret)..'\n\n')
		error "error in event loop"
	end
	task.filter = ret
	return coroutine.status(task.co) ~= "dead"
end

local function create_task(fn)
	local task = {}
	task.co = coroutine.create(fn)
	resume_task(task)
	return task
end

function M.poll(...)
	local filter = {...}
	return unpack(coroutine.yield(#filter > 0 and filter))
end

function M.sleep(secs)
	local t = 0
	while t < secs do
		local e, dt = M.poll "update"
		t = t + dt
	end
	return t
end

function M.queue(...)
	table.insert(queue, {...})
end

function M.await_any(...)
	local tasks = {...}
	for i, t in ipairs(tasks) do
		tasks[i] = create_task(t)
	end
	while true do
		local e = coroutine.yield()
		for _, t in ipairs(tasks) do
			if not resume_task(t, e) then return end
		end
	end
end

function M.mainloop(start)
	local main = create_task(function()
		start()
	end)

	return function()
		love.event.pump()
		local es = love.event.poll()
		while true do
			local e = {es()}
			if not e[1] then break end
			if e[1] == "quit" then
				if not love.quit or not love.quit() then
					return e[2] or 0
				end
			end
			if love.handlers[e[1]] then
				love.handlers[e[1]](unpack(e, 2))
			end
			table.insert(queue, e)
		end

		local q = queue
		queue = {}
		for i, e in ipairs(q) do
			if not resume_task(main, e) then return 0 end
		end

		local dt = love.timer.step()
		if not resume_task(main, {"update", dt}) then return 0 end

		if love.graphics and love.graphics.isActive() then
			love.graphics.clear(love.graphics.getBackgroundColor())
			require "viewport".origin()
			if not resume_task(main, {"draw", dt}) then return 0 end
			love.graphics.present()
		end

		love.timer.sleep(0.001)
	end
end

return M