1/*
2 *  Copyright (C) 2012 Samsung Electronics
3 *
4 *  This library is free software; you can redistribute it and/or
5 *  modify it under the terms of the GNU Library General Public
6 *  License as published by the Free Software Foundation; either
7 *  version 2 of the License, or (at your option) any later version.
8 *
9 *  This library is distributed in the hope that it will be useful,
10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 *  Library General Public License for more details.
13 *
14 *  You should have received a copy of the GNU Library General Public License
15 *  along with this library; see the file COPYING.LIB.  If not, write to
16 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 *  Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "Vibration.h"
22
23#if ENABLE(VIBRATION)
24
25#include "VibrationClient.h"
26
27namespace WebCore {
28
29// Maximum number of entries in a vibration pattern.
30const unsigned MaxVibrationPatternLength = 99;
31// Maximum duration of a vibration in 10 seconds.
32const unsigned MaxVibrationDuration = 10000;
33
34Vibration::Vibration(VibrationClient* client)
35    : m_vibrationClient(client)
36    , m_timer(this, &Vibration::timerFired)
37    , m_state(State::Idle)
38{
39}
40
41Vibration::~Vibration()
42{
43    m_vibrationClient->vibrationDestroyed();
44}
45
46bool Vibration::vibrate(const VibrationPattern& pattern)
47{
48    VibrationPattern& sanitized = const_cast<VibrationPattern&>(pattern);
49    size_t length = sanitized.size();
50
51    // If the pattern is too long then truncate it.
52    if (length > MaxVibrationPatternLength) {
53        sanitized.shrink(MaxVibrationPatternLength);
54        length = MaxVibrationPatternLength;
55    }
56
57    // If the length of pattern is even and is not zero, then remove the last entry in pattern.
58    if (length && !(length % 2)) {
59        sanitized.removeLast();
60        length--;
61    }
62
63    // If any pattern entry is too long then truncate it.
64    for (auto& duration : sanitized)
65        duration = std::min(duration, MaxVibrationDuration);
66
67    // Pre-exisiting instance need to be canceled when vibrate() is called.
68    if (isVibrating())
69        cancelVibration();
70
71    // If pattern is an empty list, then return true and terminate these steps.
72    if (!length || (length == 1 && !sanitized[0])) {
73        cancelVibration();
74        return true;
75    }
76
77    m_pattern = sanitized;
78
79    m_timer.startOneShot(0);
80    m_state = State::Waiting;
81    return true;
82}
83
84void Vibration::cancelVibration()
85{
86    m_pattern.clear();
87    if (isVibrating()) {
88        m_timer.stop();
89        m_state = State::Idle;
90        m_vibrationClient->cancelVibration();
91    }
92}
93
94void Vibration::timerFired(Timer<Vibration>* timer)
95{
96    ASSERT_UNUSED(timer, timer == &m_timer);
97
98    m_timer.stop();
99
100    if (m_pattern.isEmpty()) {
101        m_state = State::Idle;
102        return;
103    }
104
105    switch (m_state) {
106    case State::Vibrating:
107        m_state = State::Waiting;
108        break;
109    case State::Waiting:
110    case State::Idle:
111        m_state = State::Vibrating;
112        m_vibrationClient->vibrate(m_pattern[0]);
113        break;
114    }
115    m_timer.startOneShot(m_pattern[0] / 1000.0);
116    m_pattern.remove(0);
117}
118
119const char* Vibration::supplementName()
120{
121    return "Vibration";
122}
123
124void provideVibrationTo(Page* page, VibrationClient* client)
125{
126    Vibration::provideTo(page, Vibration::supplementName(), std::make_unique<Vibration>(client));
127}
128
129} // namespace WebCore
130
131#endif // ENABLE(VIBRATION)
132
133