#!/opt/lnxall_app/bin/lua

-- Created by jiaqiang.ye@lnxall.com
-- Simple Touchscreen press emulator
-- 2025/11/25

local struct = require 'struct'
local invoker = require 'invoker'

local gfmt = string.format
local g_fd = nil
local g_dev = nil

local EV_ABS               = 3
local ABS_MT_TRACKING_ID   = 57
local ABS_MT_POSITION_X    = 53
local ABS_MT_POSITION_Y    = 54
local EV_KEY               = 1
local BTN_TOUCH            = 330
local ABS_X                = 0
local ABS_Y                = 1
local EV_MSC               = 4
local MSC_TIMESTAMP        = 5
local UP                   = 0
local DOWN                 = 1
local EV_SYN               = 0
local SYN_REPORT           = 0

local function get_tracks()
	local track, nt = {}, 0
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3f0c }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3efb }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3ec9 }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3e54 }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3d86 }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3c3e }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3ae2 }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x38c4 }
	nt = nt + 1; track[nt] = { x = 0x2100, y = 0x3683 }
	return track
end

local function press_init()
	local dev = arg[1]
	if invoker.statfile(dev) == "character" then
		g_dev = dev
		return true
	end

	invoker.setenv("LANG", nil)
	local okay, output = invoker.invoke(invoker.OUTPUT, "/bin/bash", "-c",
		"libinput list-devices 2>/dev/null | grep -e Kernel: -e Capabilities:")
	if type(output) ~= "string" then output = "" end

	local devn = nil
	for line in string.gmatch(output, "[^\r\n]+") do
		local what = string.match(line, "^Kernel:%s+([^%s]+)")
		local that = string.match(line, "^Capabilities:%s+([^%s]+)")
		if what and invoker.statfile(what) == "character" then
			devn = what
		elseif that == "touch" and devn then
			dev = devn
			break
		end
	end

	if invoker.statfile(dev) ~= "character" then
		io.stderr:write("Error, input device not specified.\n")
		io.stderr:flush()
		return false
	end
	g_dev = dev
	return true
end

local function open_device()
	local fd = invoker.open(g_dev, 524290)
	if not fd then
		io.stderr:write(gfmt("Eror, failed to open device: %s\n", g_dev))
		io.stderr:flush()
		return false
	end
	g_fd = fd
	return true
end

local function write_event(evkey, evcode, value)
	local ups, upm = invoker.uptime()
	local wbuf = struct.pack("<I8I8HHi4", ups, upm * 1000, evkey, evcode, value)
	if invoker.writefd(g_fd, wbuf) ~= #wbuf then
		io.stderr:write("Error, write Event has failed.\n")
		io.stderr:flush()
		return false
	end
	return true
end

local function touchscreen()
	local track = get_tracks()
	write_event(EV_ABS, ABS_MT_TRACKING_ID,   0x0)

	for idx, t in ipairs(track) do
		write_event(EV_ABS, ABS_MT_POSITION_X, t.x)
		write_event(EV_ABS, ABS_MT_POSITION_Y, t.y)
		if idx == 1 then
			write_event(EV_KEY, BTN_TOUCH, DOWN)
		end
		write_event(EV_ABS, ABS_X, t.y)
		write_event(EV_ABS, ABS_Y, t.y)
		write_event(EV_SYN, SYN_REPORT, 0)
		invoker.msleep(50)
	end
	write_event(EV_ABS, ABS_MT_TRACKING_ID,   0xffffffff)
	write_event(EV_KEY, BTN_TOUCH,            UP)
	write_event(EV_SYN, SYN_REPORT,           0x0)
	return true
end

if not press_init() then os.exit(1) end
if not open_device() then os.exit(2) end
touchscreen()
os.exit(0)
