gosu 0.7.50 → 0.8.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,239 @@
1
+ #include <Gosu/TextInput.hpp>
2
+ #include <Gosu/Input.hpp>
3
+ #include <Gosu/Utility.hpp>
4
+ #include <SDL2/SDL.h>
5
+ #include <cwctype>
6
+
7
+ struct Gosu::TextInput::Impl
8
+ {
9
+ // These two strings contain UTF-8 data.
10
+ // See this Wiki page for an overview of what is going on here:
11
+ // http://wiki.libsdl.org/Tutorials/TextInput#CandidateList
12
+ std::wstring text, composition;
13
+ unsigned caretPos, selectionStart;
14
+
15
+ Impl()
16
+ : caretPos(0), selectionStart(0)
17
+ {
18
+ }
19
+
20
+ void insertText(const std::wstring& newText)
21
+ {
22
+ // Stop IME composition.
23
+ composition.clear();
24
+
25
+ // Delete (overwrite) previous selection.
26
+ if (caretPos != selectionStart) {
27
+ unsigned min = std::min(caretPos, selectionStart);
28
+ unsigned max = std::max(caretPos, selectionStart);
29
+ text.erase(text.begin() + min, text.begin() + max);
30
+ caretPos = selectionStart = min;
31
+ }
32
+
33
+ text.insert(text.begin() + caretPos, newText.begin(), newText.end());
34
+ caretPos += newText.size();
35
+ selectionStart = caretPos;
36
+ }
37
+
38
+ void moveLeft(bool modifySelection)
39
+ {
40
+ if (caretPos > 0)
41
+ caretPos -= 1;
42
+
43
+ if (modifySelection)
44
+ selectionStart = caretPos;
45
+ }
46
+
47
+ void moveRight(bool modifySelection)
48
+ {
49
+ if (caretPos < text.length())
50
+ caretPos += 1;
51
+
52
+ if (modifySelection)
53
+ selectionStart = caretPos;
54
+ }
55
+
56
+ void moveWordLeft(bool modifySelection)
57
+ {
58
+ if (caretPos == text.length())
59
+ --caretPos;
60
+
61
+ while (caretPos > 0 && std::iswspace(text.at(caretPos - 1)))
62
+ --caretPos;
63
+
64
+ while (caretPos > 0 && !std::iswspace(text.at(caretPos - 1)))
65
+ --caretPos;
66
+
67
+ if (modifySelection)
68
+ selectionStart = caretPos;
69
+ }
70
+
71
+ void moveWordRight(bool modifySelection)
72
+ {
73
+ while (caretPos < text.length() && std::iswspace(text.at(caretPos)))
74
+ ++caretPos;
75
+
76
+ while (caretPos < text.length() && !std::iswspace(text.at(caretPos)))
77
+ ++caretPos;
78
+
79
+ if (modifySelection)
80
+ selectionStart = caretPos;
81
+ }
82
+
83
+ void moveToBeginningOfLine(bool modifySelection)
84
+ {
85
+ caretPos = 0;
86
+
87
+ if (modifySelection)
88
+ selectionStart = caretPos;
89
+ }
90
+
91
+ void moveToEndOfLine(bool modifySelection)
92
+ {
93
+ caretPos = static_cast<unsigned>(text.length());
94
+
95
+ if (modifySelection)
96
+ selectionStart = caretPos;
97
+ }
98
+
99
+ void deleteBackward()
100
+ {
101
+ if (selectionStart != caretPos) {
102
+ unsigned min = std::min(caretPos, selectionStart);
103
+ unsigned max = std::max(caretPos, selectionStart);
104
+ text.erase(text.begin() + min, text.begin() + max);
105
+ selectionStart = caretPos = min;
106
+ }
107
+ else if (caretPos > 0) {
108
+ unsigned oldCaret = caretPos;
109
+ caretPos -= 1;
110
+ text.erase(text.begin() + caretPos, text.begin() + oldCaret);
111
+ selectionStart = caretPos;
112
+ }
113
+ }
114
+
115
+ void deleteForward()
116
+ {
117
+ if (selectionStart != caretPos) {
118
+ unsigned min = std::min(caretPos, selectionStart);
119
+ unsigned max = std::max(caretPos, selectionStart);
120
+ text.erase(text.begin() + min, text.begin() + max);
121
+ selectionStart = caretPos = min;
122
+ }
123
+ else if (caretPos < text.length()) {
124
+ unsigned oldCaret = caretPos;
125
+ caretPos += 1;
126
+ text.erase(text.begin() + oldCaret, text.begin() + caretPos);
127
+ selectionStart = caretPos = oldCaret;
128
+ }
129
+
130
+ }
131
+ };
132
+
133
+ Gosu::TextInput::TextInput()
134
+ : pimpl(new Impl)
135
+ {
136
+ }
137
+
138
+ Gosu::TextInput::~TextInput()
139
+ {
140
+ }
141
+
142
+ std::wstring Gosu::TextInput::text() const
143
+ {
144
+ std::wstring composedText = pimpl->text;
145
+ if (! pimpl->composition.empty()) {
146
+ composedText.insert(pimpl->caretPos, pimpl->composition);
147
+ }
148
+ return composedText;
149
+ }
150
+
151
+ void Gosu::TextInput::setText(const std::wstring& text)
152
+ {
153
+ pimpl->text = text;
154
+ pimpl->composition.clear();
155
+ pimpl->caretPos = pimpl->selectionStart = static_cast<unsigned>(text.length());
156
+ }
157
+
158
+ unsigned Gosu::TextInput::caretPos() const
159
+ {
160
+ return static_cast<unsigned>(pimpl->caretPos);
161
+ }
162
+
163
+ void Gosu::TextInput::setCaretPos(unsigned pos)
164
+ {
165
+ pimpl->caretPos = pos;
166
+ }
167
+
168
+ unsigned Gosu::TextInput::selectionStart() const
169
+ {
170
+ return static_cast<unsigned>(pimpl->selectionStart);
171
+ }
172
+
173
+ void Gosu::TextInput::setSelectionStart(unsigned pos)
174
+ {
175
+ pimpl->selectionStart = pos;
176
+ }
177
+
178
+ bool Gosu::TextInput::feedSDLEvent(void* event)
179
+ {
180
+ const SDL_Event* e = static_cast<SDL_Event*>(event);
181
+
182
+ switch (e->type) {
183
+ // Direct text input, and sent after IME composition completes.
184
+ case SDL_TEXTINPUT: {
185
+ std::wstring textToInsert = utf8ToWstring(e->text.text);
186
+ textToInsert = filter(textToInsert);
187
+ pimpl->insertText(textToInsert);
188
+ return true;
189
+ }
190
+ // IME composition in progress.
191
+ case SDL_TEXTEDITING: {
192
+ pimpl->composition = utf8ToWstring(e->edit.text);
193
+ return true;
194
+ }
195
+ // Emulate "standard" Windows/X11 keyboard behavior.
196
+ case SDL_KEYDOWN: {
197
+ // ...but not if the IME is currently compositing.
198
+ if (! pimpl->composition.empty()) {
199
+ return false;
200
+ }
201
+
202
+ bool ctrlDown = (e->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL));
203
+ bool shiftDown = (e->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT));
204
+ SDL_Keycode key = e->key.keysym.sym;
205
+
206
+ switch (key) {
207
+ case SDLK_LEFT:
208
+ if (ctrlDown)
209
+ pimpl->moveWordLeft(! shiftDown);
210
+ else
211
+ pimpl->moveLeft(! shiftDown);
212
+ return true;
213
+ case SDLK_RIGHT:
214
+ if (ctrlDown)
215
+ pimpl->moveWordRight(! shiftDown);
216
+ else
217
+ pimpl->moveRight(! shiftDown);
218
+ return true;
219
+ case SDLK_HOME:
220
+ pimpl->moveToBeginningOfLine(! shiftDown);
221
+ return true;
222
+ case SDLK_END:
223
+ pimpl->moveToEndOfLine(! shiftDown);
224
+ return true;
225
+ case SDLK_BACKSPACE:
226
+ pimpl->deleteBackward();
227
+ return true;
228
+ case SDLK_DELETE:
229
+ pimpl->deleteForward();
230
+ return true;
231
+ }
232
+ break;
233
+ }
234
+
235
+ // TODO: Handle copy & paste.
236
+ }
237
+
238
+ return false;
239
+ }
@@ -0,0 +1,215 @@
1
+ #include <Gosu/Gosu.hpp>
2
+ #include <SDL2/SDL.h>
3
+ #include <cstdlib>
4
+ #include <memory>
5
+ #include <stdexcept>
6
+
7
+ using namespace std::tr1::placeholders;
8
+
9
+ namespace Gosu
10
+ {
11
+ namespace FPS
12
+ {
13
+ void registerFrame();
14
+ }
15
+
16
+ SDL_DisplayMode desktopDisplayMode = { 0, 0 };
17
+ }
18
+
19
+ unsigned Gosu::screenWidth()
20
+ {
21
+ // TODO - not thread-safe
22
+ if (desktopDisplayMode.w == 0) {
23
+ SDL_GetDesktopDisplayMode(0, &desktopDisplayMode);
24
+ }
25
+ return desktopDisplayMode.w;
26
+ }
27
+
28
+ unsigned Gosu::screenHeight()
29
+ {
30
+ // TODO - not thread-safe
31
+ if (desktopDisplayMode.h == 0) {
32
+ SDL_GetDesktopDisplayMode(0, &desktopDisplayMode);
33
+ }
34
+ return desktopDisplayMode.h;
35
+ }
36
+
37
+ struct Gosu::Window::Impl
38
+ {
39
+ SDL_Window *window;
40
+ SDL_GLContext context;
41
+ double updateInterval;
42
+
43
+ std::auto_ptr<Graphics> graphics;
44
+ std::auto_ptr<Input> input;
45
+ };
46
+
47
+ Gosu::Window::Window(unsigned width, unsigned height, bool fullscreen, double updateInterval)
48
+ : pimpl(new Impl)
49
+ {
50
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
51
+ throw std::runtime_error("Failed to initialize SDL Video");
52
+
53
+ unsigned actualWidth = width;
54
+ unsigned actualHeight = height;
55
+ double scaleFactor = 1.0;
56
+ double blackBarWidth = 0;
57
+ double blackBarHeight = 0;
58
+
59
+ if (fullscreen) {
60
+ actualWidth = Gosu::screenWidth();
61
+ actualHeight = Gosu::screenHeight();
62
+
63
+ double scaleX = 1.0 * actualWidth / width;
64
+ double scaleY = 1.0 * actualHeight / height;
65
+ scaleFactor = std::min(scaleX, scaleY);
66
+
67
+ if (scaleX < scaleY) {
68
+ blackBarHeight = (actualHeight / scaleX - height) / 2;
69
+ }
70
+ else if (scaleY < scaleX) {
71
+ blackBarWidth = (actualWidth / scaleY - width) / 2;
72
+ }
73
+ }
74
+ else {
75
+ // TODO - get platform-specific best width/height we could get
76
+
77
+ double maxWidth = Gosu::screenWidth() * 0.9;
78
+ double maxHeight = Gosu::screenHeight() * 0.8;
79
+
80
+ if (width > maxWidth || height > maxHeight) {
81
+ scaleFactor = std::min(maxWidth / width, maxHeight / height);
82
+ actualWidth = width * scaleFactor;
83
+ actualHeight = height * scaleFactor;
84
+ }
85
+ }
86
+
87
+ pimpl->window = SDL_CreateWindow("",
88
+ SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
89
+ actualWidth, actualHeight,
90
+ SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI |
91
+ (fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
92
+ pimpl->context = SDL_GL_CreateContext(pimpl->window);
93
+ SDL_GL_MakeCurrent(pimpl->window, pimpl->context);
94
+ SDL_GL_SetSwapInterval(1);
95
+
96
+ pimpl->graphics.reset(new Graphics(actualWidth, actualHeight, fullscreen));
97
+ pimpl->graphics->setResolution(width, height, blackBarWidth, blackBarHeight);
98
+ pimpl->input.reset(new Input());
99
+ pimpl->input->setMouseFactors(1 / scaleFactor, 1 / scaleFactor, blackBarWidth, blackBarHeight);
100
+ input().onButtonDown = std::tr1::bind(&Window::buttonDown, this, _1);
101
+ input().onButtonUp = std::tr1::bind(&Window::buttonUp, this, _1);
102
+ pimpl->updateInterval = updateInterval;
103
+ }
104
+
105
+ Gosu::Window::~Window()
106
+ {
107
+ SDL_GL_DeleteContext(pimpl->context);
108
+ SDL_DestroyWindow(pimpl->window);
109
+
110
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
111
+ }
112
+
113
+ std::wstring Gosu::Window::caption() const
114
+ {
115
+ return utf8ToWstring(SDL_GetWindowTitle(pimpl->window));
116
+ }
117
+
118
+ void Gosu::Window::setCaption(const std::wstring& caption)
119
+ {
120
+ std::string utf8 = wstringToUTF8(caption);
121
+ SDL_SetWindowTitle(pimpl->window, utf8.c_str());
122
+ }
123
+
124
+ double Gosu::Window::updateInterval() const
125
+ {
126
+ return pimpl->updateInterval;
127
+ }
128
+
129
+ namespace GosusDarkSide
130
+ {
131
+ // TODO: Find a way for this to fit into Gosu's design.
132
+ // This can point to a function that wants to be called every
133
+ // frame, e.g. rb_thread_schedule.
134
+ typedef void (*HookOfHorror)();
135
+ HookOfHorror oncePerTick = 0;
136
+ }
137
+
138
+ void Gosu::Window::show()
139
+ {
140
+ while (true) {
141
+ unsigned long startTime = milliseconds();
142
+
143
+ SDL_Event e;
144
+ while (SDL_PollEvent(&e)) {
145
+ if (e.type == SDL_QUIT)
146
+ return;
147
+ else
148
+ input().feedSDLEvent(&e);
149
+ }
150
+
151
+ Song::update();
152
+
153
+ input().update();
154
+
155
+ update();
156
+
157
+ if (graphics().begin()) {
158
+ draw();
159
+ graphics().end();
160
+ FPS::registerFrame();
161
+ }
162
+
163
+ SDL_GL_SwapWindow(pimpl->window);
164
+
165
+ if (GosusDarkSide::oncePerTick) GosusDarkSide::oncePerTick();
166
+
167
+ // Sleep to keep this loop from eating 100% CPU.
168
+ unsigned int frameTime = milliseconds() - startTime;
169
+ if (frameTime > 0 && frameTime < pimpl->updateInterval) {
170
+ sleep(pimpl->updateInterval - frameTime);
171
+ }
172
+ }
173
+ }
174
+
175
+ void Gosu::Window::close()
176
+ {
177
+ SDL_Event e;
178
+ e.type = SDL_QUIT;
179
+ SDL_PushEvent(&e);
180
+ }
181
+
182
+ const Gosu::Graphics& Gosu::Window::graphics() const
183
+ {
184
+ return *pimpl->graphics;
185
+ }
186
+
187
+ Gosu::Graphics& Gosu::Window::graphics()
188
+ {
189
+ return *pimpl->graphics;
190
+ }
191
+
192
+ const Gosu::Input& Gosu::Window::input() const
193
+ {
194
+ return *pimpl->input;
195
+ }
196
+
197
+ Gosu::Input& Gosu::Window::input()
198
+ {
199
+ return *pimpl->input;
200
+ }
201
+
202
+ // Deprecated.
203
+
204
+ class Gosu::Audio {};
205
+ namespace { Gosu::Audio dummyAudio; }
206
+
207
+ const Gosu::Audio& Gosu::Window::audio() const
208
+ {
209
+ return dummyAudio;
210
+ }
211
+
212
+ Gosu::Audio& Gosu::Window::audio()
213
+ {
214
+ return dummyAudio;
215
+ }