source: mainline/uspace/app/barber/barber.c

Last change on this file was bdf06ad, checked in by Jiri Svoboda <jiri@…>, 15 months ago

Fix ccheck.

  • Property mode set to 100644
File size: 10.0 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * Copyright (c) 2014 Martin Decky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup barber
31 * @{
32 */
33/** @file
34 */
35
36#include <device/led_dev.h>
37#include <errno.h>
38#include <fibril_synch.h>
39#include <gfximage/tga_gz.h>
40#include <io/pixel.h>
41#include <loc.h>
42#include <stats.h>
43#include <stdbool.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <str.h>
47#include <ui/ui.h>
48#include <ui/wdecor.h>
49#include <ui/window.h>
50#include <ui/image.h>
51#include "images.h"
52#include "images_tiny.h"
53
54#define NAME "barber"
55
56#define FRAMES IMAGES
57
58#define MIN_FPS 1
59#define MAX_FPS 25
60
61#define MIN_LOAD (LOAD_UNIT / 4)
62#define MAX_LOAD (LOAD_UNIT / 3)
63
64#define LED_PERIOD 1000000
65#define LED_COLORS 7
66
67typedef struct {
68 link_t link;
69 service_id_t svc_id;
70 async_sess_t *sess;
71} led_dev_t;
72
73typedef struct {
74 ui_t *ui;
75} barber_t;
76
77static fibril_timer_t *led_timer = NULL;
78static list_t led_devs;
79static unsigned int led_color = 0;
80
81static pixel_t led_colors[LED_COLORS] = {
82 PIXEL(0xff, 0xff, 0x00, 0x00),
83 PIXEL(0xff, 0x00, 0xff, 0x00),
84 PIXEL(0xff, 0x00, 0x00, 0xff),
85 PIXEL(0xff, 0xff, 0xff, 0x00),
86 PIXEL(0xff, 0xff, 0x00, 0xff),
87 PIXEL(0xff, 0x00, 0xff, 0xff),
88 PIXEL(0xff, 0xff, 0xff, 0xff)
89};
90
91static fibril_timer_t *frame_timer = NULL;
92static ui_image_t *frame_img;
93static gfx_bitmap_t *frame_bmp[FRAMES];
94
95static unsigned int frame = 0;
96static unsigned int fps = MIN_FPS;
97static gfx_coord_t frame_width;
98static gfx_coord_t frame_height;
99
100static void led_timer_callback(void *);
101static void frame_timer_callback(void *);
102
103static void wnd_close(ui_window_t *, void *);
104static void wnd_kbd_event(ui_window_t *, void *, kbd_event_t *);
105
106static ui_window_cb_t window_cb = {
107 .close = wnd_close,
108 .kbd = wnd_kbd_event
109};
110
111/** Window close button was clicked.
112 *
113 * @param window Window
114 * @param arg Argument (barber)
115 */
116static void wnd_close(ui_window_t *window, void *arg)
117{
118 barber_t *barber = (barber_t *) arg;
119
120 ui_quit(barber->ui);
121}
122
123/** Barber unmodified key press.
124 *
125 * @param barber Barber
126 * @param event Keyboard event
127 */
128static void barber_kbd_event_unmod(barber_t *barber, kbd_event_t *event)
129{
130 if (event->key == KC_ESCAPE)
131 ui_quit(barber->ui);
132}
133
134/** Barber ctrl-key key press.
135 *
136 * @param barber Barber
137 * @param event Keyboard event
138 */
139static void barber_kbd_event_ctrl(barber_t *barber, kbd_event_t *event)
140{
141 if (event->key == KC_Q)
142 ui_quit(barber->ui);
143}
144
145/** Barber window keyboard event.
146 *
147 * @param window UI window
148 * @param arg Argument (barber_t *)
149 * @param event Keyboard event
150 */
151static void wnd_kbd_event(ui_window_t *window, void *arg, kbd_event_t *event)
152{
153 barber_t *barber = (barber_t *)arg;
154
155 if (event->type != KEY_PRESS)
156 return;
157
158 if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
159 barber_kbd_event_unmod(barber, event);
160
161 if ((event->mods & KM_CTRL) != 0 &&
162 (event->mods & (KM_ALT | KM_SHIFT)) == 0)
163 barber_kbd_event_ctrl(barber, event);
164
165 ui_window_def_kbd(window, event);
166}
167
168static bool decode_frames(gfx_context_t *gc, image_t *img)
169{
170 gfx_rect_t rect;
171 errno_t rc;
172
173 for (unsigned int i = 0; i < FRAMES; i++) {
174 rc = decode_tga_gz(gc, img[i].addr, img[i].size,
175 &frame_bmp[i], &rect);
176 if (rc != EOK) {
177 printf("Unable to decode frame %u.\n", i);
178 return false;
179 }
180
181 (void)rect;
182 }
183
184 return true;
185}
186
187static void destroy_frames(void)
188{
189 unsigned i;
190
191 for (i = 0; i < FRAMES; i++) {
192 gfx_bitmap_destroy(frame_bmp[i]);
193 frame_bmp[i] = NULL;
194 }
195}
196
197static void plan_led_timer(void)
198{
199 fibril_timer_set(led_timer, LED_PERIOD, led_timer_callback, NULL);
200}
201
202static load_t get_load(void)
203{
204 size_t count;
205 load_t *load = stats_get_load(&count);
206 load_t load_val;
207
208 if ((load != NULL) && (count > 0)) {
209 load_val = load[0];
210 free(load);
211 } else
212 load_val = 0;
213
214 return load_val;
215}
216
217static void plan_frame_timer(usec_t render_time)
218{
219 /*
220 * Crank up the FPS unless we lack
221 * behind with the rendering and
222 * unless the load is not above
223 * a lower threshold.
224 */
225
226 usec_t delta = 1000000 / fps;
227 load_t load = get_load();
228
229 if ((delta >= render_time) && (load < MIN_LOAD))
230 fps++;
231
232 if (fps > MAX_FPS)
233 fps = MAX_FPS;
234
235 /*
236 * If we lack behind then immediately
237 * go to the lowest FPS.
238 */
239
240 if (delta < render_time)
241 fps = MIN_FPS;
242
243 /*
244 * Crank down the FPS if the current
245 * load is above an upper threshold.
246 */
247
248 if (load > MAX_LOAD)
249 fps--;
250
251 if (fps < MIN_FPS)
252 fps = MIN_FPS;
253
254 delta = 1000000 / fps;
255
256 fibril_timer_set(frame_timer, delta, frame_timer_callback, NULL);
257}
258
259static void led_timer_callback(void *data)
260{
261 pixel_t next_led_color = led_colors[led_color];
262
263 led_color++;
264 if (led_color >= LED_COLORS)
265 led_color = 0;
266
267 list_foreach(led_devs, link, led_dev_t, dev) {
268 if (dev->sess)
269 led_dev_color_set(dev->sess, next_led_color);
270 }
271
272 plan_led_timer();
273}
274
275static void frame_timer_callback(void *data)
276{
277 struct timespec prev;
278 gfx_rect_t rect;
279 getuptime(&prev);
280
281 frame++;
282 if (frame >= FRAMES)
283 frame = 0;
284
285 rect.p0.x = 0;
286 rect.p0.y = 0;
287 rect.p1.x = frame_width;
288 rect.p1.y = frame_height;
289
290 ui_image_set_bmp(frame_img, frame_bmp[frame], &rect);
291 (void) ui_image_paint(frame_img);
292
293 struct timespec cur;
294 getuptime(&cur);
295
296 plan_frame_timer(NSEC2USEC(ts_sub_diff(&cur, &prev)));
297}
298
299static void loc_callback(void *arg)
300{
301 category_id_t led_cat;
302 errno_t rc = loc_category_get_id("led", &led_cat, IPC_FLAG_BLOCKING);
303 if (rc != EOK)
304 return;
305
306 service_id_t *svcs;
307 size_t count;
308 rc = loc_category_get_svcs(led_cat, &svcs, &count);
309 if (rc != EOK)
310 return;
311
312 for (size_t i = 0; i < count; i++) {
313 bool known = false;
314
315 /* Determine whether we already know this device. */
316 list_foreach(led_devs, link, led_dev_t, dev) {
317 if (dev->svc_id == svcs[i]) {
318 known = true;
319 break;
320 }
321 }
322
323 if (!known) {
324 led_dev_t *dev = (led_dev_t *) calloc(1, sizeof(led_dev_t));
325 if (!dev)
326 continue;
327
328 link_initialize(&dev->link);
329 dev->svc_id = svcs[i];
330 dev->sess = loc_service_connect(svcs[i], INTERFACE_DDF, 0);
331
332 list_append(&dev->link, &led_devs);
333 }
334 }
335
336 // FIXME: Handle LED device removal
337
338 free(svcs);
339}
340
341static void print_syntax(void)
342{
343 printf("Syntax: %s [-d <display>]\n", NAME);
344}
345
346int main(int argc, char *argv[])
347{
348 const char *display_spec = UI_ANY_DEFAULT;
349 barber_t barber;
350 ui_t *ui;
351 ui_wnd_params_t params;
352 ui_window_t *window;
353 ui_resource_t *ui_res;
354 gfx_rect_t rect;
355 gfx_rect_t wrect;
356 gfx_rect_t app_rect;
357 gfx_context_t *gc;
358 gfx_coord2_t off;
359 image_t *img;
360 int i;
361
362 i = 1;
363 while (i < argc) {
364 if (str_cmp(argv[i], "-d") == 0) {
365 ++i;
366 if (i >= argc) {
367 printf("Argument missing.\n");
368 print_syntax();
369 return 1;
370 }
371
372 display_spec = argv[i++];
373 } else {
374 printf("Invalid option '%s'.\n", argv[i]);
375 print_syntax();
376 return 1;
377 }
378 }
379
380 list_initialize(&led_devs);
381 errno_t rc = loc_register_cat_change_cb(loc_callback, NULL);
382 if (rc != EOK) {
383 printf("Unable to register callback for device discovery.\n");
384 return 1;
385 }
386
387 led_timer = fibril_timer_create(NULL);
388 if (!led_timer) {
389 printf("Unable to create LED timer.\n");
390 return 1;
391 }
392
393 frame_timer = fibril_timer_create(NULL);
394 if (!frame_timer) {
395 printf("Unable to create frame timer.\n");
396 return 1;
397 }
398
399 rc = ui_create(display_spec, &ui);
400 if (rc != EOK) {
401 printf("Error creating UI on display %s.\n", display_spec);
402 return 1;
403 }
404
405 if (ui_is_textmode(ui)) {
406 frame_width = 10;
407 frame_height = 16;
408 } else {
409 frame_width = 59;
410 frame_height = 192;
411 }
412
413 rect.p0.x = 0;
414 rect.p0.y = 0;
415 rect.p1.x = frame_width;
416 rect.p1.y = frame_height;
417
418 ui_wnd_params_init(&params);
419 params.caption = "Barber Pole";
420 params.placement = ui_wnd_place_bottom_right;
421 /*
422 * Compute window rectangle such that application area corresponds
423 * to rect
424 */
425 ui_wdecor_rect_from_app(ui, params.style, &rect, &wrect);
426 off = wrect.p0;
427 gfx_rect_rtranslate(&off, &wrect, &params.rect);
428
429 barber.ui = ui;
430
431 rc = ui_window_create(ui, &params, &window);
432 if (rc != EOK) {
433 printf("Error creating window.\n");
434 return 1;
435 }
436
437 ui_res = ui_window_get_res(window);
438 gc = ui_window_get_gc(window);
439 ui_window_get_app_rect(window, &app_rect);
440 ui_window_set_cb(window, &window_cb, (void *) &barber);
441
442 img = ui_is_textmode(ui) ? image_tinys : images;
443
444 if (!decode_frames(gc, img))
445 return 1;
446
447 rc = ui_image_create(ui_res, frame_bmp[frame], &rect,
448 &frame_img);
449 if (rc != EOK) {
450 printf("Error creating UI.\n");
451 return 1;
452 }
453
454 ui_image_set_rect(frame_img, &app_rect);
455
456 ui_window_add(window, ui_image_ctl(frame_img));
457
458 rc = ui_window_paint(window);
459 if (rc != EOK) {
460 printf("Error painting window.\n");
461 return 1;
462 }
463
464 plan_led_timer();
465 plan_frame_timer(0);
466
467 ui_run(ui);
468
469 /* Unlink bitmap from image so it is not destroyed along with it */
470 ui_image_set_bmp(frame_img, NULL, &rect);
471
472 destroy_frames();
473
474 ui_window_destroy(window);
475 ui_destroy(ui);
476
477 return 0;
478}
479
480/** @}
481 */
Note: See TracBrowser for help on using the repository browser.