Sniffly Technical Stack Deep Dive
Core Stack Justification
Why Pure Fortran?
Philosophy: Prove that Fortran can build modern GUI applications that rival commercial software written in C++/C#.
Practical Advantages:
- Performance: Native compilation, no VM/interpreter overhead
- Memory Safety: Strong typing, bounds checking (with compiler flags)
- Existing Code: Leverage proven algorithms from sniffert
- Mathematical Operations: Natural for size calculations, layout algorithms
- Array Operations: Efficient bulk operations on file trees
Challenges Addressed:
- String handling: Use allocatable character arrays (Fortran 2003+)
- Pointers: Use Fortran's C interoperability for GTK callbacks
- OOP: Use Fortran 2008 type-bound procedures where beneficial
GTK4 via gtk-fortran
Why GTK4 (not Qt, wxWidgets, etc.)?
Technical Reasons:
- gtk-fortran exists: Mature, maintained Fortran bindings
- Native on Linux: GTK is the de facto standard
- macOS Support: GTK4 uses native Cocoa backend
- Modern: GTK4 is actively developed, not legacy
- Cairo Integration: Built-in 2D drawing library
Comparison to Alternatives:
| Framework | Fortran Bindings | Native Look | Performance | Verdict |
|---|---|---|---|---|
| GTK4 | gtk-fortran | Yes (Linux/macOS) | Excellent | ✅ Chosen |
| Qt | None (would need C++ wrapper) | Yes | Excellent | ❌ No bindings |
| wxWidgets | None | Yes | Good | ❌ No bindings |
| FLTK | None | No | Excellent | ❌ No bindings, dated look |
| Tk | None (would need Tcl) | No | Poor | ❌ Dated, slow |
gtk-fortran Maturity:
- First release: 2011
- Current version: 3.24.41 (2024)
- Active maintainer: Vincent Magnin
- Production use: Scientific visualization tools
- Platform support: Linux, macOS, FreeBSD, Windows (MSYS2)
GTK4 Architecture
┌─────────────────────────────────────────────────────────┐
│ Your Fortran Code │
│ (sniffly modules) │
└─────────────────────────────────────────────────────────┘
│
↓ (iso_c_binding)
┌─────────────────────────────────────────────────────────┐
│ gtk-fortran │
│ (Fortran interfaces to GTK C functions) │
└─────────────────────────────────────────────────────────┘
│
↓ (FFI)
┌─────────────────────────────────────────────────────────┐
│ GTK4 C Library │
│ ┌─────────────┬──────────────┬────────────────────┐ │
│ │ Widgets │ GDK │ GSK │ │
│ │ (buttons, │ (events, │ (GPU rendering) │ │
│ │ windows) │ input) │ │ │
│ └─────────────┴──────────────┴────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────┐
│ Platform Backend │
│ Linux: Wayland/X11 macOS: Cocoa BSD: X11 │
└─────────────────────────────────────────────────────────┘
Cairo for 2D Graphics
Why Cairo?
Technical Strengths:
- Vector Graphics: Resolution-independent rendering
- Anti-aliasing: Beautiful, smooth edges
- Hardware Acceleration: GPU-backed on modern systems
- Compositing: Alpha blending, gradients, patterns
- Text Rendering: Pango integration for complex text
Cairo Features We'll Use:
| Feature | Usage in Sniffly |
|---|---|
cairo_rectangle() |
Draw treemap rectangles |
cairo_fill() |
Fill rectangles with color |
cairo_stroke() |
Draw borders |
cairo_set_source_rgb() |
Set solid colors |
cairo_pattern_create_linear() |
Cushion gradients |
cairo_clip() |
Clip nested rectangles |
cairo_save()/restore() |
State management |
| Pango integration | Render file names |
Performance Characteristics:
- Fast Paths: Solid fills, axis-aligned rectangles
- Slow Paths: Bezier curves, complex paths (we won't use)
- Caching: Cairo surfaces can be cached
- Expected Performance: 60fps for 1000s of rectangles
Cairo vs. OpenGL
We chose Cairo over OpenGL because:
- Simpler API: 2D-focused, easier to learn
- Better Integration: Built into GTK4
- Text Rendering: Pango support out of the box
- Sufficient Performance: 60fps achievable for our use case
If performance becomes an issue (unlikely), we can:
- Use GTK4's GSK (GPU Scene Kit) backend
- Batch render calls
- Implement viewport culling (only draw visible nodes)
Pango for Text
Why Pango?
Features:
- Font Management: System font detection, fallbacks
- Text Layout: Ellipsization, wrapping, alignment
- Internationalization: Full Unicode support, RTL text
- Integration: Native Cairo rendering
Pango Features We'll Use:
! Create Pango layout
layout = pango_cairo_create_layout(cairo_context)
! Set text
call pango_layout_set_text(layout, c_str("filename.txt"))
! Set font
font_desc = pango_font_description_from_string(c_str("Sans 10"))
call pango_layout_set_font_description(layout, font_desc)
! Set ellipsization for long text
call pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END)
call pango_layout_set_width(layout, width * PANGO_SCALE)
! Render
call pango_cairo_show_layout(cairo_context, layout)
GLib for Core Utilities
Threading
GLib Threads (not pthreads) for portability:
! Start background scan thread
thread = g_thread_new(c_str("scanner"), c_funloc(scan_thread_func), c_loc(scan_data))
! Thread-safe UI updates from background thread
call g_idle_add(c_funloc(update_ui_callback), c_loc(progress_data))
Why g_idle_add?
- GTK is not thread-safe
- g_idle_add queues callback on main thread
- Callback runs in GTK event loop
- Safe to update UI from callback
Event Loop
GTK's main loop handles:
- User input (mouse, keyboard)
- Window events (resize, close)
- Timers and idle callbacks
- Background task callbacks (g_idle_add)
! Start GTK main loop (blocks until app quits)
call gtk_main()
GLib Data Structures
We'll primarily use Fortran's native arrays, but GLib provides:
GList/GSList: Linked lists (if needed for dynamic UI)GHashTable: Hash maps (for quick file lookup)GString: Dynamic strings (if Fortran strings insufficient)
Build System: Meson
Why Meson (not Make, CMake, FPM)?
Meson Advantages:
- GTK Standard: All GTK projects use Meson
- Dependency Detection: Auto-finds GTK, Cairo, etc. via pkg-config
- Fast: Ninja backend, parallel builds
- Cross-Platform: Works on Linux, macOS, BSD
- Clean Syntax: Python-like, easy to read
Meson vs. FPM:
- FPM is great for pure Fortran projects
- FPM doesn't handle C library dependencies well
- We'll provide both Meson (primary) and FPM (fallback) builds
Sample meson.build:
project('sniffly', 'fortran',
version: '1.0.0',
default_options: ['warning_level=3'])
# Dependencies
gtk4_dep = dependency('gtk4')
cairo_dep = dependency('cairo')
pango_dep = dependency('pango')
# Source files
src = [
'src/core/types.f90',
'src/core/file_system.f90',
'src/core/disk_scanner.f90',
'src/layout/squarified.f90',
'src/layout/cushioned.f90',
'src/gui/gtk_app.f90',
'src/rendering/cairo_renderer.f90',
'app/main.f90',
]
# Executable
executable('sniffly', src,
dependencies: [gtk4_dep, cairo_dep, pango_dep],
install: true)
Building:
meson setup build
meson compile -C build
meson install -C build
Development Tools
Compiler
gfortran 9.0+ (GCC Fortran compiler)
Why gfortran?
- Free and open source
- Excellent Fortran 2008 support
- C interoperability works well
- Available on all platforms
Compiler Flags:
# Debug build
-g -Wall -Wextra -fcheck=all -fbacktrace
# Release build
-O3 -march=native -flto
Alternative: Intel Fortran (ifx) also works but not required
Debugger
GDB (GNU Debugger)
gdb ./build/sniffly
LLDB (macOS default)
lldb ./build/sniffly
Profiling
Linux: perf
perf record ./sniffly /large/directory
perf report
macOS: Instruments
instruments -t "Time Profiler" ./sniffly /large/directory
gprof (cross-platform)
# Compile with -pg flag
gfortran -pg -o sniffly ...
./sniffly /large/directory
gprof sniffly gmon.out
Memory Analysis
valgrind (Linux)
valgrind --leak-check=full ./sniffly /tmp
AddressSanitizer (both platforms)
gfortran -fsanitize=address -o sniffly ...
./sniffly /tmp
Dependency Installation
macOS (Homebrew)
# Install GTK4 and dependencies
brew install gtk4 cairo pango glib
# Install gtk-fortran (build from source)
git clone https://github.com/vmagnin/gtk-fortran.git
cd gtk-fortran
cmake -B build
cmake --build build
sudo cmake --install build
# Install build tools
brew install gfortran meson ninja
Ubuntu/Debian
# Install GTK4 and dependencies
sudo apt install libgtk-4-dev libcairo2-dev libpango1.0-dev libglib2.0-dev
# Install gtk-fortran (build from source)
git clone https://github.com/vmagnin/gtk-fortran.git
cd gtk-fortran
cmake -B build
cmake --build build
sudo cmake --install build
# Install build tools
sudo apt install gfortran meson ninja-build
Arch Linux
# Install GTK4 and dependencies
sudo pacman -S gtk4 cairo pango glib2
# gtk-fortran from AUR
yay -S gtk-fortran
# Install build tools
sudo pacman -S gcc-fortran meson ninja
Fedora/RHEL
# Install GTK4 and dependencies
sudo dnf install gtk4-devel cairo-devel pango-devel glib2-devel
# Install gtk-fortran (build from source)
git clone https://github.com/vmagnin/gtk-fortran.git
cd gtk-fortran
cmake -B build
cmake --build build
sudo cmake --install build
# Install build tools
sudo dnf install gcc-gfortran meson ninja-build
GTK4 Inspector
What is it?
- Built-in GUI debugger for GTK apps
- Inspect widget hierarchy
- View CSS styles
- Monitor signals and events
- Performance profiling
How to Enable:
# Set environment variable
export GTK_DEBUG=interactive
# Run app (inspector opens automatically)
./sniffly
Features:
- Widget tree browser
- Property editor (live changes)
- CSS inspector
- Signal log
- Performance metrics
Testing Infrastructure
Unit Testing: FUnit
Why FUnit?
- Pure Fortran testing framework
- Simple assertion API
- Integrates with Meson
Sample Test:
module test_squarified
use squarified
use funit
implicit none
contains
@test
subroutine test_aspect_ratio()
real :: ratio
ratio = aspect_ratio(10, 10)
@assertEqual(1.0, ratio, tolerance=0.01)
ratio = aspect_ratio(20, 10)
@assertEqual(2.0, ratio, tolerance=0.01)
end subroutine
end module test_squarified
Integration Testing: Python
Why Python?
- Easy to script GTK interactions
- Screenshot comparison libraries
- Rich assertion libraries
Sample Test:
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib
import subprocess
import time
def test_launch_and_scan():
# Launch sniffly
proc = subprocess.Popen(['./sniffly'])
time.sleep(2)
# TODO: Automate UI interaction
# For now, manual testing with checklist
proc.terminate()
assert proc.returncode == 0 or proc.returncode is None
Continuous Integration
GitHub Actions Workflow
.github/workflows/build.yml:
name: Build and Test
on: [push, pull_request]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y libgtk-4-dev gfortran meson ninja-build
- name: Build gtk-fortran
run: |
git clone https://github.com/vmagnin/gtk-fortran.git
cd gtk-fortran
cmake -B build && cmake --build build
sudo cmake --install build
- name: Build sniffly
run: |
meson setup build
meson compile -C build
- name: Run tests
run: meson test -C build
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
brew install gtk4 gfortran meson ninja
- name: Build gtk-fortran
run: |
git clone https://github.com/vmagnin/gtk-fortran.git
cd gtk-fortran
cmake -B build && cmake --build build
sudo cmake --install build
- name: Build sniffly
run: |
meson setup build
meson compile -C build
- name: Run tests
run: meson test -C build
Alternative Stacks Considered
1. Rust + gtk-rs
Pros:
- Excellent GTK bindings
- Memory safety guarantees
- Growing ecosystem
Cons:
- Not Fortran (defeats the purpose!)
- Would need to rewrite all sniffert logic
- Steeper learning curve
Verdict: ❌ Wrong language
2. Fortran + QML (Qt Quick)
Pros:
- Modern declarative UI
- Hardware-accelerated
Cons:
- No Fortran bindings for QML
- Would need C++/Python bridge
- Qt is heavy dependency
Verdict: ❌ No Fortran support
3. Fortran + Web (Electron-style)
Pros:
- HTML/CSS for UI
- Easy prototyping
Cons:
- Huge resource usage
- Slow startup
- Not native look & feel
- Would need REST API layer
Verdict: ❌ Defeats performance goals
4. Fortran + Custom OpenGL
Pros:
- Maximum performance
- Full control
Cons:
- Reinvent wheel (buttons, text input, etc.)
- Months of UI infrastructure work
- Platform-specific window management
Verdict: ❌ Too much work
Summary: Why This Stack is Optimal
| Requirement | Solution | Rationale |
|---|---|---|
| Pure Fortran | gtk-fortran | Only mature Fortran GUI option |
| Cross-platform | GTK4 | Native on Linux, good on macOS |
| 2D rendering | Cairo | Built-in, hardware-accelerated |
| Text rendering | Pango | Unicode, fonts, ellipsization |
| Build system | Meson | GTK standard, dependency handling |
| Threading | GLib | Safe GTK integration |
| Performance | Native compilation | No interpreter overhead |
| Maintainability | Reuse sniffert | Proven algorithms |
Risk Mitigation
Risk: gtk-fortran bugs or limitations
Mitigation:
- Join gtk-fortran community early
- Contribute fixes upstream
- Fallback: C shim layer for problematic functions
Risk: GTK4 not looking native on macOS
Mitigation:
- GTK4 uses Cocoa backend (better than GTK3)
- Test early and often on macOS
- Accept "good enough" if perfect not achievable
Risk: Performance insufficient
Mitigation:
- Profile early (Week 4)
- Viewport culling for large trees
- Consider GSK (GTK's GPU backend) if needed
Risk: Build complexity scares users
Mitigation:
- Provide pre-built binaries
- Package for all major package managers
- Clear installation instructions
- CI/CD for automated builds
Conclusion
This stack provides:
- ✅ Pure Fortran (proving the point)
- ✅ Modern GUI (GTK4)
- ✅ Cross-platform (macOS, Linux)
- ✅ Performant (native code)
- ✅ Maintainable (reuse sniffert)
- ✅ Packageable (standard tools)
We're ready to build! 🚀
View source
| 1 | # Sniffly Technical Stack Deep Dive |
| 2 | |
| 3 | ## Core Stack Justification |
| 4 | |
| 5 | ### Why Pure Fortran? |
| 6 | |
| 7 | **Philosophy:** Prove that Fortran can build modern GUI applications that rival commercial software written in C++/C#. |
| 8 | |
| 9 | **Practical Advantages:** |
| 10 | 1. **Performance:** Native compilation, no VM/interpreter overhead |
| 11 | 2. **Memory Safety:** Strong typing, bounds checking (with compiler flags) |
| 12 | 3. **Existing Code:** Leverage proven algorithms from sniffert |
| 13 | 4. **Mathematical Operations:** Natural for size calculations, layout algorithms |
| 14 | 5. **Array Operations:** Efficient bulk operations on file trees |
| 15 | |
| 16 | **Challenges Addressed:** |
| 17 | - String handling: Use allocatable character arrays (Fortran 2003+) |
| 18 | - Pointers: Use Fortran's C interoperability for GTK callbacks |
| 19 | - OOP: Use Fortran 2008 type-bound procedures where beneficial |
| 20 | |
| 21 | --- |
| 22 | |
| 23 | ## GTK4 via gtk-fortran |
| 24 | |
| 25 | ### Why GTK4 (not Qt, wxWidgets, etc.)? |
| 26 | |
| 27 | **Technical Reasons:** |
| 28 | 1. **gtk-fortran exists:** Mature, maintained Fortran bindings |
| 29 | 2. **Native on Linux:** GTK is the de facto standard |
| 30 | 3. **macOS Support:** GTK4 uses native Cocoa backend |
| 31 | 4. **Modern:** GTK4 is actively developed, not legacy |
| 32 | 5. **Cairo Integration:** Built-in 2D drawing library |
| 33 | |
| 34 | **Comparison to Alternatives:** |
| 35 | |
| 36 | | Framework | Fortran Bindings | Native Look | Performance | Verdict | |
| 37 | |-----------|-----------------|-------------|-------------|---------| |
| 38 | | GTK4 | gtk-fortran | Yes (Linux/macOS) | Excellent | ✅ Chosen | |
| 39 | | Qt | None (would need C++ wrapper) | Yes | Excellent | ❌ No bindings | |
| 40 | | wxWidgets | None | Yes | Good | ❌ No bindings | |
| 41 | | FLTK | None | No | Excellent | ❌ No bindings, dated look | |
| 42 | | Tk | None (would need Tcl) | No | Poor | ❌ Dated, slow | |
| 43 | |
| 44 | **gtk-fortran Maturity:** |
| 45 | - First release: 2011 |
| 46 | - Current version: 3.24.41 (2024) |
| 47 | - Active maintainer: Vincent Magnin |
| 48 | - Production use: Scientific visualization tools |
| 49 | - Platform support: Linux, macOS, FreeBSD, Windows (MSYS2) |
| 50 | |
| 51 | ### GTK4 Architecture |
| 52 | |
| 53 | ``` |
| 54 | ┌─────────────────────────────────────────────────────────┐ |
| 55 | │ Your Fortran Code │ |
| 56 | │ (sniffly modules) │ |
| 57 | └─────────────────────────────────────────────────────────┘ |
| 58 | │ |
| 59 | ↓ (iso_c_binding) |
| 60 | ┌─────────────────────────────────────────────────────────┐ |
| 61 | │ gtk-fortran │ |
| 62 | │ (Fortran interfaces to GTK C functions) │ |
| 63 | └─────────────────────────────────────────────────────────┘ |
| 64 | │ |
| 65 | ↓ (FFI) |
| 66 | ┌─────────────────────────────────────────────────────────┐ |
| 67 | │ GTK4 C Library │ |
| 68 | │ ┌─────────────┬──────────────┬────────────────────┐ │ |
| 69 | │ │ Widgets │ GDK │ GSK │ │ |
| 70 | │ │ (buttons, │ (events, │ (GPU rendering) │ │ |
| 71 | │ │ windows) │ input) │ │ │ |
| 72 | │ └─────────────┴──────────────┴────────────────────┘ │ |
| 73 | └─────────────────────────────────────────────────────────┘ |
| 74 | │ |
| 75 | ↓ |
| 76 | ┌─────────────────────────────────────────────────────────┐ |
| 77 | │ Platform Backend │ |
| 78 | │ Linux: Wayland/X11 macOS: Cocoa BSD: X11 │ |
| 79 | └─────────────────────────────────────────────────────────┘ |
| 80 | ``` |
| 81 | |
| 82 | --- |
| 83 | |
| 84 | ## Cairo for 2D Graphics |
| 85 | |
| 86 | ### Why Cairo? |
| 87 | |
| 88 | **Technical Strengths:** |
| 89 | 1. **Vector Graphics:** Resolution-independent rendering |
| 90 | 2. **Anti-aliasing:** Beautiful, smooth edges |
| 91 | 3. **Hardware Acceleration:** GPU-backed on modern systems |
| 92 | 4. **Compositing:** Alpha blending, gradients, patterns |
| 93 | 5. **Text Rendering:** Pango integration for complex text |
| 94 | |
| 95 | **Cairo Features We'll Use:** |
| 96 | |
| 97 | | Feature | Usage in Sniffly | |
| 98 | |---------|-----------------| |
| 99 | | `cairo_rectangle()` | Draw treemap rectangles | |
| 100 | | `cairo_fill()` | Fill rectangles with color | |
| 101 | | `cairo_stroke()` | Draw borders | |
| 102 | | `cairo_set_source_rgb()` | Set solid colors | |
| 103 | | `cairo_pattern_create_linear()` | Cushion gradients | |
| 104 | | `cairo_clip()` | Clip nested rectangles | |
| 105 | | `cairo_save()`/`restore()` | State management | |
| 106 | | Pango integration | Render file names | |
| 107 | |
| 108 | **Performance Characteristics:** |
| 109 | - **Fast Paths:** Solid fills, axis-aligned rectangles |
| 110 | - **Slow Paths:** Bezier curves, complex paths (we won't use) |
| 111 | - **Caching:** Cairo surfaces can be cached |
| 112 | - **Expected Performance:** 60fps for 1000s of rectangles |
| 113 | |
| 114 | ### Cairo vs. OpenGL |
| 115 | |
| 116 | We chose Cairo over OpenGL because: |
| 117 | - **Simpler API:** 2D-focused, easier to learn |
| 118 | - **Better Integration:** Built into GTK4 |
| 119 | - **Text Rendering:** Pango support out of the box |
| 120 | - **Sufficient Performance:** 60fps achievable for our use case |
| 121 | |
| 122 | If performance becomes an issue (unlikely), we can: |
| 123 | 1. Use GTK4's GSK (GPU Scene Kit) backend |
| 124 | 2. Batch render calls |
| 125 | 3. Implement viewport culling (only draw visible nodes) |
| 126 | |
| 127 | --- |
| 128 | |
| 129 | ## Pango for Text |
| 130 | |
| 131 | ### Why Pango? |
| 132 | |
| 133 | **Features:** |
| 134 | 1. **Font Management:** System font detection, fallbacks |
| 135 | 2. **Text Layout:** Ellipsization, wrapping, alignment |
| 136 | 3. **Internationalization:** Full Unicode support, RTL text |
| 137 | 4. **Integration:** Native Cairo rendering |
| 138 | |
| 139 | **Pango Features We'll Use:** |
| 140 | |
| 141 | ```fortran |
| 142 | ! Create Pango layout |
| 143 | layout = pango_cairo_create_layout(cairo_context) |
| 144 | |
| 145 | ! Set text |
| 146 | call pango_layout_set_text(layout, c_str("filename.txt")) |
| 147 | |
| 148 | ! Set font |
| 149 | font_desc = pango_font_description_from_string(c_str("Sans 10")) |
| 150 | call pango_layout_set_font_description(layout, font_desc) |
| 151 | |
| 152 | ! Set ellipsization for long text |
| 153 | call pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END) |
| 154 | call pango_layout_set_width(layout, width * PANGO_SCALE) |
| 155 | |
| 156 | ! Render |
| 157 | call pango_cairo_show_layout(cairo_context, layout) |
| 158 | ``` |
| 159 | |
| 160 | --- |
| 161 | |
| 162 | ## GLib for Core Utilities |
| 163 | |
| 164 | ### Threading |
| 165 | |
| 166 | **GLib Threads** (not pthreads) for portability: |
| 167 | |
| 168 | ```fortran |
| 169 | ! Start background scan thread |
| 170 | thread = g_thread_new(c_str("scanner"), c_funloc(scan_thread_func), c_loc(scan_data)) |
| 171 | |
| 172 | ! Thread-safe UI updates from background thread |
| 173 | call g_idle_add(c_funloc(update_ui_callback), c_loc(progress_data)) |
| 174 | ``` |
| 175 | |
| 176 | **Why g_idle_add?** |
| 177 | - GTK is **not thread-safe** |
| 178 | - g_idle_add queues callback on main thread |
| 179 | - Callback runs in GTK event loop |
| 180 | - Safe to update UI from callback |
| 181 | |
| 182 | ### Event Loop |
| 183 | |
| 184 | GTK's main loop handles: |
| 185 | - User input (mouse, keyboard) |
| 186 | - Window events (resize, close) |
| 187 | - Timers and idle callbacks |
| 188 | - Background task callbacks (g_idle_add) |
| 189 | |
| 190 | ```fortran |
| 191 | ! Start GTK main loop (blocks until app quits) |
| 192 | call gtk_main() |
| 193 | ``` |
| 194 | |
| 195 | ### GLib Data Structures |
| 196 | |
| 197 | We'll primarily use Fortran's native arrays, but GLib provides: |
| 198 | - `GList`/`GSList`: Linked lists (if needed for dynamic UI) |
| 199 | - `GHashTable`: Hash maps (for quick file lookup) |
| 200 | - `GString`: Dynamic strings (if Fortran strings insufficient) |
| 201 | |
| 202 | --- |
| 203 | |
| 204 | ## Build System: Meson |
| 205 | |
| 206 | ### Why Meson (not Make, CMake, FPM)? |
| 207 | |
| 208 | **Meson Advantages:** |
| 209 | 1. **GTK Standard:** All GTK projects use Meson |
| 210 | 2. **Dependency Detection:** Auto-finds GTK, Cairo, etc. via pkg-config |
| 211 | 3. **Fast:** Ninja backend, parallel builds |
| 212 | 4. **Cross-Platform:** Works on Linux, macOS, BSD |
| 213 | 5. **Clean Syntax:** Python-like, easy to read |
| 214 | |
| 215 | **Meson vs. FPM:** |
| 216 | - FPM is great for pure Fortran projects |
| 217 | - FPM doesn't handle C library dependencies well |
| 218 | - We'll provide **both** Meson (primary) and FPM (fallback) builds |
| 219 | |
| 220 | **Sample meson.build:** |
| 221 | |
| 222 | ```meson |
| 223 | project('sniffly', 'fortran', |
| 224 | version: '1.0.0', |
| 225 | default_options: ['warning_level=3']) |
| 226 | |
| 227 | # Dependencies |
| 228 | gtk4_dep = dependency('gtk4') |
| 229 | cairo_dep = dependency('cairo') |
| 230 | pango_dep = dependency('pango') |
| 231 | |
| 232 | # Source files |
| 233 | src = [ |
| 234 | 'src/core/types.f90', |
| 235 | 'src/core/file_system.f90', |
| 236 | 'src/core/disk_scanner.f90', |
| 237 | 'src/layout/squarified.f90', |
| 238 | 'src/layout/cushioned.f90', |
| 239 | 'src/gui/gtk_app.f90', |
| 240 | 'src/rendering/cairo_renderer.f90', |
| 241 | 'app/main.f90', |
| 242 | ] |
| 243 | |
| 244 | # Executable |
| 245 | executable('sniffly', src, |
| 246 | dependencies: [gtk4_dep, cairo_dep, pango_dep], |
| 247 | install: true) |
| 248 | ``` |
| 249 | |
| 250 | **Building:** |
| 251 | ```bash |
| 252 | meson setup build |
| 253 | meson compile -C build |
| 254 | meson install -C build |
| 255 | ``` |
| 256 | |
| 257 | --- |
| 258 | |
| 259 | ## Development Tools |
| 260 | |
| 261 | ### Compiler |
| 262 | |
| 263 | **gfortran 9.0+** (GCC Fortran compiler) |
| 264 | |
| 265 | **Why gfortran?** |
| 266 | - Free and open source |
| 267 | - Excellent Fortran 2008 support |
| 268 | - C interoperability works well |
| 269 | - Available on all platforms |
| 270 | |
| 271 | **Compiler Flags:** |
| 272 | ```bash |
| 273 | # Debug build |
| 274 | -g -Wall -Wextra -fcheck=all -fbacktrace |
| 275 | |
| 276 | # Release build |
| 277 | -O3 -march=native -flto |
| 278 | ``` |
| 279 | |
| 280 | **Alternative:** Intel Fortran (ifx) also works but not required |
| 281 | |
| 282 | ### Debugger |
| 283 | |
| 284 | **GDB** (GNU Debugger) |
| 285 | ```bash |
| 286 | gdb ./build/sniffly |
| 287 | ``` |
| 288 | |
| 289 | **LLDB** (macOS default) |
| 290 | ```bash |
| 291 | lldb ./build/sniffly |
| 292 | ``` |
| 293 | |
| 294 | ### Profiling |
| 295 | |
| 296 | **Linux: perf** |
| 297 | ```bash |
| 298 | perf record ./sniffly /large/directory |
| 299 | perf report |
| 300 | ``` |
| 301 | |
| 302 | **macOS: Instruments** |
| 303 | ```bash |
| 304 | instruments -t "Time Profiler" ./sniffly /large/directory |
| 305 | ``` |
| 306 | |
| 307 | **gprof** (cross-platform) |
| 308 | ```bash |
| 309 | # Compile with -pg flag |
| 310 | gfortran -pg -o sniffly ... |
| 311 | ./sniffly /large/directory |
| 312 | gprof sniffly gmon.out |
| 313 | ``` |
| 314 | |
| 315 | ### Memory Analysis |
| 316 | |
| 317 | **valgrind** (Linux) |
| 318 | ```bash |
| 319 | valgrind --leak-check=full ./sniffly /tmp |
| 320 | ``` |
| 321 | |
| 322 | **AddressSanitizer** (both platforms) |
| 323 | ```bash |
| 324 | gfortran -fsanitize=address -o sniffly ... |
| 325 | ./sniffly /tmp |
| 326 | ``` |
| 327 | |
| 328 | --- |
| 329 | |
| 330 | ## Dependency Installation |
| 331 | |
| 332 | ### macOS (Homebrew) |
| 333 | |
| 334 | ```bash |
| 335 | # Install GTK4 and dependencies |
| 336 | brew install gtk4 cairo pango glib |
| 337 | |
| 338 | # Install gtk-fortran (build from source) |
| 339 | git clone https://github.com/vmagnin/gtk-fortran.git |
| 340 | cd gtk-fortran |
| 341 | cmake -B build |
| 342 | cmake --build build |
| 343 | sudo cmake --install build |
| 344 | |
| 345 | # Install build tools |
| 346 | brew install gfortran meson ninja |
| 347 | ``` |
| 348 | |
| 349 | ### Ubuntu/Debian |
| 350 | |
| 351 | ```bash |
| 352 | # Install GTK4 and dependencies |
| 353 | sudo apt install libgtk-4-dev libcairo2-dev libpango1.0-dev libglib2.0-dev |
| 354 | |
| 355 | # Install gtk-fortran (build from source) |
| 356 | git clone https://github.com/vmagnin/gtk-fortran.git |
| 357 | cd gtk-fortran |
| 358 | cmake -B build |
| 359 | cmake --build build |
| 360 | sudo cmake --install build |
| 361 | |
| 362 | # Install build tools |
| 363 | sudo apt install gfortran meson ninja-build |
| 364 | ``` |
| 365 | |
| 366 | ### Arch Linux |
| 367 | |
| 368 | ```bash |
| 369 | # Install GTK4 and dependencies |
| 370 | sudo pacman -S gtk4 cairo pango glib2 |
| 371 | |
| 372 | # gtk-fortran from AUR |
| 373 | yay -S gtk-fortran |
| 374 | |
| 375 | # Install build tools |
| 376 | sudo pacman -S gcc-fortran meson ninja |
| 377 | ``` |
| 378 | |
| 379 | ### Fedora/RHEL |
| 380 | |
| 381 | ```bash |
| 382 | # Install GTK4 and dependencies |
| 383 | sudo dnf install gtk4-devel cairo-devel pango-devel glib2-devel |
| 384 | |
| 385 | # Install gtk-fortran (build from source) |
| 386 | git clone https://github.com/vmagnin/gtk-fortran.git |
| 387 | cd gtk-fortran |
| 388 | cmake -B build |
| 389 | cmake --build build |
| 390 | sudo cmake --install build |
| 391 | |
| 392 | # Install build tools |
| 393 | sudo dnf install gcc-gfortran meson ninja-build |
| 394 | ``` |
| 395 | |
| 396 | --- |
| 397 | |
| 398 | ## GTK4 Inspector |
| 399 | |
| 400 | **What is it?** |
| 401 | - Built-in GUI debugger for GTK apps |
| 402 | - Inspect widget hierarchy |
| 403 | - View CSS styles |
| 404 | - Monitor signals and events |
| 405 | - Performance profiling |
| 406 | |
| 407 | **How to Enable:** |
| 408 | ```bash |
| 409 | # Set environment variable |
| 410 | export GTK_DEBUG=interactive |
| 411 | |
| 412 | # Run app (inspector opens automatically) |
| 413 | ./sniffly |
| 414 | ``` |
| 415 | |
| 416 | **Features:** |
| 417 | - Widget tree browser |
| 418 | - Property editor (live changes) |
| 419 | - CSS inspector |
| 420 | - Signal log |
| 421 | - Performance metrics |
| 422 | |
| 423 | --- |
| 424 | |
| 425 | ## Testing Infrastructure |
| 426 | |
| 427 | ### Unit Testing: FUnit |
| 428 | |
| 429 | **Why FUnit?** |
| 430 | - Pure Fortran testing framework |
| 431 | - Simple assertion API |
| 432 | - Integrates with Meson |
| 433 | |
| 434 | **Sample Test:** |
| 435 | ```fortran |
| 436 | module test_squarified |
| 437 | use squarified |
| 438 | use funit |
| 439 | implicit none |
| 440 | contains |
| 441 | |
| 442 | @test |
| 443 | subroutine test_aspect_ratio() |
| 444 | real :: ratio |
| 445 | |
| 446 | ratio = aspect_ratio(10, 10) |
| 447 | @assertEqual(1.0, ratio, tolerance=0.01) |
| 448 | |
| 449 | ratio = aspect_ratio(20, 10) |
| 450 | @assertEqual(2.0, ratio, tolerance=0.01) |
| 451 | end subroutine |
| 452 | |
| 453 | end module test_squarified |
| 454 | ``` |
| 455 | |
| 456 | ### Integration Testing: Python |
| 457 | |
| 458 | **Why Python?** |
| 459 | - Easy to script GTK interactions |
| 460 | - Screenshot comparison libraries |
| 461 | - Rich assertion libraries |
| 462 | |
| 463 | **Sample Test:** |
| 464 | ```python |
| 465 | import gi |
| 466 | gi.require_version('Gtk', '4.0') |
| 467 | from gi.repository import Gtk, GLib |
| 468 | import subprocess |
| 469 | import time |
| 470 | |
| 471 | def test_launch_and_scan(): |
| 472 | # Launch sniffly |
| 473 | proc = subprocess.Popen(['./sniffly']) |
| 474 | time.sleep(2) |
| 475 | |
| 476 | # TODO: Automate UI interaction |
| 477 | # For now, manual testing with checklist |
| 478 | |
| 479 | proc.terminate() |
| 480 | assert proc.returncode == 0 or proc.returncode is None |
| 481 | ``` |
| 482 | |
| 483 | --- |
| 484 | |
| 485 | ## Continuous Integration |
| 486 | |
| 487 | ### GitHub Actions Workflow |
| 488 | |
| 489 | **.github/workflows/build.yml:** |
| 490 | ```yaml |
| 491 | name: Build and Test |
| 492 | |
| 493 | on: [push, pull_request] |
| 494 | |
| 495 | jobs: |
| 496 | build-linux: |
| 497 | runs-on: ubuntu-latest |
| 498 | steps: |
| 499 | - uses: actions/checkout@v3 |
| 500 | - name: Install dependencies |
| 501 | run: | |
| 502 | sudo apt update |
| 503 | sudo apt install -y libgtk-4-dev gfortran meson ninja-build |
| 504 | - name: Build gtk-fortran |
| 505 | run: | |
| 506 | git clone https://github.com/vmagnin/gtk-fortran.git |
| 507 | cd gtk-fortran |
| 508 | cmake -B build && cmake --build build |
| 509 | sudo cmake --install build |
| 510 | - name: Build sniffly |
| 511 | run: | |
| 512 | meson setup build |
| 513 | meson compile -C build |
| 514 | - name: Run tests |
| 515 | run: meson test -C build |
| 516 | |
| 517 | build-macos: |
| 518 | runs-on: macos-latest |
| 519 | steps: |
| 520 | - uses: actions/checkout@v3 |
| 521 | - name: Install dependencies |
| 522 | run: | |
| 523 | brew install gtk4 gfortran meson ninja |
| 524 | - name: Build gtk-fortran |
| 525 | run: | |
| 526 | git clone https://github.com/vmagnin/gtk-fortran.git |
| 527 | cd gtk-fortran |
| 528 | cmake -B build && cmake --build build |
| 529 | sudo cmake --install build |
| 530 | - name: Build sniffly |
| 531 | run: | |
| 532 | meson setup build |
| 533 | meson compile -C build |
| 534 | - name: Run tests |
| 535 | run: meson test -C build |
| 536 | ``` |
| 537 | |
| 538 | --- |
| 539 | |
| 540 | ## Alternative Stacks Considered |
| 541 | |
| 542 | ### 1. Rust + gtk-rs |
| 543 | |
| 544 | **Pros:** |
| 545 | - Excellent GTK bindings |
| 546 | - Memory safety guarantees |
| 547 | - Growing ecosystem |
| 548 | |
| 549 | **Cons:** |
| 550 | - Not Fortran (defeats the purpose!) |
| 551 | - Would need to rewrite all sniffert logic |
| 552 | - Steeper learning curve |
| 553 | |
| 554 | **Verdict:** ❌ Wrong language |
| 555 | |
| 556 | ### 2. Fortran + QML (Qt Quick) |
| 557 | |
| 558 | **Pros:** |
| 559 | - Modern declarative UI |
| 560 | - Hardware-accelerated |
| 561 | |
| 562 | **Cons:** |
| 563 | - No Fortran bindings for QML |
| 564 | - Would need C++/Python bridge |
| 565 | - Qt is heavy dependency |
| 566 | |
| 567 | **Verdict:** ❌ No Fortran support |
| 568 | |
| 569 | ### 3. Fortran + Web (Electron-style) |
| 570 | |
| 571 | **Pros:** |
| 572 | - HTML/CSS for UI |
| 573 | - Easy prototyping |
| 574 | |
| 575 | **Cons:** |
| 576 | - Huge resource usage |
| 577 | - Slow startup |
| 578 | - Not native look & feel |
| 579 | - Would need REST API layer |
| 580 | |
| 581 | **Verdict:** ❌ Defeats performance goals |
| 582 | |
| 583 | ### 4. Fortran + Custom OpenGL |
| 584 | |
| 585 | **Pros:** |
| 586 | - Maximum performance |
| 587 | - Full control |
| 588 | |
| 589 | **Cons:** |
| 590 | - Reinvent wheel (buttons, text input, etc.) |
| 591 | - Months of UI infrastructure work |
| 592 | - Platform-specific window management |
| 593 | |
| 594 | **Verdict:** ❌ Too much work |
| 595 | |
| 596 | --- |
| 597 | |
| 598 | ## Summary: Why This Stack is Optimal |
| 599 | |
| 600 | | Requirement | Solution | Rationale | |
| 601 | |-------------|----------|-----------| |
| 602 | | Pure Fortran | gtk-fortran | Only mature Fortran GUI option | |
| 603 | | Cross-platform | GTK4 | Native on Linux, good on macOS | |
| 604 | | 2D rendering | Cairo | Built-in, hardware-accelerated | |
| 605 | | Text rendering | Pango | Unicode, fonts, ellipsization | |
| 606 | | Build system | Meson | GTK standard, dependency handling | |
| 607 | | Threading | GLib | Safe GTK integration | |
| 608 | | Performance | Native compilation | No interpreter overhead | |
| 609 | | Maintainability | Reuse sniffert | Proven algorithms | |
| 610 | |
| 611 | --- |
| 612 | |
| 613 | ## Risk Mitigation |
| 614 | |
| 615 | ### Risk: gtk-fortran bugs or limitations |
| 616 | |
| 617 | **Mitigation:** |
| 618 | - Join gtk-fortran community early |
| 619 | - Contribute fixes upstream |
| 620 | - Fallback: C shim layer for problematic functions |
| 621 | |
| 622 | ### Risk: GTK4 not looking native on macOS |
| 623 | |
| 624 | **Mitigation:** |
| 625 | - GTK4 uses Cocoa backend (better than GTK3) |
| 626 | - Test early and often on macOS |
| 627 | - Accept "good enough" if perfect not achievable |
| 628 | |
| 629 | ### Risk: Performance insufficient |
| 630 | |
| 631 | **Mitigation:** |
| 632 | - Profile early (Week 4) |
| 633 | - Viewport culling for large trees |
| 634 | - Consider GSK (GTK's GPU backend) if needed |
| 635 | |
| 636 | ### Risk: Build complexity scares users |
| 637 | |
| 638 | **Mitigation:** |
| 639 | - Provide pre-built binaries |
| 640 | - Package for all major package managers |
| 641 | - Clear installation instructions |
| 642 | - CI/CD for automated builds |
| 643 | |
| 644 | --- |
| 645 | |
| 646 | ## Conclusion |
| 647 | |
| 648 | This stack provides: |
| 649 | 1. ✅ **Pure Fortran** (proving the point) |
| 650 | 2. ✅ **Modern GUI** (GTK4) |
| 651 | 3. ✅ **Cross-platform** (macOS, Linux) |
| 652 | 4. ✅ **Performant** (native code) |
| 653 | 5. ✅ **Maintainable** (reuse sniffert) |
| 654 | 6. ✅ **Packageable** (standard tools) |
| 655 | |
| 656 | **We're ready to build!** 🚀 |