glfw-ruby 0.1.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +243 -0
- data/Rakefile +8 -0
- data/examples/01_minimal_window.rb +35 -0
- data/examples/02_input_callbacks.rb +56 -0
- data/examples/03_window_attributes.rb +116 -0
- data/examples/04_monitor_info.rb +52 -0
- data/examples/05_custom_cursor_and_icon.rb +93 -0
- data/examples/06_gamepad_inspector.rb +81 -0
- data/examples/07_glfw_34_features.rb +46 -0
- data/examples/example_runtime.rb +66 -0
- data/lib/glfw/api/constants.rb +403 -0
- data/lib/glfw/api/context.rb +11 -0
- data/lib/glfw/api/init.rb +13 -0
- data/lib/glfw/api/input.rb +46 -0
- data/lib/glfw/api/monitor.rb +21 -0
- data/lib/glfw/api/types.rb +96 -0
- data/lib/glfw/api/v34.rb +21 -0
- data/lib/glfw/api/vulkan.rb +11 -0
- data/lib/glfw/api/window.rb +53 -0
- data/lib/glfw/errors.rb +72 -0
- data/lib/glfw/high_level/cursor.rb +31 -0
- data/lib/glfw/high_level/gamepad.rb +43 -0
- data/lib/glfw/high_level/gamma_ramp.rb +32 -0
- data/lib/glfw/high_level/image.rb +27 -0
- data/lib/glfw/high_level/joystick.rb +69 -0
- data/lib/glfw/high_level/monitor.rb +104 -0
- data/lib/glfw/high_level/video_mode.rb +32 -0
- data/lib/glfw/high_level/window.rb +435 -0
- data/lib/glfw/loader.rb +48 -0
- data/lib/glfw/version.rb +5 -0
- data/lib/glfw.rb +143 -0
- metadata +91 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6e48fb1ff8edb79abc14e6e0ae76ccc89954f459a97c109b385fbffe0f566c55
|
|
4
|
+
data.tar.gz: 58042deb5b8f9a4d3316dde069de3a37bb39a0665c79b0ca37760cef08d94649
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4c8c65d86740fe3db5f8a6e58aebdb5e3d3b738507e4eb0f9ff63a0699fb1e70c0794c2fd402f67ae8278b64c89414fc0ced80ab27d4e136aba45e286f28238e
|
|
7
|
+
data.tar.gz: 93560ee8ede49b87eaada628f23601ecb0a3e719a4c79af19fa8b69f2bf0929bdacf2caed240eac832209a4b28bf52030c008d3066d66778c31828a239542f29
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# glfw-ruby
|
|
2
|
+
|
|
3
|
+
Pure Ruby FFI bindings for GLFW 3.3/3.4.
|
|
4
|
+
|
|
5
|
+
This gem exposes:
|
|
6
|
+
|
|
7
|
+
- `GLFW::API` for low-level access that stays close to the C API
|
|
8
|
+
- Ruby-friendly wrappers such as `GLFW::Window`, `GLFW::Monitor`, `GLFW::Cursor`, `GLFW::Joystick`, and `GLFW::Gamepad`
|
|
9
|
+
- GLFW 3.4 additions including platform queries, allocator support, and Vulkan loader hooks
|
|
10
|
+
|
|
11
|
+
The gem does not bundle GLFW itself. You need a native GLFW shared library installed on the machine.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Ruby `>= 3.1`
|
|
16
|
+
- A native GLFW 3.3 or 3.4 shared library available to the dynamic linker
|
|
17
|
+
|
|
18
|
+
If the loader cannot find the library automatically, set `GLFW_LIB_PATH` to the full path of the shared library.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Add the gem:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle add glfw
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or install it directly:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
gem install glfw
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Install the native GLFW library separately:
|
|
35
|
+
|
|
36
|
+
### macOS
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
brew install glfw
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Ubuntu / Debian
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
sudo apt-get install libglfw3
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Windows
|
|
49
|
+
|
|
50
|
+
Install a GLFW DLL and make sure it is on `PATH`, or point `GLFW_LIB_PATH` at the DLL file.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
require "glfw"
|
|
56
|
+
|
|
57
|
+
GLFW.on_error do |code, description|
|
|
58
|
+
warn "[GLFW #{code}] #{description}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
GLFW.init
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
window = GLFW::Window.new(
|
|
65
|
+
800,
|
|
66
|
+
600,
|
|
67
|
+
"Hello GLFW",
|
|
68
|
+
visible: false,
|
|
69
|
+
context_version_major: 3,
|
|
70
|
+
context_version_minor: 3
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
window.make_context_current
|
|
74
|
+
window.show
|
|
75
|
+
|
|
76
|
+
until window.should_close?
|
|
77
|
+
# draw here
|
|
78
|
+
window.swap_buffers
|
|
79
|
+
GLFW.poll_events
|
|
80
|
+
end
|
|
81
|
+
ensure
|
|
82
|
+
window&.destroy
|
|
83
|
+
GLFW.terminate
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
88
|
+
|
|
89
|
+
Example scripts live in `examples/` and can be run from a source checkout.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
bundle exec ruby examples/01_minimal_window.rb
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Available examples:
|
|
96
|
+
|
|
97
|
+
- `examples/01_minimal_window.rb` - smallest window + event loop example
|
|
98
|
+
- `examples/02_input_callbacks.rb` - keyboard, mouse, cursor, scroll, and drop callbacks
|
|
99
|
+
- `examples/03_window_attributes.rb` - window title, size, position, opacity, and attribute changes
|
|
100
|
+
- `examples/04_monitor_info.rb` - monitor enumeration and video mode inspection
|
|
101
|
+
- `examples/05_custom_cursor_and_icon.rb` - generated cursor and window icon data
|
|
102
|
+
- `examples/06_gamepad_inspector.rb` - joystick and gamepad state inspector
|
|
103
|
+
- `examples/07_glfw_34_features.rb` - platform queries and other GLFW 3.4 runtime-only features
|
|
104
|
+
|
|
105
|
+
On headless Linux, run windowed examples under Xvfb.
|
|
106
|
+
|
|
107
|
+
## High-Level API
|
|
108
|
+
|
|
109
|
+
The high-level layer wraps common GLFW concepts in plain Ruby objects.
|
|
110
|
+
|
|
111
|
+
### `GLFW`
|
|
112
|
+
|
|
113
|
+
- `GLFW.init` / `GLFW.terminate`
|
|
114
|
+
- `GLFW.version`
|
|
115
|
+
- `GLFW.version_at_least?(major, minor)`
|
|
116
|
+
- `GLFW.poll_events`
|
|
117
|
+
- `GLFW.wait_events(timeout: nil)`
|
|
118
|
+
- `GLFW.post_empty_event`
|
|
119
|
+
- `GLFW.time` / `GLFW.time=`
|
|
120
|
+
- `GLFW.clipboard` / `GLFW.clipboard=`
|
|
121
|
+
- `GLFW.platform` (`GLFW 3.4+` runtime only)
|
|
122
|
+
- `GLFW.platform_supported?(platform_constant)` (`GLFW 3.4+` runtime only)
|
|
123
|
+
- `GLFW.on_error { |code, description| ... }`
|
|
124
|
+
|
|
125
|
+
### `GLFW::Window`
|
|
126
|
+
|
|
127
|
+
Supports window creation, context management, input polling, attributes, and callbacks.
|
|
128
|
+
|
|
129
|
+
Examples of supported operations:
|
|
130
|
+
|
|
131
|
+
- title, size, position, framebuffer size, frame size, content scale, opacity
|
|
132
|
+
- show / hide / focus / maximize / restore / iconify
|
|
133
|
+
- fullscreen and windowed transitions
|
|
134
|
+
- icon updates via `set_icon` / `clear_icon`
|
|
135
|
+
- cursor assignment and cursor position access
|
|
136
|
+
- keyboard and mouse input queries
|
|
137
|
+
- callbacks such as `on_key`, `on_char`, `on_mouse_button`, `on_cursor_pos`, `on_scroll`, `on_drop`, `on_resize`, `on_close`, and more
|
|
138
|
+
|
|
139
|
+
Window hints are passed as keyword arguments:
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
window = GLFW::Window.new(
|
|
143
|
+
1280,
|
|
144
|
+
720,
|
|
145
|
+
"Example",
|
|
146
|
+
visible: false,
|
|
147
|
+
resizable: true,
|
|
148
|
+
opengl_profile: :core,
|
|
149
|
+
context_version_major: 4,
|
|
150
|
+
context_version_minor: 1
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `GLFW::Monitor`
|
|
155
|
+
|
|
156
|
+
- `GLFW::Monitor.all`
|
|
157
|
+
- `GLFW::Monitor.primary`
|
|
158
|
+
- monitor name, position, workarea, physical size, content scale
|
|
159
|
+
- video modes and current video mode
|
|
160
|
+
- gamma and gamma ramps
|
|
161
|
+
- hotplug callback via `GLFW::Monitor.on_connect`
|
|
162
|
+
|
|
163
|
+
### `GLFW::Cursor` and `GLFW::Image`
|
|
164
|
+
|
|
165
|
+
- create standard cursors with `GLFW::Cursor.standard`
|
|
166
|
+
- create custom cursors from RGBA image data with `GLFW::Cursor.create`
|
|
167
|
+
- build image payloads with `GLFW::Image.from_rgba`
|
|
168
|
+
|
|
169
|
+
### `GLFW::Joystick` and `GLFW::Gamepad`
|
|
170
|
+
|
|
171
|
+
- joystick presence, name, GUID, axes, buttons, hats
|
|
172
|
+
- gamepad detection and state access
|
|
173
|
+
- gamepad mapping updates
|
|
174
|
+
- joystick connection callback via `GLFW::Joystick.on_connect`
|
|
175
|
+
|
|
176
|
+
## Low-Level API
|
|
177
|
+
|
|
178
|
+
`GLFW::API` mirrors the native C API through FFI `attach_function` declarations. Use it when you want direct access to GLFW without the object wrappers.
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
require "glfw"
|
|
182
|
+
|
|
183
|
+
puts GLFW::API.glfwGetVersionString
|
|
184
|
+
puts GLFW::API.glfwPlatformSupported(GLFW::GLFW_PLATFORM_X11)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Low-level types live under `GLFW::API`, including:
|
|
188
|
+
|
|
189
|
+
- `GLFWvidmode`
|
|
190
|
+
- `GLFWgammaramp`
|
|
191
|
+
- `GLFWimage`
|
|
192
|
+
- `GLFWgamepadstate`
|
|
193
|
+
- `GLFWallocator`
|
|
194
|
+
|
|
195
|
+
The binding also exposes the usual GLFW constants, error codes, window hints, platform constants, input constants, and cursor shapes.
|
|
196
|
+
|
|
197
|
+
## Errors
|
|
198
|
+
|
|
199
|
+
Base error classes:
|
|
200
|
+
|
|
201
|
+
- `GLFW::Error`
|
|
202
|
+
- `GLFW::LibraryNotFoundError`
|
|
203
|
+
- `GLFW::InitError`
|
|
204
|
+
- `GLFW::NotSupportedError`
|
|
205
|
+
- `GLFW::APIError`
|
|
206
|
+
|
|
207
|
+
If no custom error callback is registered, `GLFW.init` installs a default callback that prints GLFW error messages to stderr.
|
|
208
|
+
|
|
209
|
+
## Development
|
|
210
|
+
|
|
211
|
+
Install dependencies:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
bundle install
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Run the test suite:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
bundle exec rspec
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Or run the default task:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
bundle exec rake
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
On headless Linux, run specs under Xvfb:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
xvfb-run -a bundle exec rspec
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Contributing
|
|
236
|
+
|
|
237
|
+
Issues and pull requests are welcome at:
|
|
238
|
+
|
|
239
|
+
- https://github.com/ydah/glfw-ruby
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
Released under the [MIT License](LICENSE.txt).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "example_runtime"
|
|
5
|
+
|
|
6
|
+
GLFWExample.with_window(title: "Minimal Window", width: 800, height: 600, visible: true) do |window|
|
|
7
|
+
GLFWExample.print_banner("01 Minimal Window", [
|
|
8
|
+
"Esc: close the window",
|
|
9
|
+
"Use the window close button to exit as well."
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
window.make_context_current
|
|
13
|
+
GLFW::API.glfwSwapInterval(1)
|
|
14
|
+
|
|
15
|
+
window.on_key do |current_window, key, _scancode, action, _mods|
|
|
16
|
+
current_window.should_close = true if key == GLFW::GLFW_KEY_ESCAPE && action == :press
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
window.on_close do |_current_window|
|
|
20
|
+
puts "Close requested."
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
frame = 0
|
|
24
|
+
|
|
25
|
+
GLFWExample.loop_until_closed(window) do
|
|
26
|
+
frame += 1
|
|
27
|
+
|
|
28
|
+
if (frame % 120).zero?
|
|
29
|
+
seconds = format("%.1f", frame / 60.0)
|
|
30
|
+
window.title = "Minimal Window (#{seconds}s)"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
window.swap_buffers
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "example_runtime"
|
|
5
|
+
|
|
6
|
+
GLFWExample.with_window(title: "Input Callbacks", width: 900, height: 600, visible: true) do |window|
|
|
7
|
+
GLFWExample.print_banner("02 Input Callbacks", [
|
|
8
|
+
"Type, click, scroll, move the cursor, or drop files onto the window.",
|
|
9
|
+
"Esc: close the window"
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
window.make_context_current
|
|
13
|
+
GLFW::API.glfwSwapInterval(1)
|
|
14
|
+
|
|
15
|
+
last_cursor_report_at = 0.0
|
|
16
|
+
|
|
17
|
+
window.on_key do |current_window, key, scancode, action, mods|
|
|
18
|
+
puts "key=#{key} scancode=#{scancode} action=#{action} mods=0x#{mods.to_s(16)}"
|
|
19
|
+
current_window.should_close = true if key == GLFW::GLFW_KEY_ESCAPE && action == :press
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
window.on_char do |_current_window, codepoint|
|
|
23
|
+
character = codepoint.chr(Encoding::UTF_8)
|
|
24
|
+
puts "char=#{character.inspect} codepoint=U+#{codepoint.to_s(16).upcase.rjust(4, '0')}"
|
|
25
|
+
rescue RangeError
|
|
26
|
+
puts "char=<invalid> codepoint=#{codepoint}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
window.on_mouse_button do |_current_window, button, action, mods|
|
|
30
|
+
puts "mouse_button=#{button} action=#{action} mods=0x#{mods.to_s(16)}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
window.on_cursor_enter do |_current_window, entered|
|
|
34
|
+
puts "cursor_enter=#{entered}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
window.on_cursor_pos do |_current_window, x, y|
|
|
38
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
39
|
+
next if now - last_cursor_report_at < 0.10
|
|
40
|
+
|
|
41
|
+
last_cursor_report_at = now
|
|
42
|
+
puts format("cursor_pos=(%.1f, %.1f)", x, y)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
window.on_scroll do |_current_window, xoffset, yoffset|
|
|
46
|
+
puts format("scroll=(%.2f, %.2f)", xoffset, yoffset)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
window.on_drop do |_current_window, paths|
|
|
50
|
+
puts "drop=#{paths.join(', ')}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
GLFWExample.loop_until_closed(window) do
|
|
54
|
+
window.swap_buffers
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "example_runtime"
|
|
5
|
+
|
|
6
|
+
GLFWExample.with_window(
|
|
7
|
+
title: "Window Attributes",
|
|
8
|
+
width: 960,
|
|
9
|
+
height: 640,
|
|
10
|
+
visible: true,
|
|
11
|
+
resizable: true,
|
|
12
|
+
decorated: true
|
|
13
|
+
) do |window|
|
|
14
|
+
GLFWExample.print_banner("03 Window Attributes", [
|
|
15
|
+
"Esc: close",
|
|
16
|
+
"P: print current state",
|
|
17
|
+
"T: update title with a timestamp",
|
|
18
|
+
"Arrow keys: move the window",
|
|
19
|
+
"= / -: resize the window",
|
|
20
|
+
"1 / 2 / 3: set opacity",
|
|
21
|
+
"F: toggle floating",
|
|
22
|
+
"M / R / I: maximize, restore, iconify"
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
window.make_context_current
|
|
26
|
+
GLFW::API.glfwSwapInterval(1)
|
|
27
|
+
|
|
28
|
+
print_state = lambda do
|
|
29
|
+
puts "title=#{window.title.inspect}"
|
|
30
|
+
puts "size=#{window.size.inspect} position=#{window.position.inspect}"
|
|
31
|
+
puts "framebuffer=#{window.framebuffer_size.inspect} content_scale=#{window.content_scale.inspect}"
|
|
32
|
+
puts "frame=#{window.frame_size.inspect} opacity=#{window.opacity.round(2)}"
|
|
33
|
+
puts "decorated=#{window.decorated?} resizable=#{window.resizable?} " \
|
|
34
|
+
"floating=#{window.floating?} focus_on_show=#{window.focus_on_show?}"
|
|
35
|
+
puts
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
print_state.call
|
|
39
|
+
|
|
40
|
+
window.on_resize do |_current_window, width, height|
|
|
41
|
+
puts "resized to #{width}x#{height}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
window.on_focus do |_current_window, focused|
|
|
45
|
+
puts "focused=#{focused}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
window.on_iconify do |_current_window, iconified|
|
|
49
|
+
puts "iconified=#{iconified}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
window.on_maximize do |_current_window, maximized|
|
|
53
|
+
puts "maximized=#{maximized}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
window.on_key do |current_window, key, _scancode, action, _mods|
|
|
57
|
+
next unless action == :press || action == :repeat
|
|
58
|
+
|
|
59
|
+
case key
|
|
60
|
+
when GLFW::GLFW_KEY_ESCAPE
|
|
61
|
+
current_window.should_close = true
|
|
62
|
+
when GLFW::GLFW_KEY_P
|
|
63
|
+
print_state.call
|
|
64
|
+
when GLFW::GLFW_KEY_T
|
|
65
|
+
current_window.title = "Window Attributes @ #{Time.now.strftime('%H:%M:%S')}"
|
|
66
|
+
print_state.call
|
|
67
|
+
when GLFW::GLFW_KEY_UP
|
|
68
|
+
x, y = current_window.position
|
|
69
|
+
current_window.position = [x, y - 20]
|
|
70
|
+
print_state.call
|
|
71
|
+
when GLFW::GLFW_KEY_DOWN
|
|
72
|
+
x, y = current_window.position
|
|
73
|
+
current_window.position = [x, y + 20]
|
|
74
|
+
print_state.call
|
|
75
|
+
when GLFW::GLFW_KEY_LEFT
|
|
76
|
+
x, y = current_window.position
|
|
77
|
+
current_window.position = [x - 20, y]
|
|
78
|
+
print_state.call
|
|
79
|
+
when GLFW::GLFW_KEY_RIGHT
|
|
80
|
+
x, y = current_window.position
|
|
81
|
+
current_window.position = [x + 20, y]
|
|
82
|
+
print_state.call
|
|
83
|
+
when GLFW::GLFW_KEY_EQUAL, GLFW::GLFW_KEY_KP_ADD
|
|
84
|
+
width, height = current_window.size
|
|
85
|
+
current_window.size = [width + 20, height + 20]
|
|
86
|
+
print_state.call
|
|
87
|
+
when GLFW::GLFW_KEY_MINUS, GLFW::GLFW_KEY_KP_SUBTRACT
|
|
88
|
+
width, height = current_window.size
|
|
89
|
+
current_window.size = [[width - 20, 240].max, [height - 20, 180].max]
|
|
90
|
+
print_state.call
|
|
91
|
+
when GLFW::GLFW_KEY_1
|
|
92
|
+
current_window.opacity = 1.0
|
|
93
|
+
print_state.call
|
|
94
|
+
when GLFW::GLFW_KEY_2
|
|
95
|
+
current_window.opacity = 0.8
|
|
96
|
+
print_state.call
|
|
97
|
+
when GLFW::GLFW_KEY_3
|
|
98
|
+
current_window.opacity = 0.6
|
|
99
|
+
print_state.call
|
|
100
|
+
when GLFW::GLFW_KEY_F
|
|
101
|
+
value = current_window.floating? ? GLFW::GLFW_FALSE : GLFW::GLFW_TRUE
|
|
102
|
+
current_window.set_attrib(GLFW::GLFW_FLOATING, value)
|
|
103
|
+
print_state.call
|
|
104
|
+
when GLFW::GLFW_KEY_M
|
|
105
|
+
current_window.maximize
|
|
106
|
+
when GLFW::GLFW_KEY_R
|
|
107
|
+
current_window.restore
|
|
108
|
+
when GLFW::GLFW_KEY_I
|
|
109
|
+
current_window.iconify
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
GLFWExample.loop_until_closed(window) do
|
|
114
|
+
window.swap_buffers
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "example_runtime"
|
|
5
|
+
|
|
6
|
+
GLFWExample.with_glfw do
|
|
7
|
+
runtime_version = GLFW.version.join(".")
|
|
8
|
+
platform_line = if GLFW.version_at_least?(3, 4)
|
|
9
|
+
"Current platform: #{GLFW.platform}"
|
|
10
|
+
else
|
|
11
|
+
"Current platform: unavailable on GLFW #{runtime_version}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
GLFWExample.print_banner("04 Monitor Info", [
|
|
15
|
+
"Loaded GLFW runtime: #{runtime_version}",
|
|
16
|
+
platform_line
|
|
17
|
+
])
|
|
18
|
+
|
|
19
|
+
monitors = GLFW::Monitor.all
|
|
20
|
+
primary = GLFW::Monitor.primary
|
|
21
|
+
|
|
22
|
+
if monitors.empty?
|
|
23
|
+
puts "No monitors reported by GLFW."
|
|
24
|
+
next
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
monitors.each_with_index do |monitor, index|
|
|
28
|
+
marker = primary && primary.handle.address == monitor.handle.address ? "*" : " "
|
|
29
|
+
mode = monitor.current_video_mode
|
|
30
|
+
sample_modes = monitor.video_modes.uniq.first(5)
|
|
31
|
+
|
|
32
|
+
puts "#{marker} Monitor #{index}: #{monitor.name || '(unnamed)'}"
|
|
33
|
+
puts " position: #{monitor.position.inspect}"
|
|
34
|
+
puts " workarea: #{monitor.workarea.inspect}"
|
|
35
|
+
puts " physical_size_mm: #{monitor.physical_size.inspect}"
|
|
36
|
+
puts " content_scale: #{monitor.content_scale.inspect}"
|
|
37
|
+
puts " current_mode: #{mode ? GLFWExample.format_video_mode(mode) : '(none)'}"
|
|
38
|
+
puts " sample_modes:"
|
|
39
|
+
|
|
40
|
+
if sample_modes.empty?
|
|
41
|
+
puts " (none)"
|
|
42
|
+
else
|
|
43
|
+
sample_modes.each do |video_mode|
|
|
44
|
+
puts " - #{GLFWExample.format_video_mode(video_mode)}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
puts
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
puts "* marks the primary monitor."
|
|
52
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "example_runtime"
|
|
5
|
+
|
|
6
|
+
def checkerboard_rgba(size, color_a, color_b, cell_size: 4)
|
|
7
|
+
data = String.new(capacity: size * size * 4, encoding: Encoding::BINARY)
|
|
8
|
+
|
|
9
|
+
size.times do |y|
|
|
10
|
+
size.times do |x|
|
|
11
|
+
color = ((x / cell_size) + (y / cell_size)).even? ? color_a : color_b
|
|
12
|
+
data << color.pack("C4")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
data
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def crosshair_rgba(size, foreground, border)
|
|
20
|
+
center = size / 2
|
|
21
|
+
data = String.new(capacity: size * size * 4, encoding: Encoding::BINARY)
|
|
22
|
+
|
|
23
|
+
size.times do |y|
|
|
24
|
+
size.times do |x|
|
|
25
|
+
color =
|
|
26
|
+
if x == center || y == center
|
|
27
|
+
foreground
|
|
28
|
+
elsif x.zero? || y.zero? || x == size - 1 || y == size - 1
|
|
29
|
+
border
|
|
30
|
+
else
|
|
31
|
+
[0, 0, 0, 0]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
data << color.pack("C4")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
data
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
GLFWExample.with_window(title: "Custom Cursor and Icon", width: 840, height: 520, visible: true) do |window|
|
|
42
|
+
GLFWExample.print_banner("05 Custom Cursor and Icon", [
|
|
43
|
+
"Esc: close",
|
|
44
|
+
"C: toggle custom and standard cursors",
|
|
45
|
+
"I: toggle custom window icons"
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
window.make_context_current
|
|
49
|
+
GLFW::API.glfwSwapInterval(1)
|
|
50
|
+
|
|
51
|
+
icon_16 = GLFW::Image.from_rgba(16, 16, checkerboard_rgba(16, [255, 138, 0, 255], [32, 32, 32, 255]))
|
|
52
|
+
icon_32 = GLFW::Image.from_rgba(32, 32, checkerboard_rgba(32, [255, 196, 64, 255], [16, 16, 16, 255], cell_size: 8))
|
|
53
|
+
cursor_image = GLFW::Image.from_rgba(24, 24, crosshair_rgba(24, [80, 220, 255, 255], [255, 255, 255, 255]))
|
|
54
|
+
|
|
55
|
+
custom_cursor = GLFW::Cursor.create(cursor_image, 12, 12)
|
|
56
|
+
standard_cursor = GLFW::Cursor.standard(GLFW::GLFW_POINTING_HAND_CURSOR)
|
|
57
|
+
|
|
58
|
+
window.set_icon([icon_16, icon_32])
|
|
59
|
+
window.cursor = custom_cursor
|
|
60
|
+
|
|
61
|
+
using_custom_cursor = true
|
|
62
|
+
icon_enabled = true
|
|
63
|
+
|
|
64
|
+
window.on_key do |current_window, key, _scancode, action, _mods|
|
|
65
|
+
next unless action == :press
|
|
66
|
+
|
|
67
|
+
case key
|
|
68
|
+
when GLFW::GLFW_KEY_ESCAPE
|
|
69
|
+
current_window.should_close = true
|
|
70
|
+
when GLFW::GLFW_KEY_C
|
|
71
|
+
using_custom_cursor = !using_custom_cursor
|
|
72
|
+
current_window.cursor = using_custom_cursor ? custom_cursor : standard_cursor
|
|
73
|
+
puts "Cursor is now #{using_custom_cursor ? 'custom' : 'standard'}."
|
|
74
|
+
when GLFW::GLFW_KEY_I
|
|
75
|
+
icon_enabled = !icon_enabled
|
|
76
|
+
|
|
77
|
+
if icon_enabled
|
|
78
|
+
current_window.set_icon([icon_16, icon_32])
|
|
79
|
+
puts "Custom icon restored."
|
|
80
|
+
else
|
|
81
|
+
current_window.clear_icon
|
|
82
|
+
puts "Window icon cleared."
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
GLFWExample.loop_until_closed(window) do
|
|
88
|
+
window.swap_buffers
|
|
89
|
+
end
|
|
90
|
+
ensure
|
|
91
|
+
custom_cursor&.destroy
|
|
92
|
+
standard_cursor&.destroy
|
|
93
|
+
end
|