Initial code commit
This commit is contained in:
410
src/c/main.c
Normal file
410
src/c/main.c
Normal file
@@ -0,0 +1,410 @@
|
||||
#include <pebble.h>
|
||||
|
||||
#define ITEM_COUNT 33
|
||||
|
||||
static Window *s_main_window;
|
||||
static Window *s_cidr_window;
|
||||
static Window *s_mask_window;
|
||||
static Window *s_result_window;
|
||||
|
||||
static MenuLayer *s_main_menu_layer;
|
||||
static MenuLayer *s_cidr_menu_layer;
|
||||
static MenuLayer *s_mask_menu_layer;
|
||||
static TextLayer *s_result_text_layer;
|
||||
static TextLayer *s_bitwidth_text_layer;
|
||||
|
||||
static char s_result_text[192];
|
||||
static char s_bitwidth_text[36];
|
||||
static uint8_t s_current_prefix = 24;
|
||||
static bool s_up_boundary_haptic_sent = false;
|
||||
static bool s_down_boundary_haptic_sent = false;
|
||||
|
||||
static void trigger_soft_boundary_haptic(void) {
|
||||
static const uint32_t segments[] = {45};
|
||||
VibePattern pattern = {
|
||||
.durations = segments,
|
||||
.num_segments = ARRAY_LENGTH(segments),
|
||||
};
|
||||
vibes_enqueue_custom_pattern(pattern);
|
||||
}
|
||||
|
||||
static uint32_t cidr_to_mask_uint8(int prefix) {
|
||||
if (prefix <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (prefix >= 32) {
|
||||
return 0xFFFFFFFFu;
|
||||
}
|
||||
return 0xFFFFFFFFu << (32 - prefix);
|
||||
}
|
||||
|
||||
static void mask_to_dotted(uint32_t mask, char *out, size_t out_len) {
|
||||
uint8_t o1 = (uint8_t)((mask >> 24) & 0xFF);
|
||||
uint8_t o2 = (uint8_t)((mask >> 16) & 0xFF);
|
||||
uint8_t o3 = (uint8_t)((mask >> 8) & 0xFF);
|
||||
uint8_t o4 = (uint8_t)(mask & 0xFF);
|
||||
snprintf(out, out_len, "%u.%u.%u.%u", o1, o2, o3, o4);
|
||||
}
|
||||
|
||||
static int index_to_prefix(int index) {
|
||||
return 32 - index;
|
||||
}
|
||||
|
||||
static uint64_t prefix_to_usable_hosts(int prefix) {
|
||||
if (prefix == 31) {
|
||||
return 2;
|
||||
}
|
||||
if (prefix == 32) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t total = 1ULL << (32 - prefix);
|
||||
return total - 2ULL;
|
||||
}
|
||||
|
||||
static void layout_result_text_layer(void) {
|
||||
if (!s_result_text_layer || !s_bitwidth_text_layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
Layer *root = window_get_root_layer(s_result_window);
|
||||
GRect bounds = layer_get_bounds(root);
|
||||
const int16_t side_margin = 6;
|
||||
const int16_t width = bounds.size.w - (2 * side_margin);
|
||||
const int16_t gap = 2;
|
||||
|
||||
GSize main_size = text_layer_get_content_size(s_result_text_layer);
|
||||
GSize bit_size = text_layer_get_content_size(s_bitwidth_text_layer);
|
||||
|
||||
int16_t main_h = main_size.h;
|
||||
int16_t bit_h = bit_size.h;
|
||||
|
||||
if (main_h < 1) {
|
||||
main_h = 1;
|
||||
}
|
||||
if (bit_h < 1) {
|
||||
bit_h = 1;
|
||||
}
|
||||
|
||||
int16_t total_h = main_h + gap + bit_h;
|
||||
if (total_h > bounds.size.h - 10) {
|
||||
total_h = bounds.size.h - 10;
|
||||
}
|
||||
|
||||
int16_t start_y = (bounds.size.h - total_h) / 2;
|
||||
layer_set_frame(text_layer_get_layer(s_result_text_layer), GRect(side_margin, start_y, width, main_h));
|
||||
layer_set_frame(text_layer_get_layer(s_bitwidth_text_layer), GRect(side_margin, start_y + main_h + gap, width, bit_h));
|
||||
}
|
||||
|
||||
static void update_result_text(void) {
|
||||
uint32_t mask = cidr_to_mask_uint8(s_current_prefix);
|
||||
char mask_text[20];
|
||||
uint64_t usable_hosts = prefix_to_usable_hosts(s_current_prefix);
|
||||
int selected_row = 32 - s_current_prefix;
|
||||
|
||||
mask_to_dotted(mask, mask_text, sizeof(mask_text));
|
||||
|
||||
snprintf(
|
||||
s_result_text,
|
||||
sizeof(s_result_text),
|
||||
"CIDR: /%d\nMask: %s\nUsable hosts: %llu",
|
||||
s_current_prefix,
|
||||
mask_text,
|
||||
usable_hosts);
|
||||
|
||||
int pos = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
s_bitwidth_text[pos++] = (i < s_current_prefix) ? '1' : '0';
|
||||
if (i == 7 || i == 15 || i == 23) {
|
||||
s_bitwidth_text[pos++] = '.';
|
||||
}
|
||||
}
|
||||
s_bitwidth_text[pos] = '\0';
|
||||
|
||||
if (s_result_text_layer && s_bitwidth_text_layer) {
|
||||
text_layer_set_text(s_result_text_layer, s_result_text);
|
||||
text_layer_set_text(s_bitwidth_text_layer, s_bitwidth_text);
|
||||
layout_result_text_layer();
|
||||
}
|
||||
|
||||
if (s_cidr_menu_layer) {
|
||||
menu_layer_set_selected_index(s_cidr_menu_layer, (MenuIndex){.section = 0, .row = (uint16_t)selected_row}, MenuRowAlignCenter, false);
|
||||
}
|
||||
if (s_mask_menu_layer) {
|
||||
menu_layer_set_selected_index(s_mask_menu_layer, (MenuIndex){.section = 0, .row = (uint16_t)selected_row}, MenuRowAlignCenter, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_result_for_prefix(int prefix) {
|
||||
if (prefix < 0) {
|
||||
prefix = 0;
|
||||
}
|
||||
if (prefix > 32) {
|
||||
prefix = 32;
|
||||
}
|
||||
|
||||
s_current_prefix = prefix;
|
||||
s_up_boundary_haptic_sent = false;
|
||||
s_down_boundary_haptic_sent = false;
|
||||
|
||||
// Push first so the result window/text layer is guaranteed to be loaded.
|
||||
window_stack_push(s_result_window, true);
|
||||
|
||||
update_result_text();
|
||||
}
|
||||
|
||||
static void result_up_click_handler(ClickRecognizerRef recognizer, void *context) {
|
||||
(void)recognizer;
|
||||
(void)context;
|
||||
|
||||
if (s_current_prefix < 32) {
|
||||
s_current_prefix++;
|
||||
update_result_text();
|
||||
|
||||
s_down_boundary_haptic_sent = false;
|
||||
if (s_current_prefix == 32 && !s_up_boundary_haptic_sent) {
|
||||
trigger_soft_boundary_haptic();
|
||||
s_up_boundary_haptic_sent = true;
|
||||
} else if (s_current_prefix < 32) {
|
||||
s_up_boundary_haptic_sent = false;
|
||||
}
|
||||
} else if (!s_up_boundary_haptic_sent) {
|
||||
trigger_soft_boundary_haptic();
|
||||
s_up_boundary_haptic_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void result_down_click_handler(ClickRecognizerRef recognizer, void *context) {
|
||||
(void)recognizer;
|
||||
(void)context;
|
||||
|
||||
if (s_current_prefix > 0) {
|
||||
s_current_prefix--;
|
||||
update_result_text();
|
||||
|
||||
s_up_boundary_haptic_sent = false;
|
||||
if (s_current_prefix == 0 && !s_down_boundary_haptic_sent) {
|
||||
trigger_soft_boundary_haptic();
|
||||
s_down_boundary_haptic_sent = true;
|
||||
} else if (s_current_prefix > 0) {
|
||||
s_down_boundary_haptic_sent = false;
|
||||
}
|
||||
} else if (!s_down_boundary_haptic_sent) {
|
||||
trigger_soft_boundary_haptic();
|
||||
s_down_boundary_haptic_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void result_click_config_provider(void *context) {
|
||||
(void)context;
|
||||
window_single_repeating_click_subscribe(BUTTON_ID_UP, 120, result_up_click_handler);
|
||||
window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 120, result_down_click_handler);
|
||||
}
|
||||
|
||||
static uint16_t main_get_num_rows(MenuLayer *menu_layer, uint16_t section_index, void *context) {
|
||||
(void)menu_layer;
|
||||
(void)section_index;
|
||||
(void)context;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void main_draw_row(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *context) {
|
||||
(void)context;
|
||||
switch (cell_index->row) {
|
||||
case 0:
|
||||
menu_cell_basic_draw(ctx, cell_layer, "CIDR", NULL, NULL);
|
||||
break;
|
||||
case 1:
|
||||
menu_cell_basic_draw(ctx, cell_layer, "Mask", NULL, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void main_select(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) {
|
||||
(void)menu_layer;
|
||||
(void)context;
|
||||
if (cell_index->row == 0) {
|
||||
window_stack_push(s_cidr_window, true);
|
||||
} else {
|
||||
window_stack_push(s_mask_window, true);
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t value_get_num_rows(MenuLayer *menu_layer, uint16_t section_index, void *context) {
|
||||
(void)menu_layer;
|
||||
(void)section_index;
|
||||
(void)context;
|
||||
return ITEM_COUNT;
|
||||
}
|
||||
|
||||
static void cidr_draw_row(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *context) {
|
||||
(void)context;
|
||||
int prefix = index_to_prefix(cell_index->row);
|
||||
static char cidr_label[8];
|
||||
snprintf(cidr_label, sizeof(cidr_label), "/%d", prefix);
|
||||
menu_cell_basic_draw(ctx, cell_layer, cidr_label, NULL, NULL);
|
||||
}
|
||||
|
||||
static void mask_draw_row(GContext *ctx, const Layer *cell_layer, MenuIndex *cell_index, void *context) {
|
||||
(void)context;
|
||||
int prefix = index_to_prefix(cell_index->row);
|
||||
uint32_t mask = cidr_to_mask_uint8(prefix);
|
||||
static char mask_label[20];
|
||||
mask_to_dotted(mask, mask_label, sizeof(mask_label));
|
||||
menu_cell_basic_draw(ctx, cell_layer, mask_label, NULL, NULL);
|
||||
}
|
||||
|
||||
static void cidr_select(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) {
|
||||
(void)menu_layer;
|
||||
(void)context;
|
||||
int prefix = index_to_prefix(cell_index->row);
|
||||
show_result_for_prefix(prefix);
|
||||
}
|
||||
|
||||
static void mask_select(MenuLayer *menu_layer, MenuIndex *cell_index, void *context) {
|
||||
(void)menu_layer;
|
||||
(void)context;
|
||||
int prefix = index_to_prefix(cell_index->row);
|
||||
show_result_for_prefix(prefix);
|
||||
}
|
||||
|
||||
static void main_window_load(Window *window) {
|
||||
Layer *window_layer = window_get_root_layer(window);
|
||||
GRect bounds = layer_get_bounds(window_layer);
|
||||
|
||||
s_main_menu_layer = menu_layer_create(bounds);
|
||||
menu_layer_set_callbacks(s_main_menu_layer, NULL, (MenuLayerCallbacks){
|
||||
.get_num_rows = main_get_num_rows,
|
||||
.draw_row = main_draw_row,
|
||||
.select_click = main_select,
|
||||
});
|
||||
menu_layer_set_click_config_onto_window(s_main_menu_layer, window);
|
||||
layer_add_child(window_layer, menu_layer_get_layer(s_main_menu_layer));
|
||||
}
|
||||
|
||||
static void main_window_unload(Window *window) {
|
||||
(void)window;
|
||||
menu_layer_destroy(s_main_menu_layer);
|
||||
s_main_menu_layer = NULL;
|
||||
}
|
||||
|
||||
static void cidr_window_load(Window *window) {
|
||||
Layer *window_layer = window_get_root_layer(window);
|
||||
GRect bounds = layer_get_bounds(window_layer);
|
||||
|
||||
s_cidr_menu_layer = menu_layer_create(bounds);
|
||||
menu_layer_set_callbacks(s_cidr_menu_layer, NULL, (MenuLayerCallbacks){
|
||||
.get_num_rows = value_get_num_rows,
|
||||
.draw_row = cidr_draw_row,
|
||||
.select_click = cidr_select,
|
||||
});
|
||||
menu_layer_set_click_config_onto_window(s_cidr_menu_layer, window);
|
||||
menu_layer_set_selected_index(s_cidr_menu_layer, (MenuIndex){.section = 0, .row = (uint16_t)(32 - 24)}, MenuRowAlignCenter, false);
|
||||
layer_add_child(window_layer, menu_layer_get_layer(s_cidr_menu_layer));
|
||||
}
|
||||
|
||||
static void cidr_window_unload(Window *window) {
|
||||
(void)window;
|
||||
menu_layer_destroy(s_cidr_menu_layer);
|
||||
s_cidr_menu_layer = NULL;
|
||||
}
|
||||
|
||||
static void mask_window_load(Window *window) {
|
||||
Layer *window_layer = window_get_root_layer(window);
|
||||
GRect bounds = layer_get_bounds(window_layer);
|
||||
|
||||
s_mask_menu_layer = menu_layer_create(bounds);
|
||||
menu_layer_set_callbacks(s_mask_menu_layer, NULL, (MenuLayerCallbacks){
|
||||
.get_num_rows = value_get_num_rows,
|
||||
.draw_row = mask_draw_row,
|
||||
.select_click = mask_select,
|
||||
});
|
||||
menu_layer_set_click_config_onto_window(s_mask_menu_layer, window);
|
||||
menu_layer_set_selected_index(s_mask_menu_layer, (MenuIndex){.section = 0, .row = (uint16_t)(32 - 24)}, MenuRowAlignCenter, false);
|
||||
layer_add_child(window_layer, menu_layer_get_layer(s_mask_menu_layer));
|
||||
}
|
||||
|
||||
static void mask_window_unload(Window *window) {
|
||||
(void)window;
|
||||
menu_layer_destroy(s_mask_menu_layer);
|
||||
s_mask_menu_layer = NULL;
|
||||
}
|
||||
|
||||
static void result_window_load(Window *window) {
|
||||
s_result_text_layer = text_layer_create(GRect(0, 0, PBL_DISPLAY_WIDTH, 24));
|
||||
text_layer_set_background_color(s_result_text_layer, GColorClear);
|
||||
text_layer_set_text_color(s_result_text_layer, GColorWhite);
|
||||
text_layer_set_font(s_result_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
|
||||
text_layer_set_overflow_mode(s_result_text_layer, GTextOverflowModeWordWrap);
|
||||
text_layer_set_text(s_result_text_layer, s_result_text);
|
||||
|
||||
s_bitwidth_text_layer = text_layer_create(GRect(0, 0, PBL_DISPLAY_WIDTH, 14));
|
||||
text_layer_set_background_color(s_bitwidth_text_layer, GColorClear);
|
||||
text_layer_set_text_color(s_bitwidth_text_layer, GColorLightGray);
|
||||
text_layer_set_font(s_bitwidth_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_09));
|
||||
text_layer_set_text(s_bitwidth_text_layer, s_bitwidth_text);
|
||||
|
||||
#if PBL_ROUND
|
||||
text_layer_set_text_alignment(s_result_text_layer, GTextAlignmentCenter);
|
||||
text_layer_set_text_alignment(s_bitwidth_text_layer, GTextAlignmentCenter);
|
||||
#endif
|
||||
|
||||
Layer *window_layer = window_get_root_layer(window);
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_result_text_layer));
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_bitwidth_text_layer));
|
||||
update_result_text();
|
||||
}
|
||||
|
||||
static void result_window_unload(Window *window) {
|
||||
(void)window;
|
||||
text_layer_destroy(s_result_text_layer);
|
||||
text_layer_destroy(s_bitwidth_text_layer);
|
||||
s_result_text_layer = NULL;
|
||||
s_bitwidth_text_layer = NULL;
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
s_main_window = window_create();
|
||||
window_set_window_handlers(s_main_window, (WindowHandlers){
|
||||
.load = main_window_load,
|
||||
.unload = main_window_unload,
|
||||
});
|
||||
window_set_background_color(s_main_window, GColorBlack);
|
||||
|
||||
s_cidr_window = window_create();
|
||||
window_set_window_handlers(s_cidr_window, (WindowHandlers){
|
||||
.load = cidr_window_load,
|
||||
.unload = cidr_window_unload,
|
||||
});
|
||||
window_set_background_color(s_cidr_window, GColorBlack);
|
||||
|
||||
s_mask_window = window_create();
|
||||
window_set_window_handlers(s_mask_window, (WindowHandlers){
|
||||
.load = mask_window_load,
|
||||
.unload = mask_window_unload,
|
||||
});
|
||||
window_set_background_color(s_mask_window, GColorBlack);
|
||||
|
||||
s_result_window = window_create();
|
||||
window_set_window_handlers(s_result_window, (WindowHandlers){
|
||||
.load = result_window_load,
|
||||
.unload = result_window_unload,
|
||||
});
|
||||
window_set_background_color(s_result_window, GColorBlack);
|
||||
window_set_click_config_provider(s_result_window, result_click_config_provider);
|
||||
|
||||
window_stack_push(s_main_window, true);
|
||||
}
|
||||
|
||||
static void deinit(void) {
|
||||
window_destroy(s_result_window);
|
||||
window_destroy(s_mask_window);
|
||||
window_destroy(s_cidr_window);
|
||||
window_destroy(s_main_window);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
init();
|
||||
app_event_loop();
|
||||
deinit();
|
||||
}
|
||||
Reference in New Issue
Block a user