snow.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2018 K. Lange
  5. */
  6. #include <stdlib.h>
  7. #include <assert.h>
  8. #include <unistd.h>
  9. #include <time.h>
  10. #include <sched.h>
  11. #include <math.h>
  12. #include <sys/fswait.h>
  13. #include <sys/time.h>
  14. #include <toaru/yutani.h>
  15. #include <toaru/graphics.h>
  16. static yutani_t * yctx;
  17. static yutani_window_t * wina;
  18. static gfx_context_t * ctx;
  19. static int should_exit = 0;
  20. static sprite_t snowflake;
  21. #define FLAKES 40
  22. #define FALL_SPEED 3
  23. struct {
  24. int16_t x;
  25. int16_t y;
  26. uint8_t rotation;
  27. uint8_t alpha;
  28. int8_t wind;
  29. uint8_t exists;
  30. } flakes[FLAKES];
  31. int flakes_made = 0;
  32. static void add_flake() {
  33. for (int i = 0; i < FLAKES; ++i) {
  34. if (!flakes[i].exists) {
  35. flakes[i].exists = 1;
  36. flakes[i].y = -snowflake.height / 2;
  37. flakes[i].x = rand() % (ctx->width);
  38. flakes[i].alpha = rand() % 50 + 50;
  39. flakes[i].rotation = rand() % 255;
  40. flakes[i].wind = (rand() % 6) - 3;
  41. return;
  42. }
  43. }
  44. }
  45. static void draw(void) {
  46. draw_fill(ctx, 0);
  47. for (int i = 0; i < FLAKES; ++i) {
  48. if (flakes[i].exists) {
  49. draw_sprite_rotate(ctx, &snowflake,
  50. flakes[i].x, flakes[i].y,
  51. (float)(flakes[i].rotation) / 100.0,
  52. (float)(flakes[i].alpha) / 100.0);
  53. flakes[i].y += FALL_SPEED;
  54. flakes[i].x += flakes[i].wind;
  55. if (flakes[i].y >= ctx->height + snowflake.height / 2 ||
  56. flakes[i].x <= -snowflake.width / 2 ||
  57. flakes[i].x >= ctx->width + snowflake.width / 2) {
  58. flakes[i].exists = 0;
  59. add_flake();
  60. }
  61. }
  62. }
  63. flip(ctx);
  64. yutani_flip(yctx, wina);
  65. }
  66. void resize_finish(int w, int h) {
  67. yutani_window_resize_accept(yctx, wina, w, h);
  68. reinit_graphics_yutani(ctx, wina);
  69. draw();
  70. yutani_window_resize_done(yctx, wina);
  71. }
  72. uint64_t last_flake = 0;
  73. static uint64_t precise_current_time(void) {
  74. struct timeval t;
  75. gettimeofday(&t, NULL);
  76. time_t sec_diff = t.tv_sec;
  77. suseconds_t usec_diff = t.tv_usec;
  78. return (uint64_t)(sec_diff * 1000 + usec_diff / 1000);
  79. }
  80. static uint64_t precise_time_since(uint64_t start_time) {
  81. uint32_t now = precise_current_time();
  82. uint32_t diff = now - start_time; /* Milliseconds */
  83. return diff;
  84. }
  85. int main (int argc, char ** argv) {
  86. srand(time(NULL));
  87. memset(&flakes, 0, sizeof(flakes));
  88. yctx = yutani_init();
  89. if (!yctx) {
  90. fprintf(stderr, "%s: failed to connect to compositor\n", argv[0]);
  91. return 1;
  92. }
  93. load_sprite(&snowflake, "/usr/share/snowflake.bmp");
  94. snowflake.alpha = ALPHA_EMBEDDED;
  95. snowflake.masks = NULL;
  96. snowflake.blank = 0;
  97. wina = yutani_window_create(yctx, 100, 100);
  98. if (argc < 2 || strcmp(argv[1],"--no-ad")) {
  99. yutani_window_advertise(yctx, wina, "snow");
  100. }
  101. yutani_special_request(yctx, wina, YUTANI_SPECIAL_REQUEST_MAXIMIZE);
  102. yutani_window_update_shape(yctx, wina, 256);
  103. ctx = init_graphics_yutani_double_buffer(wina);
  104. draw_fill(ctx, rgba(0,0,0,0));
  105. flip(ctx);
  106. while (!should_exit) {
  107. int fds[1] = {fileno(yctx->sock)};
  108. int index = fswait2(1,fds,10);
  109. if (index == 0) {
  110. yutani_msg_t * m = yutani_poll(yctx);
  111. while (m) {
  112. switch (m->type) {
  113. case YUTANI_MSG_KEY_EVENT:
  114. {
  115. struct yutani_msg_key_event * ke = (void*)m->data;
  116. if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == 'q') {
  117. should_exit = 1;
  118. sched_yield();
  119. }
  120. }
  121. break;
  122. case YUTANI_MSG_WINDOW_MOUSE_EVENT:
  123. {
  124. struct yutani_msg_window_mouse_event * me = (void*)m->data;
  125. if (me->command == YUTANI_MOUSE_EVENT_DOWN && me->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
  126. yutani_window_drag_start(yctx, wina);
  127. }
  128. }
  129. break;
  130. case YUTANI_MSG_RESIZE_OFFER:
  131. {
  132. struct yutani_msg_window_resize * wr = (void*)m->data;
  133. resize_finish(wr->width, wr->height);
  134. }
  135. break;
  136. case YUTANI_MSG_WINDOW_CLOSE:
  137. case YUTANI_MSG_SESSION_END:
  138. should_exit = 1;
  139. break;
  140. default:
  141. break;
  142. }
  143. free(m);
  144. m = yutani_poll_async(yctx);
  145. }
  146. } else {
  147. if (flakes_made < 20 && precise_time_since(last_flake) > 1000) {
  148. add_flake();
  149. flakes_made += 1;
  150. last_flake = precise_current_time();
  151. }
  152. }
  153. draw();
  154. }
  155. yutani_close(yctx, wina);
  156. return 0;
  157. }