local sys = require 'lx.sys'
local protodef = require 'lx.protodef'
local mainloop = require 'lx.mainloop'
local gip = {}
gip.proto = {
-- Definitions from proto/gip.h
GIPF_DOUBLE_BUFFER = 0x1,
GIPF_DAMAGE_NOTIF = 0x2,
GIPF_MODESET = 0x4,
GIPF_MOUSE_XY = 0x10,
GIPF_MOUSE_CURSOR = 0x20,
GIPC_RESET = 0,
GIPR_INITIATE = 1,
GIPR_OK = 2,
GIPR_FAILURE = 3,
GIPC_ENABLE_FEATURES = 4,
GIPC_DISABLE_FEATURES = 5,
GIPN_BUFFER_INFO = 10,
GIPC_QUERY_MODE = 11,
GIPR_MODE_INFO = 12,
GIPC_SET_MODE = 13,
GIPN_BUFFER_DAMAGE = 14,
GIPC_SWITCH_BUFFER = 15,
GIPN_KEY_DOWN = 20,
GIPN_KEY_UP = 21,
GIPN_MOUSE_DATA = 30,
GIPN_MOUSE_XY = 31,
GIPN_MOUSE_PRESSED = 32,
GIPN_MOUSE_RELEASED = 33,
gip_msg_header = {
fmt = 'LLL', -- code, req_id, arg
len = 12
},
-- buffer_info_msg = protodef.token .. protodef.fb_info
buffer_info_msg = {
len = protodef.token.len + protodef.fb_info.len
},
-- mode_info_msg = protodef.fb_info
mode_info_msg = {
len = protodef.fb_info.len
},
-- buffer_damage_msg = protodef.fb_region
buffer_damage_msg = {
len = protodef.fb_region.len
},
}
gip.new_handler = function(fd)
local h = {}
h.fd = fd
-- Replacable callbacks
function h:cb_reset(req_id, arg) end
function h:cb_initiate(req_id, arg) end
function h:cb_ok(req_id, arg) end
function h:cb_failure(req_id, arg) end
function h:cb_enable_features(req_id, arg) end
function h:cb_disable_features(req_id, arg) end
function h:cb_query_mode(req_id, arg) end
function h:cb_set_mode(req_id, arg) end
function h:cb_switch_buffer(req_id, arg) end
function h:cb_key_down(req_id, arg) end
function h:cb_key_up(req_id, arg) end
function h:cb_buffer_info(req_id, arg, token, fb_info) end
function h:cb_mode_info(req_id, arg, fb_info) end
function h:cb_buffer_damage(req_id, arg, region) end
function h:cb_unknown_message(code, req_id, arg) error("Unknown GIP message") end
function h:fd_error() error("GIP FD error") end
h.requests_in_progress = {}
h.msg_id = 0
h.mainloop_fd = mainloop.add_fd(h.fd, function() h:fd_error() end)
function h:send_msg(code, req_id, arg, data)
if req_id == nil then
req_id = h.msg_id
h.msg_id = h.msg_id + 1
end
local msgdata = string.pack(gip.proto.gip_msg_header.fmt, code, req_id, arg)
if code == gip.proto.GIPN_BUFFER_INFO then
msgdata = msgdata .. string.pack(protodef.token.fmt, data.tok)
.. string.pack(protodef.fb_info.fmt, data.geom.width, data.geom.height,
data.geom.pitch, data.geom.bpp, data.geom.memory_model)
elseif code == gip.proto.GIPN_MODE_INFO then
error("Not implemented") --TODO
elseif code == gip.proto.GIPN_BUFFER_DAMAGE then
error("Not implemented") --TODO
end
h.mainloop_fd:write(msgdata)
return req_id
end
function h:cmd(code, arg, data, cb)
local req_id = h:send_msg(code, nil, arg, data)
h.requests_in_progress[req_id] = cb
end
function h:got_reply(code, req_id, arg, data)
if h.requests_in_progress[req_id] ~= nil then
h.requests_in_progress[req_id](code, arg, data)
h.requests_in_progress[req_id] = nil
end
end
local function gip_handler(ev)
local code, req_id, arg = string.unpack(gip.proto.gip_msg_header.fmt, ev)
if code == gip.proto.GIPC_RESET then
h:cb_reset(req_id, arg)
elseif code == gip.proto.GIPR_INITIATE then
h:cb_initiate(req_id, arg)
h:got_reply(code, req_id, arg)
elseif code == gip.proto.GIPR_OK then
h:cb_ok(req_id, arg)
h:got_reply(code, req_id, arg)
elseif code == gip.proto.GIPR_FAILURE then
h:cb_failure(req_id, arg)
h:got_reply(code, req_id, arg)
elseif code == gip.proto.GIPC_ENABLE_FEATURE then
h:cb_enable_feature(req_id, arg)
elseif code == gip.proto.GIPC_DISABLE_FEATURE then
h:cb_disable_feature(req_id, arg)
elseif code == gip.proto.GIPC_QUERY_MODE then
h:cb_query_mode(req_id, arg)
elseif code == gip.proto.GIPC_SET_MODE then
h:cb_set_mode(req_id, arg)
elseif code == gip.proto.GIPC_SWITCH_BUFFER then
h:cb_switch_buffer(req_id, arg)
elseif code == gip.proto.GIPN_KEY_DOWN then
h:cb_key_down(req_id, arg)
elseif code == gip.proto.GIPN_KEY_UP then
h:cb_key_up(req_id, arg)
elseif code == gip.proto.GIPN_BUFFER_INFO then
h.mainloop_fd:expect(gip.proto.buffer_info_msg.len, function(msg)
local tok, p1 = string.unpack(protodef.token.fmt, msg)
local w, h, pitch, bpp, mm = string.unpack(protodef.fb_info.fmt, msg, p1)
h:cb_buffer_info(req_id, arg, token, {
width = w, height = h,
pitch = pitch, bpp = bpp, memory_model = mm })
end)
elseif code == gip.proto.GIPR_MODE_INFO then
h.mainloop_fd:expect(gip.proto.mode_info_msg.len, function(msg)
local w, h, pitch, bpp, mm = string.unpack(protodef.fb_info.fmt, msg)
local info = {
width = w, height = h,
pitch = pitch, bpp = bpp, memory_model = mm }
h:cb_mode_info(req_id, arg, info)
h:got_reply(code, req_id, arg, info)
end)
elseif code == gip.proto.GIPN_BUFFER_DAMAGE then
h.mainloop_fd:expect(gip.proto.buffer_damage_msg.len, function(msg)
local rx, ry, rw, rh = string.unpack(protodef.fb_region.fmt, msg)
h:cb_buffer_damage(req_id, arg, { x = rx, y = ry, w = rw, h = rh })
end)
else
h:cb_unknown_message(code, req_id, arg)
end
h.mainloop_fd:expect(gip.proto.gip_msg_header.len, gip_handler)
end
h.mainloop_fd:expect(gip.proto.gip_msg_header.len, gip_handler)
return h
end
return gip