Skip to content
This repository was archived by the owner on Jul 8, 2025. It is now read-only.

Commit ba57130

Browse files
Merge pull request #2 from pastthepixels/rewrite
Rewrite softbuffer_quickstart (update to 0.3.0)
2 parents d125832 + ee4a321 commit ba57130

File tree

6 files changed

+433
-108
lines changed

6 files changed

+433
-108
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "softbuffer_quickstart"
33
description = "minifb-like interface for softbuffer"
44
repository = "https://github.com/pastthepixels/softbuffer-quickstart"
5-
version = "0.2.0"
5+
version = "0.3.0"
66
license = "GPL-3.0"
77
keywords = ["graphics"]
88
categories = ["graphics", "multimedia", "rendering"]

README.md

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,39 @@ A wrapper that makes using Softbuffer as easy as using minifb.
55
Running the Softbuffer example in softbuffer-quickstart:
66
```rust
77
use softbuffer_quickstart::{SoftbufferWindow, WindowProperties};
8+
use winit::event::WindowEvent;
89

910
fn main() {
10-
let mut window = SoftbufferWindow::new(
11-
// This is the "loop closure" -- called on every update loop
12-
|window, buffer| {
13-
let (width, height) = {
14-
let size = window.inner_size();
15-
(size.width, size.height)
16-
};
17-
for index in 0..(width * height) {
18-
let y = index / width;
19-
let x = index % width;
20-
let red = x % 255;
21-
let green = y % 255;
22-
let blue = (255 - (red + green).min(255)) % 255;
11+
let mut window = SoftbufferWindow::new(WindowProperties::default());
12+
window
13+
.run(move |window, event| match event {
14+
WindowEvent::RedrawRequested => {
15+
let (width, height) = window.inner_size();
16+
let mut buffer = window.buffer_mut();
17+
for index in 0..(width * height) {
18+
let y = index / width;
19+
let x = index % width;
20+
let red = x % 255;
21+
let green = y % 255;
22+
let blue = (255 - (red + green).min(255)) % 255;
2323

24-
buffer[index as usize] = blue | (green << 8) | (red << 16);
24+
buffer[index] = (blue | (green << 8) | (red << 16)).try_into().unwrap();
25+
}
2526
}
26-
},
27-
// This is how we can set properties for the window when it's initially created.
28-
WindowProperties::default()
29-
);
30-
window.run().expect("window can't run :(");
27+
_ => (),
28+
})
29+
.expect("window can't run :(");
3130
}
3231
```
3332

33+
## I wanna know more!
34+
`cargo add softbuffer_quickstart` and read the documentation at `doc/`!
35+
3436

3537
## Contributing
3638
PRs are welcome! As with any of my other projects it might take a while for me to respond to issues/pull requests. I recommend not squashing your commits before you submit a PR as doing so makes it a bit harder to review your code.
3739
I'm looking for any ways to boost performance as much as possible while making the library simpler and more intuitive.
3840

3941
## Ideas:
40-
- Handling Winit events (like resizing)
41-
- Improving performance with the buffer (for loops in general are slow! there has to be a faster way to iterate over everything in the buffer)
4242
- Adding icons to WindowProperties (probably good for new contributors)
43+
- Supporting compiling to wasm (hell)

