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
RevLine 
[a047aaa]1/*
[e229148]2 * Copyright (c) 2024 Jiri Svoboda
[a047aaa]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
[12008adf]36#include <device/led_dev.h>
[a047aaa]37#include <errno.h>
[12008adf]38#include <fibril_synch.h>
[0576df9]39#include <gfximage/tga_gz.h>
[12008adf]40#include <io/pixel.h>
[a047aaa]41#include <loc.h>
[9696b01]42#include <stats.h>
[12008adf]43#include <stdbool.h>
44#include <stdio.h>
45#include <stdlib.h>
[fd11144]46#include <str.h>
[12008adf]47#include <ui/ui.h>
48#include <ui/wdecor.h>
49#include <ui/window.h>
50#include <ui/image.h>
[a047aaa]51#include "images.h"
[211fd68]52#include "images_tiny.h"
[a047aaa]53
54#define NAME "barber"
55
[9ce911d]56#define FRAMES IMAGES
[a047aaa]57
[9696b01]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;
[a047aaa]72
[12008adf]73typedef struct {
74 ui_t *ui;
75} barber_t;
[a047aaa]76
[9696b01]77static fibril_timer_t *led_timer = NULL;
78static list_t led_devs;
79static unsigned int led_color = 0;
[a047aaa]80
[9696b01]81static pixel_t led_colors[LED_COLORS] = {
[a047aaa]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
[9696b01]91static fibril_timer_t *frame_timer = NULL;
[12008adf]92static ui_image_t *frame_img;
93static gfx_bitmap_t *frame_bmp[FRAMES];
[9696b01]94
[a047aaa]95static unsigned int frame = 0;
[9696b01]96static unsigned int fps = MIN_FPS;
[211fd68]97static gfx_coord_t frame_width;
98static gfx_coord_t frame_height;
[a047aaa]99
[9696b01]100static void led_timer_callback(void *);
101static void frame_timer_callback(void *);
102
[12008adf]103static void wnd_close(ui_window_t *, void *);
[2f11647f]104static void wnd_kbd_event(ui_window_t *, void *, kbd_event_t *);
[12008adf]105
106static ui_window_cb_t window_cb = {
[2f11647f]107 .close = wnd_close,
108 .kbd = wnd_kbd_event
[12008adf]109};
110
111/** Window close button was clicked.
112 *
113 * @param window Window
[e229148]114 * @param arg Argument (barber)
[12008adf]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
[2f11647f]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
[211fd68]168static bool decode_frames(gfx_context_t *gc, image_t *img)
[9696b01]169{
[0576df9]170 gfx_rect_t rect;
[12008adf]171 errno_t rc;
172
[9ce911d]173 for (unsigned int i = 0; i < FRAMES; i++) {
[211fd68]174 rc = decode_tga_gz(gc, img[i].addr, img[i].size,
[0576df9]175 &frame_bmp[i], &rect);
176 if (rc != EOK) {
[9ce911d]177 printf("Unable to decode frame %u.\n", i);
[9696b01]178 return false;
179 }
[12008adf]180
[211fd68]181 (void)rect;
[9696b01]182 }
[a35b458]183
[9696b01]184 return true;
185}
186
[12008adf]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
[9696b01]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;
[a35b458]207
[9696b01]208 if ((load != NULL) && (count > 0)) {
209 load_val = load[0];
210 free(load);
211 } else
212 load_val = 0;
[a35b458]213
[9696b01]214 return load_val;
215}
216
[bd41ac52]217static void plan_frame_timer(usec_t render_time)
[9696b01]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 */
[a35b458]225
[bd41ac52]226 usec_t delta = 1000000 / fps;
[9696b01]227 load_t load = get_load();
[a35b458]228
[9696b01]229 if ((delta >= render_time) && (load < MIN_LOAD))
230 fps++;
[a35b458]231
[9696b01]232 if (fps > MAX_FPS)
233 fps = MAX_FPS;
[a35b458]234
[9696b01]235 /*
236 * If we lack behind then immediately
237 * go to the lowest FPS.
238 */
[a35b458]239
[9696b01]240 if (delta < render_time)
241 fps = MIN_FPS;
[a35b458]242
[9696b01]243 /*
244 * Crank down the FPS if the current
245 * load is above an upper threshold.
246 */
[a35b458]247
[9696b01]248 if (load > MAX_LOAD)
249 fps--;
[a35b458]250
[9696b01]251 if (fps < MIN_FPS)
252 fps = MIN_FPS;
[a35b458]253
[9696b01]254 delta = 1000000 / fps;
[a35b458]255
[9696b01]256 fibril_timer_set(frame_timer, delta, frame_timer_callback, NULL);
257}
[a047aaa]258
[9696b01]259static void led_timer_callback(void *data)
[a047aaa]260{
[9696b01]261 pixel_t next_led_color = led_colors[led_color];
[a35b458]262
[9696b01]263 led_color++;
264 if (led_color >= LED_COLORS)
265 led_color = 0;
[a35b458]266
[a047aaa]267 list_foreach(led_devs, link, led_dev_t, dev) {
268 if (dev->sess)
[9696b01]269 led_dev_color_set(dev->sess, next_led_color);
[a047aaa]270 }
[a35b458]271
[9696b01]272 plan_led_timer();
273}
274
275static void frame_timer_callback(void *data)
276{
[bd41ac52]277 struct timespec prev;
[12008adf]278 gfx_rect_t rect;
[9696b01]279 getuptime(&prev);
[a35b458]280
[a047aaa]281 frame++;
282 if (frame >= FRAMES)
283 frame = 0;
[a35b458]284
[12008adf]285 rect.p0.x = 0;
286 rect.p0.y = 0;
[211fd68]287 rect.p1.x = frame_width;
288 rect.p1.y = frame_height;
[12008adf]289
290 ui_image_set_bmp(frame_img, frame_bmp[frame], &rect);
291 (void) ui_image_paint(frame_img);
[a35b458]292
[bd41ac52]293 struct timespec cur;
[9696b01]294 getuptime(&cur);
[a35b458]295
[bd41ac52]296 plan_frame_timer(NSEC2USEC(ts_sub_diff(&cur, &prev)));
[a047aaa]297}
298
[e89a06a]299static void loc_callback(void *arg)
[a047aaa]300{
301 category_id_t led_cat;
[b7fd2a0]302 errno_t rc = loc_category_get_id("led", &led_cat, IPC_FLAG_BLOCKING);
[a047aaa]303 if (rc != EOK)
304 return;
[a35b458]305
[a047aaa]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;
[a35b458]311
[a047aaa]312 for (size_t i = 0; i < count; i++) {
313 bool known = false;
[a35b458]314
[a047aaa]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 }
[a35b458]322
[a047aaa]323 if (!known) {
324 led_dev_t *dev = (led_dev_t *) calloc(1, sizeof(led_dev_t));
325 if (!dev)
326 continue;
[a35b458]327
[a047aaa]328 link_initialize(&dev->link);
329 dev->svc_id = svcs[i];
[f9b2cb4c]330 dev->sess = loc_service_connect(svcs[i], INTERFACE_DDF, 0);
[a35b458]331
[a047aaa]332 list_append(&dev->link, &led_devs);
333 }
334 }
[a35b458]335
[a047aaa]336 // FIXME: Handle LED device removal
[a35b458]337
[a047aaa]338 free(svcs);
339}
340
[fd11144]341static void print_syntax(void)
342{
343 printf("Syntax: %s [-d <display>]\n", NAME);
344}
345
[a047aaa]346int main(int argc, char *argv[])
347{
[211fd68]348 const char *display_spec = UI_ANY_DEFAULT;
[12008adf]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;
[211fd68]359 image_t *img;
[fd11144]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
[12008adf]372 display_spec = argv[i++];
[fd11144]373 } else {
374 printf("Invalid option '%s'.\n", argv[i]);
375 print_syntax();
376 return 1;
377 }
[a047aaa]378 }
[a35b458]379
[a047aaa]380 list_initialize(&led_devs);
[e89a06a]381 errno_t rc = loc_register_cat_change_cb(loc_callback, NULL);
[a047aaa]382 if (rc != EOK) {
383 printf("Unable to register callback for device discovery.\n");
384 return 1;
385 }
[a35b458]386
[9696b01]387 led_timer = fibril_timer_create(NULL);
388 if (!led_timer) {
389 printf("Unable to create LED timer.\n");
390 return 1;
391 }
[a35b458]392
[9696b01]393 frame_timer = fibril_timer_create(NULL);
394 if (!frame_timer) {
395 printf("Unable to create frame timer.\n");
[a047aaa]396 return 1;
397 }
[a35b458]398
[12008adf]399 rc = ui_create(display_spec, &ui);
400 if (rc != EOK) {
401 printf("Error creating UI on display %s.\n", display_spec);
[a047aaa]402 return 1;
[12008adf]403 }
[a35b458]404
[211fd68]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
[12008adf]413 rect.p0.x = 0;
414 rect.p0.y = 0;
[211fd68]415 rect.p1.x = frame_width;
416 rect.p1.y = frame_height;
[12008adf]417
418 ui_wnd_params_init(&params);
[a130983]419 params.caption = "Barber Pole";
[06d0c81]420 params.placement = ui_wnd_place_bottom_right;
[12008adf]421 /*
422 * Compute window rectangle such that application area corresponds
423 * to rect
424 */
[211fd68]425 ui_wdecor_rect_from_app(ui, params.style, &rect, &wrect);
[12008adf]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");
[a047aaa]434 return 1;
435 }
[a35b458]436
[12008adf]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);
[a35b458]441
[211fd68]442 img = ui_is_textmode(ui) ? image_tinys : images;
443
444 if (!decode_frames(gc, img))
[12008adf]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");
[a047aaa]451 return 1;
452 }
[a35b458]453
[12008adf]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 }
[a35b458]463
[9696b01]464 plan_led_timer();
465 plan_frame_timer(0);
[a35b458]466
[12008adf]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
[442210e]472 destroy_frames();
473
[12008adf]474 ui_window_destroy(window);
475 ui_destroy(ui);
476
[a047aaa]477 return 0;
478}
479
480/** @}
481 */
Note: See TracBrowser for help on using the repository browser.