Initial code commit

This commit is contained in:
2026-04-11 23:05:26 -04:00
parent a0aab7bce5
commit fab03edcb1
6 changed files with 503 additions and 0 deletions

410
src/c/main.c Normal file
View 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();
}