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.
- checksums.yaml +4 -4
- data/COPYING +1 -1
- data/Gosu/Buttons.hpp +97 -97
- data/Gosu/Graphics.hpp +5 -26
- data/Gosu/GraphicsBase.hpp +27 -4
- data/Gosu/Input.hpp +8 -19
- data/Gosu/Platform.hpp +5 -1
- data/Gosu/TextInput.hpp +5 -11
- data/Gosu/Version.hpp +3 -3
- data/GosuImpl/Graphics/Common.hpp +2 -11
- data/GosuImpl/Graphics/DrawOp.hpp +23 -27
- data/GosuImpl/Graphics/DrawOpQueue.hpp +2 -2
- data/GosuImpl/Graphics/Graphics.cpp +55 -18
- data/GosuImpl/Graphics/Macro.hpp +1 -1
- data/GosuImpl/Graphics/RenderState.hpp +2 -2
- data/GosuImpl/Graphics/TexChunk.cpp +1 -1
- data/GosuImpl/Graphics/TextUnix.cpp +1 -1
- data/GosuImpl/Graphics/Texture.cpp +2 -2
- data/GosuImpl/Graphics/Transform.cpp +15 -3
- data/GosuImpl/Graphics/TransformStack.hpp +2 -2
- data/GosuImpl/Input.cpp +329 -0
- data/GosuImpl/TextInput.cpp +239 -0
- data/GosuImpl/Window.cpp +215 -0
- data/lib/gosu/swig_patches.rb +1 -0
- data/linux/extconf.rb +10 -16
- metadata +8 -6
- data/GosuImpl/WindowMac.mm +0 -574
@@ -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
|
+
}
|
data/GosuImpl/Window.cpp
ADDED
@@ -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
|
+
}
|