Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca-x11-viewer.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
42#ifndef CA_X11_VIEWER_H
43#define CA_X11_VIEWER_H
44
45#include <chrono>
46#include <concepts>
47#include <cstdlib>
48#include <string>
49#include <thread>
50#include <utility>
51
52// `ALEPH_DISABLE_X11_VIEWER` is set by the build system when the
53// `ALEPH_BUILD_X11_VIEWER` option is OFF (default on Windows and on any
54// CI runner that doesn't link `-lX11`). Without this gate, an Xlib.h
55// that happens to live on the include path — e.g. via XQuartz headers
56// shipped with Homebrew on macOS — would silently re-enable the X11
57// code path, producing undefined symbols at link time.
58#if !defined(ALEPH_DISABLE_X11_VIEWER) && defined(__has_include)
59# if __has_include(<X11/Xlib.h>)
60# include <X11/Xlib.h>
61# define ALEPH_CA_HAVE_X11 1
62# ifdef None
63# undef None
64# endif
65# endif
66#endif
67
68#include <ah-errors.H>
69
70#include <ca-io.H>
71#include <tpl_ca_concepts.H>
72
73namespace Aleph {
74namespace CA {
75
78{
79 unsigned cell_size = 8;
80 unsigned delay_ms = 25;
81 std::string title = "Aleph::CA";
82 bool enabled = true;
83 bool require_display = false;
84};
85
90template <typename Mapper>
92{
95 bool disabled_ = false;
96
97#if defined(ALEPH_CA_HAVE_X11)
98 Display *display_ = nullptr;
99 Window window_{};
100 GC gc_{};
101 bool ready_ = false;
102 ca_size_t rows_ = 0;
103 ca_size_t cols_ = 0;
104
106 {
107 if (display_ != nullptr)
108 {
109 if (ready_)
110 {
112 XDestroyWindow(display_, window_);
113 }
115 }
116 display_ = nullptr;
117 ready_ = false;
118 }
119
120 void ensure_window(const ca_size_t rows, const ca_size_t cols)
121 {
122 if (disabled_ or ready_)
123 return;
124 if (not opts_.enabled)
125 {
126 disabled_ = true;
127 return;
128 }
129 if (std::getenv("DISPLAY") == nullptr)
130 {
132 << "X11_Frame_Sink: DISPLAY is not set";
133 disabled_ = true;
134 return;
135 }
136 display_ = XOpenDisplay(nullptr);
137 if (display_ == nullptr)
138 {
140 << "X11_Frame_Sink: cannot open X display";
141 disabled_ = true;
142 return;
143 }
144 rows_ = rows;
145 cols_ = cols;
146 const int screen = DefaultScreen(display_);
147 const unsigned width = static_cast<unsigned>(cols * opts_.cell_size);
148 const unsigned height = static_cast<unsigned>(rows * opts_.cell_size);
151 0,
152 0,
153 width,
154 height,
155 1,
158 XStoreName(display_, window_, opts_.title.c_str());
159 XMapWindow(display_, window_);
160 gc_ = XCreateGC(display_, window_, 0, nullptr);
162 ready_ = true;
163 }
164
165 [[nodiscard]] unsigned long pixel(const RGB8 c) const noexcept
166 {
167 return (static_cast<unsigned long>(c.r) << 16)
168 | (static_cast<unsigned long>(c.g) << 8)
169 | static_cast<unsigned long>(c.b);
170 }
171#else
173 {
175 << "X11_Frame_Sink: X11 headers are not available";
176 disabled_ = true;
177 }
178#endif
179
180public:
186 : mapper_(std::move(mapper)), opts_(std::move(opts))
187 {}
188
191
196 : mapper_(std::move(other.mapper_))
197 , opts_(std::move(other.opts_))
198 , disabled_(other.disabled_)
199#if defined(ALEPH_CA_HAVE_X11)
200 , display_(other.display_)
201 , window_(other.window_)
202 , gc_(other.gc_)
203 , ready_(other.ready_)
204 , rows_(other.rows_)
205 , cols_(other.cols_)
206#endif
207 {
208#if defined(ALEPH_CA_HAVE_X11)
209 other.display_ = nullptr;
210 other.ready_ = false;
211#endif
212 other.disabled_ = true;
213 }
214
217 {
218#if defined(ALEPH_CA_HAVE_X11)
220#endif
221 }
222
227 {
228#if defined(ALEPH_CA_HAVE_X11)
229 return ready_;
230#else
231 return false;
232#endif
233 }
234
240 template <typename Lattice>
241 void accept(const std::size_t step, const Lattice &frame)
242 {
243 (void) step;
244 static_assert(LatticeLike<Lattice>, "X11_Frame_Sink requires LatticeLike frames");
245 static_assert(Lattice::rank == 2, "X11_Frame_Sink requires rank-2 frames");
246 using coord_t = typename Lattice::coord_type;
247
248 ensure_window(frame.size(0), frame.size(1));
249#if defined(ALEPH_CA_HAVE_X11)
250 if (not ready_)
251 return;
252 if (frame.size(0) != rows_ or frame.size(1) != cols_)
253 {
255 ensure_window(frame.size(0), frame.size(1));
256 }
257 for (ca_size_t r = 0; r < frame.size(0); ++r)
258 for (ca_size_t c = 0; c < frame.size(1); ++c)
259 {
260 const RGB8 colour = mapper_(frame.at(coord_t{static_cast<ca_index_t>(r),
261 static_cast<ca_index_t>(c)}));
264 window_,
265 gc_,
266 static_cast<int>(c * opts_.cell_size),
267 static_cast<int>(r * opts_.cell_size),
270 }
272 if (opts_.delay_ms != 0)
273 std::this_thread::sleep_for(std::chrono::milliseconds(opts_.delay_ms));
274#endif
275 }
276
278 void flush()
279 {
280#if defined(ALEPH_CA_HAVE_X11)
281 if (ready_)
283#endif
284 }
285};
286
287template <typename Mapper>
289
290} // namespace CA
291} // namespace Aleph
292
293#endif // CA_X11_VIEWER_H
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
Definition ah-errors.H:271
size_t * rows
Definition ca-c-api.h:112
size_t cols
Definition ca-c-api.h:105
File-format readers and writers for cellular-automata frames.
Lattice that adds boundary-aware access on top of a storage.
typename Storage::coord_type coord_type
static constexpr std::size_t rank
ca_size_t size() const noexcept
state_type at(const coord_type &c) const
Strict access: throws if c is out of range.
Live X11 frame sink.
void accept(const std::size_t step, const Lattice &frame)
Draw one frame, or no-op when disabled/headless.
~X11_Frame_Sink()
Close the X11 window best-effort.
X11_Frame_Sink(Mapper mapper, X11_Viewer_Options opts={})
Build an X11 viewer sink.
X11_Frame_Sink & operator=(const X11_Frame_Sink &)=delete
void ensure_window(ca_size_t, ca_size_t)
void flush()
Flush pending X11 requests.
X11_Viewer_Options opts_
X11_Frame_Sink(X11_Frame_Sink &&other) noexcept
Move-construct a viewer, transferring the window handle.
bool active() const noexcept
Return whether the viewer is actively drawing.
X11_Frame_Sink(const X11_Frame_Sink &)=delete
Storage + topology that carries the cell values.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
std::size_t ca_size_t
Unsigned size component used for extents and counts.
Definition ca-traits.H:63
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
STL namespace.
RGB byte triplet used by PPM exporters.
Definition ca-io.H:85
Options for the live X11 viewer.
bool enabled
disable to force no-op behaviour
unsigned delay_ms
sleep after each drawn frame
unsigned cell_size
pixels per CA cell
std::string title
window title
bool require_display
throw instead of no-op without X11
gsl_rng * r
C++20 concepts for the Cellular Automata module.