00001
00039 #include <assert.h>
00040 #include <gfx/gfx.h>
00041 #include <gfx/win.h>
00042 #include <gfx/wtk.h>
00043 #include <gfx/sysfont.h>
00044 #include <mainloop.h>
00045 #include <membag.h>
00046 #include <physmem.h>
00047 #include <status_codes.h>
00048 #include <string.h>
00049 #include <timer.h>
00050 #include <util.h>
00051
00052 #include "app_tank.h"
00053 #include "app_desktop.h"
00054 #include "file_loader.h"
00055
00067
00068 #define COLOR_WIN_BACKGROUND GFX_COLOR(0, 0, 0)
00069
00070 #define COLOR_LEVEL_FILL GFX_COLOR(0, 0, 255)
00071
00072 #define COLOR_LEVEL_BACKGROUND GFX_COLOR(32, 32, 32)
00073
00074 #define COLOR_DEMAND_NORMAL GFX_COLOR(64, 192, 64)
00075
00076 #define COLOR_DEMAND_CRITICAL GFX_COLOR(192, 64, 64)
00077
00078 #define COLOR_DEMAND_BACKGROUND GFX_COLOR(32, 32, 32)
00079
00081
00088
00089 #define VALUE_LEVEL_MAXIMUM 127
00090
00091 #define VALUE_LEVEL_INITIAL 0
00092
00093 #define VALUE_SUPPLY_MAXIMUM VALUE_LEVEL_MAXIMUM
00094
00095 #define VALUE_SUPPLY_INITIAL (VALUE_SUPPLY_MAXIMUM / 2)
00096
00097 #define VALUE_DEMAND_MAXIMUM VALUE_LEVEL_MAXIMUM
00098
00099 #define VALUE_DEMAND_INITIAL 0
00100
00102
00109
00110 #define WIDGET_LEVEL_SIZE_X 52
00111
00112 #define WIDGET_LEVEL_SIZE_Y 121
00113
00114 #define WIDGET_LEVEL_POSITION_X 134
00115
00116 #define WIDGET_LEVEL_POSITION_Y 59
00117
00119 #define WIDGET_SUPPLY_SIZE_X 37
00120
00121 #define WIDGET_SUPPLY_SIZE_Y 105
00122
00123 #define WIDGET_SUPPLY_POSITION_X 39
00124
00125 #define WIDGET_SUPPLY_POSITION_Y 85
00126
00128 #define WIDGET_DEMAND_SIZE_X 20
00129
00130 #define WIDGET_DEMAND_SIZE_Y 45
00131
00132 #define WIDGET_DEMAND_POSITION_X 250
00133
00134 #define WIDGET_DEMAND_POSITION_Y 100
00135
00137
00144
00145 #define BITMAP_BACKGROUND_FILENAME "p_tankbg"
00146
00147 #define BITMAP_BACKGROUND_SIZE_X 320
00148
00149 #define BITMAP_BACKGROUND_SIZE_Y 240
00150
00151 #define BITMAP_BACKGROUND_POSITION_X 0
00152
00153 #define BITMAP_BACKGROUND_POSITION_Y 0
00154
00156 #define BITMAP_RED_LIGHT_FILENAME "p_lgtred"
00157
00158 #define BITMAP_GREEN_LIGHT_FILENAME "p_lgtgrn"
00159
00160 #define BITMAP_LIGHT_SIZE_X 38
00161
00162 #define BITMAP_LIGHT_SIZE_Y 38
00163
00164 #define BITMAP_LIGHT_POSITION_X 241
00165
00166 #define BITMAP_LIGHT_POSITION_Y 26
00167
00169
00182 #define TICK_RATE 30
00183
00190 #define TICKS_PER_RANDOM_UPDATE 9
00191
00193
00195 enum tank_loader_state {
00197 LOAD_RED_LIGHT,
00199 LOAD_GREEN_LIGHT,
00201 LOAD_BACKGROUND,
00203 LOAD_FINISHED,
00204 };
00205
00212 enum tank_bitmap_id {
00214 BITMAP_RED_LIGHT,
00216 BITMAP_GREEN_LIGHT,
00218 NR_OF_BITMAPS,
00219 };
00220
00227 static hugemem_ptr_t tank_bitmap_data[NR_OF_BITMAPS];
00228
00237 enum tank_command_id {
00239 CMD_NONE,
00241 CMD_EXIT,
00242 };
00243
00245 struct tank_context {
00247 struct workqueue_task *task;
00249 struct wtk_basic_frame *frame;
00251 struct font old_sysfont;
00253 struct wtk_progress_bar *level;
00255 struct wtk_slider *supply;
00257 struct wtk_progress_bar *demand;
00259 struct timer timer;
00261 enum tank_loader_state loader_state;
00263 bool level_alarm;
00265 bool flow_alarm;
00267 uint16_t timer_delay;
00269 uint16_t rand_ticks;
00271 int32_t rand;
00273 struct gfx_bitmap bitmaps[NR_OF_BITMAPS];
00274 };
00275
00283 static struct tank_context *tank_ctx;
00284
00298 static bool tank_frame_handler(struct wtk_basic_frame *basic_frame,
00299 win_command_t command_data)
00300 {
00301 switch ((enum tank_command_id)(uintptr_t)command_data) {
00302 case CMD_EXIT:
00303
00304 timer_stop(CONFIG_TIMER_ID, &tank_ctx->timer);
00305
00306
00307 memcpy(&sysfont, &tank_ctx->old_sysfont,
00308 sizeof(struct font));
00309 membag_free(tank_ctx);
00310 tank_ctx = NULL;
00311 app_desktop_restart();
00312 return true;
00313
00314 default:
00315 break;
00316 }
00317
00318 return false;
00319 }
00320
00333 static int32_t tank_logistic_map(int32_t rand)
00334 {
00335
00336
00337
00338 rand = max_s(rand, 1);
00339 rand = min_s(rand, VALUE_DEMAND_MAXIMUM - 1);
00340
00341
00342
00343
00344 rand *= VALUE_DEMAND_MAXIMUM - rand;
00345 rand *= 79;
00346 rand /= VALUE_DEMAND_MAXIMUM * 20;
00347
00348 return rand;
00349 }
00350
00371 static void tank_worker(struct workqueue_task *task)
00372 {
00373 struct gfx_bitmap *alarm_bitmap = NULL;
00374 bool alarm_update = false;
00375 int32_t rand;
00376 uint8_t supply;
00377 uint16_t demand;
00378 int16_t flow;
00379 int16_t level;
00380
00381
00382 if (!tank_ctx) {
00383 return;
00384 }
00385
00386
00387 level = wtk_progress_bar_get_value(tank_ctx->level);
00388 supply = wtk_slider_get_value(tank_ctx->supply);
00389 demand = wtk_progress_bar_get_value(tank_ctx->demand);
00390 rand = tank_ctx->rand;
00391
00392
00393 if (--(tank_ctx->rand_ticks) == 0) {
00394 tank_ctx->rand_ticks = TICKS_PER_RANDOM_UPDATE;
00395
00396
00397 rand ^= demand & 0x03;
00398
00399
00400 rand = tank_logistic_map(rand);
00401 tank_ctx->rand = rand;
00402 }
00403
00404
00405 demand = rand + (2 * demand) + level;
00406 demand /= 4;
00407 demand = min_u(demand, VALUE_DEMAND_MAXIMUM);
00408
00409
00410
00411
00412 flow = supply;
00413 flow -= demand;
00414 flow /= 4;
00415
00416
00417 level += flow;
00418
00419
00420
00421
00422 if (level <= 0) {
00423 if (!tank_ctx->flow_alarm) {
00424 tank_ctx->flow_alarm = true;
00425 wtk_progress_bar_set_colors(tank_ctx->demand,
00426 COLOR_DEMAND_CRITICAL,
00427 COLOR_DEMAND_BACKGROUND);
00428 }
00429 level = 0;
00430 } else {
00431 if (tank_ctx->flow_alarm) {
00432 tank_ctx->flow_alarm = false;
00433 wtk_progress_bar_set_colors(tank_ctx->demand,
00434 COLOR_DEMAND_NORMAL,
00435 COLOR_DEMAND_BACKGROUND);
00436 }
00437 }
00438 wtk_progress_bar_set_value(tank_ctx->demand, demand);
00439
00440
00441
00442
00443
00444
00445 if (level >= VALUE_LEVEL_MAXIMUM) {
00446 if (!tank_ctx->level_alarm) {
00447 tank_ctx->level_alarm = true;
00448 alarm_update = true;
00449 alarm_bitmap = &tank_ctx->bitmaps[BITMAP_RED_LIGHT];
00450 }
00451 level = VALUE_LEVEL_MAXIMUM;
00452 } else {
00453 if (tank_ctx->level_alarm) {
00454 tank_ctx->level_alarm = false;
00455 alarm_update = true;
00456 alarm_bitmap = &tank_ctx->bitmaps[BITMAP_GREEN_LIGHT];
00457 }
00458 }
00459 wtk_progress_bar_set_value(tank_ctx->level, level);
00460
00461
00462
00463
00464 if (alarm_update) {
00465 #ifdef CONFIG_GFX_USE_CLIPPING
00466 gfx_set_clipping(BITMAP_LIGHT_POSITION_X,
00467 BITMAP_LIGHT_POSITION_Y,
00468 BITMAP_LIGHT_POSITION_X
00469 + BITMAP_LIGHT_SIZE_X - 1,
00470 BITMAP_LIGHT_POSITION_Y
00471 + BITMAP_LIGHT_SIZE_Y - 1
00472 );
00473 #endif
00474 gfx_draw_bitmap(alarm_bitmap,
00475 BITMAP_LIGHT_POSITION_X,
00476 BITMAP_LIGHT_POSITION_Y);
00477 }
00478 }
00479
00488 static void tank_timer_callback(struct timer *timer)
00489 {
00490 timer_set_alarm(CONFIG_TIMER_ID, &tank_ctx->timer,
00491 tank_ctx->timer_delay);
00492 workqueue_add_task(&main_workqueue, tank_ctx->task);
00493 }
00494
00506 static void tank_loader(struct workqueue_task *task)
00507 {
00508 enum status_code result;
00509 hugemem_ptr_t bitmap_data;
00510
00511
00512
00513
00514
00515
00516
00517
00518 switch (tank_ctx->loader_state) {
00519 case LOAD_RED_LIGHT:
00520
00521 bitmap_data = load_file_to_hugemem(BITMAP_RED_LIGHT_FILENAME,
00522 task);
00523
00524
00525
00526
00527
00528 if (bitmap_data != HUGEMEM_NULL) {
00529 tank_bitmap_data[BITMAP_RED_LIGHT] = bitmap_data;
00530 tank_ctx->bitmaps[BITMAP_RED_LIGHT].data.hugemem =
00531 bitmap_data;
00532
00533 tank_ctx->loader_state = LOAD_GREEN_LIGHT;
00534 } else {
00535 goto exit_load_error;
00536 }
00537 break;
00538
00539 case LOAD_GREEN_LIGHT:
00540 bitmap_data = load_file_to_hugemem(BITMAP_GREEN_LIGHT_FILENAME,
00541 task);
00542
00543 if (bitmap_data != HUGEMEM_NULL) {
00544 tank_bitmap_data[BITMAP_GREEN_LIGHT] = bitmap_data;
00545 tank_ctx->bitmaps[BITMAP_GREEN_LIGHT].data.hugemem
00546 = bitmap_data;
00547
00548 tank_ctx->loader_state = LOAD_BACKGROUND;
00549 } else {
00550 goto exit_load_error;
00551 }
00552 break;
00553
00554 case LOAD_BACKGROUND:
00555
00556 result = load_file_to_screen(BITMAP_BACKGROUND_FILENAME,
00557 BITMAP_BACKGROUND_POSITION_X,
00558 BITMAP_BACKGROUND_POSITION_Y,
00559 BITMAP_BACKGROUND_SIZE_X,
00560 BITMAP_BACKGROUND_SIZE_Y, task);
00561
00562 if (result == STATUS_OK) {
00563 tank_ctx->loader_state = LOAD_FINISHED;
00564 } else {
00565 goto exit_load_error;
00566 }
00567 break;
00568
00569 case LOAD_FINISHED:
00570
00571 win_show(wtk_basic_frame_as_child(tank_ctx->frame));
00572
00573
00574 workqueue_task_set_work_func(task, tank_worker);
00575
00576
00577 timer_start(CONFIG_TIMER_ID, &tank_ctx->timer);
00578 timer_set_alarm(CONFIG_TIMER_ID, &tank_ctx->timer,
00579 tank_ctx->timer_delay);
00580 break;
00581
00582 default:
00583
00584 unhandled_case(tank_ctx->loader_state);
00585 break;
00586 }
00587
00588 return;
00589
00590
00591 exit_load_error:
00592 win_destroy(wtk_basic_frame_as_child(tank_ctx->frame));
00593 memcpy(&sysfont, &tank_ctx->old_sysfont, sizeof(struct font));
00594 membag_free(tank_ctx);
00595 tank_ctx = NULL;
00596 app_desktop_restart();
00597 }
00598
00618 void app_tank_launch(struct workqueue_task *task)
00619 {
00620 struct win_attributes attr;
00621 struct win_window *win;
00622 struct gfx_bitmap bitmap;
00623 struct wtk_basic_frame *frame;
00624 struct wtk_slider *slider;
00625 struct wtk_button *button;
00626 struct wtk_progress_bar *pbar;
00627 struct timer *timer;
00628 timer_res_t timer_res;
00629 uint32_t timer_clk;
00630
00631 assert(task);
00632
00633
00634 #ifdef CONFIG_GFX_USE_CLIPPING
00635 gfx_set_clipping(0, 0, gfx_get_width(), gfx_get_height());
00636 #endif
00637 gfx_draw_filled_rect(0, 0, gfx_get_width(), gfx_get_height(),
00638 COLOR_WIN_BACKGROUND);
00639
00640
00641 tank_ctx = membag_alloc(sizeof(struct tank_context));
00642 if (!tank_ctx)
00643 goto exit_no_context;
00644
00645
00646 memcpy(&tank_ctx->old_sysfont, &sysfont, sizeof(struct font));
00647 sysfont.scale = 2;
00648
00649
00650 attr.area.pos.x = 0;
00651 attr.area.pos.y = 0;
00652 attr.area.size.x = gfx_get_width();
00653 attr.area.size.y = gfx_get_height();
00654
00655 frame = wtk_basic_frame_create(win_get_root(), &attr.area, NULL,
00656 NULL, tank_frame_handler, NULL);
00657 if (!frame) {
00658 goto exit_no_frame;
00659 }
00660 tank_ctx->frame = frame;
00661
00662
00663 win = wtk_basic_frame_as_child(frame);
00664
00665
00666 timer = &tank_ctx->timer;
00667 timer_init(CONFIG_TIMER_ID, timer, tank_timer_callback);
00668 timer_res = timer_set_resolution(CONFIG_TIMER_ID, timer,
00669 TICK_RATE);
00670 timer_write_resolution(CONFIG_TIMER_ID, timer, timer_res);
00671
00672
00673 timer_clk = timer_get_resolution(CONFIG_TIMER_ID, timer, timer_res);
00674 tank_ctx->timer_delay = timer_clk / TICK_RATE;
00675
00676
00677 tank_ctx->rand = 1;
00678 tank_ctx->rand_ticks = TICKS_PER_RANDOM_UPDATE;
00679
00680
00681 attr.area.pos.x = WIDGET_SUPPLY_POSITION_X;
00682 attr.area.pos.y = WIDGET_SUPPLY_POSITION_Y;
00683 attr.area.size.x = WIDGET_SUPPLY_SIZE_X;
00684 attr.area.size.y = WIDGET_SUPPLY_SIZE_Y;
00685
00686 slider = wtk_slider_create(win, &attr.area, VALUE_SUPPLY_MAXIMUM,
00687 VALUE_SUPPLY_INITIAL, WTK_SLIDER_VERTICAL |
00688 WTK_SLIDER_INVERT, CMD_NONE);
00689 if (!slider) {
00690 goto exit_no_widget;
00691 }
00692 tank_ctx->supply = slider;
00693 win_show(wtk_slider_as_child(slider));
00694
00695
00696 attr.area.pos.x = WIDGET_LEVEL_POSITION_X;
00697 attr.area.pos.y = WIDGET_LEVEL_POSITION_Y;
00698 attr.area.size.x = WIDGET_LEVEL_SIZE_X;
00699 attr.area.size.y = WIDGET_LEVEL_SIZE_Y;
00700
00701 pbar = wtk_progress_bar_create(win, &attr.area, VALUE_LEVEL_MAXIMUM,
00702 VALUE_LEVEL_INITIAL, COLOR_LEVEL_FILL,
00703 COLOR_LEVEL_BACKGROUND, WTK_PROGRESS_BAR_VERTICAL |
00704 WTK_PROGRESS_BAR_INVERT);
00705 if (!pbar) {
00706 goto exit_no_widget;
00707 }
00708 tank_ctx->level = pbar;
00709 win_show(wtk_progress_bar_as_child(pbar));
00710
00711
00712 attr.area.pos.x = WIDGET_DEMAND_POSITION_X;
00713 attr.area.pos.y = WIDGET_DEMAND_POSITION_Y;
00714 attr.area.size.x = WIDGET_DEMAND_SIZE_X;
00715 attr.area.size.y = WIDGET_DEMAND_SIZE_Y;
00716
00717 pbar = wtk_progress_bar_create(win, &attr.area, VALUE_DEMAND_MAXIMUM,
00718 VALUE_DEMAND_INITIAL, COLOR_DEMAND_NORMAL,
00719 COLOR_DEMAND_BACKGROUND, WTK_PROGRESS_BAR_VERTICAL |
00720 WTK_PROGRESS_BAR_INVERT);
00721 if (!pbar) {
00722 goto exit_no_widget;
00723 }
00724 tank_ctx->demand = pbar;
00725 win_show(wtk_progress_bar_as_child(pbar));
00726
00727
00728 attr.area.pos.x = APP_EXIT_BUTTON_POS_X;
00729 attr.area.pos.y = APP_EXIT_BUTTON_POS_Y;
00730 attr.area.size.x = APP_EXIT_BUTTON_SIZE_X;
00731 attr.area.size.y = APP_EXIT_BUTTON_SIZE_Y;
00732
00733 button = wtk_button_create(win, &attr.area, APP_EXIT_BUTTON_TEXT,
00734 (win_command_t)CMD_EXIT);
00735 if (!button) {
00736 goto exit_no_widget;
00737 }
00738 win_show(wtk_button_as_child(button));
00739
00740
00741 tank_ctx->level_alarm = true;
00742 tank_ctx->flow_alarm = false;
00743 tank_ctx->task = task;
00744
00745
00746
00747
00748
00749 bitmap.width = BITMAP_LIGHT_SIZE_X;
00750 bitmap.height = BITMAP_LIGHT_SIZE_Y;
00751 bitmap.type = BITMAP_HUGEMEM;
00752 tank_ctx->bitmaps[BITMAP_RED_LIGHT] = bitmap;
00753 tank_ctx->bitmaps[BITMAP_GREEN_LIGHT] = bitmap;
00754
00755 if (tank_bitmap_data[BITMAP_GREEN_LIGHT]) {
00756 tank_ctx->loader_state = LOAD_BACKGROUND;
00757 tank_ctx->bitmaps[BITMAP_RED_LIGHT].data.hugemem =
00758 tank_bitmap_data[BITMAP_RED_LIGHT];
00759 tank_ctx->bitmaps[BITMAP_GREEN_LIGHT].data.hugemem =
00760 tank_bitmap_data[BITMAP_GREEN_LIGHT];
00761 } else {
00762 tank_ctx->loader_state = LOAD_RED_LIGHT;
00763 }
00764 workqueue_task_set_work_func(task, tank_loader);
00765 workqueue_add_task(&main_workqueue, task);
00766 return;
00767
00768
00769 exit_no_widget:
00770 win_destroy(wtk_basic_frame_as_child(tank_ctx->frame));
00771 exit_no_frame:
00772 memcpy(&sysfont, &tank_ctx->old_sysfont, sizeof(struct font));
00773 membag_free(tank_ctx);
00774 exit_no_context:
00775 app_desktop_restart();
00776 return;
00777 }
00778