-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit Initial Code stuff remove fenster.h Add fenster submodule fix header location Skip examples on nimble rename to avoid problems with "-' add license to nimble fix fenster.h location info New example Update colorProgression.nim Add files via upload add access to .keys and a example Update red_square_exit_on_esc_key.nim Delete examples/colorProgression.nim Update whiteNoise.nim Update whiteNoise.nim Update red_square_exit_on_esc_key.nim Update red_square_exit_on_esc_key.nim circle example uint32 internally Update README.md Update README.md Update README.md use destroy rename destroy to close better examples colors smoll mouse withe xamples typo Project renamed bump fix sleep, time function & 3d sample Handles FPS internally lib refactoring add access to modkey and fix bad examples filename a remove .close basic line shape Create LICENSE better line & example shapes folder use tuple on mouse add config.nims for shapes colored fractal tree Audio implementation with example Update fenstim_audio.nim Update fenstim_audio.nim Update README.md Update README.md Pixie support with example. Shapes removed. Update pixie/text.nim Accepts RGB pixie example with system font trailing comma removed Update README.md Update README.md Update fenstim.nimble Update README.md Gets SomeInteger instead of uint8 Plasma example more arts Update README.md Julia Set get fonts GUI app example Update README.md Update Julia Set Update README.md Add close Update white_noise.nim Performance tests Add FPS and access to sleep and time Avoid SomeInteger Fast clear using C Memset Use templates Small Fix Update README examples Create blank.yml Update blank.yml Update blank.yml Create config.nims Move partial working audio to README Faster examples with danger, lto, speed and arc Multi-threaded Mandelbrot Set Move examples Update README.md Update README.md Add files via upload Update README.md Update README.md Conways game of life Example Typo Update Fenster Add MacOS to action Update blank.yml Update blank.yml Update blank.yml Audio workaround closes #1 Move audio example from README to Examples folder apt install libasound2-dev for ubuntu on workflow Replace Fenster with FensterB Fork Bump Version Strip binaries on Workflow Update FensterB with new mouse function Fix mouse examples Create audio_bytebeats.nim Create walkable_carpet.nim Update README.md Delete examples/gifs directory Update fenstim.nimble Update FensterB
- Loading branch information
0 parents
commit 1213fca
Showing
24 changed files
with
902 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
name: Compile Fenstim Examples | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
workflow_dispatch: | ||
|
||
jobs: | ||
build: | ||
strategy: | ||
matrix: | ||
os: [ubuntu-latest, windows-latest, macOS-latest] | ||
runs-on: ${{ matrix.os }} | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
submodules: 'recursive' | ||
|
||
- name: Set up Nim | ||
uses: jiro4989/setup-nim-action@v1 | ||
with: | ||
nim-version: 'stable' | ||
|
||
- name: Install dependencies on Ubuntu | ||
if: matrix.os == 'ubuntu-latest' | ||
run: sudo apt-get install -y libasound2-dev | ||
shell: bash | ||
|
||
- name: Create build directory | ||
run: mkdir build | ||
shell: bash | ||
|
||
- name: Compile Examples | ||
run: | | ||
for file in examples/*.nim; do | ||
filename=$(basename "$file" .nim) | ||
echo "Compiling $filename" | ||
nim c -f -d:danger -d:lto -d:strip --mm:arc --passC:"-march=native" -o:"build/${filename}_${{ runner.os }}" "$file" | ||
done | ||
shell: bash | ||
|
||
- name: Upload artifact | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: fenstim-examples-${{ runner.os }} | ||
path: build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "src/fensterb"] | ||
path = src/fensterb | ||
url = https://github.com/CardealRusso/fensterb/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Cardeal Russo | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Fenstim | ||
Fenstim is a Nim wrapper for [Fenster](https://github.com/zserge/fenster), the most minimal cross-platform GUI with 2D canvas library. It provides a simple and efficient way to create graphical applications in Nim. | ||
|
||
# Implementation status | ||
- [x] Minimal 24-bit RGB framebuffer. | ||
- [x] Application lifecycle and system events are all handled automatically. | ||
- [x] Simple polling API without a need for callbacks or multithreading (like Arduino/Processing). | ||
- [x] Cross-platform keyboard events (keycodes). | ||
- [x] Cross-platform mouse events (X/Y + mouse click). | ||
- [x] Cross-platform timers to have a stable FPS rate. (builtin) | ||
- [x] Cross-platform audio playback (WinMM, CoreAudio, ALSA). | ||
|
||
# Credits | ||
Project done in collaboration with @ElegantBeef and @morturo at https://forum.nim-lang.org/t/12504 | ||
|
||
# Examples | ||
Basic usage | ||
```nim | ||
import fenstim, colors | ||
var app = init(Fenster, "My Window", 800, 600, 60) | ||
if app.loop: | ||
echo "Window target FPS: ", app.targetFps, ", Resolution: ", app.width, "x", app.height | ||
while app.loop: | ||
# Set pixel color | ||
app.pixel(400, 300) = 16711680 # Decimal red | ||
app.pixel(420, 300) = 0x0000FF # Hex blue | ||
pixel(app, 410, 300) = 0x00ff00 # Hex green | ||
# With colors module | ||
app.pixel(390, 300) = rgb(255, 0, 0).uint32 # Red | ||
app.pixel(380, 300) = parseColor("#00FF00").uint32 # Green | ||
app.pixel(370, 300) = colAliceBlue.uint32 # Alice Blue | ||
app.pixel(360, 300) = parseColor("silver").uint32 # Silver | ||
# Get pixel color | ||
let color = app.pixel(420, 300) # Decimal | ||
# Check key press if A key is pressed | ||
if app.keys[ord('A')] == 1: | ||
echo "A key is pressed" | ||
# Check if scape key is pressed | ||
if keys(app)[27] == 1: | ||
app.close | ||
break | ||
# Get mouse position and click state | ||
if app.mouse.mclick[0] == 1: | ||
echo "Clicked at: ", app.mouse.pos.x, "x", app.mouse.pos.y | ||
# Adjust FPS | ||
app.targetFps = 30 | ||
``` | ||
|
||
Opens a 60fps 800x600 window, draws a red square and exits when pressing the Escape key: | ||
|
||
```nim | ||
# examples/red_square.nim | ||
import fenstim | ||
var app = init(Fenster, "Red square with fenstim", 800, 600, 60) | ||
while app.loop and app.keys[27] == 0: | ||
for x in 350 .. 450: | ||
for y in 250 .. 350: | ||
app.pixel(x, y) = 0xFF0000 | ||
``` | ||
|
||
# API usage | ||
### Initialization | ||
```nim | ||
init*(_: type Fenster, title: string, width, height: int, fps: int = 60): Fenster | ||
``` | ||
Creates a new Fenster window with the specified title, dimensions, and target FPS. | ||
|
||
### Main Loop | ||
```nim | ||
loop*(self: var Fenster): bool | ||
``` | ||
Handles events and updates the display. Returns false when the window should close. | ||
|
||
### Pixel Manipulation | ||
```nim | ||
pixel*(self: Fenster, x, y: int): uint32 | ||
``` | ||
Get or set a uint32 pixel color at (x, y). | ||
|
||
### Window Properties | ||
```nim | ||
width*(self: Fenster): int | ||
height*(self: Fenster): int | ||
targetFps*(self: Fenster): int | ||
close*(self: var Fenster) | ||
clear*(self: Fenster) | ||
``` | ||
|
||
### Input Handling | ||
```nim | ||
keys*(self: Fenster): array[256, cint] | ||
mouse*(self: Fenster): tuple[pos: tuple[x, y: int], mclick: array[5, cint], mhold: array[3, cint]] | ||
modkey*(self: Fenster): int | ||
``` | ||
keys = Array of key states. Index corresponds to ASCII value (0-255), but arrows are 17..20. | ||
mouse = Get mouse position (x, y), clicked (left, right, middle, scroll up, scroll down) and holding buttons (left, right, middle) | ||
modkey = 4 bits mask, ctrl=1, shift=2, alt=4, meta=8 | ||
|
||
# Examples | ||
### Galery | ||
![lca1](https://github.com/user-attachments/assets/4da9fa7f-e201-4f18-a262-d53fcbdb0380) | ||
![lca2](https://github.com/user-attachments/assets/9a8654af-e5fc-4b1e-9c3f-2eeaf2bf2016) | ||
![lca3](https://github.com/user-attachments/assets/c2fe1c8f-b138-491f-be7b-0baa1f553259) | ||
![lca5](https://github.com/user-attachments/assets/73e1280f-a988-4190-bf6e-1f94fee1d8d9) | ||
![lca4](https://github.com/user-attachments/assets/93b30453-de4e-4952-8729-87f149b02a13) | ||
[interactive_julia_set.nim](examples/interactive_julia_set.nim) | ||
![ezgif-2-1a773886a7](https://github.com/user-attachments/assets/95116b60-cdf0-4308-9f90-593e97bd60d0) | ||
[mandelbrot.nim](examples/mandelbrot.nim) | ||
[threaded_mandelbrot.nim](examples/threaded_mandelbrot.nim) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import fenstim, fenstim_audio | ||
|
||
var | ||
app = init(Fenster, "Audio Example", 320, 240, 60) | ||
app_audio = init(FensterAudio) | ||
t, u = 0 | ||
|
||
proc generateAudio(n: int): seq[float32] = | ||
result = newSeq[float32](n) | ||
for i in 0..<n: | ||
u.inc | ||
let x = (u * 80 div 441).int | ||
result[i] = float32(((((x shr 10) and 42) * x) and 0xff)) / 256.0 | ||
|
||
while app.loop and app.keys[27] == 0: | ||
t.inc | ||
|
||
let n = app_audio.available | ||
if n > 0: | ||
let audio = generateAudio(n) | ||
app_audio.write(audio) | ||
|
||
for i in 0..<app.width: | ||
for j in 0..<app.height: | ||
app.pixel(i, j) = (i * j * t).uint32 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import fenstim, fenstim_audio, math | ||
|
||
var | ||
app = init(Fenster, "Bytebeats. Switch with Left Click.", 800, 600, 60) | ||
app_audio = init(FensterAudio) | ||
t, u, currentBeat = 0 | ||
|
||
let byteBeats = [ | ||
proc(x: int): float32 = float32(((((x shr 10) and 42) * x) and 0xff)) / 256.0, | ||
proc(x: int): float32 = float32((x * ((x shr 12 or x shr 8) and 63 and x shr 4)) and 0xff) / 256.0, | ||
proc(x: int): float32 = float32(((not (x shr 2)) * ((127 and x * (7 and x shr 10)) < (245 and x * (2 + (5 and x shr 14)))).int) and 0xff) / 256.0, | ||
proc(x: int): float32 = float32((((x shr 10 xor x shr 11) mod 5 * ((x shr 14 and 3 xor x shr 15 and 1) + 1) * x mod 99 + ((3 + (x shr 14 and 3) - (x shr 16 and 1)) div 3 * x mod 99 and 64))) and 0xff) / 256.0 | ||
] | ||
|
||
let arts = [ | ||
proc(x, y, t: int): uint32 = (x * y * t).uint32, | ||
proc(x, y, t: int): uint32 = ((x xor y xor t) * 65793).uint32, | ||
proc(x, y, t: int): uint32 = (((x * x + y * y + t) and 255) * 0x010101).uint32, | ||
proc(x, y, t: int): uint32 = | ||
( | ||
let freqX = 0.04 + app.mouse.pos.x / app.width * 0.1 | ||
let freqY = 0.03 + app.mouse.pos.y / app.height * 0.1 | ||
|
||
let plasma = sin(x.float * freqX + 0.1 * t.float) + | ||
sin(y.float * freqY) + | ||
sin((x.float + y.float) * 0.02 + 0.1 * t.float) | ||
|
||
let color = uint32((plasma + 3) * 85) | ||
|
||
result = color shl 16 or (color shl 1) shl 8 or color shl 2 | ||
), | ||
] | ||
|
||
proc generateAudio(n: int): seq[float32] = | ||
result = newSeq[float32](n) | ||
for i in 0..<n: | ||
u.inc | ||
result[i] = byteBeats[currentBeat]((u * 80 div 441).int) | ||
|
||
while app.loop and app.keys[27] == 0: | ||
t.inc | ||
if app.mouse.mclick[0] == 1 or app.mouse.mclick[1] == 1: | ||
currentBeat = (currentBeat + (if app.mouse.mclick[0] == 1: 1 else: -1) + byteBeats.len) mod byteBeats.len | ||
echo "Switched to ByteBeat ", currentBeat + 1 | ||
|
||
if app_audio.available > 0: | ||
app_audio.write(generateAudio(app_audio.available)) | ||
|
||
for i in 0..<app.width: | ||
for j in 0..<app.height: | ||
app.pixel(i, j) = arts[currentBeat](i, j, t) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import fenstim | ||
|
||
var app = init(Fenster, "Bubbles pattern", 320, 240) | ||
var t = 0 | ||
|
||
while app.loop and app.keys[27] == 0: | ||
inc t | ||
for x in 0..<app.width: | ||
for y in 0..<app.height: | ||
app.pixel(x, y) = uint32(((x * x + y * y + t) and 255) * 0x010101) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import fenstim | ||
|
||
var | ||
app = init(Fenster, "Colorful patterns", 320, 240) | ||
t = 1 | ||
|
||
while app.loop and app.keys[27] == 0: | ||
t += 1 | ||
for i in 0..<app.width: | ||
for j in 0..<app.height: | ||
app.pixel(i, j) = (i * j * t).uint32 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
switch("path", "$projectDir/../src") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import fenstim, random | ||
|
||
const | ||
WIDTH = 400 | ||
HEIGHT = 300 | ||
CELL_SIZE = 2 | ||
|
||
var | ||
app = init(Fenster, "Conway's Game of Life", WIDTH * CELL_SIZE, HEIGHT * CELL_SIZE, 30) | ||
grid: array[WIDTH, array[HEIGHT, bool]] | ||
newGrid: array[WIDTH, array[HEIGHT, bool]] | ||
|
||
randomize() | ||
|
||
for x in 0..<WIDTH: | ||
for y in 0..<HEIGHT: | ||
grid[x][y] = rand(1.0) < 0.2 | ||
|
||
proc countNeighbors(x, y: int): int = | ||
for dx in -1..1: | ||
for dy in -1..1: | ||
if dx == 0 and dy == 0: continue | ||
let | ||
nx = (x + dx + WIDTH) mod WIDTH | ||
ny = (y + dy + HEIGHT) mod HEIGHT | ||
if grid[nx][ny]: result += 1 | ||
|
||
while app.loop and app.keys[27] == 0: | ||
for x in 0..<WIDTH: | ||
for y in 0..<HEIGHT: | ||
let neighbors = countNeighbors(x, y) | ||
if grid[x][y]: | ||
newGrid[x][y] = neighbors == 2 or neighbors == 3 | ||
else: | ||
newGrid[x][y] = neighbors == 3 | ||
|
||
for x in 0..<WIDTH: | ||
for y in 0..<HEIGHT: | ||
grid[x][y] = newGrid[x][y] | ||
let color = if grid[x][y]: 0xFFFFFF else: 0x000000 | ||
for dx in 0..<CELL_SIZE: | ||
for dy in 0..<CELL_SIZE: | ||
app.pixel(x * CELL_SIZE + dx, y * CELL_SIZE + dy) = color.uint32 | ||
|
||
if app.keys[ord('R')] == 1: | ||
for x in 0..<WIDTH: | ||
for y in 0..<HEIGHT: | ||
grid[x][y] = rand(1.0) < 0.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import fenstim, math | ||
|
||
var | ||
app = init(Fenster, "Interactive Julia Set", 800, 600, 60) | ||
cx, cy: float32 = 0 | ||
oldpos = (801, 601) | ||
|
||
proc julia(x, y, cx, cy: float32, maxIter: int): int = | ||
var | ||
zx = x | ||
zy = y | ||
|
||
for i in 1..maxIter: | ||
let | ||
zx2 = zx*zx | ||
zy2 = zy*zy | ||
|
||
if zx2 + zy2 > 4: return i | ||
|
||
zy = 2*zx*zy + cy | ||
zx = zx2 - zy2 + cx | ||
|
||
return 0 | ||
|
||
while app.loop and app.keys[27] == 0: | ||
let (mouseX, mouseY) = app.mouse.pos | ||
if (mouseX, mouseY) != oldpos: | ||
oldpos = (mouseX, mouseY) | ||
cx = mouseX.float32 / app.width.float32 * 4 - 2 | ||
cy = mouseY.float32 / app.height.float32 * 4 - 2 | ||
|
||
for px in 0..<app.width: | ||
for py in 0..<app.height: | ||
let | ||
x = px.float32 / app.width.float32 * 4 - 2 | ||
y = py.float32 / app.height.float32 * 4 - 2 | ||
c = julia(x, y, cx, cy, 100) | ||
r = uint8((sin(c.float32 * 0.1) + 1) * 127) | ||
g = uint8((sin(c.float32 * 0.13 + 1) + 1) * 127) | ||
b = uint8((sin(c.float32 * 0.17 + 2) + 1) * 127) | ||
|
||
app.pixel(px, py) = (r.uint32 shl 16) or (g.uint32 shl 8) or b.uint32 |
Oops, something went wrong.