gosu 0.13.3 → 0.14.0.pre2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gosu/Audio.hpp +15 -11
- data/Gosu/Font.hpp +24 -20
- data/Gosu/Fwd.hpp +1 -1
- data/Gosu/Graphics.hpp +8 -9
- data/Gosu/ImageData.hpp +1 -1
- data/Gosu/Input.hpp +1 -1
- data/Gosu/Math.hpp +0 -18
- data/Gosu/Text.hpp +22 -30
- data/Gosu/TextInput.hpp +13 -0
- data/Gosu/Utility.hpp +2 -0
- data/Gosu/Window.hpp +3 -3
- data/README.md +3 -4
- data/ext/gosu/extconf.rb +7 -9
- data/lib/gosu/swig_patches.rb +1 -4
- data/rdoc/gosu.rb +34 -9
- data/src/Audio.cpp +6 -6
- data/src/AudioImpl.cpp +2 -2
- data/src/Bitmap.cpp +1 -2
- data/src/BitmapIO.cpp +21 -2
- data/src/BlockAllocator.cpp +1 -1
- data/src/Channel.cpp +7 -1
- data/src/ClipRectStack.hpp +4 -1
- data/src/Color.cpp +2 -1
- data/src/DirectoriesWin.cpp +1 -1
- data/src/DrawOp.hpp +8 -4
- data/src/DrawOpQueue.hpp +13 -24
- data/src/FileUnix.cpp +3 -1
- data/src/Font.cpp +92 -96
- data/src/GosuGLView.cpp +59 -31
- data/src/GosuGLView.hpp +14 -0
- data/src/GosuViewController.cpp +21 -21
- data/src/{GosuViewController.h → GosuViewController.hpp} +2 -4
- data/src/Graphics.cpp +71 -38
- data/src/GraphicsImpl.hpp +12 -29
- data/src/Image.cpp +5 -7
- data/src/Input.cpp +7 -5
- data/src/InputUIKit.cpp +19 -37
- data/src/Macro.cpp +10 -2
- data/src/MarkupParser.cpp +241 -0
- data/src/MarkupParser.hpp +61 -0
- data/src/Math.cpp +1 -1
- data/src/OffScreenTarget.cpp +99 -0
- data/src/OffScreenTarget.hpp +23 -0
- data/src/OggFile.hpp +10 -0
- data/src/RenderState.hpp +0 -2
- data/src/Resolution.cpp +2 -2
- data/src/RubyGosu.cxx +457 -244
- data/src/TexChunk.cpp +8 -6
- data/src/Text.cpp +58 -345
- data/src/TextBuilder.cpp +138 -0
- data/src/TextBuilder.hpp +55 -0
- data/src/TextInput.cpp +27 -10
- data/src/Texture.cpp +22 -17
- data/src/Texture.hpp +19 -20
- data/src/TimingApple.cpp +5 -7
- data/src/TimingUnix.cpp +1 -4
- data/src/TimingWin.cpp +4 -1
- data/src/TrueTypeFont.cpp +282 -0
- data/src/TrueTypeFont.hpp +66 -0
- data/src/TrueTypeFontApple.cpp +65 -0
- data/src/TrueTypeFontUnix.cpp +91 -0
- data/src/TrueTypeFontWin.cpp +82 -0
- data/src/Utility.cpp +40 -0
- data/src/Window.cpp +7 -6
- data/src/WindowUIKit.cpp +9 -4
- data/src/stb_truetype.h +4589 -0
- data/src/utf8proc.c +755 -0
- data/src/utf8proc.h +699 -0
- data/src/utf8proc_data.h +14386 -0
- metadata +23 -16
- data/src/FormattedString.cpp +0 -237
- data/src/FormattedString.hpp +0 -47
- data/src/GosuAppDelegate.cpp +0 -30
- data/src/GosuAppDelegate.h +0 -8
- data/src/GosuGLView.h +0 -8
- data/src/TextApple.cpp +0 -212
- data/src/TextTTFWin.cpp +0 -197
- data/src/TextUnix.cpp +0 -280
- data/src/TextWin.cpp +0 -191
data/src/TextBuilder.cpp
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#include "TextBuilder.hpp"
|
2
|
+
#include <Gosu/Text.hpp>
|
3
|
+
#include <cassert>
|
4
|
+
#include <cmath>
|
5
|
+
#include "utf8proc.h"
|
6
|
+
using namespace std;
|
7
|
+
|
8
|
+
Gosu::WordInfo::WordInfo(const string& font_name, double font_height, vector<FormattedString> parts)
|
9
|
+
{
|
10
|
+
assert (! parts.empty());
|
11
|
+
|
12
|
+
auto* properties = utf8proc_get_property(parts.front().text.front());
|
13
|
+
|
14
|
+
// Also check the BiDi class to filter out non-breaking spaces.
|
15
|
+
is_whitespace = properties->category == UTF8PROC_CATEGORY_ZS &&
|
16
|
+
properties->bidi_class == UTF8PROC_BIDI_CLASS_WS;
|
17
|
+
|
18
|
+
is_end_of_line = parts.back().text.back() == '\n';
|
19
|
+
// Remove the trailing backspace character to avoid errors from Gosu::text_width().
|
20
|
+
if (is_end_of_line) parts.back().text.pop_back();
|
21
|
+
|
22
|
+
width = 0;
|
23
|
+
for (const auto& part : parts) {
|
24
|
+
assert (! part.text.empty());
|
25
|
+
|
26
|
+
width += text_width(part.text, font_name, font_height, part.flags);
|
27
|
+
}
|
28
|
+
|
29
|
+
this->parts = move(parts);
|
30
|
+
}
|
31
|
+
|
32
|
+
void Gosu::TextBuilder::flush_current_line(EndOfLineReason reason)
|
33
|
+
{
|
34
|
+
if (current_line.empty()) {
|
35
|
+
if (reason == END_OF_PARAGRAPH) allocate_next_line();
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
|
39
|
+
allocate_next_line();
|
40
|
+
|
41
|
+
// Remove trailing whitespace so that justifying the text across the line works.
|
42
|
+
if (current_line.back().is_whitespace) current_line.pop_back();
|
43
|
+
|
44
|
+
// Shouldn't happen because the first word on a line should never be whitespace.
|
45
|
+
assert (! current_line.empty());
|
46
|
+
|
47
|
+
double words_width = 0, whitespace_width = 0;
|
48
|
+
for (const auto& word : current_line) {
|
49
|
+
(word.is_whitespace ? whitespace_width : words_width) += word.width;
|
50
|
+
}
|
51
|
+
|
52
|
+
double x = 0;
|
53
|
+
if (align == AL_RIGHT) {
|
54
|
+
x = result.width() - words_width - whitespace_width;
|
55
|
+
}
|
56
|
+
else if (align == AL_CENTER) {
|
57
|
+
x = (result.width() - words_width - whitespace_width) / 2.0;
|
58
|
+
}
|
59
|
+
|
60
|
+
double whitespace_factor = 1.0;
|
61
|
+
if (align == AL_JUSTIFY && reason == LINE_TOO_LONG) {
|
62
|
+
whitespace_factor = (result.width() - words_width) / whitespace_width;
|
63
|
+
}
|
64
|
+
|
65
|
+
double y = (used_lines - 1) * (font_height + line_spacing);
|
66
|
+
|
67
|
+
for (const auto& word : current_line) {
|
68
|
+
if (word.is_whitespace) {
|
69
|
+
x += word.width * whitespace_factor;
|
70
|
+
}
|
71
|
+
else {
|
72
|
+
for (const auto& part : word.parts) {
|
73
|
+
draw_text(result, x, y, part.color, part.text, font_name, font_height, part.flags);
|
74
|
+
}
|
75
|
+
x += word.width;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
current_line.clear();
|
80
|
+
current_line_width = 0;
|
81
|
+
}
|
82
|
+
|
83
|
+
void Gosu::TextBuilder::allocate_next_line()
|
84
|
+
{
|
85
|
+
if (used_lines == allocated_lines) {
|
86
|
+
allocated_lines += 10;
|
87
|
+
resize_to_allocated_lines();
|
88
|
+
}
|
89
|
+
|
90
|
+
++used_lines;
|
91
|
+
}
|
92
|
+
|
93
|
+
void Gosu::TextBuilder::resize_to_allocated_lines()
|
94
|
+
{
|
95
|
+
double new_height = font_height * allocated_lines + line_spacing * max(0, allocated_lines - 1);
|
96
|
+
result.resize(result.width(), ceil(new_height));
|
97
|
+
}
|
98
|
+
|
99
|
+
Gosu::TextBuilder::TextBuilder(const string& font_name, int font_height, int line_spacing,
|
100
|
+
int width, Alignment align)
|
101
|
+
: font_name(font_name), font_height(font_height), line_spacing(line_spacing), align(align)
|
102
|
+
{
|
103
|
+
// This class uses result.width() to remember its destination width, so store it in there.
|
104
|
+
result.resize(width, 0);
|
105
|
+
}
|
106
|
+
|
107
|
+
Gosu::Bitmap Gosu::TextBuilder::move_into_bitmap()
|
108
|
+
{
|
109
|
+
flush_current_line(END_OF_TEXT);
|
110
|
+
|
111
|
+
// Shrink to fit the currently used height.
|
112
|
+
allocated_lines = used_lines;
|
113
|
+
resize_to_allocated_lines();
|
114
|
+
return move(result);
|
115
|
+
}
|
116
|
+
|
117
|
+
void Gosu::TextBuilder::feed_word(vector<FormattedString>&& word)
|
118
|
+
{
|
119
|
+
WordInfo new_word(font_name, font_height, word);
|
120
|
+
|
121
|
+
if (current_line_width + new_word.width > result.width()) {
|
122
|
+
// Can't fit it on the same line as before, so flush the last line before adding the word to
|
123
|
+
// the next line.
|
124
|
+
flush_current_line(LINE_TOO_LONG);
|
125
|
+
|
126
|
+
if (new_word.is_whitespace) {
|
127
|
+
// Do not wrap trailing whitespace onto the start of the next line - discard this word.
|
128
|
+
return;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
current_line.emplace_back(new_word);
|
133
|
+
current_line_width += new_word.width;
|
134
|
+
|
135
|
+
if (current_line_width > result.width() || new_word.is_end_of_line) {
|
136
|
+
flush_current_line(new_word.is_end_of_line ? END_OF_PARAGRAPH : LINE_TOO_LONG);
|
137
|
+
}
|
138
|
+
}
|
data/src/TextBuilder.hpp
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "GraphicsImpl.hpp"
|
4
|
+
#include "MarkupParser.hpp"
|
5
|
+
#include <vector>
|
6
|
+
|
7
|
+
namespace Gosu
|
8
|
+
{
|
9
|
+
struct WordInfo
|
10
|
+
{
|
11
|
+
std::vector<FormattedString> parts;
|
12
|
+
bool is_whitespace;
|
13
|
+
bool is_end_of_line;
|
14
|
+
double width;
|
15
|
+
|
16
|
+
WordInfo(const std::string& font_name, double font_height,
|
17
|
+
std::vector<FormattedString> parts);
|
18
|
+
};
|
19
|
+
|
20
|
+
class TextBuilder
|
21
|
+
{
|
22
|
+
// Parameters.
|
23
|
+
std::string font_name;
|
24
|
+
double font_height;
|
25
|
+
double line_spacing;
|
26
|
+
Alignment align;
|
27
|
+
|
28
|
+
enum EndOfLineReason {
|
29
|
+
LINE_TOO_LONG,
|
30
|
+
END_OF_PARAGRAPH,
|
31
|
+
END_OF_TEXT
|
32
|
+
};
|
33
|
+
|
34
|
+
// Input.
|
35
|
+
std::vector<WordInfo> current_line;
|
36
|
+
int current_line_width = 0;
|
37
|
+
void flush_current_line(EndOfLineReason reason);
|
38
|
+
|
39
|
+
// Output.
|
40
|
+
Bitmap result;
|
41
|
+
int used_lines = 0;
|
42
|
+
int allocated_lines = 0;
|
43
|
+
void allocate_next_line();
|
44
|
+
void resize_to_allocated_lines();
|
45
|
+
|
46
|
+
public:
|
47
|
+
TextBuilder(const std::string& font_name, int font_height, int line_spacing,
|
48
|
+
int width, Alignment align);
|
49
|
+
|
50
|
+
void feed_word(std::vector<FormattedString>&& word);
|
51
|
+
|
52
|
+
Bitmap move_into_bitmap();
|
53
|
+
};
|
54
|
+
}
|
55
|
+
|
data/src/TextInput.cpp
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
-
#include <Gosu/Platform.hpp>
|
2
|
-
#if !defined(GOSU_IS_IPHONE)
|
3
|
-
|
4
1
|
#include <Gosu/TextInput.hpp>
|
5
2
|
#include <Gosu/Input.hpp>
|
6
3
|
#include <Gosu/Platform.hpp>
|
4
|
+
|
5
|
+
#ifndef GOSU_IS_IPHONE
|
7
6
|
#include <SDL.h>
|
8
7
|
#include <cctype>
|
8
|
+
#endif
|
9
|
+
|
9
10
|
using namespace std;
|
10
11
|
|
11
12
|
struct Gosu::TextInput::Impl
|
12
13
|
{
|
13
14
|
string text;
|
14
15
|
|
15
|
-
// This is the current IME composition.
|
16
|
+
// This is the current IME composition (not used on iOS).
|
16
17
|
// http://wiki.libsdl.org/Tutorials/TextInput#CandidateList
|
17
18
|
string composition;
|
18
19
|
|
@@ -152,6 +153,7 @@ Gosu::TextInput::TextInput()
|
|
152
153
|
|
153
154
|
Gosu::TextInput::~TextInput()
|
154
155
|
{
|
156
|
+
// TODO: Unset Input::text_input to avoid stale pointers?
|
155
157
|
}
|
156
158
|
|
157
159
|
string Gosu::TextInput::text() const
|
@@ -172,12 +174,12 @@ void Gosu::TextInput::set_text(const string& text)
|
|
172
174
|
|
173
175
|
unsigned Gosu::TextInput::caret_pos() const
|
174
176
|
{
|
175
|
-
return
|
177
|
+
return pimpl->caret_pos;
|
176
178
|
}
|
177
179
|
|
178
|
-
void Gosu::TextInput::set_caret_pos(unsigned
|
180
|
+
void Gosu::TextInput::set_caret_pos(unsigned caret_pos)
|
179
181
|
{
|
180
|
-
pimpl->caret_pos =
|
182
|
+
pimpl->caret_pos = caret_pos;
|
181
183
|
}
|
182
184
|
|
183
185
|
unsigned Gosu::TextInput::selection_start() const
|
@@ -185,11 +187,12 @@ unsigned Gosu::TextInput::selection_start() const
|
|
185
187
|
return pimpl->selection_start;
|
186
188
|
}
|
187
189
|
|
188
|
-
void Gosu::TextInput::set_selection_start(unsigned
|
190
|
+
void Gosu::TextInput::set_selection_start(unsigned selection_start)
|
189
191
|
{
|
190
|
-
pimpl->selection_start =
|
192
|
+
pimpl->selection_start = selection_start;
|
191
193
|
}
|
192
194
|
|
195
|
+
#ifndef GOSU_IS_IPHONE
|
193
196
|
bool Gosu::TextInput::feed_sdl_event(void* event)
|
194
197
|
{
|
195
198
|
const SDL_Event* e = static_cast<SDL_Event*>(event);
|
@@ -275,5 +278,19 @@ bool Gosu::TextInput::feed_sdl_event(void* event)
|
|
275
278
|
|
276
279
|
return false;
|
277
280
|
}
|
278
|
-
|
279
281
|
#endif
|
282
|
+
|
283
|
+
void Gosu::TextInput::insert_text(string text)
|
284
|
+
{
|
285
|
+
pimpl->insert_text(text);
|
286
|
+
}
|
287
|
+
|
288
|
+
void Gosu::TextInput::delete_forward()
|
289
|
+
{
|
290
|
+
pimpl->delete_forward();
|
291
|
+
}
|
292
|
+
|
293
|
+
void Gosu::TextInput::delete_backward()
|
294
|
+
{
|
295
|
+
pimpl->delete_backward();
|
296
|
+
}
|
data/src/Texture.cpp
CHANGED
@@ -11,16 +11,14 @@ namespace Gosu
|
|
11
11
|
bool undocumented_retrofication = false;
|
12
12
|
}
|
13
13
|
|
14
|
-
Gosu::Texture::Texture(unsigned
|
15
|
-
: allocator_(
|
14
|
+
Gosu::Texture::Texture(unsigned width, unsigned height, bool retro)
|
15
|
+
: allocator_(width, height), retro_(retro)
|
16
16
|
{
|
17
17
|
ensure_current_context();
|
18
18
|
|
19
19
|
// Create texture name.
|
20
20
|
glGenTextures(1, &tex_name_);
|
21
|
-
if (tex_name_ == static_cast<GLuint>(-1))
|
22
|
-
throw runtime_error("Couldn't create OpenGL texture");
|
23
|
-
}
|
21
|
+
if (tex_name_ == static_cast<GLuint>(-1)) throw runtime_error("Couldn't create OpenGL texture");
|
24
22
|
|
25
23
|
// Create empty texture.
|
26
24
|
glBindTexture(GL_TEXTURE_2D, tex_name_);
|
@@ -57,9 +55,14 @@ Gosu::Texture::~Texture()
|
|
57
55
|
glDeleteTextures(1, &tex_name_);
|
58
56
|
}
|
59
57
|
|
60
|
-
unsigned Gosu::Texture::
|
58
|
+
unsigned Gosu::Texture::width() const
|
59
|
+
{
|
60
|
+
return allocator_.width();
|
61
|
+
}
|
62
|
+
|
63
|
+
unsigned Gosu::Texture::height() const
|
61
64
|
{
|
62
|
-
return allocator_.
|
65
|
+
return allocator_.height();
|
63
66
|
}
|
64
67
|
|
65
68
|
GLuint Gosu::Texture::tex_name() const
|
@@ -72,19 +75,18 @@ bool Gosu::Texture::retro() const
|
|
72
75
|
return retro_;
|
73
76
|
}
|
74
77
|
|
75
|
-
unique_ptr<Gosu::TexChunk> Gosu::Texture::try_alloc(
|
76
|
-
unsigned padding)
|
78
|
+
unique_ptr<Gosu::TexChunk> Gosu::Texture::try_alloc(const Bitmap& bmp, unsigned padding)
|
77
79
|
{
|
78
80
|
BlockAllocator::Block block;
|
79
81
|
|
80
82
|
if (!allocator_.alloc(bmp.width(), bmp.height(), block)) return nullptr;
|
81
83
|
|
82
|
-
unique_ptr<
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
unique_ptr<TexChunk> result(new TexChunk(shared_from_this(),
|
85
|
+
block.left + padding,
|
86
|
+
block.top + padding,
|
87
|
+
block.width - 2 * padding,
|
88
|
+
block.height - 2 * padding,
|
89
|
+
padding));
|
88
90
|
|
89
91
|
ensure_current_context();
|
90
92
|
|
@@ -108,14 +110,17 @@ void Gosu::Texture::free(unsigned x, unsigned y, unsigned width, unsigned height
|
|
108
110
|
Gosu::Bitmap Gosu::Texture::to_bitmap(unsigned x, unsigned y, unsigned width, unsigned height) const
|
109
111
|
{
|
110
112
|
#ifdef GOSU_IS_OPENGLES
|
113
|
+
// See here for one possible implementation: https://github.com/apitrace/apitrace/issues/70
|
114
|
+
// (Could reuse a lot of code from OffScreenTarget)
|
111
115
|
throw logic_error("Texture::to_bitmap not supported on iOS");
|
112
116
|
#else
|
113
117
|
ensure_current_context();
|
114
118
|
|
115
|
-
|
119
|
+
Bitmap full_texture(this->width(), this->height());
|
116
120
|
glBindTexture(GL_TEXTURE_2D, tex_name());
|
121
|
+
// TODO: There are ways to retrieve only part of a texture, which we should use sooner or later.
|
117
122
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, full_texture.data());
|
118
|
-
|
123
|
+
Bitmap bitmap(width, height);
|
119
124
|
bitmap.insert(full_texture, -int(x), -int(y));
|
120
125
|
|
121
126
|
return bitmap;
|
data/src/Texture.hpp
CHANGED
@@ -5,26 +5,25 @@
|
|
5
5
|
#include "TexChunk.hpp"
|
6
6
|
#include <Gosu/Fwd.hpp>
|
7
7
|
#include <Gosu/Bitmap.hpp>
|
8
|
+
#include <memory>
|
8
9
|
#include <vector>
|
9
10
|
|
10
|
-
|
11
|
+
class Gosu::Texture : public std::enable_shared_from_this<Texture>
|
11
12
|
{
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
};
|
30
|
-
}
|
13
|
+
// BlockAllocator can't be copied or moved, so neither can Texture.
|
14
|
+
BlockAllocator allocator_;
|
15
|
+
GLuint tex_name_;
|
16
|
+
bool retro_;
|
17
|
+
|
18
|
+
public:
|
19
|
+
Texture(unsigned width, unsigned height, bool retro);
|
20
|
+
~Texture();
|
21
|
+
unsigned width() const;
|
22
|
+
unsigned height() const;
|
23
|
+
GLuint tex_name() const;
|
24
|
+
bool retro() const;
|
25
|
+
std::unique_ptr<TexChunk> try_alloc(const Bitmap& bmp, unsigned padding);
|
26
|
+
void block(unsigned x, unsigned y, unsigned width, unsigned height);
|
27
|
+
void free(unsigned x, unsigned y, unsigned width, unsigned height);
|
28
|
+
Bitmap to_bitmap(unsigned x, unsigned y, unsigned width, unsigned height) const;
|
29
|
+
};
|
data/src/TimingApple.cpp
CHANGED
@@ -16,16 +16,14 @@ void Gosu::sleep(unsigned milliseconds)
|
|
16
16
|
|
17
17
|
unsigned long Gosu::milliseconds()
|
18
18
|
{
|
19
|
-
static uint64_t first_tick = 0;
|
20
19
|
static mach_timebase_info_data_t info;
|
21
|
-
|
22
|
-
if (first_tick == 0) {
|
20
|
+
static uint64_t first_tick = [] {
|
23
21
|
mach_timebase_info(&info);
|
24
|
-
|
25
|
-
}
|
26
|
-
|
22
|
+
return mach_absolute_time();
|
23
|
+
}();
|
24
|
+
|
27
25
|
uint64_t runtime = mach_absolute_time() - first_tick;
|
28
|
-
return runtime * info.numer / info.denom / 1000000
|
26
|
+
return runtime * info.numer / info.denom / 1000000;
|
29
27
|
}
|
30
28
|
|
31
29
|
#endif
|
data/src/TimingUnix.cpp
CHANGED
@@ -21,10 +21,7 @@ unsigned long Gosu::milliseconds()
|
|
21
21
|
start = tp.tv_usec / 1000UL + tp.tv_sec * 1000UL;
|
22
22
|
}
|
23
23
|
|
24
|
-
|
25
|
-
// have a happy GC on 32-bit systems.
|
26
|
-
// No, don't ask why this is an unsigned long then :)
|
27
|
-
return (tp.tv_usec / 1000UL + tp.tv_sec * 1000UL - start) & 0x1fffffff;
|
24
|
+
return tp.tv_usec / 1000UL + tp.tv_sec * 1000UL - start;
|
28
25
|
}
|
29
26
|
|
30
27
|
#endif
|
data/src/TimingWin.cpp
CHANGED
@@ -12,7 +12,10 @@ void Gosu::sleep(unsigned milliseconds)
|
|
12
12
|
|
13
13
|
unsigned long Gosu::milliseconds()
|
14
14
|
{
|
15
|
-
static unsigned long start =
|
15
|
+
static unsigned long start = [] {
|
16
|
+
timeBeginPeriod(1);
|
17
|
+
return timeGetTime();
|
18
|
+
}();
|
16
19
|
return timeGetTime() - start;
|
17
20
|
}
|
18
21
|
|