summaryrefslogtreecommitdiff
path: root/docs/tech-stack.md
blob: f8b54b30d8a701403aef98876db04cbecfd6dd59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# Tech Stack

## Core

| Concern        | Choice                                    | Notes                                   |
|----------------|-------------------------------------------|-----------------------------------------|
| Language       | C (C11)                                   | `-Wall -Wextra`, no glib-style boilerplate beyond what GTK needs |
| UI toolkit    | GTK4                                      | custom viewer widget; `GtkDropTarget` for file/folder drops |
| Native look   | libadwaita (decided)                     | GNOME styling; see decision #29 |
| Object/async  | GLib / GObject / GTask                    | bundled with GTK                         |
| Config        | GSettings + Gio                           | schema `org.buetow.ggaze` *(placeholder)* |
| Build          | Meson + Ninja                             | GNOME/Fedora standard                    |
| Packaging     | RPM (Fedora), optional Flatpak            | AppStream metainfo                       |
| Conventions   | c-best-practices skill                   | see [coding-conventions.md](coding-conventions.md) |
| Image proc.  | GEGL + babl *(optional, feature-gated)* | quick enhance, color mgmt, export copy; see [gegl.md](gegl.md) |
| Clipboard    | GdkClipboard + GdkContentProvider        | `image/png` (pixels) and `text/uri-list` (files) for `Ctrl+c` |
://| Folder watch | GFileMonitor (GIO)                       | live auto-refresh of the current dir (external adds/deletes) |
| Testing      | `meson test` + gcov/lcov                  | ≥80% on plain-C modules; see Testing below |
| Quality audit | `auditing-code-quality` skill            | at milestones (C: c-best-practices + find-code-bugs + SOLID + beyond-SOLID) |

## Image decode

Tiered: a sniffed-format dispatcher selects a backend; GdkPixbuf is the
fallback for anything common it already supports.

- **GdkPixbuf** (fallback) — PNG, JPEG, GIF (animated via `GdkPixbufAnimation`),
  WebP, TIFF, ICO.
- **libjpeg-turbo** (optional, direct) — faster JPEG + progressive first-scan
  low-res preview.
- **libjxl** — JPEG XL.
- **libavif** — AVIF.
- **libheif** — HEIF / HEIC (and AVIF via libheif if libavif absent).
- **libpng** — only if GdkPixbuf path is insufficient (unlikely first cut).
- **GEGL loaders** (optional, if GEGL enabled) — `gegl:jpg/png/tiff/webp/…-load`
  can augment the enhance/export path and bring ICC-aware decode; JXL/AVIF/HEIF
  still need their own libs.
- **EXIF orientation** — every backend honors the EXIF Orientation tag so the
  decoded texture is upright (GdkPixbuf: `gdk_pixbuf_apply_embedded_orientation`;
  others read the tag via libexif and rotate/flip). Manual rotate/straighten
  compose on top; `s` export resets the tag to "normal" (1) to avoid
  double-rotation in other apps.

Each backend behind a `GgazeLoaderBackend` struct:
`gboolean (*can_load)(const guint8 *head, gsize len);`
`GdkTexture *(*load)(GFile *file, GCancellable *, GError **);`
Backends compiled in conditionally via meson `feature` options so a minimal
build (GdkPixbuf only) is possible.

## Progressive preview (low-res first)

For large/slow images, show a quick low-res or progressive scan before the full
frame is decoded. Concretely: libjpeg-turbo can yield a downscaled scan after
reading only the header + a few MCU rows; libjxl supports partial decode. The
loader API should allow streaming a low-res `GdkTexture` first, then replacing
it. Treat as a later milestone, but design the loader signature for it now.

## Thumbnail cache

- freedesktop Thumbnail Managing Standard: `~/.cache/thumbnails/normal`
  (128×128) and `large` (256×256); shared with other compliant apps.
- Store PNG with `Thumb::URI`, `Thumb::MTime`, `Thumb::Size` keys.
- Verify mtime before trust; re-decode on mismatch.

## Settings keys (GSettings schema `org.buetow.ggaze`)

- `sort`            — enum: name / capture-time / size
- `wrap`            — bool: wrap at folder ends
- `background`      — enum: black / dark / grey / checker
- `scroll-behavior` — enum: zoom / pan-when-zoomed / navigate
- `slideshow-delay` — double (seconds)
- `thumbnail-size` — int: grid thumbnail pixel size (resizable via `+`/`-`)
- `hide-trashed`    — bool
- `window-geometry` — `(iiib)` width, height, maximized; persisted, restored on launch
- `destinations`    — `a(ss)`: ordered array of `(name, path)` pairs for the
  `m` move popup. List order determines auto-assigned hotkeys (`1`, `2`, …).
  Edited via the Preferences dialog (`,`) or `gsettings`.
- `editors`          — `a(ss)`: ordered array of `(name, command)` pairs for
  the `e` open-in-external popup. Commands use freedesktop `Exec` placeholders
  (`%f` = current file path); e.g. `gimp %f`, `identify %f`. Launched detached
  via `GSubprocess` (GLib), so ggaze stays responsive.
- `scripts`          — `a(ss)`: ordered array of `(name, command)` pairs for
  the `!` run-script popup. Run **asynchronously** through `/bin/sh -c`
  (pipes/redirection/`~/` work). Placeholders: `%f` = current image path,
  `%d` = current folder (paths single-quoted against injection). On exit,
  ggaze rescans the directory.
- `enhance-presets` — `a(ss)`: ordered array of `(name, gegl-graph)` pairs
  for the `a` enhance popup (GEGL only). Order = auto-assigned hotkey order.
  Ships with built-in defaults.

## External deps (Fedora package names, approximate)

```
gtk4-devel   glib2-devel   libadwaita-devel (?)
gdk-pixbuf2-devel
libjpeg-turbo-devel   libjxl-devel   libavif-devel   libheif-devel
gegl-devel   babl-devel   # optional, feature-gated
meson   gcc
```

## Testing

- Unit tests for the plain-C modules — `navigator`, `detect`, `thumbnail`,
  `mover`, `opener`, `runner`, `enhancer`, `trash`, `settings` (no GTK/display
  needed); run with `meson test`.
- **Coverage target ≥80%** on those modules, measured with `gcov`/`lcov`
  (`meson setup -Db_coverage=true && meson test && ninja -C build coverage`).
  A coverage gate in CI rejects drops below 80%.
- GTK-side: `gtk-test`-style smoke tests where useful; mostly manual.
- A small `tests/fixtures/` image set covering each supported format.