1 #include <stdlib.h>
2 #include <string.h>
3 #include "mixer_clickable.h"
4
5 extern int screen_cols;
6 extern int screen_lines;
7
8 static struct clickable_rect *clickable_rects = NULL;
9 static unsigned int clickable_rects_count = 0;
10 static unsigned int last_rect = 0;
11
12 /* Using 0 instead of -1 for marking free rectangles allows us to use
13 * memset for `freeing` all rectangles at once.
14 * Zero is actually a valid coordinate in ncurses, but since we don't have
15 * any clickables in the top line this is fine. */
16 #define FREE_MARKER 0
17 #define RECT_IS_FREE(RECT) ((RECT).y1 == FREE_MARKER)
18 #define RECT_FREE(RECT) ((RECT).y1 = FREE_MARKER)
19
clickable_set(int y1,int x1,int y2,int x2,command_enum command,int arg1)20 void clickable_set(int y1, int x1, int y2, int x2, command_enum command, int arg1) {
21 struct clickable_rect* tmp;
22 unsigned int i;
23
24 for (i = last_rect; i < clickable_rects_count; ++i) {
25 if (RECT_IS_FREE(clickable_rects[i])) {
26 last_rect = i;
27 goto SET_CLICKABLE_DATA;
28 }
29 }
30
31 for (i = 0; i < last_rect; ++i) {
32 if (RECT_IS_FREE(clickable_rects[i])) {
33 last_rect = i;
34 goto SET_CLICKABLE_DATA;
35 }
36 }
37
38 last_rect = clickable_rects_count;
39 tmp = realloc(clickable_rects, (clickable_rects_count + 8) * sizeof(*clickable_rects));
40 if (!tmp) {
41 free(clickable_rects);
42 clickable_rects = NULL;
43 clickable_rects_count = 0;
44 last_rect = 0;
45 return;
46 }
47 clickable_rects = tmp;
48 #if FREE_MARKER == 0
49 memset(clickable_rects + clickable_rects_count, 0, 8 * sizeof(*clickable_rects));
50 #else
51 for (i = clickable_rects_count; i < clickable_rects_count + 8; ++i)
52 RECT_FREE(clickable_rects[i]);
53 #endif
54 clickable_rects_count += 8;
55
56 SET_CLICKABLE_DATA:
57 clickable_rects[last_rect] = (struct clickable_rect) {
58 .y1 = y1,
59 .x1 = x1,
60 .x2 = x2,
61 .y2 = y2,
62 .command = command,
63 .arg1 = arg1
64 };
65 }
66
clickable_set_relative(WINDOW * win,int y1,int x1,int y2,int x2,command_enum command,int arg1)67 void clickable_set_relative(WINDOW *win, int y1, int x1, int y2, int x2, command_enum command, int arg1) {
68 int y, x;
69 getyx(win, y, x);
70 y1 = y + y1;
71 x1 = x + x1;
72 y2 = y + y2;
73 x2 = x + x2;
74 clickable_set(y1, x1, y2, x2, command, arg1);
75 }
76
clickable_clear(int y1,int x1,int y2,int x2)77 void clickable_clear(int y1, int x1, int y2, int x2) {
78 #define IS_IN_RECT(Y, X) (Y >= y1 && Y <= y2 && X >= x1 && X <= x2)
79 unsigned int i;
80
81 if (x1 == 0 && x2 == -1 && y2 == -1) {
82 if (y1 == 0) {
83 // Optimize case: clear all
84 #if FREE_MARKER == 0
85 if (clickable_rects)
86 memset(clickable_rects, 0,
87 clickable_rects_count * sizeof(*clickable_rects));
88 #else
89 for (i = 0; i < clickable_rects_count; ++i)
90 RECT_FREE(clickable_rects[i]);
91 #endif
92 }
93 else {
94 // Optimize case: clear all lines beyond y1
95 for (i = 0; i < clickable_rects_count; ++i) {
96 if (clickable_rects[i].y2 >= y1)
97 RECT_FREE(clickable_rects[i]);
98 }
99 }
100 return;
101 }
102
103 if (y2 < 0)
104 y2 = screen_lines + y2 + 1;
105 if (x2 < 0)
106 x2 = screen_cols + x2 + 1;
107
108 for (i = 0; i < clickable_rects_count; ++i) {
109 if (!RECT_IS_FREE(clickable_rects[i]) && (
110 IS_IN_RECT(clickable_rects[i].y1, clickable_rects[i].x1) ||
111 IS_IN_RECT(clickable_rects[i].y2, clickable_rects[i].x2)
112 ))
113 {
114 RECT_FREE(clickable_rects[i]);
115 }
116 }
117 }
118
clickable_find(int y,int x)119 struct clickable_rect* clickable_find(int y, int x) {
120 unsigned int i;
121
122 for (i = 0; i < clickable_rects_count; ++i) {
123 if (
124 !RECT_IS_FREE(clickable_rects[i]) &&
125 y >= clickable_rects[i].y1 &&
126 x >= clickable_rects[i].x1 &&
127 y <= clickable_rects[i].y2 &&
128 x <= clickable_rects[i].x2
129 )
130 {
131 return &clickable_rects[i];
132 }
133 }
134
135 return NULL;
136 }
137