├── icon └── icon.png ├── fonts ├── Oswald-Bold.ttf └── OFL.txt ├── .github └── docs │ └── preview.png ├── .gitignore ├── .vscode └── settings.json ├── config.json ├── launch.sh ├── src ├── main.c ├── destroy.c ├── init.c ├── pollevent.c ├── render.c └── draw.c ├── include ├── KeyMap.h └── FlipClock.h ├── README.md └── Makefile /icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaeSeoKim/sdl-flip-clock/HEAD/icon/icon.png -------------------------------------------------------------------------------- /fonts/Oswald-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaeSeoKim/sdl-flip-clock/HEAD/fonts/Oswald-Bold.ttf -------------------------------------------------------------------------------- /.github/docs/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JaeSeoKim/sdl-flip-clock/HEAD/.github/docs/preview.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Trash files 2 | .DS_Store 3 | 4 | # output 5 | objs 6 | flipClock 7 | FlipClock-v*.zip 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "functional": "c", 4 | "flipclock.h": "c" 5 | }, 6 | "color-highlight.useARGB": true, 7 | } 8 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Flip Clock", 3 | "icon": "icon/icon.png", 4 | "launch": "launch.sh", 5 | "description": "Simple Filp Clock by JaeSeoKim (v1.1)" 6 | } 7 | -------------------------------------------------------------------------------- /launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd /mnt/SDCARD/App/FlipClock/ 3 | 4 | if pgrep flipClock > /dev/null; then 5 | killall -9 flipClock 6 | fi 7 | 8 | /mnt/SDCARD/App/FlipClock/flipClock 9 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "FlipClock.h" 2 | 3 | FlipClock _G = { 4 | 640, // DISPLAY_WIDTH 5 | 480, // DISPLAY_HEIGHT 6 | NULL, // Fill the remaining fields with NULL 7 | }; 8 | 9 | void quit(int exitcode) { 10 | destroy(); 11 | exit(exitcode); 12 | } 13 | 14 | int main() { 15 | int ret = EXIT_SUCCESS; 16 | signal(SIGTERM, quit); 17 | signal(SIGSEGV, quit); 18 | 19 | if (init() < 0) { 20 | printf("init failed\n"); 21 | ret = EXIT_FAILURE; 22 | } 23 | 24 | if (draw_loop() < 0) { 25 | printf("draw_lopp failed\n"); 26 | ret = EXIT_FAILURE; 27 | } 28 | 29 | destroy(); 30 | return ret; 31 | } 32 | -------------------------------------------------------------------------------- /src/destroy.c: -------------------------------------------------------------------------------- 1 | #include "FlipClock.h" 2 | 3 | void destroy() { 4 | printf("##Start destory\n"); 5 | 6 | if (access("/tmp/stay_awake", F_OK) != -1) { 7 | printf("\tStop stay awake\n"); 8 | remove("/tmp/stay_awake"); 9 | } 10 | 11 | if (_G.renderer != NULL) { 12 | printf("\tdestorying renderer...\n"); 13 | #if defined SDL && SDL == 1 14 | SDL_FreeSurface(_G.renderer); 15 | #else 16 | SDL_DestroyRenderer(_G.renderer); 17 | #endif 18 | } 19 | 20 | if (_G.window != NULL) { 21 | printf("\tdestorying window...\n"); 22 | #if defined SDL && SDL == 1 23 | SDL_FreeSurface(_G.window); 24 | #else 25 | SDL_DestroyWindow(_G.window); 26 | #endif 27 | } 28 | 29 | if (_G.timeFont != NULL) { 30 | printf("\tClosing font...\n"); 31 | TTF_CloseFont(_G.timeFont); 32 | } 33 | if (_G.ampmFont != NULL) { 34 | printf("\tClosing smFont...\n"); 35 | TTF_CloseFont(_G.ampmFont); 36 | } 37 | 38 | TTF_Quit(); 39 | SDL_Quit(); 40 | printf("##Finish destory\n\n"); 41 | } 42 | -------------------------------------------------------------------------------- /include/KeyMap.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYMAP_H__ 2 | #define KEYMAP_H__ 3 | 4 | #include "FlipClock.h" 5 | 6 | #if SDL != 1 7 | typedef SDL_Keycode SDLKey; 8 | #endif 9 | 10 | #define BTN_UP SDLK_UP 11 | #define BTN_UP_INDEX 0 12 | #define BTN_DOWN SDLK_DOWN 13 | #define BTN_DOWN_INDEX 1 14 | #define BTN_LEFT SDLK_LEFT 15 | #define BTN_LEFT_INDEX 2 16 | #define BTN_RIGHT SDLK_RIGHT 17 | #define BTN_RIGHT_INDEX 3 18 | #define BTN_A SDLK_SPACE 19 | #define BTN_A_INDEX 4 20 | #define BTN_B SDLK_LCTRL 21 | #define BTN_B_INDEX 5 22 | #define BTN_X SDLK_LSHIFT 23 | #define BTN_X_INDEX 6 24 | #define BTN_Y SDLK_LALT 25 | #define BTN_Y_INDEX 7 26 | #define BTN_L1 SDLK_e 27 | #define BTN_L1_INDEX 8 28 | #define BTN_R1 SDLK_t 29 | #define BTN_R1_INDEX 9 30 | #define BTN_L2 SDLK_TAB 31 | #define BTN_L2_INDEX 10 32 | #define BTN_R2 SDLK_BACKSPACE 33 | #define BTN_R2_INDEX 11 34 | #define BTN_SELECT SDLK_RCTRL 35 | #define BTN_SELECT_INDEX 12 36 | #define BTN_START SDLK_RETURN 37 | #define BTN_START_INDEX 13 38 | #define BTN_MENU SDLK_ESCAPE 39 | #define BTN_MENU_INDEX 14 40 | #define BTN_POWER SDLK_UNKNOWN // SDLK_FIRST = 0; SDLK_UNKNOWN = 0; 41 | #define BTN_POWER_INDEX 15 42 | 43 | #endif // KEYMAP_H__ 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

