Skip to content

Commit b7b6254

Browse files
committed
add notifications
1 parent 778426a commit b7b6254

File tree

9 files changed

+298
-110
lines changed

9 files changed

+298
-110
lines changed

linux/makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ BOOTROM_SOURCE = ../roms/Kiwi8_logo_2.ch8
4747
# Source files
4848
CORE_SRCS = ../shared/audio.cc ../shared/chip8.cc ../shared/crc32.cc ../shared/display.cc ../shared/gui.cc \
4949
../shared/imgui_impl_sdl.cc ../shared/input.cc ../shared/main.cc \
50-
../shared/open_file_dialog.cc ../shared/profiles.cc
50+
../shared/notifications.cc ../shared/open_file_dialog.cc ../shared/profiles.cc
5151
IMGUI_SRCS = ../external/imgui/imgui.cpp ../external/imgui/imgui_draw.cpp
5252
LINUX_SRCS = src/file_dialog.cc
5353

macos/makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ BOOTROM_SOURCE = ../roms/Kiwi8_logo_2.ch8
3232
# Source files
3333
CORE_SRCS = ../shared/audio.cc ../shared/chip8.cc ../shared/crc32.cc ../shared/display.cc ../shared/gui.cc \
3434
../shared/imgui_impl_sdl.cc ../shared/input.cc ../shared/main.cc \
35-
../shared/open_file_dialog.cc ../shared/profiles.cc
35+
../shared/notifications.cc ../shared/open_file_dialog.cc ../shared/profiles.cc
3636
IMGUI_SRCS = ../external/imgui/imgui.cpp ../external/imgui/imgui_draw.cpp
3737
MACOS_SRCS = src/file_dialog.mm
3838

