File size: 4,999 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
local max_thumb_value = (1 << 15) - 1

DefineClass.MouseViaGamepad = {
	__parents = { "XWindow", "TerminalTarget" },
	properties = {
		{ category = "General", id = "enabled", name = "Enabled", editor = "bool", default = false },
	},
	
	Id = "idMouseViaGamepad",
	IdNode = true,
	HandleMouse = false,
	Dock = "box",
	ZOrder = 10000000, -- above everything, even above loading screens
	DrawOnTop = true,
	Clip = false,
	UseClipBox = false,
	
	LeftClickButton = "ButtonA",
	RightClickButton = "ButtonB",
	LastClickTimes = false,
	DoubleClickTime = 300, --ms
}

function MouseViaGamepad:Init()
	terminal.AddTarget(self)
	
	self.LastClickTimes = { }
	
	local image = XImage:new({
		Id = "idCursor",
		HAlign = "left",
		VAlign = "top",
		Clip = false,
		UseClipBox = false,
	}, self)
	image:AddDynamicPosModifier({id = "cursor", target = "gamepad"})
end

function MouseViaGamepad:Done()
	terminal.RemoveTarget(self)
end

function MouseViaGamepad:OnXNewPacket(_, controller_id, last_state, current_state)
	if not self.enabled then return end
	
	local left_trigger = current_state.LeftTrigger > 0
	local right_trigger = current_state.RightTrigger > 0
	hr.GamepadMouseEnabled = not left_trigger and not right_trigger
end

function MouseViaGamepad:OnXButtonDown(button, controller_id)
	if not self.enabled then return end
	
	if not self.visible then
		ForceHideMouseCursor("MouseViaGamepad")
		self:SetVisible(true)
		GamepadMouseSetPos(terminal.GetMousePos())
	end
	
	local mouse_btn = (button == self.LeftClickButton and "L") or (button == self.RightClickButton and "R")
	if mouse_btn then
		local pt = GamepadMouseGetPos()
		
		local now = now()
		local last_click_time = self.LastClickTimes[mouse_btn]
		self.LastClickTimes[mouse_btn] = now
		local is_double_click = last_click_time and (now - last_click_time) <= self.DoubleClickTime
		if is_double_click then
			return terminal.MouseEvent("OnMouseButtonDoubleClick", pt, mouse_btn, "gamepad")
		else
			return terminal.MouseEvent("OnMouseButtonDown", pt, mouse_btn, "gamepad")
		end
	end
	return "continue"
end

function MouseViaGamepad:OnXButtonUp(button, controller_id)
	local mouse_btn = (button == self.LeftClickButton and "L") or (button == self.RightClickButton and "R")
	if mouse_btn then
		local pt = GamepadMouseGetPos()
		return terminal.MouseEvent("OnMouseButtonUp", pt, mouse_btn)
	end
	return "continue"
end

function MouseViaGamepad:OnMousePos(pt)
	if not self.enabled then return end
	
	if self.visible then
		UnforceHideMouseCursor("MouseViaGamepad")
		self:SetVisible(false)
	end
	GamepadMouseSetPos(pt)
	
	return "continue"
end

function MouseViaGamepad:SetEnabled(enabled)
	self.enabled = enabled
	hr.GamepadMouseEnabled = enabled
	if enabled then
		if not self:IsThreadRunning("UpdateMousePosThread") then
			self:CreateThread("UpdateMousePosThread", self.UpdateMousePosThread, self)
		end
	else
		UnforceHideMouseCursor("MouseViaGamepad")
		if self:IsThreadRunning("UpdateMousePosThread") then
			self:DeleteThread("UpdateMousePosThread")
		end
	end
end

function MouseViaGamepad:SetCursorImage(image)
	self.idCursor:SetImage(image)
end

function MouseViaGamepad:UpdateMousePosThread()
	GamepadMouseSetPos(terminal.GetMousePos())

	local previous_pos
	while true do
		WaitNextFrame()
		local pos = GamepadMouseGetPos()
		if pos ~= previous_pos then
			terminal.SetMousePos(pos)
			self.parent:MouseEvent("OnMousePos", pos)
			
			previous_pos = pos
		end
	end
end

----------------------
function GetMouseViaGamepadCtrl()
	return terminal.desktop and rawget(terminal.desktop, "idMouseViaGamepad")
end

MouseViaGamepadHideSkipReasons = {
	["MouseViaGamepad"] = true,
}

function OnMsg.ShowMouseCursor(visible)
	local mouse_win = GetMouseViaGamepadCtrl()
	if not mouse_win then return end
	
	local show = not not next(ShowMouseReasons)
	
	local force_hide
	for reason in pairs(ForceHideMouseReasons) do
		if not MouseViaGamepadHideSkipReasons[reason] then
			force_hide = true
			break
		end
	end
	
	local my_visible = show and not force_hide
	
	mouse_win:SetVisible(my_visible)
end

function OnMsg.MouseCursor(cursor)
	local mouse_win = GetMouseViaGamepadCtrl()
	if not mouse_win then return end
	
	local path, name, ext = SplitPath(cursor)
	--local gamepad_cursor = string.format("%s%s_pad%s", path, name, ext)
	local gamepad_cursor = string.format("%s%s%s", path, name, ext)
	mouse_win:SetCursorImage(gamepad_cursor)
end

function ShowMouseViaGamepad(show)
	local mouse_win = GetMouseViaGamepadCtrl()
	if not mouse_win and show then
		mouse_win = MouseViaGamepad:new({}, terminal.desktop)
	end
	if mouse_win then
		if show then
			ForceHideMouseCursor("MouseViaGamepad")
			mouse_win:SetVisible(true)
			GamepadMouseSetPos(terminal.GetMousePos())
		end	
		mouse_win:SetEnabled(show)
	end
end

function DeleteMouseViaGamepad()
	local mouse_win = GetMouseViaGamepadCtrl()
	if mouse_win then
		mouse_win:delete()
	end	
end

function IsMouseViaGamepadActive()
	return hr.GamepadMouseEnabled == true or hr.GamepadMouseEnabled == 1
end