Initial code commit
This commit is contained in:
7
.clang-format
Normal file
7
.clang-format
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
UseTab: ForIndentation
|
||||
ColumnLimit: 0
|
||||
...
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/.clangd
|
||||
/build
|
||||
/.lock*
|
||||
18
clangd.zsh
Executable file
18
clangd.zsh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env zsh
|
||||
echo "CompileFlags:
|
||||
Add:
|
||||
[
|
||||
-DPBL_DISPLAY_WIDTH=200,
|
||||
-DPBL_DISPLAY_HEIGHT=228,
|
||||
-xc,
|
||||
-nostdinc,
|
||||
-DPBL_COLOR,
|
||||
-I${HOME}/.pebble-sdk/SDKs/current/sdk-core/pebble/emery/include,
|
||||
-include$(pwd)/build/include/message_keys.auto.h,
|
||||
-I$(pwd)/build/emery,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/stdint.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/stdlib.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/time.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/include/stddef.h,
|
||||
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/include/stdbool.h,
|
||||
]" > ./.clangd
|
||||
29
package.json
Normal file
29
package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"author": "RandyTheSilly",
|
||||
"keywords": [
|
||||
"networking",
|
||||
"subnet",
|
||||
"converter"
|
||||
],
|
||||
"name": "subnet-helper",
|
||||
"pebble": {
|
||||
"displayName": "Subnet Helper",
|
||||
"enableMultiJS": false,
|
||||
"projectType": "native",
|
||||
"sdkVersion": "3",
|
||||
"targetPlatforms": [
|
||||
"aplite",
|
||||
"basalt",
|
||||
"chalk",
|
||||
"diorite",
|
||||
"flint",
|
||||
"emery",
|
||||
"gabbro"
|
||||
],
|
||||
"uuid": "09853807-57bd-47e5-8684-e8f2bb3eecad",
|
||||
"watchapp": {
|
||||
"watchface": false
|
||||
}
|
||||
},
|
||||
"version": "1.0.0"
|
||||
}
|
||||
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();
|
||||
}
|
||||
36
wscript
Normal file
36
wscript
Normal file
@@ -0,0 +1,36 @@
|
||||
import os.path
|
||||
|
||||
top = '.'
|
||||
out = 'build'
|
||||
|
||||
def options(ctx):
|
||||
ctx.load('pebble_sdk')
|
||||
|
||||
def configure(ctx):
|
||||
ctx.load('pebble_sdk')
|
||||
|
||||
def build(ctx):
|
||||
ctx.load('pebble_sdk')
|
||||
build_worker = os.path.exists('worker_src')
|
||||
binaries = []
|
||||
cached_env = ctx.env
|
||||
for platform in ctx.env.TARGET_PLATFORMS:
|
||||
ctx.env = ctx.all_envs[platform]
|
||||
ctx.set_group(ctx.env.PLATFORM_NAME)
|
||||
app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
|
||||
ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'),
|
||||
target=app_elf, bin_type='app')
|
||||
if build_worker:
|
||||
worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
|
||||
binaries.append({'platform': platform, 'app_elf': app_elf,
|
||||
'worker_elf': worker_elf})
|
||||
ctx.pbl_build(source=ctx.path.ant_glob('worker_src/c/**/*.c'),
|
||||
target=worker_elf, bin_type='worker')
|
||||
else:
|
||||
binaries.append({'platform': platform, 'app_elf': app_elf})
|
||||
ctx.env = cached_env
|
||||
ctx.set_group('bundle')
|
||||
ctx.pbl_bundle(binaries=binaries,
|
||||
js=ctx.path.ant_glob(['src/pkjs/**/*.js',
|
||||
'src/pkjs/**/*.json']),
|
||||
js_entry_file='src/pkjs/index.js')
|
||||
Reference in New Issue
Block a user