Skip to content

Commit

Permalink
Rebase
Browse files Browse the repository at this point in the history
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
CardealRusso committed Oct 17, 2024
0 parents commit 1213fca
Show file tree
Hide file tree
Showing 24 changed files with 902 additions and 0 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/blank.yml
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/
3 changes: 3 additions & 0 deletions .gitmodules
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/
21 changes: 21 additions & 0 deletions LICENSE
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.
120 changes: 120 additions & 0 deletions README.md
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)
25 changes: 25 additions & 0 deletions examples/audio.nim
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
51 changes: 51 additions & 0 deletions examples/audio_bytebeats.nim
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)
10 changes: 10 additions & 0 deletions examples/bubbles_patterns.nim
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)
11 changes: 11 additions & 0 deletions examples/colorful_patterns.nim
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
1 change: 1 addition & 0 deletions examples/config.nims
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
switch("path", "$projectDir/../src")
48 changes: 48 additions & 0 deletions examples/conways_game_of_life.nim
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
42 changes: 42 additions & 0 deletions examples/interactive_julia_set.nim
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
Loading

0 comments on commit 1213fca

Please sign in to comment.