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

Commit cbd1023

Browse files
committed
Document softbuffer_quickstart 0.3.0
1 parent 09be6be commit cbd1023

File tree

5 files changed

+244
-9
lines changed

5 files changed

+244
-9
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ fn main() {
3030
}
3131
```
3232

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

3437
## Contributing
3538
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.
3639
I'm looking for any ways to boost performance as much as possible while making the library simpler and more intuitive.
3740

3841
## Ideas:
3942
- 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.

src/lib.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl Default for WindowProperties {
3737
}
3838
}
3939

40-
/// Shorthand to run a struct that implements `ApplicationHandler`
40+
/// Shorthand to run a struct that implements winit's [`ApplicationHandler`](https://rust-windowing.github.io/winit/winit/application/trait.ApplicationHandler.html)
4141
pub fn run<A: ApplicationHandler<()>>(window: &mut A) -> Result<(), EventLoopError> {
4242
let event_loop = EventLoop::new()?;
4343
event_loop.set_control_flow(ControlFlow::Poll);
@@ -47,11 +47,29 @@ pub fn run<A: ApplicationHandler<()>>(window: &mut A) -> Result<(), EventLoopErr
4747
/// Initialises and returns a new RawWindow and RawSurface given an `ActiveEventLoop` and `WindowProperties`.
4848
/// For instance, implementation within `ApplicationHandler::resumed` may look like:
4949
/// ```rust
50-
/// //...
51-
/// fn resumed(&mut self, event_loop: &ActiveEventLoop) {
52-
/// (self.window, self.surface) = init(event_loop, &self.properties);
50+
/// use winit::application::ApplicationHandler;
51+
/// use winit::event::WindowEvent;
52+
/// use winit::event_loop::ActiveEventLoop;
53+
/// use winit::window::WindowId;
54+
///
55+
///
56+
/// use softbuffer_quickstart::{init, RawSurface, RawWindow, WindowProperties};
57+
///
58+
/// struct MyWindow {
59+
/// window: RawWindow,
60+
/// surface: RawSurface
61+
/// }
62+
///
63+
/// impl ApplicationHandler for MyWindow {
64+
/// /// `ApplicationHandler::resumed()` implementation here
65+
/// fn resumed(&mut self, event_loop: &ActiveEventLoop) {
66+
/// (self.window, self.surface) = init(event_loop, &WindowProperties::default());
67+
/// }
68+
///
69+
/// fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
70+
/// todo!()
71+
/// }
5372
/// }
54-
/// //...
5573
/// ```
5674
pub fn init(event_loop: &ActiveEventLoop, properties: &WindowProperties) -> (RawWindow, RawSurface) {
5775
let window = {
@@ -78,7 +96,7 @@ pub fn close(event_loop: &ActiveEventLoop, event: &WindowEvent) {
7896
}
7997
}
8098

81-
/// Shorthand to listen for and handle WindowEvent::Resized by resizing a buffer
99+
/// Shorthand to listen for and handle WindowEvent::Resized by resizing a buffer (`RawSurface`)
82100
pub fn resize(event: &WindowEvent, surface: &mut RawSurface) {
83101
if let WindowEvent::Resized(size) = event {
84102
surface
@@ -92,7 +110,8 @@ pub fn resize(event: &WindowEvent, surface: &mut RawSurface) {
92110
}
93111
}
94112

95-
/// Redraws a RawSurface. Call this on `Window::RedrawRequested`.
113+
/// Redraws a `RawSurface`. Call this on `WindowEvent::RedrawRequested` inside `ApplicationHandler::window_event`,
114+
/// right after you've drawn everything to the `RawSurface`.
96115
pub fn redraw(window: &mut RawWindow, surface: &mut RawSurface) {
97116
surface
98117
.as_mut()
@@ -104,7 +123,9 @@ pub fn redraw(window: &mut RawWindow, surface: &mut RawSurface) {
104123
window.as_ref().unwrap().request_redraw();
105124
}
106125

107-
/// Gets a mutable reference to a buffer from a `RawSurface`
126+
/// Gets a mutable reference to a buffer from a `RawSurface`. Colors are `u32`s.
127+
/// Accessing an array might look like `softbuffer_quickstart::buffer_mut(&mut self.surface)[y * width + x] = 0xffffff`.
128+
/// Keep in mind you have to keep track of the buffer width yourself--the RawBuffer type can't do that.
108129
pub fn buffer_mut(surface: &mut RawSurface) -> softbuffer::Buffer<'_, Rc<Window>, Rc<Window>> {
109130
surface.as_mut().unwrap().buffer_mut().unwrap()
110131
}

src/sb_window.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
99
use winit::window::{Window, WindowId};
1010
use crate::{close, init, redraw, resize, run, RawSurface, RawWindow, WindowProperties};
1111

12-
/// Wrapper for Softbuffer and a Winit window
12+
/// Wrapper for Softbuffer and a Winit window.
1313
pub struct SoftbufferWindow {
1414
window: RawWindow,
1515
surface: RawSurface,

0 commit comments

Comments
 (0)