SDL Flip Clock for the MiyooMini series

2 | 3 |
4 | 8 |
9 | 10 | This is a simple Flip Clock created using SDL. 11 | 12 | ## Features 13 | 14 | - Always awake 15 | - Quit the program using Menu + Select 16 | - Change 12/24 mode using Start 17 | - And ... that's it all! 18 | 19 | ## Download 20 | 21 | [Click here to get the last version released](https://github.com/JaeSeoKim/sdl-flip-clock/releases) 22 | 23 | ## Installation 24 | 25 | ### To use with Onion-OS 26 | 27 | 1. Download and unzip the latest release file 28 | 2. Move the unzipped folder to the `/App` path on the SD card 29 | 30 | > [!IMPORTANT] 31 | > The directory name must be `FlipClock`! 32 | 33 | 3. Boot up your Miyoomini and run `Flip Clock` in the Apps section! 34 | 35 | ## ChangeLogs 36 | 37 | ### v1.1 38 | 39 | #### Features 40 | 41 | - Change 12/24 mode using Start 42 | 43 | #### Fix 44 | 45 | - Improve Performance(6fps -> 58fps) 46 | - refactor `RenderFillCircle` 47 | 48 | #### Etc 49 | 50 | - Refactor bunch of codes... 51 | 52 | ### v1.0 53 | 54 | #### Features 55 | 56 | - Always awake 57 | - Quit the program using Menu + Select 58 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET=flipClock 2 | VERSION=1.1 3 | 4 | CC=$(CROSS_COMPILE)gcc 5 | # CFLAGS+=-I$(INC_DIR) -Werror 6 | CFLAGS+=-I$(INC_DIR) 7 | 8 | ifeq ($(MIYOOMINI),1) 9 | CFLAGS+=-DSDL=1 10 | LDLIBS=-DSDL=1 -lSDL -lpthread -lSDL_ttf 11 | else 12 | LDLIBS=-lSDL2 -lpthread -lSDL2_ttf 13 | endif 14 | 15 | ifeq ($(MIYOOMINI),1) 16 | CROSS_COMPILE?=~/buildroot/output/host/bin/arm-linux-gnueabihf- 17 | SYSROOT ?= $(shell ${CC} --print-sysroot) 18 | ARCH=-marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7ve+simd 19 | CFLAGS+=-Os $(ARCH) -I${SYSROOT}/usr/include -ffunction-sections -fdata-sections -Wall 20 | LDFLAGS+=-I${SYSROOT}/usr/lib $(ARCH) -Bstatic,-lutil,-Bdynamic 21 | endif 22 | 23 | RM=rm 24 | RMFLAGS=-rf 25 | 26 | MKDIR=mkdir 27 | MKDIRFLAGS=-p 28 | 29 | OUTPUT_DIR=build 30 | INC_DIR=include 31 | SRC_DIR=src 32 | OBJ_DIR=objs 33 | 34 | SRCS=$(wildcard $(SRC_DIR)/*.c) 35 | OBJS=$(addprefix $(OBJ_DIR)/, $(notdir $(SRCS:.c=.o))) 36 | 37 | all: $(TARGET) 38 | 39 | re: clean all 40 | 41 | clean: 42 | $(RM) $(RMFLAGS) $(OBJS) $(TARGET) FlipClock-v$(VERSION).zip 43 | 44 | $(TARGET): ${OBJS} 45 | $(CC) $(LDFLAGS) $(LDLIBS) $(OBJS) -o $(TARGET) 46 | 47 | $(OBJ_DIR)/%.o : $(SRC_DIR)/%.c | $(OBJ_DIR) 48 | $(CC) $(CFLAGS) -c $< -o $@ 49 | 50 | $(OBJ_DIR): 51 | @$(MKDIR) $(MKDIRFLAGS) $(OBJ_DIR) 52 | 53 | pack: FlipClock-v$(VERSION).zip 54 | 55 | FlipClock-v$(VERSION).zip: $(TARGET) 56 | zip -r FlipClock-v$(VERSION).zip fonts flipClock icon config.json launch.sh 57 | 58 | 59 | .PHONY: all clean re 60 | -------------------------------------------------------------------------------- /include/FlipClock.h: -------------------------------------------------------------------------------- 1 | #ifndef FLIP_CLOCK_H__ 2 | #define FLIP_CLOCK_H__ 3 | 4 | #if defined SDL && SDL == 1 5 | #include "SDL/SDL.h" 6 | #include "SDL/SDL_ttf.h" 7 | #else 8 | #include "SDL2/SDL.h" 9 | #include "SDL2/SDL_ttf.h" 10 | #endif 11 | 12 | #include "fcntl.h" 13 | #include "signal.h" 14 | #include "stdio.h" 15 | #include "stdlib.h" 16 | #include "time.h" 17 | #include "unistd.h" 18 | 19 | #include "KeyMap.h" 20 | 21 | #if defined SDL && SDL == 1 22 | typedef SDL_Surface SDL_Window; 23 | typedef SDL_Surface SDL_Renderer; 24 | #endif 25 | 26 | #define FRAME_RATE 60.0f 27 | 28 | #define FLIP_COLOR 0xFF222222 29 | #define FONT_COLOR 0xFFbababa 30 | #define BG_COLOR 0xFF000000 31 | 32 | struct FlipClock { 33 | int32_t DISPLAY_WIDTH; 34 | int32_t DISPLAY_HEIGHT; 35 | SDL_Window *window; 36 | SDL_Renderer *renderer; 37 | TTF_Font *timeFont; 38 | int timeFontDescent; 39 | TTF_Font *ampmFont; 40 | int ampmFontDescent; 41 | int KEY_STATUS[16]; 42 | int quit; 43 | int mode; 44 | int lastTickCount; 45 | int curTickCount; 46 | int GAP; 47 | int DIVIDER; 48 | int MARGIN_X; 49 | int FLIP_SIZE; 50 | int FLIP_PADDING; 51 | int TIME_SIZE; 52 | int AMPM_SIZE; 53 | } typedef FlipClock; 54 | 55 | #define is_active_mode(MODE) _G.mode & (1 << (MODE)) 56 | #define MODE_AMPM 0 57 | #define MODE_FPS 1 58 | 59 | #define INPUT_DELAY 300; 60 | 61 | extern FlipClock _G; 62 | 63 | // init.c 64 | int init(); 65 | int init_display_Resolution(); 66 | 67 | // destroy.c 68 | void destroy(); 69 | 70 | // draw.c 71 | int draw_loop(); 72 | int handle_key(); 73 | int draw_screen(float fps); 74 | void draw_flip(int x, int y); 75 | void draw_time(int x, int y, int time); 76 | void draw_ampm(int x, int y, int hour); 77 | void draw_divider(); 78 | void draw_fps(float fps); 79 | struct tm *get_local_time(); 80 | 81 | // pollevent.c 82 | void pollevent(); 83 | 84 | // render.c 85 | void RenderFillRoundedRect(SDL_Renderer *renderer, int x, int y, int w, int h, 86 | int percent, Uint32 color); 87 | void RenderFillCircle(SDL_Renderer *renderer, int cx, int cy, int radius, 88 | Uint32 color); 89 | int RenderFillRect(SDL_Renderer *dst, SDL_Rect *rect, Uint32 color); 90 | int RenderClear(SDL_Renderer *dst, Uint32 color); 91 | int RenderPresent(SDL_Renderer *renderer, SDL_Window *window); 92 | int BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Renderer *dst, 93 | SDL_Rect *dstrect); 94 | SDL_Surface *RenderText_Blended(TTF_Font *font, const char *text, Uint32 color); 95 | SDL_Color uint32ToSDLColor(Uint32 color); 96 | 97 | #endif // FLIP_CLOCK_H__ 98 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include "FlipClock.h" 2 | 3 | int init() { 4 | printf("##Start initialization\n"); 5 | 6 | if (access("/tmp/stay_awake", F_OK) == -1) { 7 | printf("\tSet stay awake\n"); 8 | close(creat("/tmp/stay_awake", 666)); 9 | } 10 | 11 | if (SDL_Init(SDL_INIT_VIDEO) < 0) { 12 | printf("\tSDL_Init Fail: %s\n", SDL_GetError()); 13 | return -1; 14 | } 15 | 16 | if (TTF_Init() < 0) { 17 | printf("\tTTF_Init Fail: %s\n", TTF_GetError()); 18 | return -1; 19 | } 20 | 21 | if (init_display_Resolution() < 0) { 22 | return -1; 23 | } 24 | printf("\tDisplay Resolution: %dx%d\n", _G.DISPLAY_WIDTH, _G.DISPLAY_HEIGHT); 25 | 26 | _G.mode = 1 << MODE_AMPM; 27 | 28 | _G.MARGIN_X = _G.DISPLAY_WIDTH / 20; 29 | _G.GAP = 16; 30 | _G.DIVIDER = 4; 31 | _G.FLIP_SIZE = (_G.DISPLAY_WIDTH - _G.MARGIN_X * 2 - _G.GAP) / 2; 32 | _G.FLIP_PADDING = 32; 33 | _G.TIME_SIZE = _G.FLIP_SIZE - _G.FLIP_PADDING * 2; 34 | _G.AMPM_SIZE = _G.TIME_SIZE / 10; 35 | 36 | #if defined SDL && SDL == 1 37 | const char *FONT_LOCATION = "/mnt/SDCARD/App/FlipClock/fonts/Oswald-Bold.ttf"; 38 | #else 39 | const char *FONT_LOCATION = "./fonts/Oswald-Bold.ttf"; 40 | #endif 41 | _G.timeFont = TTF_OpenFont(FONT_LOCATION, _G.TIME_SIZE); 42 | _G.ampmFont = TTF_OpenFont(FONT_LOCATION, _G.AMPM_SIZE); 43 | if (_G.timeFont == NULL || _G.ampmFont == NULL) { 44 | printf("\tTTF_OpenFont Fail: %s\n", TTF_GetError()); 45 | return -1; 46 | } 47 | _G.timeFontDescent = TTF_FontDescent(_G.timeFont); 48 | _G.ampmFontDescent = TTF_FontDescent(_G.ampmFont); 49 | 50 | #if defined SDL && SDL == 1 51 | _G.window = 52 | SDL_SetVideoMode(_G.DISPLAY_WIDTH, _G.DISPLAY_HEIGHT, 32, SDL_HWSURFACE); 53 | #else 54 | _G.window = SDL_CreateWindow("Flip Clock", SDL_WINDOWPOS_UNDEFINED, 55 | SDL_WINDOWPOS_UNDEFINED, _G.DISPLAY_WIDTH, 56 | _G.DISPLAY_HEIGHT, SDL_WINDOW_SHOWN); 57 | #endif 58 | if (_G.window == NULL) { 59 | printf("\tSDL_SetVideoMode Fail: %s\n", SDL_GetError()); 60 | return -1; 61 | } 62 | 63 | #if defined SDL && SDL == 1 64 | _G.renderer = SDL_CreateRGBSurface(SDL_HWSURFACE, _G.DISPLAY_WIDTH, 65 | _G.DISPLAY_HEIGHT, 32, 0, 0, 0, 0); 66 | #else 67 | _G.renderer = SDL_CreateRenderer(_G.window, -1, 0); 68 | #endif 69 | 70 | if (_G.renderer == NULL) { 71 | printf("\tSDL_CreateRGBSurface Fail: %s\n", SDL_GetError()); 72 | return -1; 73 | } 74 | 75 | printf("##Finish initialization\n\n"); 76 | return 0; 77 | } 78 | 79 | int init_display_Resolution() { 80 | #if defined SDL && SDL == 1 81 | const SDL_VideoInfo *info = SDL_GetVideoInfo(); 82 | _G.DISPLAY_WIDTH = info->current_w; 83 | _G.DISPLAY_HEIGHT = info->current_h; 84 | #else 85 | SDL_DisplayMode mode; 86 | if (SDL_GetDesktopDisplayMode(0, &mode)) { 87 | printf("SDL_GetDesktopDisplayMode Fail: %s\n", SDL_GetError()); 88 | return -1; 89 | } 90 | _G.DISPLAY_WIDTH = mode.w; 91 | _G.DISPLAY_HEIGHT = mode.h; 92 | #endif 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /src/pollevent.c: -------------------------------------------------------------------------------- 1 | #include "FlipClock.h" 2 | 3 | void handle_keydown(SDLKey sym); 4 | void handle_keyup(SDLKey sym); 5 | 6 | void pollevent() { 7 | SDL_Event event; 8 | 9 | while (SDL_PollEvent(&event)) { 10 | switch (event.type) { 11 | case SDL_QUIT: 12 | _G.quit = 1; 13 | break; 14 | case SDL_KEYDOWN: 15 | handle_keydown(event.key.keysym.sym); 16 | break; 17 | case SDL_KEYUP: 18 | handle_keyup(event.key.keysym.sym); 19 | break; 20 | default: 21 | break; 22 | } 23 | } 24 | } 25 | 26 | void handle_keydown(SDLKey sym) { 27 | switch (sym) { 28 | case BTN_UP: 29 | if (_G.KEY_STATUS[BTN_UP_INDEX] == 0) 30 | _G.KEY_STATUS[BTN_UP_INDEX] = _G.curTickCount; 31 | break; 32 | case BTN_DOWN: 33 | if (_G.KEY_STATUS[BTN_DOWN_INDEX] == 0) 34 | _G.KEY_STATUS[BTN_DOWN_INDEX] = _G.curTickCount; 35 | break; 36 | case BTN_LEFT: 37 | if (_G.KEY_STATUS[BTN_LEFT_INDEX] == 0) 38 | _G.KEY_STATUS[BTN_LEFT_INDEX] = _G.curTickCount; 39 | break; 40 | case BTN_RIGHT: 41 | if (_G.KEY_STATUS[BTN_RIGHT_INDEX] == 0) 42 | _G.KEY_STATUS[BTN_RIGHT_INDEX] = _G.curTickCount; 43 | break; 44 | case BTN_A: 45 | if (_G.KEY_STATUS[BTN_A_INDEX] == 0) 46 | _G.KEY_STATUS[BTN_A_INDEX] = _G.curTickCount; 47 | break; 48 | case BTN_B: 49 | if (_G.KEY_STATUS[BTN_B_INDEX] == 0) 50 | _G.KEY_STATUS[BTN_B_INDEX] = _G.curTickCount; 51 | break; 52 | case BTN_X: 53 | if (_G.KEY_STATUS[BTN_X_INDEX] == 0) 54 | _G.KEY_STATUS[BTN_X_INDEX] = _G.curTickCount; 55 | break; 56 | case BTN_Y: 57 | if (_G.KEY_STATUS[BTN_Y_INDEX] == 0) 58 | _G.KEY_STATUS[BTN_Y_INDEX] = _G.curTickCount; 59 | break; 60 | case BTN_L1: 61 | if (_G.KEY_STATUS[BTN_L1_INDEX] == 0) 62 | _G.KEY_STATUS[BTN_L1_INDEX] = _G.curTickCount; 63 | break; 64 | case BTN_R1: 65 | if (_G.KEY_STATUS[BTN_R1_INDEX] == 0) 66 | _G.KEY_STATUS[BTN_R1_INDEX] = _G.curTickCount; 67 | break; 68 | case BTN_L2: 69 | if (_G.KEY_STATUS[BTN_L2_INDEX] == 0) 70 | _G.KEY_STATUS[BTN_L2_INDEX] = _G.curTickCount; 71 | break; 72 | case BTN_R2: 73 | if (_G.KEY_STATUS[BTN_R2_INDEX] == 0) 74 | _G.KEY_STATUS[BTN_R2_INDEX] = _G.curTickCount; 75 | break; 76 | case BTN_SELECT: 77 | if (_G.KEY_STATUS[BTN_SELECT_INDEX] == 0) 78 | _G.KEY_STATUS[BTN_SELECT_INDEX] = _G.curTickCount; 79 | break; 80 | case BTN_START: 81 | if (_G.KEY_STATUS[BTN_START_INDEX] == 0) 82 | _G.KEY_STATUS[BTN_START_INDEX] = _G.curTickCount; 83 | break; 84 | case BTN_MENU: 85 | if (_G.KEY_STATUS[BTN_MENU_INDEX] == 0) 86 | _G.KEY_STATUS[BTN_MENU_INDEX] = _G.curTickCount; 87 | break; 88 | case BTN_POWER: 89 | if (_G.KEY_STATUS[BTN_POWER_INDEX] == 0) 90 | _G.KEY_STATUS[BTN_POWER_INDEX] = _G.curTickCount; 91 | break; 92 | default: 93 | break; 94 | } 95 | printf("Key pressed: %s\n", SDL_GetKeyName(sym)); 96 | } 97 | 98 | void handle_keyup(SDLKey sym) { 99 | switch (sym) { 100 | case BTN_UP: 101 | _G.KEY_STATUS[BTN_UP_INDEX] = 0; 102 | break; 103 | case BTN_DOWN: 104 | _G.KEY_STATUS[BTN_DOWN_INDEX] = 0; 105 | break; 106 | case BTN_LEFT: 107 | _G.KEY_STATUS[BTN_LEFT_INDEX] = 0; 108 | break; 109 | case BTN_RIGHT: 110 | _G.KEY_STATUS[BTN_RIGHT_INDEX] = 0; 111 | break; 112 | case BTN_A: 113 | _G.KEY_STATUS[BTN_A_INDEX] = 0; 114 | break; 115 | case BTN_B: 116 | _G.KEY_STATUS[BTN_B_INDEX] = 0; 117 | break; 118 | case BTN_X: 119 | _G.KEY_STATUS[BTN_X_INDEX] = 0; 120 | break; 121 | case BTN_Y: 122 | _G.KEY_STATUS[BTN_Y_INDEX] = 0; 123 | break; 124 | case BTN_L1: 125 | _G.KEY_STATUS[BTN_L1_INDEX] = 0; 126 | break; 127 | case BTN_R1: 128 | _G.KEY_STATUS[BTN_R1_INDEX] = 0; 129 | break; 130 | case BTN_L2: 131 | _G.KEY_STATUS[BTN_L2_INDEX] = 0; 132 | break; 133 | case BTN_R2: 134 | _G.KEY_STATUS[BTN_R2_INDEX] = 0; 135 | break; 136 | case BTN_SELECT: 137 | _G.KEY_STATUS[BTN_SELECT_INDEX] = 0; 138 | break; 139 | case BTN_START: 140 | _G.KEY_STATUS[BTN_START_INDEX] = 0; 141 | break; 142 | case BTN_MENU: 143 | _G.KEY_STATUS[BTN_MENU_INDEX] = 0; 144 | break; 145 | case BTN_POWER: 146 | _G.KEY_STATUS[BTN_POWER_INDEX] = 0; 147 | break; 148 | default: 149 | break; 150 | } 151 | printf("Key released: %s\n", SDL_GetKeyName(sym)); 152 | } 153 | -------------------------------------------------------------------------------- /src/render.c: -------------------------------------------------------------------------------- 1 | #include "FlipClock.h" 2 | 3 | int calculateRadius(int width, int height, int percent) { 4 | float smallest = width < height ? width : height; 5 | if (percent > 100) { 6 | percent = 100; 7 | } else if (percent < 0) { 8 | percent = 0; 9 | } 10 | return (smallest / 2 * percent) / 100; 11 | } 12 | 13 | void RenderFillRoundedRect(SDL_Renderer *renderer, int x, int y, int w, int h, 14 | int percent, Uint32 color) { 15 | int radius = calculateRadius(w, h, percent); 16 | 17 | int x1 = x + radius; 18 | int x2 = x + w - radius; 19 | int y1 = y + radius; 20 | int y2 = y + h - radius; 21 | 22 | SDL_Rect rect = {x1, y, w - radius * 2, h}; 23 | RenderFillRect(renderer, &rect, color); 24 | rect = (SDL_Rect){x, y1, w, h - radius * 2}; 25 | RenderFillRect(renderer, &rect, color); 26 | 27 | RenderFillCircle(renderer, x1, y1, radius, color); 28 | RenderFillCircle(renderer, x2, y1, radius, color); 29 | RenderFillCircle(renderer, x1, y2, radius, color); 30 | RenderFillCircle(renderer, x2, y2, radius, color); 31 | } 32 | 33 | void RenderFillCircle(SDL_Renderer *renderer, int cx, int cy, int radius, 34 | Uint32 color) { 35 | const int diameter = (radius * 2); 36 | 37 | int x = (radius - 1); 38 | int y = 0; 39 | int tx = 1; 40 | int ty = 1; 41 | int error = (tx - diameter); 42 | 43 | SDL_Surface *surface = 44 | SDL_CreateRGBSurface(0, diameter, diameter, 32, 0x000000FF, 0x0000FF00, 45 | 0x00FF0000, 0xFF000000); 46 | 47 | while (x >= y) { 48 | for (int i = radius - x; i <= radius + x; i++) { 49 | *(Uint32 *)(surface->pixels + surface->pitch * (radius + y) + 50 | (i * surface->format->BytesPerPixel)) = color; 51 | *(Uint32 *)(surface->pixels + surface->pitch * (radius - y) + 52 | (i * surface->format->BytesPerPixel)) = color; 53 | } 54 | for (int i = radius - y; i <= radius + y; i++) { 55 | *(Uint32 *)(surface->pixels + surface->pitch * (radius + x) + 56 | (i * surface->format->BytesPerPixel)) = color; 57 | *(Uint32 *)(surface->pixels + surface->pitch * (radius - x) + 58 | (i * surface->format->BytesPerPixel)) = color; 59 | } 60 | 61 | if (error <= 0) { 62 | ++y; 63 | error += ty; 64 | ty += 2; 65 | } 66 | if (error > 0) { 67 | --x; 68 | tx += 2; 69 | error += (tx - diameter); 70 | } 71 | } 72 | BlitSurface(surface, NULL, renderer, 73 | &(SDL_Rect){cx - radius, cy - radius, diameter, diameter}); 74 | 75 | SDL_FreeSurface(surface); 76 | } 77 | 78 | int RenderFillRect(SDL_Renderer *dst, SDL_Rect *rect, Uint32 color) { 79 | #if defined SDL && SDL == 1 80 | return SDL_FillRect(dst, rect, color); 81 | #else 82 | SDL_SetRenderDrawColor(dst, color >> 16 & 255, color >> 8 & 255, color & 255, 83 | color >> 24 & 255); 84 | return SDL_RenderFillRect(dst, rect); 85 | #endif 86 | } 87 | 88 | int RenderPresent(SDL_Renderer *renderer, SDL_Window *window) { 89 | #if defined SDL && SDL == 1 90 | return SDL_BlitSurface(renderer, NULL, window, NULL); 91 | #else 92 | SDL_RenderPresent(renderer); 93 | return 0; 94 | #endif 95 | } 96 | 97 | int RenderClear(SDL_Renderer *dst, Uint32 color) { 98 | #if defined SDL && SDL == 1 99 | return RenderFillRect(dst, NULL, color); 100 | #else 101 | SDL_SetRenderDrawColor(dst, color >> 16 & 255, color >> 8 & 255, color & 255, 102 | color >> 24 & 255); 103 | return SDL_RenderClear(dst); 104 | #endif 105 | } 106 | 107 | int BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Renderer *dst, 108 | SDL_Rect *dstrect) { 109 | #if defined SDL && SDL == 1 110 | return SDL_BlitSurface(src, srcrect, dst, dstrect); 111 | #else 112 | SDL_Texture *mTexture = SDL_CreateTextureFromSurface(dst, src); 113 | if (mTexture == NULL) { 114 | return -1; 115 | } 116 | int ret = SDL_RenderCopy(dst, mTexture, NULL, dstrect); 117 | SDL_DestroyTexture(mTexture); 118 | return ret; 119 | #endif 120 | }; 121 | 122 | SDL_Surface *RenderText_Blended(TTF_Font *font, const char *text, 123 | Uint32 color) { 124 | return TTF_RenderText_Blended(font, text, uint32ToSDLColor(color)); 125 | } 126 | 127 | SDL_Color uint32ToSDLColor(Uint32 color) { 128 | return (SDL_Color){color >> 16 & 255, color >> 8 & 255, color & 255, 129 | color >> 24 & 255}; 130 | } 131 | -------------------------------------------------------------------------------- /fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | https://openfontlicense.org 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /src/draw.c: -------------------------------------------------------------------------------- 1 | #include "FlipClock.h" 2 | 3 | int draw_loop() { 4 | printf("##Start rendering loop\n"); 5 | 6 | _G.lastTickCount = SDL_GetTicks(); 7 | _G.curTickCount = _G.lastTickCount; 8 | 9 | float fps = 0.0f; 10 | int frame = _G.curTickCount; 11 | int frameCount = 0; 12 | 13 | while (_G.quit == 0) { 14 | _G.curTickCount = SDL_GetTicks(); 15 | pollevent(); 16 | handle_key(); 17 | 18 | // sync frame rate 19 | if (_G.curTickCount - _G.lastTickCount < 1000 / FRAME_RATE) { 20 | continue; 21 | } 22 | 23 | // calculate FPS 24 | ++frameCount; 25 | if (_G.curTickCount - frame >= 1000) { 26 | fps = frameCount / ((_G.curTickCount - frame) / 1000.f); 27 | frameCount = 0; 28 | frame = _G.curTickCount; 29 | } 30 | 31 | // before draw 32 | RenderClear(_G.renderer, BG_COLOR); 33 | 34 | // draw 35 | draw_screen(fps); 36 | 37 | // after draw 38 | RenderPresent(_G.renderer, _G.window); 39 | _G.lastTickCount = _G.curTickCount; 40 | } 41 | 42 | printf("##Finish rendering loop\n"); 43 | return 0; 44 | } 45 | 46 | int handle_key() { 47 | // MENU + SELECT -> Exit 48 | if (_G.KEY_STATUS[BTN_MENU_INDEX] && _G.KEY_STATUS[BTN_SELECT_INDEX]) { 49 | _G.quit = 1; 50 | } 51 | 52 | // START -> toggle `12/24` mode 53 | if (_G.KEY_STATUS[BTN_START_INDEX] && 54 | _G.KEY_STATUS[BTN_START_INDEX] <= _G.curTickCount) { 55 | _G.KEY_STATUS[BTN_START_INDEX] = _G.curTickCount + INPUT_DELAY; 56 | if (is_active_mode(MODE_AMPM)) { 57 | _G.mode &= ~(1 << MODE_AMPM); 58 | } else { 59 | _G.mode |= 1 << MODE_AMPM; 60 | } 61 | } 62 | 63 | // MENU + X -> toggle `FPS` mode 64 | if (_G.KEY_STATUS[BTN_MENU_INDEX] && 65 | _G.KEY_STATUS[BTN_MENU_INDEX] <= _G.curTickCount && 66 | _G.KEY_STATUS[BTN_X_INDEX] && 67 | _G.KEY_STATUS[BTN_X_INDEX] <= _G.curTickCount) { 68 | 69 | _G.KEY_STATUS[BTN_MENU_INDEX] = _G.curTickCount + INPUT_DELAY; 70 | _G.KEY_STATUS[BTN_X_INDEX] = _G.curTickCount + INPUT_DELAY; 71 | 72 | if (is_active_mode(MODE_FPS)) { 73 | _G.mode &= ~(1 << MODE_FPS); 74 | } else { 75 | _G.mode |= 1 << MODE_FPS; 76 | } 77 | } 78 | 79 | return 0; 80 | } 81 | 82 | #define FLIPCLOCK_HOUR 0 83 | #define FLIPCLOCK_MINIUTE 1 84 | 85 | int draw_screen(float fps) { 86 | struct tm *time = get_local_time(); 87 | 88 | int x = _G.MARGIN_X; 89 | int y = (_G.DISPLAY_HEIGHT - _G.FLIP_SIZE) / 2; 90 | 91 | for (int layout = 0; layout < 2; ++layout) { 92 | int display_time_number = 93 | layout == FLIPCLOCK_HOUR ? time->tm_hour : time->tm_min; 94 | 95 | if (layout == FLIPCLOCK_HOUR && is_active_mode(MODE_AMPM)) { 96 | display_time_number %= 12; 97 | if (display_time_number == 0) { 98 | display_time_number = 12; 99 | } 100 | } 101 | 102 | draw_flip(x, y); 103 | draw_time(x, y, display_time_number); 104 | 105 | if (layout == FLIPCLOCK_HOUR && is_active_mode(MODE_AMPM)) { 106 | draw_ampm(x, y, time->tm_hour); 107 | } 108 | 109 | // Update Position 110 | x += _G.GAP + _G.FLIP_SIZE; 111 | } 112 | 113 | draw_divider(); 114 | 115 | if (is_active_mode(MODE_FPS)) { 116 | draw_fps(fps); 117 | } 118 | 119 | return 0; 120 | } 121 | 122 | struct tm *get_local_time() { 123 | time_t ltime; 124 | ltime = time(<ime); 125 | return localtime(<ime); 126 | } 127 | 128 | void draw_flip(int x, int y) { 129 | RenderFillRoundedRect(_G.renderer, x, y, _G.FLIP_SIZE, _G.FLIP_SIZE, 10, 130 | FLIP_COLOR); 131 | } 132 | 133 | void draw_time(int x, int y, int time) { 134 | char timeString[3] = { 135 | time / 10 + '0', 136 | time % 10 + '0', 137 | '\0', 138 | }; 139 | 140 | SDL_Surface *textSurface = 141 | RenderText_Blended(_G.timeFont, timeString, FONT_COLOR); 142 | 143 | BlitSurface( 144 | textSurface, NULL, _G.renderer, 145 | &(SDL_Rect){x + _G.FLIP_PADDING + (_G.TIME_SIZE - textSurface->w) / 2, 146 | y + _G.FLIP_PADDING + _G.timeFontDescent, textSurface->w, 147 | textSurface->h}); 148 | 149 | // free allocated memories 150 | SDL_FreeSurface(textSurface); 151 | } 152 | 153 | void draw_ampm(int x, int y, int hour) { 154 | SDL_Surface *textSurface = 155 | RenderText_Blended(_G.ampmFont, hour < 12 ? "AM" : "PM", FONT_COLOR); 156 | 157 | BlitSurface(textSurface, NULL, _G.renderer, 158 | &(SDL_Rect){x + _G.FLIP_PADDING / 2, 159 | y + _G.FLIP_SIZE - _G.FLIP_PADDING / 2 - 160 | _G.AMPM_SIZE + _G.ampmFontDescent, 161 | textSurface->w, textSurface->h}); 162 | 163 | // free allocated memories 164 | SDL_FreeSurface(textSurface); 165 | } 166 | 167 | void draw_divider() { 168 | RenderFillRect(_G.renderer, 169 | &(SDL_Rect){0, _G.DISPLAY_HEIGHT / 2 - _G.DIVIDER, 170 | _G.DISPLAY_WIDTH, _G.DIVIDER}, 171 | BG_COLOR); 172 | } 173 | 174 | void draw_fps(float fps) { 175 | char FPS[11] = { 176 | '\0', 177 | }; 178 | 179 | sprintf(FPS, "FPS: %.2f", fps); 180 | SDL_Surface *textSurface = RenderText_Blended(_G.ampmFont, FPS, FONT_COLOR); 181 | BlitSurface(textSurface, NULL, _G.renderer, 182 | &(SDL_Rect){4, 4, textSurface->w, textSurface->h}); 183 | 184 | // free allocated memories 185 | SDL_FreeSurface(textSurface); 186 | } 187 | --------------------------------------------------------------------------------