cursor.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. //========================================================================
  2. // Cursor & input mode tests
  3. // Copyright (c) Camilla Löwy <[email protected]>
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would
  16. // be appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not
  19. // be misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. //
  24. //========================================================================
  25. //
  26. // This test provides an interface to the cursor image and cursor mode
  27. // parts of the API.
  28. //
  29. // Custom cursor image generation by urraka.
  30. //
  31. //========================================================================
  32. #include <glad/glad.h>
  33. #include <GLFW/glfw3.h>
  34. #if defined(_MSC_VER)
  35. // Make MS math.h define M_PI
  36. #define _USE_MATH_DEFINES
  37. #endif
  38. #include <math.h>
  39. #include <stdio.h>
  40. #include <stdlib.h>
  41. #include "linmath.h"
  42. #define CURSOR_FRAME_COUNT 60
  43. static const char* vertex_shader_text =
  44. "#version 110\n"
  45. "uniform mat4 MVP;\n"
  46. "attribute vec2 vPos;\n"
  47. "void main()\n"
  48. "{\n"
  49. " gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
  50. "}\n";
  51. static const char* fragment_shader_text =
  52. "#version 110\n"
  53. "void main()\n"
  54. "{\n"
  55. " gl_FragColor = vec4(1.0);\n"
  56. "}\n";
  57. static double cursor_x;
  58. static double cursor_y;
  59. static int swap_interval = 1;
  60. static int wait_events = GLFW_TRUE;
  61. static int animate_cursor = GLFW_FALSE;
  62. static int track_cursor = GLFW_FALSE;
  63. static GLFWcursor* standard_cursors[6];
  64. static void error_callback(int error, const char* description)
  65. {
  66. fprintf(stderr, "Error: %s\n", description);
  67. }
  68. static float star(int x, int y, float t)
  69. {
  70. const float c = 64 / 2.f;
  71. const float i = (0.25f * (float) sin(2.f * M_PI * t) + 0.75f);
  72. const float k = 64 * 0.046875f * i;
  73. const float dist = (float) sqrt((x - c) * (x - c) + (y - c) * (y - c));
  74. const float salpha = 1.f - dist / c;
  75. const float xalpha = (float) x == c ? c : k / (float) fabs(x - c);
  76. const float yalpha = (float) y == c ? c : k / (float) fabs(y - c);
  77. return (float) fmax(0.f, fmin(1.f, i * salpha * 0.2f + salpha * xalpha * yalpha));
  78. }
  79. static GLFWcursor* create_cursor_frame(float t)
  80. {
  81. int i = 0, x, y;
  82. unsigned char buffer[64 * 64 * 4];
  83. const GLFWimage image = { 64, 64, buffer };
  84. for (y = 0; y < image.width; y++)
  85. {
  86. for (x = 0; x < image.height; x++)
  87. {
  88. buffer[i++] = 255;
  89. buffer[i++] = 255;
  90. buffer[i++] = 255;
  91. buffer[i++] = (unsigned char) (255 * star(x, y, t));
  92. }
  93. }
  94. return glfwCreateCursor(&image, image.width / 2, image.height / 2);
  95. }
  96. static void cursor_position_callback(GLFWwindow* window, double x, double y)
  97. {
  98. printf("%0.3f: Cursor position: %f %f (%+f %+f)\n",
  99. glfwGetTime(),
  100. x, y, x - cursor_x, y - cursor_y);
  101. cursor_x = x;
  102. cursor_y = y;
  103. }
  104. static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
  105. {
  106. if (action != GLFW_PRESS)
  107. return;
  108. switch (key)
  109. {
  110. case GLFW_KEY_A:
  111. {
  112. animate_cursor = !animate_cursor;
  113. if (!animate_cursor)
  114. glfwSetCursor(window, NULL);
  115. break;
  116. }
  117. case GLFW_KEY_ESCAPE:
  118. {
  119. if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED)
  120. {
  121. glfwSetWindowShouldClose(window, GLFW_TRUE);
  122. break;
  123. }
  124. /* FALLTHROUGH */
  125. }
  126. case GLFW_KEY_N:
  127. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
  128. printf("(( cursor is normal ))\n");
  129. break;
  130. case GLFW_KEY_D:
  131. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  132. printf("(( cursor is disabled ))\n");
  133. break;
  134. case GLFW_KEY_H:
  135. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
  136. printf("(( cursor is hidden ))\n");
  137. break;
  138. case GLFW_KEY_SPACE:
  139. swap_interval = 1 - swap_interval;
  140. printf("(( swap interval: %i ))\n", swap_interval);
  141. glfwSwapInterval(swap_interval);
  142. break;
  143. case GLFW_KEY_W:
  144. wait_events = !wait_events;
  145. printf("(( %sing for events ))\n", wait_events ? "wait" : "poll");
  146. break;
  147. case GLFW_KEY_T:
  148. track_cursor = !track_cursor;
  149. break;
  150. case GLFW_KEY_0:
  151. glfwSetCursor(window, NULL);
  152. break;
  153. case GLFW_KEY_1:
  154. glfwSetCursor(window, standard_cursors[0]);
  155. break;
  156. case GLFW_KEY_2:
  157. glfwSetCursor(window, standard_cursors[1]);
  158. break;
  159. case GLFW_KEY_3:
  160. glfwSetCursor(window, standard_cursors[2]);
  161. break;
  162. case GLFW_KEY_4:
  163. glfwSetCursor(window, standard_cursors[3]);
  164. break;
  165. case GLFW_KEY_5:
  166. glfwSetCursor(window, standard_cursors[4]);
  167. break;
  168. case GLFW_KEY_6:
  169. glfwSetCursor(window, standard_cursors[5]);
  170. break;
  171. }
  172. }
  173. int main(void)
  174. {
  175. int i;
  176. GLFWwindow* window;
  177. GLFWcursor* star_cursors[CURSOR_FRAME_COUNT];
  178. GLFWcursor* current_frame = NULL;
  179. GLuint vertex_buffer, vertex_shader, fragment_shader, program;
  180. GLint mvp_location, vpos_location;
  181. glfwSetErrorCallback(error_callback);
  182. if (!glfwInit())
  183. exit(EXIT_FAILURE);
  184. for (i = 0; i < CURSOR_FRAME_COUNT; i++)
  185. {
  186. star_cursors[i] = create_cursor_frame(i / (float) CURSOR_FRAME_COUNT);
  187. if (!star_cursors[i])
  188. {
  189. glfwTerminate();
  190. exit(EXIT_FAILURE);
  191. }
  192. }
  193. for (i = 0; i < sizeof(standard_cursors) / sizeof(standard_cursors[0]); i++)
  194. {
  195. const int shapes[] = {
  196. GLFW_ARROW_CURSOR,
  197. GLFW_IBEAM_CURSOR,
  198. GLFW_CROSSHAIR_CURSOR,
  199. GLFW_HAND_CURSOR,
  200. GLFW_HRESIZE_CURSOR,
  201. GLFW_VRESIZE_CURSOR
  202. };
  203. standard_cursors[i] = glfwCreateStandardCursor(shapes[i]);
  204. if (!standard_cursors[i])
  205. {
  206. glfwTerminate();
  207. exit(EXIT_FAILURE);
  208. }
  209. }
  210. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  211. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  212. window = glfwCreateWindow(640, 480, "Cursor Test", NULL, NULL);
  213. if (!window)
  214. {
  215. glfwTerminate();
  216. exit(EXIT_FAILURE);
  217. }
  218. glfwMakeContextCurrent(window);
  219. gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
  220. glGenBuffers(1, &vertex_buffer);
  221. glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
  222. vertex_shader = glCreateShader(GL_VERTEX_SHADER);
  223. glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
  224. glCompileShader(vertex_shader);
  225. fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
  226. glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
  227. glCompileShader(fragment_shader);
  228. program = glCreateProgram();
  229. glAttachShader(program, vertex_shader);
  230. glAttachShader(program, fragment_shader);
  231. glLinkProgram(program);
  232. mvp_location = glGetUniformLocation(program, "MVP");
  233. vpos_location = glGetAttribLocation(program, "vPos");
  234. glEnableVertexAttribArray(vpos_location);
  235. glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
  236. sizeof(vec2), (void*) 0);
  237. glUseProgram(program);
  238. glfwGetCursorPos(window, &cursor_x, &cursor_y);
  239. printf("Cursor position: %f %f\n", cursor_x, cursor_y);
  240. glfwSetCursorPosCallback(window, cursor_position_callback);
  241. glfwSetKeyCallback(window, key_callback);
  242. while (!glfwWindowShouldClose(window))
  243. {
  244. glClear(GL_COLOR_BUFFER_BIT);
  245. if (track_cursor)
  246. {
  247. int wnd_width, wnd_height, fb_width, fb_height;
  248. float scale;
  249. vec2 vertices[4];
  250. mat4x4 mvp;
  251. glfwGetWindowSize(window, &wnd_width, &wnd_height);
  252. glfwGetFramebufferSize(window, &fb_width, &fb_height);
  253. glViewport(0, 0, fb_width, fb_height);
  254. scale = (float) fb_width / (float) wnd_width;
  255. vertices[0][0] = 0.f;
  256. vertices[0][1] = (float) (fb_height - cursor_y * scale);
  257. vertices[1][0] = (float) fb_width;
  258. vertices[1][1] = (float) (fb_height - cursor_y * scale);
  259. vertices[2][0] = (float) (cursor_x * scale);
  260. vertices[2][1] = 0.f;
  261. vertices[3][0] = (float) (cursor_x * scale);
  262. vertices[3][1] = (float) fb_height;
  263. glBufferData(GL_ARRAY_BUFFER,
  264. sizeof(vertices),
  265. vertices,
  266. GL_STREAM_DRAW);
  267. mat4x4_ortho(mvp, 0.f, (float) fb_width, 0.f, (float) fb_height, 0.f, 1.f);
  268. glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp);
  269. glDrawArrays(GL_LINES, 0, 4);
  270. }
  271. glfwSwapBuffers(window);
  272. if (animate_cursor)
  273. {
  274. const int i = (int) (glfwGetTime() * 30.0) % CURSOR_FRAME_COUNT;
  275. if (current_frame != star_cursors[i])
  276. {
  277. glfwSetCursor(window, star_cursors[i]);
  278. current_frame = star_cursors[i];
  279. }
  280. }
  281. else
  282. current_frame = NULL;
  283. if (wait_events)
  284. {
  285. if (animate_cursor)
  286. glfwWaitEventsTimeout(1.0 / 30.0);
  287. else
  288. glfwWaitEvents();
  289. }
  290. else
  291. glfwPollEvents();
  292. // Workaround for an issue with msvcrt and mintty
  293. fflush(stdout);
  294. }
  295. glfwDestroyWindow(window);
  296. for (i = 0; i < CURSOR_FRAME_COUNT; i++)
  297. glfwDestroyCursor(star_cursors[i]);
  298. for (i = 0; i < sizeof(standard_cursors) / sizeof(standard_cursors[0]); i++)
  299. glfwDestroyCursor(standard_cursors[i]);
  300. glfwTerminate();
  301. exit(EXIT_SUCCESS);
  302. }