shared/audio.cc

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "audio.h"
2+
#include "notifications.h"
23
#include <stdio.h>
34
#include <string.h>
45
#include <math.h>
@@ -61,21 +62,28 @@ int audio_init(void) {
6162
int driver_found = 0;
6263
for (int i = 0; i < sizeof(drivers) / sizeof(drivers[0]); i++) {
6364
if (SDL_AudioInit(drivers[i]) == 0) {
64-
fprintf(stdout, "Successfully initialized audio with driver: %s\n", drivers[i]);
65+
fprintf(stdout, "Loaded audio driver: %s\n", drivers[i]);
66+
char msg[256];
67+
snprintf(msg, sizeof(msg), "Audio driver: %s", drivers[i]);
68+
notify_show(NOTIFY_INFO, msg);
6569
driver_found = 1;
6670
break;
6771
}
6872
}
6973
if (!driver_found) {
70-
fprintf(stderr, "Warning: Could not initialize any audio driver, continuing without sound.\n");
74+
fprintf(stderr, "Could not initialize any audio driver, continuing without sound.\n");
75+
notify_show(NOTIFY_ERROR, "Could not initialize audio driver");
76+
notify_show(NOTIFY_INFO, "Continuing without sound");
7177
}
7278
audio.device = SDL_OpenAudioDevice(NULL, 0, &audio.audiospec, NULL, SDL_AUDIO_ALLOW_ANY_CHANGE);
7379
if (!audio.device) {
74-
fprintf(stderr, "Warning: No audio device available, using dummy driver. %s\n", SDL_GetError());
80+
fprintf(stderr, "No audio device available, using dummy driver. %s\n", SDL_GetError());
81+
notify_show(NOTIFY_INFO, "Using dummy audio driver");
7582
SDL_setenv("SDL_AUDIODRIVER", "dummy", 1);
7683
audio.device = SDL_OpenAudioDevice(NULL, 0, &audio.audiospec, NULL, SDL_AUDIO_ALLOW_ANY_CHANGE);
7784
if (!audio.device) {
78-
fprintf(stderr, "Error: Failed to initialize audio (even with dummy driver): %s\n", SDL_GetError());
85+
fprintf(stderr, "Failed to initialize audio (even with dummy driver): %s\n", SDL_GetError());
86+
notify_show(NOTIFY_ERROR, "Failed to initialize audio");
7987
return 1;
8088
}
8189
}

shared/chip8.cc

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "opcodes.h"
33
#include "profiles.h"
44
#include "crc32.h"
5+
#include "notifications.h"
56
#include "open_file_dialog.h"
67
#include <SDL2/SDL.h>
78
#include <stdio.h>
@@ -63,6 +64,9 @@ int chip8_init(
6364
memset(chip8.vram[i], 0, HEIGHT * sizeof(unsigned char));
6465
}
6566

67+
/* Initialize notification system first (before audio) */
68+
notify_init();
69+
6670
audio_init();
6771

6872
if (display_init(fullscreen)) return 1;
@@ -121,7 +125,7 @@ int chip8_load(const char *rom_name) {
121125
FILE *file;
122126
file = fopen(rom_name, "rb");
123127
if(file == NULL){
124-
fprintf(stderr, "File not opened.\n");
128+
notify_show(NOTIFY_ERROR, "Unable to open ROM file");
125129
return 1;
126130
}
127131

@@ -130,22 +134,25 @@ int chip8_load(const char *rom_name) {
130134
chip8.rom_size = ftell(file);
131135
rewind(file);
132136
if (chip8.rom_size > MEM_SIZE - ENTRY_POINT) {
133-
fprintf(stderr, "Rom is too large or not formatted properly.\n");
137+
notify_show(NOTIFY_ERROR, "ROM is too large or not formatted properly");
138+
fclose(file);
134139
return 1;
135140
}
136141

137142
/* allocate or free and reallocate as necessary */
138143
free(chip8.rom);
139144
chip8.rom = (unsigned char *)malloc(chip8.rom_size);
140145
if(!chip8.rom) {
141-
fprintf(stderr, "Unable to allocate memory for rom.\n");
146+
notify_show(NOTIFY_ERROR, "Unable to allocate memory for ROM");
147+
fclose(file);
142148
return 1;
143149
}
144150
memset(chip8.rom, 0 , chip8.rom_size);
145151

146152
/* save the rom for later (soft-resets) */
147153
if (!fread(chip8.rom, sizeof(unsigned char), chip8.rom_size, file)) {
148-
fprintf(stderr, "Unable to read Rom file after successfully opening.\n");
154+
notify_show(NOTIFY_ERROR, "Unable to read ROM file");
155+
fclose(file);
149156
return 1;
150157
}
151158

@@ -168,10 +175,13 @@ int chip8_load(const char *rom_name) {
168175
if (profile) {
169176
/* Apply profile quirks */
170177
chip8.quirks = profile->quirks;
171-
printf("Applied ROM profile for: %s (CRC32: 0x%X)\n", chip8.rom_filename, crc);
178+
char notif_msg[256];
179+
snprintf(notif_msg, sizeof(notif_msg), "Profile applied: %s", chip8.rom_filename);
180+
notify_show(NOTIFY_SUCCESS, notif_msg);
172181
} else {
173-
printf("No profile found for: %s (CRC32: 0x%X) - using current quirks\n",
174-
chip8.rom_filename, crc);
182+
char notif_msg[256];
183+
snprintf(notif_msg, sizeof(notif_msg), "No profile found: %s", chip8.rom_filename);
184+
notify_show(NOTIFY_INFO, notif_msg);
175185
}
176186

177187
chip8_soft_reset();
@@ -258,6 +268,9 @@ void chip8_run(){
258268
gui.save_profile_flag = 0;
259269
}
260270

271+
/* Update notification timers */
272+
notify_update((double)interval / 1000.0);
273+
261274
if (!chip8.paused) {
262275
/* emulate a number of cycles */
263276
chip8_step_cpu(chip8.cycles);

shared/gui.cc

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "display.h"
33
#include "gui.h"
44
#include "license.h" // Generated at build time from LICENSE
5+
#include "notifications.h"
56
#include "usage.h"
67
#include <stdio.h>
78

@@ -96,6 +97,86 @@ static void gui_help_windows(void) {
9697
}
9798
}
9899

100+
static void gui_notifications(void) {
101+
int count;
102+
const struct notification *notifications = notify_get_notifications(&count);
103+
104+
if (count == 0) {
105+
return;
106+
}
107+
108+
ImGuiIO& io = ImGui::GetIO();
109+
float spacing = 10.0f;
110+
float y_offset = io.DisplaySize.y - spacing;
111+
112+
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
113+
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
114+
115+
for (int i = 0; i < 8; i++) {
116+
if (!notify_is_active(&notifications[i])) {
117+
continue;
118+
}
119+
120+
const char *message;
121+
int type;
122+
double time_remaining;
123+
notify_get_info(&notifications[i], &message, &type, &time_remaining);
124+
125+
/* Calculate alpha for fade out effect */
126+
float alpha = 1.0f;
127+
if (time_remaining < 0.5) {
128+
alpha = (float)(time_remaining / 0.5);
129+
}
130+
131+
/* Set colors based on notification type */
132+
ImVec4 bg_color;
133+
if (type == NOTIFY_SUCCESS) {
134+
bg_color = ImVec4(0.1f, 0.4f, 0.1f, 0.85f * alpha);
135+
} else if (type == NOTIFY_ERROR) {
136+
bg_color = ImVec4(0.5f, 0.1f, 0.1f, 0.85f * alpha);
137+
} else {
138+
bg_color = ImVec4(0.25f, 0.25f, 0.25f, 0.85f * alpha);
139+
}
140+
141+
ImGui::PushStyleColor(ImGuiCol_WindowBg, bg_color);
142+
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, alpha));
143+
144+
/* Begin window to calculate size */
145+
char window_name[32];
146+
snprintf(window_name, sizeof(window_name), "##notification%d", i);
147+
148+
/* Set max width for notifications */
149+
float max_width = 300.0f;
150+
ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(max_width, FLT_MAX));
151+
152+
ImGui::Begin(window_name, NULL,
153+
ImGuiWindowFlags_NoTitleBar |
154+
ImGuiWindowFlags_NoResize |
155+
ImGuiWindowFlags_NoMove |
156+
ImGuiWindowFlags_NoScrollbar |
157+
ImGuiWindowFlags_NoSavedSettings |
158+
ImGuiWindowFlags_AlwaysAutoResize);
159+
160+
/* Wrap text to max width */
161+
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + max_width - 20);
162+
ImGui::TextWrapped("%s", message);
163+
ImGui::PopTextWrapPos();
164+
165+
/* Get actual window size and reposition from bottom-right */
166+
ImVec2 window_size = ImGui::GetWindowSize();
167+
ImVec2 window_pos = ImVec2(io.DisplaySize.x - window_size.x - spacing, y_offset - window_size.y);
168+
ImGui::SetWindowPos(window_pos);
169+
170+
y_offset -= window_size.y + spacing;
171+
172+
ImGui::End();
173+
174+
ImGui::PopStyleColor(2);
175+
}
176+
177+
ImGui::PopStyleVar(2);
178+
}
179+
99180
static void gui_main_menu(void) {
100181
bool before;
101182

@@ -242,6 +323,7 @@ void gui_process_events(SDL_Event *event) {
242323
void gui_new_frame(void) {
243324
ImGui_ImplSdl_NewFrame(display.window);
244325
gui_main_menu();
326+
gui_notifications();
245327
}
246328

247329
void gui_render(void) {

shared/notifications.cc

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "notifications.h"
2+
#include <string.h>
3+
#include <stdio.h>
4+
5+
#define MAX_NOTIFICATIONS 8
6+
#define NOTIFICATION_DURATION 3.0
7+
8+
static struct notification notifications[MAX_NOTIFICATIONS];
9+
10+
void notify_init(void) {
11+
memset(notifications, 0, sizeof(notifications));
12+
}
13+
14+
void notify_show(int type, const char *message) {
15+
int slot = -1;
16+
17+
/* Find first inactive slot */
18+
for (int i = 0; i < MAX_NOTIFICATIONS; i++) {
19+
if (!notifications[i].active) {
20+
slot = i;
21+
break;
22+
}
23+
}
24+
25+
/* If all slots full, overwrite the one with least time remaining */
26+
if (slot == -1) {
27+
slot = 0;
28+
double min_time = notifications[0].time_remaining;
29+
for (int i = 1; i < MAX_NOTIFICATIONS; i++) {
30+
if (notifications[i].time_remaining < min_time) {
31+
min_time = notifications[i].time_remaining;
32+
slot = i;
33+
}
34+
}
35+
}
36+
37+
/* Populate the notification */
38+
notifications[slot].type = type;
39+
notifications[slot].time_remaining = NOTIFICATION_DURATION;
40+
notifications[slot].active = 1;
41+
strncpy(notifications[slot].message, message, sizeof(notifications[slot].message) - 1);
42+
notifications[slot].message[sizeof(notifications[slot].message) - 1] = '\0';
43+
}
44+
45+
void notify_update(double delta_time) {
46+
for (int i = 0; i < MAX_NOTIFICATIONS; i++) {
47+
if (notifications[i].active) {
48+
notifications[i].time_remaining -= delta_time;
49+
if (notifications[i].time_remaining <= 0.0) {
50+
notifications[i].active = 0;
51+
}
52+
}
53+
}
54+
}
55+
56+
void notify_render(void) {
57+
/* Rendering is done in gui.cc via notify_get_active() */
58+
}
59+
60+
/* Helper function for gui.cc to access active notifications */
61+
const struct notification* notify_get_notifications(int *count) {
62+
static int active_count = 0;
63+
active_count = 0;
64+
65+
for (int i = 0; i < MAX_NOTIFICATIONS; i++) {
66+
if (notifications[i].active) {
67+
active_count++;
68+
}
69+
}
70+
71+
*count = active_count;
72+
return notifications;
73+
}
74+
75+
/* Helper to get notification info */
76+
void notify_get_info(const struct notification *notif, const char **message, int *type, double *time) {
77+
*message = notif->message;
78+
*type = notif->type;
79+
*time = notif->time_remaining;
80+
}
81+
82+
int notify_is_active(const struct notification *notif) {
83+
return notif->active;
84+
}

shared/notifications.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#ifndef NOTIFICATIONS_H
2+
#define NOTIFICATIONS_H
3+
4+
/* Notification struct definition */
5+
struct notification {
6+
char message[256];
7+
int type;
8+
double time_remaining;
9+
int active;
10+
};
11+
12+
/* Notification types */
13+
#define NOTIFY_INFO 0
14+
#define NOTIFY_SUCCESS 1
15+
#define NOTIFY_ERROR 2
16+
17+
/* Initialize notification system */
18+
void notify_init(void);
19+
20+
/* Show a notification */
21+
void notify_show(int type, const char *message);
22+
23+
/* Update notification timers (call each frame with delta time) */
24+
void notify_update(double delta_time);
25+
26+
/* Render notifications (called from gui.cc) */
27+
void notify_render(void);
28+
29+
/* Helper functions for accessing notification data */
30+
const struct notification* notify_get_notifications(int *count);
31+
int notify_is_active(const struct notification *notif);
32+
void notify_get_info(const struct notification *notif, const char **message, int *type, double *time);
33+
34+
#endif

0 commit comments

Comments
 (0)