1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "platformwindow.h"
24
25#if defined(WIN32)
26#include "win32window.h"
27WIN32Window window;
28#elif defined(ANDROID)
29#include "androidwindow.h"
30#elif defined(__EMSCRIPTEN__)
31#include "sdlwindow.h"
32SDLWindow window;
33#else
34#include "x11window.h"
35#include <framework/core/clock.h>
36X11Window window;
37#endif
38
39#include <framework/core/clock.h>
40#include <framework/core/eventdispatcher.h>
41#include <framework/graphics/image.h>
42
43#ifdef ANDROID
44PlatformWindow& g_window = g_androidWindow;
45#else
46PlatformWindow& g_window = window;
47#endif
48
49int PlatformWindow::loadMouseCursor(const std::string& file, const Point& hotSpot)
50{
51 ImagePtr image = Image::load(file);
52
53 if(!image) {
54 g_logger.traceError(stdext::format("unable to load cursor image file %s", file));
55 return -1;
56 }
57
58 if(image->getBpp() != 4) {
59 g_logger.error("the cursor image must have 4 channels");
60 return -1;
61 }
62
63 if(image->getWidth() != 32 || image->getHeight() != 32) {
64 g_logger.error("the cursor image must have 32x32 dimension");
65 return -1;
66 }
67
68 return internalLoadMouseCursor(image, hotSpot);
69}
70
71void PlatformWindow::updateUnmaximizedCoords()
72{
73 if(!isMaximized() && !isFullscreen()) {
74 m_unmaximizedPos = m_position;
75 m_unmaximizedSize = m_size;
76 }
77}
78
79void PlatformWindow::processKeyDown(Fw::Key keyCode)
80{
81 if (std::this_thread::get_id() != g_dispatcherThreadId) {
82 g_dispatcher.addEvent(std::bind(&PlatformWindow::processKeyDown, this, keyCode));
83 return;
84 }
85
86 if(keyCode == Fw::KeyUnknown)
87 return;
88
89 if(keyCode == Fw::KeyCtrl) {
90 m_inputEvent.keyboardModifiers |= Fw::KeyboardCtrlModifier;
91 return;
92 } else if(keyCode == Fw::KeyAlt) {
93 m_inputEvent.keyboardModifiers |= Fw::KeyboardAltModifier;
94 return;
95 } else if(keyCode == Fw::KeyShift) {
96 m_inputEvent.keyboardModifiers |= Fw::KeyboardShiftModifier;
97 return;
98 }
99
100 if(m_keysState[keyCode])
101 return;
102
103 m_keysState[keyCode] = true;
104 m_lastKeysPress[keyCode] = -1;
105
106 m_inputEvent.reset(Fw::KeyDownInputEvent);
107 m_inputEvent.type = Fw::KeyDownInputEvent;
108 m_inputEvent.keyCode = keyCode;
109
110 if(m_onInputEvent) {
111 m_onInputEvent(m_inputEvent);
112
113 m_inputEvent.reset(Fw::KeyPressInputEvent);
114 m_inputEvent.keyCode = keyCode;
115 m_lastKeysPress[keyCode] = g_clock.millis();
116 m_firstKeysPress[keyCode] = g_clock.millis();
117 m_onInputEvent(m_inputEvent);
118 }
119}
120
121void PlatformWindow::processKeyUp(Fw::Key keyCode)
122{
123 if (std::this_thread::get_id() != g_dispatcherThreadId) {
124 g_dispatcher.addEvent(std::bind(&PlatformWindow::processKeyUp, this, keyCode));
125 return;
126 }
127
128 if(keyCode == Fw::KeyUnknown)
129 return;
130
131 if(keyCode == Fw::KeyCtrl) {
132 m_inputEvent.keyboardModifiers &= ~Fw::KeyboardCtrlModifier;
133 return;
134 } else if(keyCode == Fw::KeyAlt) {
135 m_inputEvent.keyboardModifiers &= ~Fw::KeyboardAltModifier;
136 return;
137 } else if(keyCode == Fw::KeyShift) {
138 m_inputEvent.keyboardModifiers &= ~Fw::KeyboardShiftModifier;
139 return;
140 } else if(keyCode == Fw::KeyNumLock) {
141 for(uchar k = Fw::KeyNumpad0; k <= Fw::KeyNumpad9; ++k) {
142 if(m_keysState[(Fw::Key)k])
143 processKeyUp((Fw::Key)k);
144 }
145 }
146
147 if(!m_keysState[keyCode])
148 return;
149
150 m_keysState[keyCode] = false;
151
152 if(m_onInputEvent) {
153 m_inputEvent.reset(Fw::KeyUpInputEvent);
154 m_inputEvent.keyCode = keyCode;
155 m_onInputEvent(m_inputEvent);
156 }
157}
158
159void PlatformWindow::releaseAllKeys()
160{
161 if (std::this_thread::get_id() != g_dispatcherThreadId) {
162 g_dispatcher.addEvent(std::bind(&PlatformWindow::releaseAllKeys, this));
163 return;
164 }
165
166 for(auto it : m_keysState) {
167 Fw::Key keyCode = it.first;
168 bool pressed = it.second;
169
170 if(!pressed)
171 continue;
172
173 processKeyUp(keyCode);
174 }
175
176 m_inputEvent.keyboardModifiers = 0;
177
178 for(int i=0;i<Fw::MouseButtonLast + 1;++i)
179 m_mouseButtonStates[i] = false;
180}
181
182void PlatformWindow::fireKeysPress()
183{
184 if (std::this_thread::get_id() != g_dispatcherThreadId) {
185 g_dispatcher.addEvent(std::bind(&PlatformWindow::fireKeysPress, this));
186 return;
187 }
188
189 // avoid massive checks
190 if(m_keyPressTimer.ticksElapsed() < 10)
191 return;
192 m_keyPressTimer.restart();
193
194 for(auto it : m_keysState) {
195 Fw::Key keyCode = it.first;
196 bool pressed = it.second;
197
198 if(!pressed)
199 continue;
200
201 ticks_t lastPressTicks = m_lastKeysPress[keyCode];
202 ticks_t firstKeyPress = m_firstKeysPress[keyCode];
203 if(g_clock.millis() - lastPressTicks >= KEY_PRESS_REPEAT_INTERVAL) {
204 if(m_onInputEvent) {
205 m_inputEvent.reset(Fw::KeyPressInputEvent);
206 m_inputEvent.keyCode = keyCode;
207 m_inputEvent.autoRepeatTicks = g_clock.millis() - firstKeyPress;
208 m_onInputEvent(m_inputEvent);
209 }
210 m_lastKeysPress[keyCode] = g_clock.millis();
211 }
212 }
213}
214
215void PlatformWindow::flash() { }
216
217