1// MIT License
2
3// Copyright (c) 2020 Vadim Grigoruk @nesbox // grigoruk@gmail.com
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23#include "api.h"
24#include "core.h"
25
26#include "tic_assert.h"
27
28static_assert(sizeof(tic80_gamepad) == 1, "tic80_gamepad");
29static_assert(sizeof(tic80_gamepads) == 4, "tic80_gamepads");
30static_assert(sizeof(tic80_mouse) == 4, "tic80_mouse");
31static_assert(sizeof(tic80_keyboard) == 4, "tic80_keyboard");
32static_assert(sizeof(tic80_input) == 12, "tic80_input");
33
34static bool isKeyPressed(const tic80_keyboard* input, tic_key key)
35{
36 for (s32 i = 0; i < TIC80_KEY_BUFFER; i++)
37 if (input->keys[i] == key)
38 return true;
39
40 return false;
41}
42
43u32 tic_api_btnp(tic_mem* tic, s32 index, s32 hold, s32 period)
44{
45 tic_core* core = (tic_core*)tic;
46
47 if (index < 0)
48 {
49 return (~core->state.gamepads.previous.data) & core->memory.ram->input.gamepads.data;
50 }
51 else if (hold < 0 || period < 0)
52 {
53 return ((~core->state.gamepads.previous.data) & core->memory.ram->input.gamepads.data) & (1 << index);
54 }
55
56 tic80_gamepads previous;
57
58 previous.data = core->state.gamepads.holds[index] >= (u32)hold
59 ? period && core->state.gamepads.holds[index] % period ? core->state.gamepads.previous.data : 0
60 : core->state.gamepads.previous.data;
61
62 return ((~previous.data) & core->memory.ram->input.gamepads.data) & (1 << index);
63}
64
65u32 tic_api_btn(tic_mem* tic, s32 index)
66{
67 tic_core* core = (tic_core*)tic;
68
69 if (index < 0)
70 {
71 return core->memory.ram->input.gamepads.data;
72 }
73 else
74 {
75 return core->memory.ram->input.gamepads.data & (1 << index);
76 }
77}
78
79bool tic_api_key(tic_mem* tic, tic_key key)
80{
81 return key > tic_key_unknown
82 ? isKeyPressed(&tic->ram->input.keyboard, key)
83 : tic->ram->input.keyboard.data;
84}
85
86bool tic_api_keyp(tic_mem* tic, tic_key key, s32 hold, s32 period)
87{
88 tic_core* core = (tic_core*)tic;
89
90 if (key > tic_key_unknown)
91 {
92 bool prevDown = hold >= 0 && period >= 0 && core->state.keyboard.holds[key] >= (u32)hold
93 ? period && core->state.keyboard.holds[key] % period
94 ? isKeyPressed(&core->state.keyboard.previous, key)
95 : false
96 : isKeyPressed(&core->state.keyboard.previous, key);
97
98 bool down = isKeyPressed(&tic->ram->input.keyboard, key);
99
100 return !prevDown && down;
101 }
102
103 for (s32 i = 0; i < TIC80_KEY_BUFFER; i++)
104 {
105 tic_key key = tic->ram->input.keyboard.keys[i];
106
107 if (key)
108 {
109 bool wasPressed = false;
110
111 for (s32 p = 0; p < TIC80_KEY_BUFFER; p++)
112 {
113 if (core->state.keyboard.previous.keys[p] == key)
114 {
115 wasPressed = true;
116 break;
117 }
118 }
119
120 if (!wasPressed)
121 return true;
122 }
123 }
124
125 return false;
126}
127
128tic_point tic_api_mouse(tic_mem* memory)
129{
130 return memory->ram->input.mouse.relative
131 ? (tic_point){memory->ram->input.mouse.rx, memory->ram->input.mouse.ry}
132 : (tic_point){memory->ram->input.mouse.x - TIC80_OFFSET_LEFT, memory->ram->input.mouse.y - TIC80_OFFSET_TOP};
133}
134
135void tic_core_tick_io(tic_mem* tic)
136{
137 tic_core* core = (tic_core*)tic;
138
139 // process gamepads mapping
140 u8* keycodes = tic->ram->mapping.data;
141 for(s32 i = 0; i < sizeof(tic_mapping); ++i)
142 if(keycodes[i] && tic_api_key(tic, keycodes[i]))
143 tic->ram->input.gamepads.data |= 1 << i;
144
145 // process gamepad
146 for (s32 i = 0; i < COUNT_OF(core->state.gamepads.holds); i++)
147 {
148 u32 mask = 1 << i;
149 u32 prevDown = core->state.gamepads.previous.data & mask;
150 u32 down = tic->ram->input.gamepads.data & mask;
151
152 u32* hold = &core->state.gamepads.holds[i];
153 if (prevDown && prevDown == down) (*hold)++;
154 else *hold = 0;
155 }
156
157 // process keyboard
158 for (s32 i = 0; i < tic_keys_count; i++)
159 {
160 bool prevDown = isKeyPressed(&core->state.keyboard.previous, i);
161 bool down = isKeyPressed(&tic->ram->input.keyboard, i);
162
163 u32* hold = &core->state.keyboard.holds[i];
164
165 if (prevDown && down) (*hold)++;
166 else *hold = 0;
167 }
168}
169