doc/ADVANCED.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
Advanced Introduction to `softbuffer-quickstart`
2+
================================================
3+
<small>a.k.a, "Well, what *more* can I do?"</small>
4+
5+
> [!NOTE]
6+
> This assumes that you've read the basic introduction first. If you haven't taken a look at it--and you have time--I encourage you to do so!`
7+
8+
`softbuffer_quickstart`, at its core, actually provides utility functions for people who want to implement their own Winit windows. Let's work backwards. Say you want to implement your own Winit window. Then you'd certainly implement [`ApplicationHandler`](https://rust-windowing.github.io/winit/winit/application/trait.ApplicationHandler.html):
9+
10+
```rust
11+
use winit::application::ApplicationHandler;
12+
use winit::event::WindowEvent;
13+
use winit::event_loop::ActiveEventLoop;
14+
use winit::window::WindowId;
15+
16+
17+
use softbuffer_quickstart::{init, WindowProperties};
18+
19+
struct MyWindow {}
20+
21+
impl ApplicationHandler for MyWindow {
22+
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
23+
todo!()
24+
}
25+
26+
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
27+
todo!()
28+
}
29+
}
30+
```
31+
32+
But wait! How does `softbuffer_quickstart` fit into this? Well, let's take a look at those TODOs.
33+
34+
## 1. Initializing `softbuffer_quickstart`
35+
36+
Initializing `softbuffer_quickstart` is simple. We'll call `init` with the given event loop and a set of initial window properties (remember `WindowProperties`?), which will give us a `RawWindow` and `RawSurface` to store ourselves:
37+
38+
```rust
39+
use winit::application::ApplicationHandler;
40+
use winit::event::WindowEvent;
41+
use winit::event_loop::ActiveEventLoop;
42+
use winit::window::WindowId;
43+
44+
45+
use softbuffer_quickstart::{init, RawSurface, RawWindow, WindowProperties};
46+
47+
struct MyWindow {
48+
window: RawWindow,
49+
surface: RawSurface
50+
}
51+
52+
impl ApplicationHandler for MyWindow {
53+
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
54+
(self.window, self.surface) = init(event_loop, &WindowProperties::default());
55+
}
56+
57+
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
58+
todo!()
59+
}
60+
}
61+
```
62+
63+
## 2. Drawing to a window (and other handy functions)
64+
65+
Under `window_event` we're given free rein to handle anything. There are two handy functions that can make handling some events simpler:
66+
- `softbuffer_quickstart::close(&event_loop, &event)` checks to see if a "close" event has been signalled, and closes the window if it has.
67+
- `softbuffer_quickstart::resize(&event, &mut surface)` check to see if a "resize" event has been signalled, and resizes the underlying buffer.
68+
69+
We can of course omit these and implement those checks ourselves, if we'd like. (However, no one probably wants to unwrap and clone and get references a bunch of times.)
70+
71+
Everything else is stupid simple. We update the buffer how we want, and then call `redraw` once we're done.
72+
- `softbuffer_quickstart::buffer_mut(surface: &mut RawSurface)` returns a mutable reference to a `RawSurface`. (Hey, just like in the basic tutorial!)
73+
- `softbuffer_quickstart::redraw(window: &mut RawWindow, surface: &mut RawSurface)` presents the `RawSurface` to the screen, using the `RawWindow`.
74+
75+
The final piece of the puzzle is *running* the thing. All we have to do is call `softbuffer_quickstart::run()` with a mutable reference to our window.
76+
77+
So if we recreated the code in the basic introduction, it'd look like this:
78+
79+
```rust
80+
use winit::application::ApplicationHandler;
81+
use winit::event::WindowEvent;
82+
use winit::event_loop::ActiveEventLoop;
83+
use winit::window::WindowId;
84+
85+
86+
use softbuffer_quickstart::{init, RawSurface, RawWindow, WindowProperties};
87+
88+
fn main() {
89+
let mut window = MyWindow {
90+
window: None,
91+
surface: None,
92+
size: (800, 600)
93+
};
94+
softbuffer_quickstart::run(&mut window).expect("Window can't run");
95+
}
96+
97+
struct MyWindow {
98+
window: RawWindow,
99+
surface: RawSurface,
100+
size: (usize, usize)
101+
}
102+
103+
impl ApplicationHandler for MyWindow {
104+
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
105+
(self.window, self.surface) = init(event_loop, &WindowProperties::default());
106+
}
107+
108+
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
109+
softbuffer_quickstart::close(event_loop, &event);
110+
softbuffer_quickstart::resize(&event, &mut self.surface);
111+
112+
match event {
113+
WindowEvent::Resized(size) => {
114+
self.size = (size.width as usize, size.height as usize);
115+
}
116+
WindowEvent::RedrawRequested => {
117+
let (width, height) = self.size;
118+
let mut buffer = softbuffer_quickstart::buffer_mut(&mut self.surface);
119+
for index in 0..(width * height) {
120+
let y = index / width;
121+
let x = index % width;
122+
let red = x % 255;
123+
let green = y % 255;
124+
let blue = (255 - (red + green).min(255)) % 255;
125+
126+
buffer[index] = (blue | (green << 8) | (red << 16)).try_into().unwrap();
127+
}
128+
softbuffer_quickstart::redraw(&mut self.window, &mut self.surface);
129+
}
130+
_ => (),
131+
}
132+
}
133+
}
134+
```

doc/BASIC.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
Basic Introduction to `softbuffer-quickstart`
2+
=============================================
3+
<small>a.k.a, "I just wanna make a window, dab nagit!"</small>
4+
5+
If you want to make a simple window, `softbuffer-quickstart` has you covered. (I mean, that's its entire thing!) Keep in mind that you'll lose a bit of control over window/device events, so if having more control is important to you, check out the advanced guide.
6+
7+
## Quick, gimmie the source code!
8+
```rust
9+
use softbuffer_quickstart::{SoftbufferWindow, WindowProperties};
10+
use winit::event::WindowEvent;
11+
12+
fn main() {
13+
let mut window = SoftbufferWindow::new(WindowProperties::default());
14+
window
15+
.run(move |window, event| match event {
16+
WindowEvent::RedrawRequested => {
17+
let (width, height) = window.inner_size();
18+
let mut buffer = window.buffer_mut();
19+
for index in 0..(width * height) {
20+
let y = index / width;
21+
let x = index % width;
22+
let red = x % 255;
23+
let green = y % 255;
24+
let blue = (255 - (red + green).min(255)) % 255;
25+
26+
buffer[index] = (blue | (green << 8) | (red << 16)).try_into().unwrap();
27+
}
28+
}
29+
_ => (),
30+
})
31+
.expect("window can't run :(");
32+
}
33+
```
34+
35+
## What does this do?
36+
`softbuffer_quickstart` has a handy dandy `SoftbufferWindow` class that pretty much handles the pain of using Winit. To create a new window, just use `SoftbufferWindow::new()` with a `WindowProperties` struct.
37+
38+
`WindowProperties` is a quick way to define things like the width, height, and title of a window. This will help `softbuffer_quickstart` to create a Winit window and resize the buffer. You can create a `WindowProperties` like this:
39+
40+
```rust
41+
use softbuffer_quickstart::{SoftbufferWindow, WindowProperties};
42+
43+
fn main() {
44+
let properties = WindowProperties {
45+
title: "My super cool window",
46+
width: 800, // Measurements in pixels
47+
height: 600
48+
};
49+
}
50+
```
51+
52+
or this:
53+
54+
```rust
55+
use softbuffer_quickstart::{SoftbufferWindow, WindowProperties};
56+
57+
fn main() {
58+
let properties = WindowProperties {
59+
title: "My super cool window",
60+
..
61+
WindowProperties::default() // We can just tell Rust to
62+
// make the rest of the properties
63+
// default ones.
64+
};
65+
}
66+
```
67+
68+
Next. What does `window.run` do?
69+
70+
When you run a window, you have to move ownership of every variable you used outside the loop function *into* the loop function with the `move` keyword. Then, that function is called every time Winit polls your operating system for new events. Handling `WindowEvents` is in the [Winit documentation](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html), but all we really need is to see when we're supposed to redraw a window. Winit caps this to the monitor's refresh rate.
71+
72+
Inside the loop function, we get the window's width and height from Winit with `window.inner_size()`, get a mutable reference to the buffer with `window.buffer_mut()`, and then set each pixel to some weird thing. All that matters about the weird thing we do in the for loop is that the indices in the buffer are referenced and set to a `u32` value. What are some ways to define 32-bit color values? Look no further than hexadecimal notation, which you've probably seen when styling things with CSS. For instance, pure red can be defined with `0xff0000`. Each channel (R, G, B) is represented with two hexadecimal values that go up to 255.
73+
74+
After the loop function has been run, `softbuffer_quickstart` will automatically show the buffer and clear it for the next loop.
75+
76+
Finally, the `expect()` at the bottom is because Winit sometimes can't make a window. In this case--which is rare--we'll just print out some small message and Rust will handle a stack trace for us.

0 commit comments

Comments
 (0)