source: trunk/src/input/input.cpp @ 2645

Last change on this file since 2645 was 2645, checked in by sam, 9 years ago

input: make keyboard state platform-independent.

  • Property svn:keywords set to Id
File size: 11.2 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://www.wtfpl.net/ for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include <cstdlib>
16
17#include "core.h"
18
19namespace lol
20{
21
22/*
23 * Input implementation class
24 */
25
26InputTracker* Input::m_input_tracker = nullptr;
27
28static class InputData
29{
30    friend class Input;
31
32public:
33    InputData()
34      : mouse(-1),
35        buttons(0),
36        nentities(0),
37        lastfocus(0)
38    {
39        m_keystate.Resize(Key::Last);
40        memset(&m_keystate[0], 0, m_keystate.Bytes());
41    }
42
43private:
44    ivec2 mouse;
45    uint32_t buttons;
46
47    Array<uint8_t> m_keystate;
48
49    static int const MAX_ENTITIES = 100;
50    WorldEntity *entities[MAX_ENTITIES];
51    int nentities;
52    WorldEntity *lastfocus;
53
54    Array<Stick *> m_sticks;
55}
56inputdata;
57
58static InputData * const data = &inputdata;
59
60/*
61 * ButtonSetting class
62 */
63
64int ButtonSetting::GetActionSettingIdx(Action a)
65{
66    for (int i = 0; i < m_associated_action_list.Count(); i++)
67        if (ACTION_CMP(m_associated_action_list[i].m_action, a))
68            return i;
69    return -1;
70}
71
72/*
73 * InputTracker class
74 */
75
76InputTracker::InputTracker()
77{
78    m_gamegroup = GAMEGROUP_BEFORE;
79
80    for (int i = 0; i < Key::Last * 2; ++i)
81        m_input_status << 0;
82
83    Ticker::Ref(this);
84}
85
86//Internal
87int InputTracker::GetButtonSettingIdx(Key k)
88{
89    for (int i = 0; i < m_input_assocation_list.Count(); i++)
90        if (m_input_assocation_list[i].m_raw_button == k)
91            return i;
92    return -1;
93}
94
95//-----
96int InputTracker::GetCurrentButtonStatus(Key k)
97{
98    if (k < m_input_status.Count())
99        return m_input_status[k];
100    return 0;
101}
102
103//-----
104int InputTracker::GetPreviousButtonStatus(Key k)
105{
106    if (k < m_input_status.Count())
107        return m_input_status[(int)k + (int)Key::Last];
108    return 0;
109}
110
111//Internal : Updates the action status & timers
112void InputTracker::UpdateActionStatus(float seconds)
113{
114    Array<uint8_t> &keystate = Input::GetKeyboardState();
115
116    //SOOOoooo ugly.
117    for (int i = 0; i < Key::Last; ++i)
118    {
119        m_input_status[i + Key::Last] = m_input_status[i];
120        m_input_status[i] = keystate[i];
121    }
122
123    for (int i = 0; i < m_input_assocation_list.Count(); i++)
124    {
125        ButtonSetting &CurIT = m_input_assocation_list[i];
126
127        for (int j = 0; j < CurIT.m_associated_action_list.Count(); j++)
128        {
129            ActionSetting &CurAS = CurIT.m_associated_action_list[j];
130
131            if (CurAS.m_buffered_since <= CurAS.m_buffering_time)
132                CurAS.m_buffered_since += seconds;
133
134            if (GetCurrentButtonStatus(CurIT.m_raw_button) &&
135                CurAS.m_buffering_time >= .0f)
136                CurAS.m_buffered_since = .0f;
137        }
138    }
139}
140
141//Helps link a software input Action-Id to an hardware input Button-Id.
142void InputTracker::LinkActionToKey(Action a, Key k)
143{
144    int ITIdx = GetButtonSettingIdx(k);
145    if (ITIdx == -1)
146    {
147        ITIdx = m_input_assocation_list.Count();
148        m_input_assocation_list << ButtonSetting(k);
149    }
150
151    ButtonSetting &CurIT = m_input_assocation_list[ITIdx];
152
153    int ASIdx = CurIT.GetActionSettingIdx(a);
154    if (ASIdx == -1)
155    {
156        ASIdx = CurIT.m_associated_action_list.Count();
157        CurIT.m_associated_action_list << ActionSetting(a);
158    }
159}
160
161//Helps unlink a software input Action-Id to an hardware input k-Id.
162void InputTracker::UnlinkAction(Action a)
163{
164    for (int i = 0; i < m_input_assocation_list.Count(); i++)
165    {
166        ButtonSetting &CurIT = m_input_assocation_list[i];
167        int ASIdx = CurIT.GetActionSettingIdx(a);
168        if (ASIdx != -1)
169            CurIT.m_associated_action_list.Remove(ASIdx);
170    }
171}
172
173//Returns the current status of a given action
174int InputTracker::GetStatus(Action a)
175{
176    for (int i = 0; i < m_input_assocation_list.Count(); i++)
177    {
178        ButtonSetting &CurIT = m_input_assocation_list[i];
179        int ASIdx = CurIT.GetActionSettingIdx(a);
180        if (ASIdx != -1)
181        {
182            ActionSetting &CurAS = CurIT.m_associated_action_list[ASIdx];
183
184            if (CurAS.m_buffering_time >= .0f && CurAS.m_buffered_since <= CurAS.m_buffering_time)
185                return 1;
186            return 0;
187        }
188    }
189    return 0;
190}
191
192//Returns TRUE if action status went from Active to Inactive this frame
193bool InputTracker::WasReleased(Action a)
194{
195    for (int i = 0; i < m_input_assocation_list.Count(); i++)
196    {
197        ButtonSetting &CurIT = m_input_assocation_list[i];
198        int ASIdx = CurIT.GetActionSettingIdx(a);
199        if (ASIdx != -1)
200        {
201
202            if (GetPreviousButtonStatus(CurIT.m_raw_button) &&
203                !GetCurrentButtonStatus(CurIT.m_raw_button))
204                return true;
205            return false;
206        }
207    }
208    return false;
209}
210
211//Returns TRUE if action status went from Inactive to Active this frame
212bool InputTracker::WasPressed(Action a)
213{
214    for (int i = 0; i < m_input_assocation_list.Count(); i++)
215    {
216        ButtonSetting &CurIT = m_input_assocation_list[i];
217        int ASIdx = CurIT.GetActionSettingIdx(a);
218        if (ASIdx != -1)
219        {
220            if (!GetPreviousButtonStatus(CurIT.m_raw_button) &&
221                GetCurrentButtonStatus(CurIT.m_raw_button))
222                return true;
223            return false;
224        }
225    }
226    return false;
227}
228
229//Returns the current status of a given action
230int InputTracker::GetStatus(Key k)
231{
232    return GetCurrentButtonStatus(k);
233}
234
235//Returns TRUE if action status went from Active to Inactive this frame
236bool InputTracker::WasReleased(Key k)
237{
238    if (GetPreviousButtonStatus(k) &&
239        !GetCurrentButtonStatus(k))
240        return true;
241    return false;
242}
243
244//Returns TRUE if action status went from Inactive to Active this frame
245bool InputTracker::WasPressed(Key k)
246{
247    if (!GetPreviousButtonStatus(k) &&
248        GetCurrentButtonStatus(k))
249        return true;
250    return false;
251}
252
253/*
254 * Public Input class
255 */
256
257#if 0
258vec2 Input::GetAxis(int axis)
259{
260    vec2 ret;
261
262    /* Simulate a joystick using the keyboard. */
263    int left = GetKeyState(Key::D) - (GetKeyState(Key::A) | GetKeyState(Key::Q));
264    int up = (GetKeyState(Key::W) | GetKeyState(Key::Z)) - GetKeyState(Key::S);
265    ret.x += left;
266    ret.y += up;
267    if (left && up)
268        ret = ret * sqrtf(0.5f);
269
270    return ret;
271}
272#endif
273
274ivec2 Input::GetMousePos()
275{
276    return data->mouse;
277}
278
279uint32_t Input::GetMouseButtons()
280{
281    return data->buttons;
282}
283
284Array<uint8_t> &Input::GetKeyboardState()
285{
286    return data->m_keystate;
287}
288
289int Input::GetKeyState(int key)
290{
291    return data->m_keystate[key];
292}
293
294//Helps link a software input Action-Id to an hardware input Button-Id.
295void Input::LinkActionToKey(Action a, Key k)
296{
297    if (CheckInputTrackerInit())
298        Input::m_input_tracker->LinkActionToKey(a, k);
299}
300
301//Helps unlink a software input Action-Id to an hardware input Button-Id.
302void Input::UnlinkAction(Action a)
303{
304    if (CheckInputTrackerInit())
305        Input::m_input_tracker->UnlinkAction(a);
306}
307
308//Returns the current status of a given action
309int Input::GetStatus(Action a)
310{
311    if (CheckInputTrackerInit())
312        return Input::m_input_tracker->GetStatus(a);
313    return 0;
314}
315
316//Returns TRUE if action status when from Active to Inactive this frame
317bool Input::WasPressed(Action a)
318{
319    if (CheckInputTrackerInit())
320        return Input::m_input_tracker->WasPressed(a);
321    return false;
322}
323
324//Returns TRUE if action status when from Active to Inactive this frame
325bool Input::WasReleased(Action a)
326{
327    if (CheckInputTrackerInit())
328        return Input::m_input_tracker->WasReleased(a);
329    return false;
330}
331
332//Returns the current status of a given action
333int Input::GetStatus(Key k)
334{
335    if (CheckInputTrackerInit())
336        return Input::m_input_tracker->GetStatus(k);
337    return 0;
338}
339
340//Returns TRUE if action status when from Active to Inactive this frame
341bool Input::WasPressed(Key k)
342{
343    if (CheckInputTrackerInit())
344        return Input::m_input_tracker->WasPressed(k);
345    return false;
346}
347
348//Returns TRUE if action status when from Active to Inactive this frame
349bool Input::WasReleased(Key k)
350{
351    if (CheckInputTrackerInit())
352        return Input::m_input_tracker->WasReleased(k);
353    return false;
354}
355
356//--
357void Input::TrackMouse(WorldEntity *e)
358{
359    if (data->nentities >= InputData::MAX_ENTITIES)
360        return;
361    data->entities[data->nentities] = e;
362    data->nentities++;
363}
364
365void Input::UntrackMouse(WorldEntity *e)
366{
367    for (int n = 0; n < data->nentities; n++)
368    {
369        if (data->entities[n] != e)
370            continue;
371
372        data->entities[n] = data->entities[data->nentities - 1];
373        data->nentities--;
374        n--;
375    }
376}
377
378void Input::SetMousePos(ivec2 coord)
379{
380    data->mouse = coord;
381
382    WorldEntity *top = nullptr;
383
384    /* Find the top “widget” amongst all entities that match the
385     * mouse coordinates */
386    for (int n = 0; n < data->nentities; n++)
387    {
388        if (coord.x < data->entities[n]->m_bbox[0].x
389             || coord.x >= data->entities[n]->m_bbox[1].x
390             || coord.y < data->entities[n]->m_bbox[0].y
391             || coord.y >= data->entities[n]->m_bbox[1].y)
392            continue;
393
394        if (!top || top->m_bbox[1].z < data->entities[n]->m_bbox[1].z)
395            top = data->entities[n];
396    }
397
398    for (int n = 0; n < data->nentities; n++)
399    {
400        if (data->entities[n] == top)
401        {
402            data->entities[n]->m_mousepos = coord - (ivec2)top->m_bbox[0].xy;
403            if (top != data->lastfocus)
404                data->entities[n]->m_pressed = data->buttons;
405            else
406                data->entities[n]->m_clicked = 0;
407        }
408        else
409        {
410            data->entities[n]->m_mousepos = ivec2(-1);
411            /* FIXME */
412            data->entities[n]->m_released = 0;
413            data->entities[n]->m_pressed = 0;
414            data->entities[n]->m_clicked = 0;
415        }
416    }
417
418    data->lastfocus = top;
419}
420
421void Input::SetMouseButton(int index)
422{
423    uint32_t flag = 1 << index;
424    data->buttons |= flag;
425
426    if (data->lastfocus)
427    {
428        if (!(data->lastfocus->m_pressed & flag))
429            data->lastfocus->m_clicked |= flag;
430        data->lastfocus->m_pressed |= flag;
431        data->lastfocus->m_released &= ~flag;
432    }
433}
434
435void Input::UnsetMouseButton(int index)
436{
437    uint32_t flag = 1 << index;
438    data->buttons &= ~flag;
439
440    if (data->lastfocus)
441    {
442        if (!(data->lastfocus->m_pressed & flag))
443            data->lastfocus->m_released |= flag;
444        data->lastfocus->m_pressed &= ~flag;
445        data->lastfocus->m_clicked &= ~flag;
446    }
447}
448
449Stick *Input::CreateStick()
450{
451    Stick *stick = new Stick();
452    Ticker::Ref(stick);
453    data->m_sticks.Push(stick);
454    return stick;
455}
456
457void Input::DestroyStick(Stick *stick)
458{
459    for (int i = 0; i < data->m_sticks.Count(); i++)
460        if (data->m_sticks[i] == stick)
461            data->m_sticks.Remove(i);
462    Ticker::Unref(stick);
463}
464
465Stick *Input::TrackStick(int desired)
466{
467    /* FIXME: add the possibility to choose amongst sticks */
468    if (desired >= data->m_sticks.Count())
469        return nullptr;
470    Ticker::Ref(data->m_sticks[desired]);
471    return data->m_sticks[desired];
472}
473
474void Input::UntrackStick(Stick *stick)
475{
476    Ticker::Unref(stick);
477}
478
479} /* namespace lol */
480
Note: See TracBrowser for help on using the repository browser.