1 #ifndef NOTCURSES_DEMO
2 #define NOTCURSES_DEMO
3 
4 #include <time.h>
5 #include <assert.h>
6 #include <unistd.h>
7 #include <limits.h>
8 #include <stdatomic.h>
9 #include <notcurses/notcurses.h>
10 #include <builddef.h>
11 #include <version.h>
12 #include "compat/compat.h"
13 
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17 
18 // configured via command line option -- the base number of ns between demos
19 extern struct timespec demodelay;
20 extern float delaymultiplier; // scales demodelay (applied internally)
21 
22 // checked in demo_render() and between demos
23 extern atomic_bool interrupted;
24 
25 // heap-allocated, caller must free. locates data files per command line args.
26 char* find_data(const char* datum);
27 
28 int animate_demo(struct notcurses* nc, uint64_t startns);
29 int uniblock_demo(struct notcurses* nc, uint64_t startns);
30 int whiteout_demo(struct notcurses* nc, uint64_t startns);
31 int box_demo(struct notcurses* nc, uint64_t startns);
32 int trans_demo(struct notcurses* nc, uint64_t startns);
33 int chunli_demo(struct notcurses* nc, uint64_t startns);
34 int dragon_demo(struct notcurses* nc, uint64_t startns);
35 int qrcode_demo(struct notcurses* nc, uint64_t startns);
36 int grid_demo(struct notcurses* nc, uint64_t startns);
37 int fission_demo(struct notcurses* nc, uint64_t startns);
38 int highcon_demo(struct notcurses* nc, uint64_t startns);
39 int jungle_demo(struct notcurses* nc, uint64_t startns);
40 int yield_demo(struct notcurses* nc, uint64_t startns);
41 int mojibake_demo(struct notcurses* nc, uint64_t startns);
42 int normal_demo(struct notcurses* nc, uint64_t startns);
43 int sliders_demo(struct notcurses* nc, uint64_t startns);
44 int view_demo(struct notcurses* nc, uint64_t startns);
45 int eagle_demo(struct notcurses* nc, uint64_t startns);
46 int reel_demo(struct notcurses* nc, uint64_t startns);
47 int xray_demo(struct notcurses* nc, uint64_t startns);
48 int keller_demo(struct notcurses* nc, uint64_t startns);
49 int luigi_demo(struct notcurses* nc, uint64_t startns);
50 int zoo_demo(struct notcurses* nc, uint64_t startns);
51 int intro_demo(struct notcurses* nc, uint64_t startns);
52 int outro_demo(struct notcurses* nc, uint64_t startns);
53 
54 /*------------------------------- demo input API --------------------------*/
55 int input_dispatcher(struct notcurses* nc);
56 int stop_input(void);
57 
58 // if 'q' is pressed at any time during the demo, gracefully interrupt/exit
59 void interrupt_demo(void);
60 void interrupt_and_restart_demos(void);
61 
62 // demos should not call notcurses_getc() directly, as it's being monitored by
63 // the toplevel event listener. instead, call this intermediate API. just
64 // replace 'notcurses' with 'demo'.
65 uint32_t demo_getc(struct notcurses* nc, const struct timespec* ts, ncinput* ni);
66 
67 // 'ni' may be NULL if the caller is uninterested in event details. If no event
68 // is ready, returns 0.
69 static inline uint32_t
demo_getc_nblock(struct notcurses * nc,ncinput * ni)70 demo_getc_nblock(struct notcurses* nc, ncinput* ni){
71   struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
72   return demo_getc(nc, &ts, ni);
73 }
74 
75 // Get a fd which can be poll()ed to check for outstanding input. Do not close
76 // this file descriptor.
77 int demo_input_fd(void);
78 
79 // 'ni' may be NULL if the caller is uninterested in event details. Blocks
80 // until an event is processed or a signal is received.
81 static inline uint32_t
demo_getc_blocking(struct notcurses * nc,ncinput * ni)82 demo_getc_blocking(struct notcurses* nc, ncinput* ni){
83   return demo_getc(nc, NULL, ni);
84 }
85 /*----------------------------- end demo input API -------------------------*/
86 
87 /*-------------------------------time helpers----------------------------*/
88 #define MAXSLEEP (NANOSECS_IN_SEC / 80) // nanoseconds
89 
90 static inline int64_t
timespec_subtract_ns(const struct timespec * time1,const struct timespec * time0)91 timespec_subtract_ns(const struct timespec* time1, const struct timespec* time0){
92   int64_t ns = timespec_to_ns(time1);
93   ns -= timespec_to_ns(time0);
94   return ns;
95 }
96 
97 static inline int
timespec_subtract(struct timespec * result,const struct timespec * time0,struct timespec * time1)98 timespec_subtract(struct timespec *result, const struct timespec *time0,
99                   struct timespec *time1){
100   ns_to_timespec(timespec_subtract_ns(time0, time1), result);
101   return timespec_to_ns(time0) < timespec_to_ns(time1);
102 }
103 
104 static inline uint64_t
timespec_add(struct timespec * result,const struct timespec * time0,struct timespec * time1)105 timespec_add(struct timespec *result, const struct timespec *time0,
106              struct timespec *time1){
107   uint64_t ns = timespec_to_ns(time0) + timespec_to_ns(time1);
108   ns_to_timespec(ns, result);
109   return ns;
110 }
111 
112 // divide the provided timespec 'ts' by 'divisor' into 'quots'
113 static inline void
timespec_div(const struct timespec * ts,unsigned divisor,struct timespec * quots)114 timespec_div(const struct timespec* ts, unsigned divisor, struct timespec* quots){
115   uint64_t ns = timespec_to_ns(ts);
116   ns /= divisor;
117   quots->tv_nsec = ns % NANOSECS_IN_SEC;
118   quots->tv_sec = ns / NANOSECS_IN_SEC;
119 }
120 
121 // divide the provided timespec 'ts' by 'multiplier' into 'product'
122 static inline void
timespec_mul(const struct timespec * ts,unsigned multiplier,struct timespec * product)123 timespec_mul(const struct timespec* ts, unsigned multiplier, struct timespec* product){
124   uint64_t ns = timespec_to_ns(ts);
125   ns *= multiplier;
126   product->tv_nsec = ns % NANOSECS_IN_SEC;
127   product->tv_sec = ns / NANOSECS_IN_SEC;
128 }
129 /*-------------------------------time helpers----------------------------*/
130 
131 /*---------------------------------FPS plot-------------------------------*/
132 int fpsgraph_init(struct notcurses* nc);
133 int fpsgraph_stop(void);
134 int fpsplot_grab(int y);
135 int fpsplot_release(void);
136 /*----------------------------------HUD----------------------------------*/
137 extern struct ncplane* hud;
138 struct ncplane* hud_create(struct notcurses* nc);
139 struct ncmenu* menu_create(struct notcurses* nc);
140 int hud_destroy(void);
141 
142 // let the HUD know about an upcoming demo
143 int hud_schedule(const char* demoname, uint64_t startns);
144 
145 // demos should not call notcurses_render() themselves, but instead call
146 // demo_render(), which will ensure the HUD stays on the top of the z-stack.
147 // returns -1 on error, 1 if the demo has been aborted, and 0 on success.
148 // this result ought be propagated out so that the demo is reported as having
149 // been aborted, rather than having failed.
150 int demo_render(struct notcurses* nc);
151 
152 #define DEMO_RENDER(nc) { int demo_render_err = demo_render(nc); if(demo_render_err){ return demo_render_err; }}
153 
154 // if you won't be doing things, and it's a long sleep, consider using
155 // demo_nanosleep(). it updates the HUD, which looks better to the user, and
156 // dispatches input to the menu/HUD. don't use it if you have other threads
157 // rendering or otherwise manipulating state, as it calls notcurses_render().
158 int demo_nanosleep(struct notcurses* nc, const struct timespec *ts);
159 
160 // the same as demo_nanosleep, but using TIMER_ABSTIME (deadline, not interval)
161 int demo_nanosleep_abstime(struct notcurses* nc, const struct timespec* ts);
162 
163 static inline int
demo_simple_streamer(struct ncvisual * ncv,struct ncvisual_options * vopts,const struct timespec * tspec,void * curry)164 demo_simple_streamer(struct ncvisual* ncv __attribute__ ((unused)),
165                      struct ncvisual_options* vopts,
166                      const struct timespec* tspec, void* curry __attribute__ ((unused))){
167   DEMO_RENDER(ncplane_notcurses(vopts->n));
168   return demo_nanosleep_abstime(ncplane_notcurses(vopts->n), tspec);
169 }
170 
171 // simple fadecb that makes proper use of demo_render() and demo_nanosleep()
172 static inline int
demo_fader(struct notcurses * nc,struct ncplane * ncp,const struct timespec * abstime,void * curry)173 demo_fader(struct notcurses* nc, struct ncplane* ncp __attribute__ ((unused)),
174            const struct timespec* abstime, void* curry __attribute__ ((unused))){
175   DEMO_RENDER(nc);
176   return demo_nanosleep_abstime(nc, abstime);
177 }
178 
179 
180 // grab the hud with the mouse
181 int hud_grab(int y, int x);
182 
183 // release the hud
184 int hud_release(void);
185 
186 typedef struct demoresult {
187   char selector;
188   struct ncstats stats;
189   uint64_t timens;
190   int result; // positive == aborted, negative == failed
191 } demoresult;
192 
193 // let the HUD know that a demo has completed, reporting the stats
194 int hud_completion_notify(const demoresult* result);
195 
196 // HUD retrieves results on demand from core
197 const demoresult* demoresult_lookup(int idx);
198 /*----------------------------------HUD----------------------------------*/
199 
200 // destroy about popup
201 void about_destroy(struct notcurses* nc);
202 
203 // returns true if the input was handled by the menu/HUD
204 bool menu_or_hud_key(struct notcurses *nc, const struct ncinput *ni);
205 
206 // returns 2 if we've successfully passed the deadline, 1 if we've been aborted
207 // (as returned by demo_render(), 0 if we ought keep going, or -1 if there was
208 // an error. in general, propagate out -1 or 1, keep going on 2, and don't
209 // expect to ever see 0.
210 static inline int
pulser(struct notcurses * nc,struct ncplane * ncp,const struct timespec * ts,void * curry)211 pulser(struct notcurses* nc, struct ncplane* ncp __attribute__ ((unused)),
212        const struct timespec* ts __attribute__ ((unused)), void* curry){
213   struct timespec* start = curry;
214   struct timespec now;
215   clock_gettime(CLOCK_MONOTONIC, &now);
216   if(timespec_to_ns(&now) - timespec_to_ns(start) >= timespec_to_ns(&demodelay)){
217     return 2;
218   }
219   return demo_render(nc);
220 }
221 
222 #ifdef __cplusplus
223 }
224 #endif
225 
226 #endif
227