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