1#include <stdlib.h> 2#include <string.h> 3#include "mixer_clickable.h" 4 5extern int screen_cols; 6extern int screen_lines; 7 8static struct clickable_rect *clickable_rects = NULL; 9static unsigned int clickable_rects_count = 0; 10static 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 20void 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 56SET_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 67void 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 77void 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 119struct 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