moved code here. still needs to update gh

This commit is contained in:
klein panic
2025-02-24 15:18:44 -05:00
parent 9ca9f577a4
commit 2726432fe1
106 changed files with 4816 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
CC = gcc
CFLAGS = `pkg-config --cflags gtk+-3.0` -I$(INC_DIR)
LDFLAGS = `pkg-config --libs gtk+-3.0`
SRC_DIR = src
INC_DIR = include
OBJ_DIR = obj
OBJS = $(OBJ_DIR)/battery_monitor.o $(OBJ_DIR)/notification.o $(OBJ_DIR)/process_monitor.o
TARGET = battery_monitor
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(OBJ_DIR)
$(CC) $(CFLAGS) -I$(INC_DIR) -o $@ -c $<
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS)
clean:
rm -rf $(OBJ_DIR) $(TARGET)
.PHONY: all clean

View File

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Set up environment if needed
export DISPLAY=:0
export XAUTHORITY="$HOME/.Xauthority"
# Path to the battery monitor binary
BINARY_PATH="/usr/local/bin/battery_monitor"
# Log file
LOG_FILE="/tmp/battery_monitor.log"
# Create log directory if it doesn't exist
mkdir -p "$(dirname "$LOG_FILE")"
# Log starting message and environment variables
echo "Starting battery monitor script" >> "$LOG_FILE"
echo "DISPLAY=$DISPLAY" >> "$LOG_FILE"
echo "XAUTHORITY=$XAUTHORITY" >> "$LOG_FILE"
# Check if binary exists and is executable
if [ ! -x "$BINARY_PATH" ]; then
echo "Binary not found or not executable: $BINARY_PATH" >> "$LOG_FILE"
exit 1
fi
# Start the battery monitor and redirect output to log file
exec "$BINARY_PATH" >> "$LOG_FILE" 2>&1

Binary file not shown.

View File

@@ -0,0 +1,17 @@
[Unit]
Description=Battery Monitor Service
PartOf=graphical.target
After=graphical.target
[Service]
Type=simple
ExecStart=/usr/local/bin/battery_daemon.sh
Restart=always
Environment=DISPLAY=:0
Environment=XAUTHORITY=%h/.Xauthority
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=graphical.target

View File

@@ -0,0 +1,4 @@
Starting battery monitor script
DISPLAY=:0
XAUTHORITY=/home/klein/.Xauthority
Failed to open log file: Permission denied

View File

@@ -0,0 +1,16 @@
#ifndef BATTERY_MONITOR_H
#define BATTERY_MONITOR_H
void show_notification(const char *message, const char *title);
int get_battery_level();
int is_charging();
int activate_battery_saving_mode();
int enter_sleep_mode();
int kill_processes(const char *filename);
int set_brightness(int brightness);
void log_message(const char *message);
// New function declaration for process monitoring
int get_high_cpu_processes(char *process_list[], int max_processes);
#endif // BATTERY_MONITOR_H

View File

@@ -0,0 +1,8 @@
#ifndef PROCESS_MONITOR_H
#define PROCESS_MONITOR_H
int get_high_cpu_processes(char *process_list[], int max_processes);
void free_process_list(char *process_list[], int count);
#endif // PROCESS_MONITOR_H

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Function to check if a command exists
check_dependency() {
if ! command -v "$1" &> /dev/null; then
echo "$1 is not installed. Installing..."
sudo apt-get install -y "$1"
else
echo "$1 is already installed."
fi
}
# Step 1: Finding the current working directory and script location
SCRIPT_DIR=$(pwd)
BASH_SCRIPT="$SCRIPT_DIR/battery_daemon.sh"
SRC_SCRIPT="$SCRIPT_DIR/battery_monitor"
# Check if battery_daemon.sh exists
if [[ -f "$BASH_SCRIPT" ]]; then
echo "Found battery_daemon.sh. Moving to /usr/local/bin."
sudo cp "$BASH_SCRIPT" /usr/local/bin/battery_daemon.sh
sudo cp "$SRC_SCRIPT" /usr/local/bin/battery_monitor
sudo chmod +x /usr/local/bin/battery_daemon
sudo chmod +x /usr/local/bin/battery_daemon.sh
else
echo "battery_daemon.sh not found in the current directory!"
exit 1
fi
# Step 2: Check for dependencies and install if not present
dependencies=("gcc" "make" "brightnessctl")
for dep in "${dependencies[@]}"; do
check_dependency "$dep"
done
# Step 3: Copy battery_monitor.service to systemd folder
SYSTEMD_SERVICE="$SCRIPT_DIR/battery_monitor.service"
if [[ -f "$SYSTEMD_SERVICE" ]]; then
echo "Found battery_monitor.service. Copying to /etc/systemd/system/"
sudo cp "$SYSTEMD_SERVICE" /etc/systemd/system/
else
echo "battery_monitor.service not found in the current directory!"
exit 1
fi
# Step 4: Reload the systemd daemon, enable and restart the service
echo "Reloading systemd daemon..."
sudo systemctl daemon-reload
echo "Enabling battery_monitor.service..."
sudo systemctl enable battery_monitor.service
echo "Restarting battery_monitor.service..."
sudo systemctl restart battery_monitor.service
# Check if the service was successfully started
if systemctl is-active --quiet battery_monitor.service; then
echo "Service started successfully!"
else
echo "Failed to start the service."
exit 1
fi

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,70 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "battery_monitor.h"
// Define the battery thresholds
#define THRESHOLD_LOW 15
#define THRESHOLD_CRITICAL 5
#define THRESHOLD_HIGH 80
// Track if notifications have been sent
int notified_low = 0;
int notified_critical = 0;
int main() {
log_message("Battery monitor started");
while (1) {
if (is_charging()) {
// Reset notifications if the battery is charging
log_message("Battery is charging, notifications reset");
notified_low = 0;
notified_critical = 0;
sleep(300); // Sleep for 5 minutes while charging
continue;
}
int battery_level = get_battery_level();
if (battery_level == -1) {
log_message("Battery level read failed, retrying in 1 minute");
sleep(60);
continue;
}
// Dynamic sleep interval based on battery level
int sleep_duration = 60; // Default 1 minute
if (battery_level > THRESHOLD_HIGH) {
sleep_duration = 300; // Sleep for 5 minutes
} else if (battery_level <= THRESHOLD_CRITICAL) {
sleep_duration = 30; // Sleep for 30 seconds when critically low
} else if (battery_level <= THRESHOLD_LOW) {
sleep_duration = 60; // Sleep for 1 minute when low
}
// Check if the battery level is below the critical threshold
if (battery_level <= THRESHOLD_CRITICAL && !notified_critical) {
log_message("Battery critically low, showing notification");
show_notification("Battery is critically low, below 5%", "Critical Battery Warning");
notified_critical = 1;
} else if (battery_level <= THRESHOLD_LOW && !notified_low) {
log_message("Battery low, showing notification");
show_notification("Battery is low, below 15%", "Low Battery Warning");
notified_low = 1;
}
// Reset notifications if battery level goes back up
if (battery_level > THRESHOLD_LOW) {
notified_low = 0;
}
if (battery_level > THRESHOLD_CRITICAL) {
notified_critical = 0;
}
// Wait for the dynamically determined duration before checking again
sleep(sleep_duration);
}
return 0;
}

View File

@@ -0,0 +1,281 @@
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include "battery_monitor.h"
#include "process_monitor.h"
#define CSS_STYLE "\
* { \
background-color: #333333; \
color: white; \
} \
button { \
background-color: #555555; \
color: white; \
} \
"
// Function to get the battery level
int get_battery_level() {
const char *battery_paths[] = {
"/sys/class/power_supply/BAT0/capacity",
"/sys/class/power_supply/BAT1/capacity"
};
FILE *file;
int battery_level = -1;
for (int i = 0; i < sizeof(battery_paths) / sizeof(battery_paths[0]); i++) {
file = fopen(battery_paths[i], "r");
if (file != NULL) {
break;
}
}
if (file == NULL) {
perror("Failed to open capacity file");
log_message("Failed to open capacity file");
return -1;
}
if (fscanf(file, "%d", &battery_level) != 1) {
perror("Failed to read battery level");
log_message("Failed to read battery level");
fclose(file);
return -1;
}
fclose(file);
return battery_level;
}
// Function to get the base directory of the executable
char *get_base_directory() {
static char base_dir[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", base_dir, PATH_MAX);
if (count != -1) {
dirname(base_dir);
}
return base_dir;
}
// Function to log messages to a file
void log_message(const char *message) {
char log_file[PATH_MAX];
snprintf(log_file, PATH_MAX, "/tmp/battery_monitor.log");
FILE *log_file_ptr = fopen(log_file, "a");
if (log_file_ptr) {
fprintf(log_file_ptr, "%s\n", message);
fclose(log_file_ptr);
} else {
perror("Failed to open log file");
}
}
// Function to activate battery saving mode
int activate_battery_saving_mode() {
log_message("Activating battery saving mode");
char process_list_file[PATH_MAX];
snprintf(process_list_file, PATH_MAX, "%s/../docs/process_list.txt", get_base_directory());
if (kill_processes(process_list_file) == -1) {
return -1; // Return failure if processes couldn't be killed
}
if (set_brightness(50) == -1) {
return -1; // Return failure if brightness couldn't be set
}
return 0; // Success
}
// Function to enter sleep mode
int enter_sleep_mode() {
log_message("Entering sleep mode");
return system("systemctl suspend"); // Return system command result
}
// Function to apply custom CSS styles to the GTK widgets
void apply_css(GtkWidget *widget, const char *css) {
GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider, css, -1, NULL);
GtkStyleContext *context = gtk_widget_get_style_context(widget);
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
g_object_unref(provider);
}
// Function to check the battery status and close the dialog if charging
gboolean check_battery_status(gpointer user_data) {
GtkWidget *dialog = GTK_WIDGET(user_data);
if (is_charging()) {
log_message("Battery started charging, closing notification");
gtk_widget_destroy(dialog);
gtk_main_quit(); // Exit the GTK main loop
return FALSE; // Stop checking
}
return TRUE; // Continue checking
}
// Function to handle dialog response
void on_dialog_response(GtkDialog *dialog, gint response_id, gpointer user_data) {
switch (response_id) {
case GTK_RESPONSE_OK:
log_message("User clicked OK");
break;
case GTK_RESPONSE_APPLY:
log_message("User activated Battery Saving Mode");
activate_battery_saving_mode();
break;
case GTK_RESPONSE_CLOSE:
log_message("User triggered Sleep Mode");
enter_sleep_mode();
break;
default:
break;
}
gtk_widget_destroy(GTK_WIDGET(dialog));
gtk_main_quit(); // Exit the GTK main loop
}
// Function to show the notification dialog
void show_notification(const char *message, const char *title) {
log_message("Showing notification");
GtkWidget *dialog;
gtk_init(0, NULL);
dialog = gtk_message_dialog_new(NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_NONE,
"%s", message);
gtk_dialog_add_button(GTK_DIALOG(dialog), "OK", GTK_RESPONSE_OK);
gtk_dialog_add_button(GTK_DIALOG(dialog), "Battery Saving Mode", GTK_RESPONSE_APPLY);
if (g_strcmp0(title, "Critical Battery Warning") == 0) {
gtk_dialog_add_button(GTK_DIALOG(dialog), "Sleep", GTK_RESPONSE_CLOSE);
}
gtk_window_set_title(GTK_WINDOW(dialog), title);
// Apply CSS styles
apply_css(dialog, CSS_STYLE);
// Set up the callback to check battery status and close if charging
g_timeout_add(1000, check_battery_status, dialog);
// Connect the dialog response to handle button clicks and ensure proper cleanup
g_signal_connect(dialog, "response", G_CALLBACK(on_dialog_response), NULL);
// Show the dialog and enter the GTK main loop
gtk_widget_show_all(dialog);
gtk_main(); // Start the GTK main loop
}
// Function to check if the battery is charging
int is_charging() {
const char *status_paths[] = {
"/sys/class/power_supply/BAT0/status",
"/sys/class/power_supply/BAT1/status"
};
FILE *file;
char status[16];
for (int i = 0; i < sizeof(status_paths) / sizeof(status_paths[0]); i++) {
file = fopen(status_paths[i], "r");
if (file != NULL) {
break;
}
}
if (file == NULL) {
perror("Failed to open status file");
log_message("Failed to open status file");
return -1;
}
if (fscanf(file, "%15s", status) != 1) {
perror("Failed to read battery status");
log_message("Failed to read battery status");
fclose(file);
return -1;
}
fclose(file);
return (strcmp(status, "Charging") == 0);
}
// Function to kill processes based on a dynamic process list
int kill_processes(const char *filename) {
// Dynamic process list based on CPU usage
char *process_list[100]; // Assuming a max of 100 processes to kill
int process_count = get_high_cpu_processes(process_list, 100);
if (process_count == -1) {
log_message("Failed to get high CPU processes");
return -1;
}
for (int i = 0; i < process_count; i++) {
char command[300];
snprintf(command, sizeof(command), "pkill %s", process_list[i]);
log_message(command); // Log the command being executed
if (system(command) == -1) {
free_process_list(process_list, process_count);
return -1; // Return failure if the command fails
}
}
free_process_list(process_list, process_count);
return 0; // Success
}
// Function to set the screen brightness
int set_brightness(int brightness) {
const char *brightness_path = "/sys/class/backlight/intel_backlight/brightness";
const char *max_brightness_path = "/sys/class/backlight/intel_backlight/max_brightness";
int max_brightness = 100;
int new_brightness = 0;
char buffer[4];
int fd = open(max_brightness_path, O_RDONLY);
if (fd == -1) {
perror("Failed to open max brightness file");
log_message("Failed to open max brightness file");
return -1; // Return failure if the file can't be opened
}
if (read(fd, buffer, sizeof(buffer)) != -1) {
max_brightness = atoi(buffer);
} else {
perror("Failed to read max brightness");
log_message("Failed to read max brightness");
close(fd);
return -1; // Return failure if the file can't be read
}
close(fd);
new_brightness = max_brightness * brightness / 100;
fd = open(brightness_path, O_WRONLY);
if (fd == -1) {
perror("Failed to open brightness file");
log_message("Failed to open brightness file");
return -1; // Return failure if the file can't be opened
}
snprintf(buffer, sizeof(buffer), "%d", new_brightness);
if (write(fd, buffer, strlen(buffer)) == -1) {
perror("Failed to write to brightness file");
log_message("Failed to write to brightness file");
close(fd);
return -1; // Return failure if the write fails
}
close(fd);
return 0; // Success
}

View File

@@ -0,0 +1,143 @@
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "battery_monitor.h"
#define LOG_FILE "/home/klein/codeWS/C/bat0daemon/docs/battery_monitor.log"
void log_message(const char *message) {
FILE *log_file = fopen(LOG_FILE, "a");
if (log_file) {
fprintf(log_file, "%s\n", message);
fclose(log_file);
} else {
perror("Failed to open log file");
}
}
void activate_battery_saving_mode() {
log_message("Activating battery saving mode");
kill_processes("/home/klein/codeWS/C/bat0daemon/docs/procress_list.txt");
set_brightness(50);
}
void enter_sleep_mode() {
log_message("Entering sleep mode");
system("systemctl suspend");
}
void show_notification(const char *message, const char *title) {
log_message("Showing notification");
GtkWidget *dialog;
gtk_init(0, NULL);
dialog = gtk_message_dialog_new(NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_NONE,
"%s", message);
gtk_dialog_add_button(GTK_DIALOG(dialog), "OK", GTK_RESPONSE_OK);
gtk_dialog_add_button(GTK_DIALOG(dialog), "Battery Saving Mode", GTK_RESPONSE_APPLY);
if (g_strcmp0(title, "Critical Battery Warning") == 0) {
gtk_dialog_add_button(GTK_DIALOG(dialog), "Sleep", GTK_RESPONSE_CLOSE);
}
gtk_window_set_title(GTK_WINDOW(dialog), title);
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
if (response == GTK_RESPONSE_APPLY) {
activate_battery_saving_mode();
} else if (response == GTK_RESPONSE_CLOSE) {
enter_sleep_mode();
}
gtk_widget_destroy(dialog);
while (g_main_context_iteration(NULL, FALSE));
}
int get_battery_level() {
FILE *file;
int battery_level = -1;
file = fopen("/sys/class/power_supply/BAT0/capacity", "r");
if (file == NULL) {
perror("Failed to open capacity file");
log_message("Failed to open capacity file");
return -1;
}
if (fscanf(file, "%d", &battery_level) != 1) {
perror("Failed to read battery level");
log_message("Failed to read battery level");
fclose(file);
return -1;
}
fclose(file);
return battery_level;
}
void kill_processes(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
perror("Failed to open process list file");
log_message("Failed to open process list file");
return;
}
char process_name[256];
while (fgets(process_name, sizeof(process_name), file)) {
process_name[strcspn(process_name, "\n")] = 0; // Remove newline character
if (strlen(process_name) > 0) {
char command[300];
snprintf(command, sizeof(command), "pkill %s", process_name);
log_message(command); // Log the command being executed
system(command);
}
}
fclose(file);
}
void set_brightness(int brightness) {
const char *brightness_path = "/sys/class/backlight/intel_backlight/brightness";
const char *max_brightness_path = "/sys/class/backlight/intel_backlight/max_brightness";
int max_brightness = 100;
int new_brightness = 0;
char buffer[4];
int fd = open(max_brightness_path, O_RDONLY);
if (fd == -1) {
perror("Failed to open max brightness file");
log_message("Failed to open max brightness file");
return;
}
if (read(fd, buffer, sizeof(buffer)) != -1) {
max_brightness = atoi(buffer);
} else {
perror("Failed to read max brightness");
log_message("Failed to read max brightness");
}
close(fd);
new_brightness = max_brightness * brightness / 100;
fd = open(brightness_path, O_WRONLY);
if (fd == -1) {
perror("Failed to open brightness file");
log_message("Failed to open brightness file");
return;
}
snprintf(buffer, sizeof(buffer), "%d", new_brightness);
if (write(fd, buffer, strlen(buffer)) == -1) {
perror("Failed to write to brightness file");
log_message("Failed to write to brightness file");
}
close(fd);
}

View File

@@ -0,0 +1,45 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "battery_monitor.h"
#define BUFFER_SIZE 1024
int get_high_cpu_processes(char *process_list[], int max_processes) {
FILE *fp;
char buffer[BUFFER_SIZE];
int process_count = 0;
// Command to get the top CPU-using processes for the user 'klein', excluding vi, neovim, vim, and root processes
const char *command = "ps -eo user,pid,comm,%cpu --sort=-%cpu | grep '^klein' | grep -vE '(vi|vim|neovim|root)'";
fp = popen(command, "r");
if (fp == NULL) {
log_message("Failed to run command to get high CPU processes");
return -1;
}
while (fgets(buffer, sizeof(buffer), fp) != NULL && process_count < max_processes) {
char user[50], command_name[100];
int pid;
float cpu_usage;
// Parse the line to extract the user, PID, command name, and CPU usage
sscanf(buffer, "%49s %d %99s %f", user, &pid, command_name, &cpu_usage);
// Store the command in the process list
process_list[process_count] = malloc(BUFFER_SIZE);
snprintf(process_list[process_count], BUFFER_SIZE, "%d", pid);
process_count++;
}
pclose(fp);
return process_count;
}
void free_process_list(char *process_list[], int count) {
for (int i = 0; i < count; i++) {
free(process_list[i]);
}
}

View File

@@ -0,0 +1,27 @@
CC = gcc
CFLAGS = -Wall -Wextra -std=c99
LDFLAGS = -lX11
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS))
DEPS = $(OBJS:.o=.d)
TARGET = $(BIN_DIR)/brightnessctl
$(TARGET): $(OBJS)
@mkdir -p $(BIN_DIR)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
@mkdir -p $(OBJ_DIR)
$(CC) $(CFLAGS) -MMD -c $< -o $@
-include $(DEPS)
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
.PHONY: clean

View File

@@ -0,0 +1,2 @@
obj/brightness.o: src/brightness.c src/brightness.h src/display.h \
src/utils.h

Binary file not shown.

View File

@@ -0,0 +1 @@
obj/display.o: src/display.c src/display.h src/brightness.h src/utils.h

Binary file not shown.

View File

@@ -0,0 +1 @@
obj/main.o: src/main.c src/brightness.h

View File

@@ -0,0 +1,184 @@
#include "brightness.h"
#include "display.h"
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // Include string.h for strdup
static int min_value = 0;
static float exponent = 1.0;
static char *device = NULL;
static char *class = NULL;
static int quiet_mode = 0;
static int pretend_mode = 0;
void set_brightness(int value, int raw) {
if (pretend_mode) {
if (!quiet_mode) {
printf("Pretend mode: setting brightness to %d\n", value);
}
return;
}
FILE *brightness_file = fopen(BRIGHTNESS_PATH, "w");
if (brightness_file == NULL) {
perror("Failed to open brightness file");
exit(EXIT_FAILURE);
}
if (!raw) {
FILE *max_brightness_file = fopen(MAX_BRIGHTNESS_PATH, "r");
if (max_brightness_file == NULL) {
perror("Failed to open max brightness file");
fclose(brightness_file);
exit(EXIT_FAILURE);
}
int max_brightness;
fscanf(max_brightness_file, "%d", &max_brightness);
fclose(max_brightness_file);
value = (value * max_brightness) / 100;
}
fprintf(brightness_file, "%d", value);
fclose(brightness_file);
display_brightness(value);
}
void adjust_brightness(const char *direction) {
if (pretend_mode) {
if (!quiet_mode) {
printf("Pretend mode: adjusting brightness %s\n", direction);
}
return;
}
FILE *brightness_file = fopen(BRIGHTNESS_PATH, "r+");
if (brightness_file == NULL) {
perror("Failed to open brightness file");
exit(EXIT_FAILURE);
}
int brightness;
fscanf(brightness_file, "%d", &brightness);
FILE *max_brightness_file = fopen(MAX_BRIGHTNESS_PATH, "r");
if (max_brightness_file == NULL) {
perror("Failed to open max brightness file");
fclose(brightness_file);
exit(EXIT_FAILURE);
}
int max_brightness;
fscanf(max_brightness_file, "%d", &max_brightness);
fclose(max_brightness_file);
if (strcmp(direction, "up") == 0) {
brightness += max_brightness / 10;
} else if (strcmp(direction, "down") == 0) {
brightness -= max_brightness / 10;
}
if (brightness < 0) {
brightness = 0;
} else if (brightness > max_brightness) {
brightness = max_brightness;
}
rewind(brightness_file);
fprintf(brightness_file, "%d", brightness);
fclose(brightness_file);
display_brightness(brightness);
}
void save_brightness(void) {
if (pretend_mode) {
if (!quiet_mode) {
printf("Pretend mode: saving brightness\n");
}
return;
}
FILE *brightness_file = fopen(BRIGHTNESS_PATH, "r");
if (brightness_file == NULL) {
perror("Failed to open brightness file");
exit(EXIT_FAILURE);
}
int brightness;
fscanf(brightness_file, "%d", &brightness);
fclose(brightness_file);
FILE *save_file = fopen("/tmp/brightnessctl_save", "w");
if (save_file == NULL) {
perror("Failed to open save file");
exit(EXIT_FAILURE);
}
fprintf(save_file, "%d", brightness);
fclose(save_file);
if (!quiet_mode) {
printf("Brightness saved: %d\n", brightness);
}
}
void restore_brightness(void) {
if (pretend_mode) {
if (!quiet_mode) {
printf("Pretend mode: restoring brightness\n");
}
return;
}
FILE *save_file = fopen("/tmp/brightnessctl_save", "r");
if (save_file == NULL) {
perror("Failed to open save file");
exit(EXIT_FAILURE);
}
int brightness;
fscanf(save_file, "%d", &brightness);
fclose(save_file);
set_brightness(brightness, 1);
if (!quiet_mode) {
printf("Brightness restored: %d\n", brightness);
}
}
void list_devices(void) {
printf("Listing available devices...\n");
}
void print_info(void) {
printf("Printing device info...\n");
}
void set_min_value(int value) {
min_value = value;
}
void set_exponent(float exp) {
exponent = exp;
}
void set_device(char *device_name) {
device = strdup(device_name);
}
void set_class(char *class_name) {
class = strdup(class_name);
}
void enable_quiet_mode(void) {
quiet_mode = 1;
}
void enable_pretend_mode(void) {
pretend_mode = 1;
}

View File

@@ -0,0 +1,21 @@
#ifndef BRIGHTNESS_H
#define BRIGHTNESS_H
#define BRIGHTNESS_PATH "/sys/class/backlight/intel_backlight/brightness"
#define MAX_BRIGHTNESS_PATH "/sys/class/backlight/intel_backlight/max_brightness"
void set_brightness(int value, int raw);
void adjust_brightness(const char *direction);
void display_brightness(int brightness);
void save_brightness(void);
void restore_brightness(void);
void list_devices(void);
void print_info(void);
void set_min_value(int value);
void set_exponent(float exponent);
void set_device(char *device_name);
void set_class(char *class_name);
void enable_quiet_mode(void);
void enable_pretend_mode(void);
#endif

View File

@@ -0,0 +1,95 @@
#include "display.h"
#include "brightness.h"
#include "utils.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
void draw_icon(Display *d, Window w, GC gc) {
XDrawArc(d, w, gc, 70, 10, 60, 60, 0, 360 * 64);
for (int i = 0; i < 8; ++i) {
int x1 = 100 + 30 * cos(i * M_PI / 4);
int y1 = 40 + 30 * sin(i * M_PI / 4);
int x2 = 100 + 45 * cos(i * M_PI / 4);
int y2 = 40 + 45 * sin(i * M_PI / 4);
XDrawLine(d, w, gc, x1, y1, x2, y2);
}
}
void display_brightness(int brightness) {
Display *d;
Window w;
XEvent e;
int screen;
unsigned int display_width, display_height;
int width = 200, height = 120;
int sections = 10;
int graph_height = 10;
if ((d = XOpenDisplay(NULL)) == NULL) {
exit(1);
}
screen = DefaultScreen(d);
display_width = DisplayWidth(d, screen);
display_height = DisplayHeight(d, screen);
w = XCreateSimpleWindow(d, RootWindow(d, screen), (display_width - width) / 2, (display_height - height) / 2, width, height, 1,
BlackPixel(d, screen), WhitePixel(d, screen));
XSetWindowBackground(d, w, 0xD3D3D3);
XStoreName(d, w, "BrightnessControl");
XClassHint *classHint = XAllocClassHint();
classHint->res_name = "brightnesscontrol";
classHint->res_class = "BrightnessControl";
XSetClassHint(d, w, classHint);
XFree(classHint);
XSelectInput(d, w, ExposureMask | KeyPressMask | StructureNotifyMask);
XMapWindow(d, w);
GC gc = XCreateGC(d, w, 0, NULL);
GC bg_gc = XCreateGC(d, w, 0, NULL);
XSetForeground(d, gc, BlackPixel(d, screen));
XSetBackground(d, bg_gc, WhitePixel(d, screen));
XSetLineAttributes(d, gc, 3, LineSolid, CapButt, JoinMiter);
FILE *max_brightness_file = fopen(MAX_BRIGHTNESS_PATH, "r");
int max_brightness;
fscanf(max_brightness_file, "%d", &max_brightness);
fclose(max_brightness_file);
while (1) {
XNextEvent(d, &e);
if (e.type == MapNotify) {
break;
}
}
draw_icon(d, w, gc);
XSetForeground(d, bg_gc, 0xA9A9A9);
XFillRectangle(d, w, bg_gc, 10, 100, 180, graph_height);
int filled_sections = (int)((brightness / (double)max_brightness) * sections);
for (int i = 0; i < sections; ++i) {
if (i < filled_sections) {
XSetForeground(d, gc, 0xFFFFFF);
} else {
XSetForeground(d, gc, 0xA9A9A9);
}
XFillRectangle(d, w, gc, 10 + i * (180 / sections), 100, 180 / sections - 2, graph_height);
}
XFlush(d);
sleep(1);
XDestroyWindow(d, w);
XCloseDisplay(d);
}

View File

@@ -0,0 +1,9 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include <X11/Xlib.h> // Include X11 headers for Display, Window, and GC types
void draw_icon(Display *d, Window w, GC gc);
void display_brightness(int brightness);
#endif

View File

@@ -0,0 +1,113 @@
#include "brightness.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void print_usage(const char *prog_name) {
printf("Usage: %s [OPTION]...\n", prog_name);
printf("Options:\n");
printf(" -s, --save Save current brightness\n");
printf(" -r, --restore Restore saved brightness\n");
printf(" --set VALUE Set brightness to VALUE\n");
printf(" -a, --adjust DIR Adjust brightness up or down\n");
printf(" -h, --help Display this help and exit\n");
printf(" -l, --list List devices with available brightness controls\n");
printf(" -q, --quiet Suppress output\n");
printf(" -p, --pretend Do not perform write operations\n");
printf(" -n, --min-value MIN_VALUE Set minimum brightness value\n");
printf(" -e, --exponent EXPONENT Change percentage curve to exponential\n");
printf(" -c, --class CLASS Specify device class\n");
printf(" -d, --device DEVICE Specify device name\n");
printf(" -v, --version Print version and exit\n");
}
int main(int argc, char *argv[]) {
int opt;
static struct option long_options[] = {
{"save", no_argument, NULL, 's'},
{"restore", no_argument, NULL, 'r'},
{"set", required_argument, NULL, 0},
{"adjust", required_argument, NULL, 'a'},
{"help", no_argument, NULL, 'h'},
{"list", no_argument, NULL, 'l'},
{"quiet", no_argument, NULL, 'q'},
{"pretend", no_argument, NULL, 'p'},
{"min-value", required_argument, NULL, 'n'},
{"exponent", required_argument, NULL, 'e'},
{"class", required_argument, NULL, 'c'},
{"device", required_argument, NULL, 'd'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}
};
while ((opt = getopt_long(argc, argv, "sra:hlqpne:c:d:v", long_options, NULL)) != -1) {
switch (opt) {
case 's':
save_brightness();
break;
case 'r':
restore_brightness();
break;
case 0: // --set
if (optarg) {
int value = atoi(optarg);
set_brightness(value, 0);
}
break;
case 'a':
if (optarg) {
adjust_brightness(optarg);
}
break;
case 'h':
print_usage(argv[0]);
return EXIT_SUCCESS;
case 'l':
list_devices();
break;
case 'q':
enable_quiet_mode();
break;
case 'p':
enable_pretend_mode();
break;
case 'n':
if (optarg) {
int min_value = atoi(optarg);
set_min_value(min_value);
}
break;
case 'e':
if (optarg) {
float exponent = atof(optarg);
set_exponent(exponent);
}
break;
case 'c':
if (optarg) {
set_class(optarg);
}
break;
case 'd':
if (optarg) {
set_device(optarg);
}
break;
case 'v':
printf("Version 1.0\n");
return EXIT_SUCCESS;
default:
print_usage(argv[0]);
return EXIT_FAILURE;
}
}
if (argc == 1) {
print_usage(argv[0]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,29 @@
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
char *read_file_to_string(const char *filepath) {
FILE *file = fopen(filepath, "r");
if (!file) {
perror("Failed to open file");
exit(EXIT_FAILURE);
}
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
char *content = malloc(length + 1);
if (!content) {
perror("Failed to allocate memory");
fclose(file);
exit(EXIT_FAILURE);
}
fread(content, 1, length, file);
content[length] = '\0';
fclose(file);
return content;
}

View File

@@ -0,0 +1,6 @@
#ifndef UTILS_H
#define UTILS_H
char *read_file_to_string(const char *filepath);
#endif

20
brightness_menu/Makefile Normal file
View File

@@ -0,0 +1,20 @@
CC = gcc
CFLAGS = -Wall -Wextra -Iinclude `pkg-config --cflags gtk+-3.0`
LDFLAGS = -lncurses -lX11 -lXrandr `pkg-config --libs gtk+-3.0`
SRC = $(wildcard src/*.c)
OBJ = $(patsubst src/%.c, build/%.o, $(SRC))
TARGET = build/screen_control
all: $(TARGET)
$(TARGET): $(OBJ)
$(CC) -o $(TARGET) $(OBJ) $(LDFLAGS)
build/%.o: src/%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f build/*.o $(TARGET)
.PHONY: all clean

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#ifndef BRIGHTNESS_H
#define BRIGHTNESS_H
void adjust_brightness();
#endif // BRIGHTNESS_H

View File

@@ -0,0 +1,6 @@
#ifndef COLOR_TEMPERATURE_H
#define COLOR_TEMPERATURE_H
void adjust_color_temperature();
#endif // COLOR_TEMPERATURE_H

View File

@@ -0,0 +1,7 @@
#ifndef DISPLAY_H
#define DISPLAY_H
void display_main_screen();
void handle_option(int option);
#endif // DISPLAY_H

View File

@@ -0,0 +1,6 @@
#ifndef REFRESH_RATE_H
#define REFRESH_RATE_H
void adjust_refresh_rate();
#endif // REFRESH_RATE_H

View File

@@ -0,0 +1,6 @@
#ifndef RESOLUTION_H
#define RESOLUTION_H
void adjust_resolution();
#endif // RESOLUTION_H

View File

@@ -0,0 +1,53 @@
#include <ncurses.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "brightness.h"
void adjust_brightness() {
FILE *brightness_file;
int brightness;
char path[] = "/sys/class/backlight/intel_backlight/brightness";
char max_brightness_path[] = "/sys/class/backlight/intel_backlight/max_brightness";
int max_brightness;
brightness_file = fopen(path, "r+");
if (brightness_file == NULL) {
mvprintw(4, 0, "Error: %s", strerror(errno));
refresh();
return;
}
FILE *max_brightness_file = fopen(max_brightness_path, "r");
if (max_brightness_file == NULL) {
mvprintw(4, 0, "Error: %s", strerror(errno));
fclose(brightness_file);
refresh();
return;
}
fscanf(max_brightness_file, "%d", &max_brightness);
fclose(max_brightness_file);
fscanf(brightness_file, "%d", &brightness);
mvprintw(4, 0, "Current Brightness: %d", brightness);
mvprintw(5, 0, "Max Brightness: %d", max_brightness);
mvprintw(6, 0, "Enter new brightness (0-%d): ", max_brightness);
refresh();
echo();
scanw("%d", &brightness);
noecho();
if (brightness < 0 || brightness > max_brightness) {
mvprintw(7, 0, "Invalid brightness value");
} else {
rewind(brightness_file);
fprintf(brightness_file, "%d", brightness);
fflush(brightness_file);
mvprintw(7, 0, "Brightness set to %d", brightness);
}
fclose(brightness_file);
refresh();
getch();
}

Binary file not shown.

View File

@@ -0,0 +1,26 @@
#include <ncurses.h>
#include <stdlib.h>
#include <stdio.h>
#include "color_temperature.h"
void adjust_color_temperature() {
mvprintw(4, 0, "Enter the desired color temperature (in Kelvin, e.g., 6500): ");
refresh();
echo();
int color_temp;
scanw("%d", &color_temp);
noecho();
char command[128];
snprintf(command, sizeof(command), "xcalib -co %d", color_temp);
int result = system(command);
if (result == -1) {
mvprintw(6, 0, "Failed to set color temperature");
} else {
mvprintw(6, 0, "Color temperature set to %dK", color_temp);
}
refresh();
getch();
}

View File

@@ -0,0 +1,49 @@
#include <ncurses.h>
#include <stdlib.h>
#include "display.h"
#include "brightness.h"
#include "resolution.h"
#include "refresh_rate.h"
#include "color_temperature.h"
void display_main_screen() {
int option;
mvprintw(0, 0, "Select an option:");
mvprintw(1, 0, "1. Screen Brightness");
mvprintw(2, 0, "2. Screen Resolution");
mvprintw(3, 0, "3. Refresh Rate");
mvprintw(4, 0, "4. Color Temperature");
mvprintw(5, 0, "5. Exit");
refresh();
option = getch() - '0';
handle_option(option);
}
void handle_option(int option) {
switch(option) {
case 1:
adjust_brightness();
break;
case 2:
adjust_resolution();
break;
case 3:
adjust_refresh_rate();
break;
case 4:
adjust_color_temperature();
break;
case 5:
endwin();
exit(0);
default:
mvprintw(6, 0, "Invalid option");
refresh();
getch();
break;
}
display_main_screen();
}

Binary file not shown.

View File

@@ -0,0 +1,60 @@
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include "display.h"
void run_cli();
void run_gui();
int main(int argc, char *argv[]) {
int opt;
int gui_mode = 0;
// Parse command-line arguments
while ((opt = getopt(argc, argv, "g")) != -1) {
switch (opt) {
case 'g':
gui_mode = 1;
break;
default:
fprintf(stderr, "Usage: %s [-g]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
if (gui_mode) {
run_gui(argc, argv);
} else {
run_cli();
}
return 0;
}
void run_cli() {
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
display_main_screen();
endwin();
}
void run_gui(int argc, char *argv[]) {
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_title(GTK_WINDOW(window), "Screen Control GUI");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_widget_show_all(window);
gtk_main();
}

BIN
brightness_menu/src/main.o Normal file

Binary file not shown.

View File

@@ -0,0 +1,76 @@
#include <ncurses.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <stdlib.h>
#include <stdio.h>
#include "refresh_rate.h"
void list_refresh_rates(XRRScreenResources *res, XRRCrtcInfo *crtc_info) {
mvprintw(5, 0, "Available Refresh Rates:");
int rate_count = 0;
for (int i = 0; i < res->nmode; i++) {
XRRModeInfo mode = res->modes[i];
for (int j = 0; j < crtc_info->noutput; j++) {
if (crtc_info->outputs[j] == mode.id) {
double refresh_rate = (double)mode.dotClock / (mode.hTotal * mode.vTotal);
mvprintw(6 + rate_count, 0, "%d: %.2f Hz", rate_count, refresh_rate);
rate_count++;
}
}
}
refresh();
}
void set_refresh_rate() {
// Placeholder for setting the refresh rate based on selected index
mvprintw(7, 0, "Setting refresh rate feature coming soon...");
refresh();
}
void adjust_refresh_rate() {
Display *dpy = XOpenDisplay(NULL);
if (dpy == NULL) {
mvprintw(4, 0, "Unable to open X display");
refresh();
getch();
return;
}
Window root = DefaultRootWindow(dpy);
XRRScreenResources *res = XRRGetScreenResources(dpy, root);
if (res == NULL) {
mvprintw(4, 0, "Unable to get screen resources");
XCloseDisplay(dpy);
refresh();
getch();
return;
}
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(dpy, res, res->crtcs[0]);
if (crtc_info == NULL) {
mvprintw(4, 0, "Unable to get CRTC info");
XRRFreeScreenResources(res);
XCloseDisplay(dpy);
refresh();
getch();
return;
}
list_refresh_rates(res, crtc_info);
mvprintw(6 + res->nmode, 0, "Enter the index of the desired refresh rate: ");
refresh();
echo();
int rate_index;
scanw("%d", &rate_index);
noecho();
set_refresh_rate();
XRRFreeCrtcInfo(crtc_info);
XRRFreeScreenResources(res);
XCloseDisplay(dpy);
getch();
}

View File

@@ -0,0 +1,66 @@
#include <ncurses.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <stdlib.h>
#include <stdio.h>
#include "resolution.h"
void list_resolutions(XRRScreenResources *res) {
mvprintw(5, 0, "Available Resolutions:");
for (int i = 0; i < res->nmode; i++) {
XRRModeInfo mode = res->modes[i];
mvprintw(6 + i, 0, "%d: %dx%d", i, mode.width, mode.height);
}
refresh();
}
void set_resolution(Display *dpy, Window root, XRRScreenResources *res, int mode_index) {
if (mode_index < 0 || mode_index >= res->nmode) {
mvprintw(7 + res->nmode, 0, "Invalid mode index");
return;
}
XRRModeInfo mode = res->modes[mode_index];
XRRSetScreenSize(dpy, root, mode.width, mode.height, mode.width, mode.height);
XRRSetCrtcConfig(dpy, res, res->crtcs[0], CurrentTime, 0, 0, mode.id, RR_Rotate_0, NULL, 0);
mvprintw(7 + res->nmode, 0, "Resolution set to %dx%d", mode.width, mode.height);
refresh();
}
void adjust_resolution() {
Display *dpy = XOpenDisplay(NULL);
if (dpy == NULL) {
mvprintw(4, 0, "Unable to open X display");
refresh();
getch();
return;
}
Window root = DefaultRootWindow(dpy);
XRRScreenResources *res = XRRGetScreenResources(dpy, root);
if (res == NULL) {
mvprintw(4, 0, "Unable to get screen resources");
XCloseDisplay(dpy);
refresh();
getch();
return;
}
list_resolutions(res);
mvprintw(6 + res->nmode, 0, "Enter the index of the desired resolution: ");
refresh();
echo();
int mode_index;
scanw("%d", &mode_index);
noecho();
set_resolution(dpy, root, res, mode_index);
XRRFreeScreenResources(res);
XCloseDisplay(dpy);
getch();
}

1
mpvd/build/Install Normal file
View File

@@ -0,0 +1 @@
libasound2-dev

19
mpvd/build/Makefile Normal file
View File

@@ -0,0 +1,19 @@
CC = gcc
CFLAGS = -I../include -Wall -g
LDFLAGS = -lasound -lncurses # Added -lncurses for ncurses functions
OBJDIR = ../obj
SRCDIR = ../src
INCLUDEDIR = ../include
OBJS = $(OBJDIR)/main.o $(OBJDIR)/audio.o $(OBJDIR)/ui.o $(OBJDIR)/dir.o
all: mpvd
mpvd: $(OBJS)
$(CC) $(CFLAGS) -o mpvd $(OBJS) $(LDFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJDIR)/*.o mpvd

BIN
mpvd/build/mpvd Executable file

Binary file not shown.

6
mpvd/include/audio.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef AUDIO_H
#define AUDIO_H
void play_audio(const char *file_path);
#endif

10
mpvd/include/dir.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef DIR_H
#define DIR_H
#define MAX_PATH_LEN 512 // Declare MAX_PATH_LEN for use across files
char* get_music_directory();
char** load_songs(int *count, const char *music_dir);
void navigate_directory(const char *dir, char **songs, int *song_count); // Declare navigate_directory
#endif

14
mpvd/include/ui.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef UI_H
#define UI_H
#include <ncurses.h>
void init_ncurses();
void end_ncurses();
void display_songs(WINDOW *win, char **songs, int song_count, int selected);
void display_controls(WINDOW *control_win, int is_playing);
// Update handle_input to accept current_directory
void handle_input(int ch, char **songs, int *selected_song, int *song_count, WINDOW *song_win, WINDOW *control_win, int *is_playing, char *current_directory);
#endif

BIN
mpvd/obj/audio.o Normal file

Binary file not shown.

BIN
mpvd/obj/dir.o Normal file

Binary file not shown.

BIN
mpvd/obj/main.o Normal file

Binary file not shown.

BIN
mpvd/obj/ui.o Normal file

Binary file not shown.

41
mpvd/src/audio.c Normal file
View File

@@ -0,0 +1,41 @@
#include "audio.h"
#include <alsa/asoundlib.h>
void play_audio(const char *file_path) {
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int rate = 44100;
int dir;
snd_pcm_uframes_t frames;
int rc;
// Open PCM device for playback
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "Unable to open PCM device: %s\n", snd_strerror(rc));
return;
}
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, 2);
snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
snd_pcm_hw_params(handle, params);
// Simulate audio playback (replace this with actual file reading and playback)
unsigned char *buffer = malloc(128 * 4);
memset(buffer, 0, 128 * 4);
for (int i = 0; i < 100; ++i) {
rc = snd_pcm_writei(handle, buffer, 128);
if (rc == -EPIPE) {
snd_pcm_prepare(handle);
}
}
free(buffer);
snd_pcm_drain(handle);
snd_pcm_close(handle);
}

49
mpvd/src/dir.c Normal file
View File

@@ -0,0 +1,49 @@
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <stdio.h>
#include <unistd.h>
#include "dir.h"
char* get_music_directory() {
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
char *music_dir = malloc(strlen(homedir) + strlen("/Music/mpvd") + 1);
strcpy(music_dir, homedir);
strcat(music_dir, "/Music/mpvd");
return music_dir;
}
char** load_songs(int *count, const char *music_dir) {
DIR *d;
struct dirent *dir;
d = opendir(music_dir);
if (!d) {
perror("Failed to open music directory");
exit(1);
}
char **songs = malloc(sizeof(char*) * 100);
int index = 0;
while ((dir = readdir(d)) != NULL) {
if (dir->d_type == DT_REG) {
songs[index] = strdup(dir->d_name);
index++;
} else if (dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0) {
char dir_name[512];
snprintf(dir_name, sizeof(dir_name), "%s/", dir->d_name);
songs[index] = strdup(dir_name); // Append '/' to indicate a directory
index++;
}
}
closedir(d);
*count = index;
return songs;
}
void navigate_directory(const char *dir, char **songs, int *song_count) {
free(songs); // Free old song list
songs = load_songs(song_count, dir); // Load new song list
}

55
mpvd/src/main.c Normal file
View File

@@ -0,0 +1,55 @@
// src/main.c
#include <ncurses.h>
#include "audio.h"
#include "ui.h"
#include "dir.h"
int main() {
// Initialize ncurses
initscr();
noecho();
cbreak();
curs_set(FALSE);
keypad(stdscr, TRUE);
// Initialize UI and audio
init_ui();
init_audio();
// Load the directory contents
load_directory("/Music/mpvd");
int ch;
while ((ch = getch()) != 'q') {
// Handle navigation and controls
switch (ch) {
case KEY_UP:
navigate_up();
break;
case KEY_DOWN:
navigate_down();
break;
case '\n': // Enter key
select_item();
break;
case ' ': // Spacebar
toggle_playback();
break;
case KEY_LEFT:
decrease_volume();
break;
case KEY_RIGHT:
increase_volume();
break;
}
update_ui();
}
// Cleanup
cleanup_ui();
cleanup_audio();
endwin();
return 0;
}

282
mpvd/src/mpvd.c Normal file
View File

@@ -0,0 +1,282 @@
#include <ncurses.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <pwd.h>
#include <alsa/asoundlib.h>
#define MAX_SONGS 100
#define MAX_PATH_LEN 512
// Function declarations
void init_ncurses();
void end_ncurses();
void display_songs(WINDOW *win, char **songs, int song_count, int selected);
char** load_songs(int *count, const char *music_dir);
char* get_music_directory();
void display_controls(WINDOW *control_win, int is_playing);
void play_audio(const char *file_path);
void stop_audio();
void add_to_queue(const char *song);
void play_next_in_queue();
void navigate_directory(const char *dir, char **songs, int *song_count);
// Global variables
char *queue[MAX_SONGS];
int queue_size = 0;
int queue_position = 0;
int is_playing = 0;
char current_directory[MAX_PATH_LEN];
int main() {
int song_count;
char **songs;
int selected_song = 0;
int ch;
// Initialize music directory
strcpy(current_directory, get_music_directory());
songs = load_songs(&song_count, current_directory);
// Initialize ncurses
init_ncurses();
// Set up windows
int half_height = LINES / 2;
int width = COLS;
WINDOW *song_win = newwin(half_height, width, 0, 0);
WINDOW *control_win = newwin(half_height, width, half_height, 0);
// Display initial songs and controls
display_songs(song_win, songs, song_count, selected_song);
display_controls(control_win, is_playing);
// Main loop to handle input
while ((ch = getch()) != 'q') {
switch (ch) {
case KEY_UP:
case 'k':
if (selected_song > 0) selected_song--;
break;
case KEY_DOWN:
case 'j':
if (selected_song < song_count - 1) selected_song++;
break;
case '\n':
if (songs[selected_song][strlen(songs[selected_song]) - 1] == '/') {
// Enter directory
char new_dir[MAX_PATH_LEN];
snprintf(new_dir, MAX_PATH_LEN, "%s/%s", current_directory, songs[selected_song]);
navigate_directory(new_dir, songs, &song_count);
selected_song = 0; // Reset selection
} else {
if (is_playing) {
// Add to queue if something is playing
add_to_queue(songs[selected_song]);
} else {
// Play selected song immediately
char song_path[MAX_PATH_LEN];
snprintf(song_path, MAX_PATH_LEN, "%s/%s", current_directory, songs[selected_song]);
play_audio(song_path);
is_playing = 1;
}
}
break;
case ' ': // Pause/play functionality
if (is_playing) {
stop_audio();
is_playing = 0;
} else {
play_next_in_queue();
is_playing = 1;
}
break;
case KEY_RIGHT:
case 'l': // Fast-forward (skipping in the queue)
play_next_in_queue();
break;
case KEY_LEFT:
case 'h': // Go back in the queue (not implemented yet)
break;
case KEY_NPAGE:
case 'L': // Skip track
play_next_in_queue();
break;
case KEY_PPAGE:
case 'H': // Go back track (not implemented yet)
break;
}
display_songs(song_win, songs, song_count, selected_song); // Update song selection
display_controls(control_win, is_playing); // Update controls display
}
// Clean up ncurses
end_ncurses();
return 0;
}
void init_ncurses() {
initscr();
noecho();
cbreak();
keypad(stdscr, TRUE);
}
void end_ncurses() {
endwin();
}
char* get_music_directory() {
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
char *music_dir = malloc(strlen(homedir) + strlen("/Music/mpvd") + 1);
strcpy(music_dir, homedir);
strcat(music_dir, "/Music/mpvd");
return music_dir;
}
char** load_songs(int *count, const char *music_dir) {
DIR *d;
struct dirent *dir;
d = opendir(music_dir);
if (!d) {
perror("Failed to open music directory");
exit(1);
}
char **songs = malloc(sizeof(char*) * MAX_SONGS);
int index = 0;
while ((dir = readdir(d)) != NULL) {
if (dir->d_type == DT_REG) {
songs[index] = strdup(dir->d_name);
index++;
} else if (dir->d_type == DT_DIR) {
if (strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0) {
char dir_name[MAX_PATH_LEN];
snprintf(dir_name, MAX_PATH_LEN, "%s/", dir->d_name);
songs[index] = strdup(dir_name);
index++;
}
}
}
closedir(d);
*count = index;
return songs;
}
void display_songs(WINDOW *win, char **songs, int song_count, int selected) {
werase(win);
box(win, 0, 0);
mvwprintw(win, 0, 1, "Select a song or directory to play/open (Up/Down to navigate, Enter to select)");
for (int i = 0; i < song_count; i++) {
if (i == selected)
wattron(win, A_REVERSE);
mvwprintw(win, i + 1, 1, "%s", songs[i]);
wattroff(win, A_REVERSE);
}
wrefresh(win);
}
void display_controls(WINDOW *control_win, int is_playing) {
werase(control_win);
box(control_win, 0, 0);
// Display Unicode controls for play/pause, forward, and back
if (is_playing) {
mvwprintw(control_win, 1, 1, "⏸ Pausing");
} else {
mvwprintw(control_win, 1, 1, "⏵ Playing");
}
mvwprintw(control_win, 1, 20, "⏪ Previous");
mvwprintw(control_win, 1, 40, "⏩ Next");
wrefresh(control_win);
}
void navigate_directory(const char *dir, char **songs, int *song_count) {
strcpy(current_directory, dir);
songs = load_songs(song_count, current_directory);
}
void add_to_queue(const char *song) {
if (queue_size < MAX_SONGS) {
queue[queue_size] = strdup(song);
queue_size++;
}
}
void play_next_in_queue() {
if (queue_position < queue_size) {
char song_path[MAX_PATH_LEN];
snprintf(song_path, MAX_PATH_LEN, "%s/%s", current_directory, queue[queue_position]);
play_audio(song_path);
queue_position++;
} else {
// Queue is empty
stop_audio();
}
}
void play_audio(const char *file_path) {
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int rate = 44100; // Default sample rate
int dir;
snd_pcm_uframes_t frames;
int rc;
// Open PCM device for playback
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr, "Unable to open PCM device: %s\n", snd_strerror(rc));
return;
}
// Allocate a hardware parameters object
snd_pcm_hw_params_alloca(&params);
// Fill it in with default values
snd_pcm_hw_params_any(handle, params);
// Set the desired hardware parameters
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, 2);
snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
// Write the parameters to the driver
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr, "Unable to set HW parameters: %s\n", snd_strerror(rc));
return;
}
// Playback the audio (This is a simplified example, you would actually need to read the file)
unsigned char *buffer = malloc(128 * 4);
memset(buffer, 0, 128 * 4);
for (int i = 0; i < 100; ++i) {
rc = snd_pcm_writei(handle, buffer, 128);
if (rc == -EPIPE) {
// EPIPE means underrun
fprintf(stderr, "Underrun occurred\n");
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr, "Error writing to PCM device: %s\n", snd_strerror(rc));
}
}
// Cleanup
free(buffer);
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
void stop_audio() {
// Placeholder for stopping audio playback
mvprintw(LINES - 1, 0, "Audio stopped.");
refresh();
}

87
mpvd/src/ui.c Normal file
View File

@@ -0,0 +1,87 @@
#include <ncurses.h>
#include <stdlib.h>
#include <string.h>
#include "ui.h"
#include "dir.h"
#include "audio.h"
void init_ncurses() {
initscr();
noecho();
cbreak();
keypad(stdscr, TRUE);
}
void end_ncurses() {
endwin();
}
void display_songs(WINDOW *win, char **songs, int song_count, int selected) {
werase(win);
box(win, 0, 0);
mvwprintw(win, 0, 1, "Select a song or directory to play/open");
for (int i = 0; i < song_count; i++) {
if (i == selected)
wattron(win, A_REVERSE);
mvwprintw(win, i + 1, 1, "%s", songs[i]);
wattroff(win, A_REVERSE);
}
wrefresh(win);
}
void display_controls(WINDOW *control_win, int is_playing, const char *current_song, int volume) {
werase(control_win);
box(control_win, 0, 0);
// Display current song and volume
mvwprintw(control_win, 1, 1, "Now Playing: %s", current_song);
mvwprintw(control_win, 2, 1, "Volume: %d%%", volume);
// Display if song is playing or paused
if (is_playing) {
mvwprintw(control_win, 1, COLS - 10, "⏸ Paused");
} else {
mvwprintw(control_win, 1, COLS - 10, "⏵ Playing");
}
wrefresh(control_win);
}
void handle_input(int ch, char **songs, int *selected_song, int *song_count, WINDOW *song_win, WINDOW *control_win, int *is_playing, char *current_directory, char *current_song, int *volume) {
switch (ch) {
case KEY_UP:
case 'k':
if (*selected_song > 0) (*selected_song)--;
break;
case KEY_DOWN:
case 'j':
if (*selected_song < *song_count - 1) (*selected_song)++;
break;
case '\n': // Handle directory or song selection
if (songs[*selected_song][strlen(songs[*selected_song]) - 1] == '/') {
char new_dir[MAX_PATH_LEN];
snprintf(new_dir, MAX_PATH_LEN, "%s/%s", current_directory, songs[*selected_song]);
navigate_directory(new_dir, songs, song_count);
strcpy(current_directory, new_dir); // Update current directory
*selected_song = 0; // Reset selection after entering directory
} else {
char song_path[MAX_PATH_LEN];
snprintf(song_path, MAX_PATH_LEN, "%s/%s", current_directory, songs[*selected_song]);
play_audio(song_path);
strcpy(current_song, songs[*selected_song]); // Update current song
*is_playing = 1;
}
break;
case ' ': // Pause/Play toggle
*is_playing = !(*is_playing);
break;
case KEY_RIGHT: // Increase volume
if (*volume < 100) (*volume)++;
break;
case KEY_LEFT: // Decrease volume
if (*volume > 0) (*volume)--;
break;
}
display_songs(song_win, songs, *song_count, *selected_song);
display_controls(control_win, *is_playing, current_song, *volume);
}

View File

@@ -0,0 +1,33 @@
# Compiler and Flags
CC = gcc
CFLAGS = -Wall -Wextra -std=c11 -g
LDFLAGS = -lncurses
# Source Files
SRCS = main.c
OBJS = $(SRCS:.c=.o)
# Output Binary
TARGET = calendar
# Default Target
all: $(TARGET)
# Build Target
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS)
# Object Files
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# Clean Target
clean:
rm -f $(OBJS) $(TARGET)
# Run Target
run: $(TARGET)
./$(TARGET)
# Phony Targets
.PHONY: all clean run

BIN
ncursescalander/backup/calendar Executable file

Binary file not shown.

View File

@@ -0,0 +1,326 @@
#include <ncurses.h>
#include <string.h>
#include <time.h>
#define MAX_TITLE_LEN 50
#define MAX_CALENDAR_LEN 30
#define MAX_NOTES_LEN 256
void draw_calendar(int month, int year, int selected_day);
void draw_day_view(int *day, int *month, int *year);
void open_add_event_tui();
void open_repeat_tui();
int main() {
int ch, month, year, day = 1;
time_t now;
struct tm *local;
// Initialize ncurses
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK); // Red color pair for the current time
// Get current date
now = time(NULL);
local = localtime(&now);
month = local->tm_mon + 1;
year = local->tm_year + 1900;
day = local->tm_mday;
// Main loop
while ((ch = getch()) != 'q') {
bool redraw = false;
switch (ch) {
case KEY_LEFT:
case 'j':
if (day > 1) {
day--;
redraw = true;
}
break;
case KEY_RIGHT:
case 'k':
if (day < 31) { // Simplified for demo purposes
day++;
redraw = true;
}
break;
case 'a': // 'a' for "Add Event"
open_add_event_tui();
redraw = true;
break;
case '\n': // Enter key to open the day view
draw_day_view(&day, &month, &year);
redraw = true;
break;
}
if (redraw) {
clear();
mvprintw(0, 0, "Calendar View - Press 'a' to Add Event");
draw_calendar(month, year, day);
refresh();
}
}
// End ncurses mode
endwin();
return 0;
}
void draw_calendar(int month, int year, int selected_day) {
mvprintw(1, 0, "Month: %d, Year: %d", month, year);
mvprintw(2, 0, "Su Mo Tu We Th Fr Sa");
// Placeholder for calendar drawing
for (int i = 1; i <= 30; i++) { // Simplified example for 30 days
if (i == selected_day) {
attron(A_REVERSE); // Highlight the selected day
}
mvprintw(3 + (i / 7), (i % 7) * 3, "%2d", i);
if (i == selected_day) {
attroff(A_REVERSE);
}
}
}
void draw_day_view(int *day, int *month, int *year) {
time_t now;
struct tm *local;
int ch;
bool redraw = true;
while (true) {
if (redraw) {
clear();
mvprintw(0, 0, "Day View");
mvprintw(1, 0, "Date: %02d-%02d-%04d", *day, *month, *year);
// Draw the hours of the day
for (int i = 0; i < 24; i++) {
mvprintw(i + 3, 0, "%02d:00 |", i);
mvhline(i + 3, 8, ' ', COLS - 8);
}
now = time(NULL);
local = localtime(&now);
if (local->tm_mday == *day && (local->tm_mon + 1) == *month && (local->tm_year + 1900) == *year) {
int current_hour = local->tm_hour;
int current_minute = local->tm_min;
attron(COLOR_PAIR(1)); // Red color
mvhline(current_hour + 3, 8, '-', COLS - 8);
mvprintw(current_hour + 3, 0, "%02d:%02d |", current_hour, current_minute);
attroff(COLOR_PAIR(1));
}
refresh();
redraw = false;
}
nodelay(stdscr, TRUE); // Make getch non-blocking
ch = getch();
if (ch == 'q') break; // Exit to Calendar View
if (ch == KEY_LEFT || ch == 'j') {
if (*day > 1) {
(*day)--;
} else {
if (*month > 1) {
(*month)--;
} else {
*month = 12;
(*year)--;
}
*day = 31; // Simplified for demo purposes
}
redraw = true;
} else if (ch == KEY_RIGHT || ch == 'k') {
if (*day < 31) { // Simplified for demo purposes
(*day)++;
} else {
*day = 1;
if (*month < 12) {
(*month)++;
} else {
*month = 1;
(*year)++;
}
}
redraw = true;
}
napms(1000);
}
nodelay(stdscr, FALSE);
}
void open_add_event_tui() {
char title[MAX_TITLE_LEN] = "";
char calendar[MAX_CALENDAR_LEN] = "";
char notes[MAX_NOTES_LEN] = "";
int ch;
bool all_day = false;
int start_day = 1, end_day = 1;
int start_hour = 9, start_minute = 0;
int end_hour = 10, end_minute = 0;
int repeat_option = 0;
char repeat_options[4][20] = {"Never", "Every Day", "Every Week", "Custom"};
int cursor_pos = 0;
while (true) {
clear();
mvprintw(0, 0, "Add New Event");
// Title input
mvprintw(2, 0, "Title: ");
if (cursor_pos == 0) attron(A_REVERSE);
mvprintw(2, 7, title);
if (cursor_pos == 0) attroff(A_REVERSE);
// All-day toggle
mvprintw(4, 0, "All Day: ");
if (cursor_pos == 1) attron(A_REVERSE);
mvprintw(4, 9, all_day ? "[X]" : "[ ]");
if (cursor_pos == 1) attroff(A_REVERSE);
// Start day and time
mvprintw(6, 0, "Start Day: %02d", start_day);
mvprintw(7, 0, "Start Time: ");
if (cursor_pos == 2) attron(A_REVERSE);
mvprintw(7, 12, "%02d:%02d", start_hour, start_minute);
if (cursor_pos == 2) attroff(A_REVERSE);
// End day and time
mvprintw(9, 0, "End Day: %02d", end_day);
mvprintw(10, 0, "End Time: ");
if (cursor_pos == 3) attron(A_REVERSE);
mvprintw(10, 10, "%02d:%02d", end_hour, end_minute);
if (cursor_pos == 3) attroff(A_REVERSE);
// Repeat options
mvprintw(12, 0, "Repeat: ");
if (cursor_pos == 4) attron(A_REVERSE);
mvprintw(12, 8, repeat_options[repeat_option]);
if (cursor_pos == 4) attroff(A_REVERSE);
// Calendar name input
mvprintw(14, 0, "Calendar: ");
if (cursor_pos == 5) attron(A_REVERSE);
mvprintw(14, 10, calendar);
if (cursor_pos == 5) attroff(A_REVERSE);
// Notes input
mvprintw(16, 0, "Notes: ");
if (cursor_pos == 6) attron(A_REVERSE);
mvprintw(17, 0, notes);
if (cursor_pos == 6) attroff(A_REVERSE);
// Instructions
mvprintw(19, 0, "Press 'q' to cancel, 's' to save");
refresh();
ch = getch();
if (ch == 'q') break; // Exit without saving
if (ch == 's') {
// Here, you would normally save the event
break;
}
// Handle navigation
if (ch == KEY_DOWN || ch == 'j') {
cursor_pos = (cursor_pos + 1) % 7;
} else if (ch == KEY_UP || ch == 'k') {
cursor_pos = (cursor_pos - 1 + 7) % 7;
}
// Handle specific field input
if (cursor_pos == 0 && ch != KEY_DOWN && ch != KEY_UP && ch != 's' && ch != 'q') {
int len = strlen(title);
if (ch == KEY_BACKSPACE || ch == 127) {
if (len > 0) title[len - 1] = '\0';
} else if (len < MAX_TITLE_LEN - 1) {
title[len] = ch;
title[len + 1] = '\0';
}
} else if (cursor_pos == 1 && (ch == ' ' || ch == '\n')) {
all_day = !all_day;
} else if (cursor_pos == 2 && (ch == ' ' || ch == '\n')) {
// Placeholder: Handle start day and time input
} else if (cursor_pos == 3 && (ch == ' ' || ch == '\n')) {
// Placeholder: Handle end day and time input
} else if (cursor_pos == 4 && (ch == ' ' || ch == '\n')) {
// Open repeat TUI
open_repeat_tui();
} else if (cursor_pos == 5 && ch != KEY_DOWN && ch != KEY_UP && ch != 's' && ch != 'q') {
int len = strlen(calendar);
if (ch == KEY_BACKSPACE || ch == 127) {
if (len > 0) calendar[len - 1] = '\0';
} else if (len < MAX_CALENDAR_LEN - 1) {
calendar[len] = ch;
calendar[len + 1] = '\0';
}
} else if (cursor_pos == 6 && ch != KEY_DOWN && ch != KEY_UP && ch != 's' && ch != 'q') {
int len = strlen(notes);
if (ch == KEY_BACKSPACE || ch == 127) {
if (len > 0) notes[len - 1] = '\0';
} else if (len < MAX_NOTES_LEN - 1) {
notes[len] = ch;
notes[len + 1] = '\0';
}
}
}
}
void open_repeat_tui() {
int ch;
int cursor_pos = 0;
while (true) {
clear();
mvprintw(0, 0, "Repeat Options");
mvprintw(2, 0, "1. Never");
if (cursor_pos == 0) attron(A_REVERSE);
mvprintw(2, 10, "Select");
if (cursor_pos == 0) attroff(A_REVERSE);
mvprintw(4, 0, "2. Every Day");
if (cursor_pos == 1) attron(A_REVERSE);
mvprintw(4, 10, "Select");
if (cursor_pos == 1) attroff(A_REVERSE);
mvprintw(6, 0, "3. Every Week");
if (cursor_pos == 2) attron(A_REVERSE);
mvprintw(6, 10, "Select");
if (cursor_pos == 2) attroff(A_REVERSE);
mvprintw(8, 0, "4. Custom");
if (cursor_pos == 3) attron(A_REVERSE);
mvprintw(8, 10, "Select");
if (cursor_pos == 3) attroff(A_REVERSE);
mvprintw(10, 0, "Press 'q' to return");
refresh();
ch = getch();
if (ch == 'q') break; // Return to the add event TUI
if (ch == KEY_DOWN || ch == 'j') {
cursor_pos = (cursor_pos + 1) % 4;
} else if (ch == KEY_UP || ch == 'k') {
cursor_pos = (cursor_pos - 1 + 4) % 4;
} else if (ch == '\n') {
// Here, you would handle selection logic
break; // Return after selection for now
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,26 @@
# Makefile for ncurses calendar app
CC = gcc
CFLAGS = -Wall -Wextra -I../include
LDFLAGS = -lncurses -lsqlite3
SRC_DIR = ../src
BUILD_DIR = ../build
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRC_FILES))
TARGET = calendar_app
all: $(TARGET)
$(TARGET): $(OBJ_FILES)
$(CC) -o $@ $^ $(LDFLAGS)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(BUILD_DIR)/*.o $(TARGET)
.PHONY: all clean

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,29 @@
#ifndef ADD_EVENT_H
#define ADD_EVENT_H
#include <sqlite3.h>
#include <stdbool.h>
#define MAX_TITLE_LEN 50
#define MAX_CALENDAR_LEN 30
#define MAX_NOTES_LEN 256
#define MAX_DATE_LEN 50
typedef struct {
char title[MAX_TITLE_LEN];
bool all_day;
char start_date[MAX_DATE_LEN];
char end_date[MAX_DATE_LEN];
int start_hour, start_minute;
int end_hour, end_minute;
int repeat_option;
char calendar[MAX_CALENDAR_LEN];
char notes[MAX_NOTES_LEN];
} Event;
Event *create_event();
void save_event_to_db(sqlite3 *db, Event *event);
sqlite3* open_database();
void open_add_event_tui(sqlite3 *db);
#endif

View File

@@ -0,0 +1,8 @@
#ifndef CALENDAR_H
#define CALENDAR_H
int is_leap_year(int year);
int days_in_month(int month, int year);
void draw_calendar(int month, int year, int selected_day);
#endif

View File

@@ -0,0 +1,7 @@
#ifndef DAY_VIEW_H
#define DAY_VIEW_H
void draw_day_view(int *day, int *month, int *year);
#endif

View File

@@ -0,0 +1,7 @@
#ifndef REPEAT_TUI_H
#define REPEAT_TUI_H
void open_repeat_tui();
#endif

View File

@@ -0,0 +1,292 @@
#include "add_event.h"
#include "repeat_tui.h"
#include <ncurses.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <libgen.h>
#include <stdio.h>
#include <sqlite3.h>
#include <unistd.h>
// Create and initialize a new event
Event *create_event() {
Event *event = (Event *)malloc(sizeof(Event));
if (event) {
memset(event, 0, sizeof(Event));
strcpy(event->start_date, "01/01/2000");
strcpy(event->end_date, "01/01/2000");
event->start_hour = 9;
event->start_minute = 0;
event->end_hour = 10;
event->end_minute = 0;
event->repeat_option = 0;
}
return event;
}
// Function to open the SQLite database
sqlite3* open_database() {
char path[PATH_MAX];
char base_dir[PATH_MAX];
char db_path[PATH_MAX];
// Get the current working directory
if (getcwd(path, sizeof(path)) != NULL) {
printf("Current working directory: %s\n", path);
// Go up one directory level to the 'ncursescalander' directory
strncpy(base_dir, path, sizeof(base_dir));
dirname(base_dir);
printf("Base directory (one level up): %s\n", base_dir);
// Calculate required size
int required_size = snprintf(NULL, 0, "%s/databases/calendar_database.db", base_dir) + 1;
if (required_size > (int)sizeof(db_path)) { // Cast to int for comparison
fprintf(stderr, "Error: Path length exceeds buffer size.\n");
return NULL;
}
// Construct the final path to the database file
snprintf(db_path, sizeof(db_path), "%s/databases/calendar_database.db", base_dir);
printf("Final database path: %s\n", db_path);
sqlite3 *db;
if (sqlite3_open(db_path, &db)) {
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return NULL;
} else {
printf("Opened database successfully\n");
return db;
}
} else {
perror("getcwd() error");
return NULL;
}
}
// Function to save an event to the SQLite database
void save_event_to_db(sqlite3 *db, Event *event) {
// Set default values if necessary
if (strlen(event->title) == 0) {
strcpy(event->title, "Untitled");
}
if (strlen(event->calendar) == 0) {
strcpy(event->calendar, "");
}
if (strlen(event->notes) == 0) {
strcpy(event->notes, "");
}
// Prepare SQL statement
char *sql = "INSERT INTO events (title, all_day, start_date, end_date, start_time, end_time, repeat_option, calendar, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) == SQLITE_OK) {
// Bind values to SQL statement
sqlite3_bind_text(stmt, 1, event->title, -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 2, event->all_day);
sqlite3_bind_text(stmt, 3, event->start_date, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, event->end_date, -1, SQLITE_STATIC);
// Format time as HH:MM
char start_time[6];
snprintf(start_time, sizeof(start_time), "%02d:%02d", event->start_hour, event->start_minute);
sqlite3_bind_text(stmt, 5, start_time, -1, SQLITE_STATIC);
char end_time[6];
snprintf(end_time, sizeof(end_time), "%02d:%02d", event->end_hour, event->end_minute);
sqlite3_bind_text(stmt, 6, end_time, -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 7, event->repeat_option);
sqlite3_bind_text(stmt, 8, event->calendar, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 9, event->notes, -1, SQLITE_STATIC);
// Execute the SQL statement
if (sqlite3_step(stmt) != SQLITE_DONE) {
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
} else {
printf("Event saved successfully.\n");
}
sqlite3_finalize(stmt);
} else {
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
}
}
// Function to display the Add Event TUI and handle user input
void open_add_event_tui(sqlite3 *db) {
Event *event = create_event();
int ch;
int cursor_pos = 0;
bool exit = false;
while (!exit) {
clear();
mvprintw(0, 0, "Add New Event");
// Title input
mvprintw(2, 0, "Title: ");
if (cursor_pos == 0) attron(A_REVERSE);
mvprintw(2, 7, event->title);
if (cursor_pos == 0) attroff(A_REVERSE);
// All-day toggle
mvprintw(4, 0, "All Day: ");
if (cursor_pos == 1) attron(A_REVERSE);
mvprintw(4, 9, event->all_day ? "[X]" : "[ ]");
if (cursor_pos == 1) attroff(A_REVERSE);
// Start date input
mvprintw(6, 0, "Start Date: ");
if (cursor_pos == 2) attron(A_REVERSE);
mvprintw(6, 12, event->start_date);
if (cursor_pos == 2) attroff(A_REVERSE);
// End date input
mvprintw(8, 0, "End Date: ");
if (cursor_pos == 3) attron(A_REVERSE);
mvprintw(8, 10, event->end_date);
if (cursor_pos == 3) attroff(A_REVERSE);
// Start time input
mvprintw(10, 0, "Start Time: ");
if (cursor_pos == 4) attron(A_REVERSE);
mvprintw(10, 12, "%02d:%02d", event->start_hour, event->start_minute);
if (cursor_pos == 4) attroff(A_REVERSE);
// End time input
mvprintw(12, 0, "End Time: ");
if (cursor_pos == 5) attron(A_REVERSE);
mvprintw(12, 10, "%02d:%02d", event->end_hour, event->end_minute);
if (cursor_pos == 5) attroff(A_REVERSE);
// Repeat options
mvprintw(14, 0, "Repeat: ");
if (cursor_pos == 6) attron(A_REVERSE);
mvprintw(14, 8, "Choose..."); // Display the repeat choice interactively
if (cursor_pos == 6) attroff(A_REVERSE);
// Calendar name input
mvprintw(16, 0, "Calendar: ");
if (cursor_pos == 7) attron(A_REVERSE);
mvprintw(16, 10, event->calendar);
if (cursor_pos == 7) attroff(A_REVERSE);
// Notes input
mvprintw(18, 0, "Notes: ");
if (cursor_pos == 8) attron(A_REVERSE);
mvprintw(19, 0, event->notes);
if (cursor_pos == 8) attroff(A_REVERSE);
// Instructions
mvprintw(21, 0, "Esc: Cancel Ctrl+S: Save");
refresh();
ch = getch();
switch (ch) {
case 27: // ESC to cancel
exit = true;
break;
case 19: // Ctrl+S to save (^S)
save_event_to_db(db, event);
exit = true; // Return to calendar view after saving
break;
case KEY_DOWN: // Navigate down
cursor_pos = (cursor_pos + 1) % 9;
break;
case KEY_UP: // Navigate up
cursor_pos = (cursor_pos - 1 + 9) % 9;
break;
case KEY_LEFT:
case 'j': // Decrease minute
if (cursor_pos == 4) {
event->start_minute = (event->start_minute > 0) ? event->start_minute - 1 : 59;
} else if (cursor_pos == 5) {
event->end_minute = (event->end_minute > 0) ? event->end_minute - 1 : 59;
}
break;
case KEY_RIGHT:
case 'k': // Increase minute
if (cursor_pos == 4) {
event->start_minute = (event->start_minute < 59) ? event->start_minute + 1 : 0;
} else if (cursor_pos == 5) {
event->end_minute = (event->end_minute < 59) ? event->end_minute + 1 : 0;
}
break;
case KEY_SLEFT:
case 'h': // Decrease hour (Shift+Left or 'h')
if (cursor_pos == 4) {
event->start_hour = (event->start_hour > 0) ? event->start_hour - 1 : 23;
} else if (cursor_pos == 5) {
event->end_hour = (event->end_hour > 0) ? event->end_hour - 1 : 23;
}
break;
case KEY_SRIGHT:
case 'l': // Increase hour (Shift+Right or 'l')
if (cursor_pos == 4) {
event->start_hour = (event->start_hour < 23) ? event->start_hour + 1 : 0;
} else if (cursor_pos == 5) {
event->end_hour = (event->end_hour < 23) ? event->end_hour + 1 : 0;
}
break;
case ' ':
case '\n':
if (cursor_pos == 1) {
event->all_day = !event->all_day;
} else if (cursor_pos == 6) {
open_repeat_tui(); // Placeholder for opening the repeat TUI
}
break;
default:
if (cursor_pos == 0 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->title);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->title[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_TITLE_LEN - 1) {
event->title[len] = ch;
event->title[len + 1] = '\0';
}
} else if (cursor_pos == 2 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->start_date);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->start_date[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_DATE_LEN - 1) {
event->start_date[len] = ch;
event->start_date[len + 1] = '\0';
}
} else if (cursor_pos == 3 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->end_date);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->end_date[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_DATE_LEN - 1) {
event->end_date[len] = ch;
event->end_date[len + 1] = '\0';
}
} else if (cursor_pos == 7 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->calendar);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->calendar[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_CALENDAR_LEN - 1) {
event->calendar[len] = ch;
event->calendar[len + 1] = '\0';
}
} else if (cursor_pos == 8 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->notes);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->notes[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_NOTES_LEN - 1) {
event->notes[len] = ch;
event->notes[len + 1] = '\0';
}
}
break;
}
}
free(event); // Free the allocated memory
}

View File

@@ -0,0 +1,225 @@
#include "add_event.h"
#include "repeat_tui.h"
#include <ncurses.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX_TITLE_LEN 50
#define MAX_CALENDAR_LEN 30
#define MAX_NOTES_LEN 256
#define MAX_DATE_LEN 50
typedef struct {
char title[MAX_TITLE_LEN];
bool all_day;
char start_date[MAX_DATE_LEN];
char end_date[MAX_DATE_LEN];
int start_hour, start_minute;
int end_hour, end_minute;
int repeat_option;
char calendar[MAX_CALENDAR_LEN];
char notes[MAX_NOTES_LEN];
} Event;
Event *create_event() {
Event *event = (Event *)malloc(sizeof(Event));
if (event) {
memset(event, 0, sizeof(Event));
strcpy(event->start_date, "01/01/2000");
strcpy(event->end_date, "01/01/2000");
event->start_hour = 9;
event->start_minute = 0;
event->end_hour = 10;
event->end_minute = 0;
event->repeat_option = 0;
}
return event;
}
void save_event(Event *event) {
FILE *file = fopen("events.txt", "a");
if (file) {
fprintf(file, "Title: %s\nAll Day: %d\nStart Date: %s\nEnd Date: %s\nStart: %02d:%02d\nEnd: %02d:%02d\nRepeat: %d\nCalendar: %s\nNotes: %s\n\n",
event->title, event->all_day, event->start_date, event->end_date, event->start_hour, event->start_minute,
event->end_hour, event->end_minute, event->repeat_option, event->calendar, event->notes);
fclose(file);
} else {
mvprintw(23, 0, "Error saving event!"); // Error message
refresh();
getch(); // Pause to let the user see the error
}
}
void open_add_event_tui() {
Event *event = create_event();
int ch;
int cursor_pos = 0;
bool exit = false;
while (!exit) {
clear();
mvprintw(0, 0, "Add New Event");
// Title input
mvprintw(2, 0, "Title: ");
if (cursor_pos == 0) attron(A_REVERSE);
mvprintw(2, 7, event->title);
if (cursor_pos == 0) attroff(A_REVERSE);
// All-day toggle
mvprintw(4, 0, "All Day: ");
if (cursor_pos == 1) attron(A_REVERSE);
mvprintw(4, 9, event->all_day ? "[X]" : "[ ]");
if (cursor_pos == 1) attroff(A_REVERSE);
// Start date input
mvprintw(6, 0, "Start Date: ");
if (cursor_pos == 2) attron(A_REVERSE);
mvprintw(6, 12, event->start_date);
if (cursor_pos == 2) attroff(A_REVERSE);
// End date input
mvprintw(8, 0, "End Date: ");
if (cursor_pos == 3) attron(A_REVERSE);
mvprintw(8, 10, event->end_date);
if (cursor_pos == 3) attroff(A_REVERSE);
// Start time input
mvprintw(10, 0, "Start Time: ");
if (cursor_pos == 4) attron(A_REVERSE);
mvprintw(10, 12, "%02d:%02d", event->start_hour, event->start_minute);
if (cursor_pos == 4) attroff(A_REVERSE);
// End time input
mvprintw(12, 0, "End Time: ");
if (cursor_pos == 5) attron(A_REVERSE);
mvprintw(12, 10, "%02d:%02d", event->end_hour, event->end_minute);
if (cursor_pos == 5) attroff(A_REVERSE);
// Repeat options
mvprintw(14, 0, "Repeat: ");
if (cursor_pos == 6) attron(A_REVERSE);
mvprintw(14, 8, "Choose..."); // Display the repeat choice interactively
if (cursor_pos == 6) attroff(A_REVERSE);
// Calendar name input
mvprintw(16, 0, "Calendar: ");
if (cursor_pos == 7) attron(A_REVERSE);
mvprintw(16, 10, event->calendar);
if (cursor_pos == 7) attroff(A_REVERSE);
// Notes input
mvprintw(18, 0, "Notes: ");
if (cursor_pos == 8) attron(A_REVERSE);
mvprintw(19, 0, event->notes);
if (cursor_pos == 8) attroff(A_REVERSE);
// Instructions
mvprintw(21, 0, "Esc: Cancel Ctrl+S: Save");
refresh();
ch = getch();
switch (ch) {
case 27: // ESC to cancel
exit = true;
break;
case 19: // Ctrl+S to save (^S)
save_event(event);
exit = true; // Return to calendar view after saving
break;
case KEY_DOWN: // Navigate down
cursor_pos = (cursor_pos + 1) % 9;
break;
case KEY_UP: // Navigate up
cursor_pos = (cursor_pos - 1 + 9) % 9;
break;
case KEY_LEFT:
case 'j': // Decrease minute
if (cursor_pos == 4) {
event->start_minute = (event->start_minute > 0) ? event->start_minute - 1 : 59;
} else if (cursor_pos == 5) {
event->end_minute = (event->end_minute > 0) ? event->end_minute - 1 : 59;
}
break;
case KEY_RIGHT:
case 'k': // Increase minute
if (cursor_pos == 4) {
event->start_minute = (event->start_minute < 59) ? event->start_minute + 1 : 0;
} else if (cursor_pos == 5) {
event->end_minute = (event->end_minute < 59) ? event->end_minute + 1 : 0;
}
break;
case KEY_SLEFT:
case 'h': // Decrease hour (Shift+Left or 'h')
if (cursor_pos == 4) {
event->start_hour = (event->start_hour > 0) ? event->start_hour - 1 : 23;
} else if (cursor_pos == 5) {
event->end_hour = (event->end_hour > 0) ? event->end_hour - 1 : 23;
}
break;
case KEY_SRIGHT:
case 'l': // Increase hour (Shift+Right or 'l')
if (cursor_pos == 4) {
event->start_hour = (event->start_hour < 23) ? event->start_hour + 1 : 0;
} else if (cursor_pos == 5) {
event->end_hour = (event->end_hour < 23) ? event->end_hour + 1 : 0;
}
break;
case ' ':
case '\n':
if (cursor_pos == 1) {
event->all_day = !event->all_day;
} else if (cursor_pos == 6) {
open_repeat_tui(); // Placeholder for opening the repeat TUI
}
break;
default:
if (cursor_pos == 0 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->title);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->title[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_TITLE_LEN - 1) {
event->title[len] = ch;
event->title[len + 1] = '\0';
}
} else if (cursor_pos == 2 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->start_date);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->start_date[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_DATE_LEN - 1) {
event->start_date[len] = ch;
event->start_date[len + 1] = '\0';
}
} else if (cursor_pos == 3 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->end_date);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->end_date[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_DATE_LEN - 1) {
event->end_date[len] = ch;
event->end_date[len + 1] = '\0';
}
} else if (cursor_pos == 7 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->calendar);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->calendar[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_CALENDAR_LEN - 1) {
event->calendar[len] = ch;
event->calendar[len + 1] = '\0';
}
} else if (cursor_pos == 8 && ch != KEY_DOWN && ch != KEY_UP) {
int len = strlen(event->notes);
if ((ch == KEY_BACKSPACE || ch == 127) && len > 0) {
event->notes[len - 1] = '\0';
} else if (isprint(ch) && len < MAX_NOTES_LEN - 1) {
event->notes[len] = ch;
event->notes[len + 1] = '\0';
}
}
break;
}
}
free(event); // Free the allocated memory
}

View File

@@ -0,0 +1,56 @@
#include "calendar.h"
#include <ncurses.h>
// Function to determine if a year is a leap year
int is_leap_year(int year) {
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0)
return 1;
else
return 0;
} else
return 1;
} else
return 0;
}
// Function to get the number of days in a given month and year
int days_in_month(int month, int year) {
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
return 31;
case 4: case 6: case 9: case 11:
return 30;
case 2:
if (is_leap_year(year))
return 29;
else
return 28;
default:
return 30; // Fallback, though we shouldn't hit this
}
}
void draw_calendar(int month, int year, int selected_day) {
int days = days_in_month(month, year);
// Adjust selected_day if it exceeds the number of days in the month
if (selected_day > days) {
selected_day = days;
}
mvprintw(1, 0, "Month: %d, Year: %d", month, year);
mvprintw(2, 0, "Su Mo Tu We Th Fr Sa");
// Placeholder for calendar drawing
for (int i = 1; i <= days; i++) {
if (i == selected_day) {
attron(A_REVERSE); // Highlight the selected day
}
mvprintw(3 + (i / 7), (i % 7) * 3, "%2d", i);
if (i == selected_day) {
attroff(A_REVERSE);
}
}
}

View File

@@ -0,0 +1,127 @@
#include "day_view.h"
#include <ncurses.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#define MAX_TITLE_LEN 50
typedef struct {
char title[MAX_TITLE_LEN];
int start_hour;
int start_minute;
int end_hour;
int end_minute;
int event_day;
int event_month;
int event_year;
} DisplayEvent;
void load_events(DisplayEvent *events, int *count, int day, int month, int year) {
FILE *file = fopen("events.txt", "r");
if (file) {
char line[256];
int current_day, current_month, current_year;
while (fgets(line, sizeof(line), file)) {
if (sscanf(line, "Date: %d-%d-%d", &current_day, &current_month, &current_year) == 3) {
if (current_day == day && current_month == month && current_year == year) {
fgets(line, sizeof(line), file);
sscanf(line, "Start: %02d:%02d", &events[*count].start_hour, &events[*count].start_minute);
fgets(line, sizeof(line), file);
sscanf(line, "End: %02d:%02d", &events[*count].end_hour, &events[*count].end_minute);
fgets(line, sizeof(line), file);
sscanf(line, "Title: %49[^\n]", events[*count].title);
events[*count].event_day = current_day;
events[*count].event_month = current_month;
events[*count].event_year = current_year;
(*count)++;
}
}
}
fclose(file);
}
}
void draw_day_view(int *day, int *month, int *year) {
time_t now;
struct tm *local;
int ch;
bool redraw = true;
DisplayEvent events[10];
int event_count = 0;
load_events(events, &event_count, *day, *month, *year);
while (true) {
if (redraw) {
clear();
mvprintw(0, 0, "Day View");
mvprintw(1, 0, "Date: %02d-%02d-%04d", *day, *month, *year);
// Draw the hours of the day
for (int i = 0; i < 24; i++) {
mvprintw(i + 3, 0, "%02d:00 |", i);
mvhline(i + 3, 8, ' ', COLS - 8);
}
// Display events
for (int i = 0; i < event_count; i++) {
mvprintw(events[i].start_hour + 3, 10, "%s", events[i].title);
mvhline(events[i].start_hour + 3, 10 + strlen(events[i].title), '-', COLS - 20);
}
now = time(NULL);
local = localtime(&now);
if (local->tm_mday == *day && (local->tm_mon + 1) == *month && (local->tm_year + 1900) == *year) {
int current_hour = local->tm_hour;
int current_minute = local->tm_min;
attron(COLOR_PAIR(1)); // Red color
mvhline(current_hour + 3, 8, '-', COLS - 8);
mvprintw(current_hour + 3, 0, "%02d:%02d |", current_hour, current_minute);
attroff(COLOR_PAIR(1));
}
refresh();
redraw = false;
}
nodelay(stdscr, TRUE); // Make getch non-blocking
ch = getch();
if (ch == 'q') break; // Exit to Calendar View
if (ch == KEY_LEFT || ch == 'j') {
if (*day > 1) {
(*day)--;
} else {
if (*month > 1) {
(*month)--;
} else {
*month = 12;
(*year)--;
}
*day = 31; // Simplified for demo purposes
}
redraw = true;
} else if (ch == KEY_RIGHT || ch == 'k') {
if (*day < 31) { // Simplified for demo purposes
(*day)++;
} else {
*day = 1;
if (*month < 12) {
(*month)++;
} else {
*month = 1;
(*year)++;
}
}
redraw = true;
}
napms(1000);
}
nodelay(stdscr, FALSE);
}

106
ncursescalander/src/main.c Normal file
View File

@@ -0,0 +1,106 @@
#include <ncurses.h>
#include <string.h>
#include <time.h>
#include "calendar.h"
#include "day_view.h"
#include "add_event.h"
int main() {
int ch, month, year, day = 1;
time_t now;
struct tm *local;
// Open the SQLite database
sqlite3 *db = open_database();
if (db == NULL) {
return 1; // Exit if the database can't be opened
}
// Initialize ncurses
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK); // Red color pair for the current time
// Get current date
now = time(NULL);
local = localtime(&now);
month = local->tm_mon + 1;
year = local->tm_year + 1900;
day = local->tm_mday;
// Main loop
while ((ch = getch()) != 'q') {
int days_in_current_month = days_in_month(month, year);
bool redraw = false;
switch (ch) {
case KEY_LEFT:
case 'j':
if (day > 1) {
day--;
redraw = true;
}
break;
case KEY_RIGHT:
case 'k':
if (day < days_in_current_month) {
day++;
redraw = true;
}
break;
case 'a': // 'a' for "Add Event"
open_add_event_tui(db); // Pass the database connection here
redraw = true;
break;
case '\n': // Enter key to open the day view
draw_day_view(&day, &month, &year);
redraw = true;
break;
case 'h': // 'h' for previous month
case KEY_SLEFT: // Left Shift for previous month
if (month > 1) {
month--;
} else {
month = 12;
year--;
}
// Adjust the day if it exceeds the number of days in the new month
if (day > days_in_month(month, year)) {
day = days_in_month(month, year);
}
redraw = true;
break;
case 'l': // 'l' for next month
case KEY_SRIGHT: // Right Shift for next month
if (month < 12) {
month++;
} else {
month = 1;
year++;
}
// Adjust the day if it exceeds the number of days in the new month
if (day > days_in_month(month, year)) {
day = days_in_month(month, year);
}
redraw = true;
break;
}
if (redraw) {
clear();
mvprintw(0, 0, "Calendar View - Press 'a' to Add Event");
draw_calendar(month, year, day);
refresh();
}
}
// Close the SQLite database connection
sqlite3_close(db);
// End ncurses mode
endwin();
return 0;
}

View File

@@ -0,0 +1,48 @@
#include <ncurses.h>
#include "repeat_tui.h"
void open_repeat_tui() {
int ch;
int cursor_pos = 0;
while (true) {
clear();
mvprintw(0, 0, "Repeat Options");
mvprintw(2, 0, "1. Never");
if (cursor_pos == 0) attron(A_REVERSE);
mvprintw(2, 10, "Select");
if (cursor_pos == 0) attroff(A_REVERSE);
mvprintw(4, 0, "2. Every Day");
if (cursor_pos == 1) attron(A_REVERSE);
mvprintw(4, 10, "Select");
if (cursor_pos == 1) attroff(A_REVERSE);
mvprintw(6, 0, "3. Every Week");
if (cursor_pos == 2) attron(A_REVERSE);
mvprintw(6, 10, "Select");
if (cursor_pos == 2) attroff(A_REVERSE);
mvprintw(8, 0, "4. Every Other Day");
if (cursor_pos == 3) attron(A_REVERSE);
mvprintw(8, 18, "Select");
if (cursor_pos == 3) attroff(A_REVERSE);
mvprintw(10, 0, "Press 'q' to return");
refresh();
ch = getch();
if (ch == 'q') break; // Return to the add event TUI
if (ch == KEY_DOWN || ch == 'j') {
cursor_pos = (cursor_pos + 1) % 4;
} else if (ch == KEY_UP || ch == 'k') {
cursor_pos = (cursor_pos - 1 + 4) % 4;
} else if (ch == '\n') {
// Here, you would handle selection logic
break; // Return after selection for now
}
}
}

1
ssh_gui Submodule

Submodule ssh_gui added at ed35ae7f3a

View File

@@ -0,0 +1,24 @@
CC = gcc
CFLAGS = -Wall -Wextra -std=c11
# The final executable name
TARGET = terrarium
# Source files
SRCS = main.c game.c ascii_plant_data.c
# Object files
OBJS = $(SRCS:.c=.o)
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c $<
clean:
rm -f $(TARGET) *.o
rm -f terrarium_save.dat

View File

@@ -0,0 +1,581 @@
/******************************************************************************
* ascii_plant_data.c
*
* Defines 40 unique ASCII plants, each 10 lines of up to 20 chars.
*
* For demonstration, theyll all be similar in shape, but each one will have
* some small text or symbol changes so that no two are exactly alike.
* You can replace these with more distinct art as desired.
******************************************************************************/
#include "ascii_plant_data.h"
/*
* We store all 40 plants ASCII lines in a 2D array of strings:
* PLANTS[40][10]
* where each string is up to 20 chars + null terminator.
*
* Each plant: 10 lines. Each line up to 20 chars wide.
*/
/*
Template shape (10 lines, 20 chars each):
" . "
" .. # "
" . ==+*- . - "
" :++*====.# "
" ==++-=#*+ "
" -%*=-#+#. "
" .-+**#. "
" .**+#. "
" .:=*++ "
" :--=. "
Well introduce slight variations for each plant to ensure uniqueness.
*/
static const char PLANT_ART[40][10][21] = {
/* 0: Snake Plant */
{
" . ",
" .. # ",
" . ==+*- . - ",
" :++*====.# ",
" S==++-=#*+ ",
" -%*=-#+#. ",
" .-+**#. S ",
" .**+#. ",
" .:=*++ S ",
" :--=. "
},
/* 1: Aloe Vera */
{
" . ",
" .. A ",
" . ==+*- . - ",
" :++*====.# A ",
" ==++-=#*+ ",
" A -%*=-#+#. ",
" .-+**#. ",
" .**+#. A ",
" .:=*++ ",
" :--=. "
},
/* 2: Rubber fig */
{
" (Rubber) ",
" .. # ",
" R . ==+*- . - ",
" :++*====.# ",
" ==++-=#*+ R ",
" -%*=-#+#. ",
" R .-+**#. ",
" .**+#. R ",
" .:=*++ ",
" :--=. R "
},
/* 3: Maidenhair fern */
{
" M ",
" fern .. # ",
" . ==+*- . - ",
" :++*====.# M ",
" ==++-=#*+ f ",
" -%*=-#+#. ",
" .-+**#. ",
" .**+#. fern ",
" .:=*++ M ",
" :--=. "
},
/* 4: Zebra Plant */
{
" Z e b r a ",
" .. Z ",
" . ==+*- . - ",
" :++*====.# Z ",
" Z ==++-=#*+ ",
" -%*=-#+#.Z ",
" .-+**#. ",
" .**+#. ",
" Z .:=*++ ",
" :--=. Z "
},
/* 5: Jade plant */
{
" J ",
" .. # ",
" . ==+*- . - ",
" :++*====.# J ",
" ==++-=#*+ ",
" -%*=-#+#. ",
" Jade.-+**#. ",
" .**+#. ",
" pl. :=*++ ",
" :--=. Jade "
},
/* 6: Yucca */
{
" Yucca ",
" .. Y ",
" . ==+*- . - Y ",
" :++*====.# ",
" ==++-=#*+ Y ",
" -%*=-#+#. ",
" .-+**#. Y ",
" .**+#. ",
" .:=*++ Y ",
" :--=. "
},
/* 7: Chain of Hearts */
{
" Chain <3 Hearts ",
" .. # ",
" . ==+*- . - <3 ",
" :++*====.# ",
" ==++-=#*+ <3 ",
" <3 -%*=-#+#. ",
" .-+**#. ",
" .**+#. <3 ",
" Chain.:=*++ ",
" :--=. Hearts"
},
/* 8: ZZ plant */
{
" ZZZ ",
" .. # Z ",
" . ==+*- . - ",
" Z :++*====.# ",
" ==++-=#*+ ",
" Z -%*=-#+#. ",
" .-+**#. Z ",
" ZZ .**+#. ",
" .:=*++ ",
" :--=. ZZ "
},
/* 9: Moonstones */
{
" (Moon) ",
" .. # ",
" . ==+*- . - ",
" :++*====.# M ",
" ==++-=#*+ ",
" oo -%*=-#+#. ",
" .-+**#. oo ",
" .**+#. ",
" stones:=*++ ",
" :--=. "
},
/* 10: Chinese Money plant */
{
" Chinese Money ",
" .. # $ ",
" . ==+*- . - ",
" :++*====.# $ ",
" ==++-=#*+ $$ ",
" $$ -%*=-#+#. ",
" .-+**#. ",
" $$ .**+#. ",
" .:=*++ $$ ",
" :--=. "
},
/* 11: String of pearls */
{
" String pearls ",
" ... # o o ",
" . ==+*- . - o ",
" :++*====.# o ",
" ==++-=#*+ o ",
" -%*=-#+#. o ",
" .-+**#. o ",
" .**+#. o ",
" .:=*++ o ",
" :--=. "
},
/* 12: Air plant */
{
" Air plant ",
" .. # ~ ",
" . ==+*- . - ~ ",
" :++*====.# ",
" A ==++-=#*+ ~ ",
" -%*=-#+#. ",
" .-+**#. ~ ",
" ~ .**+#. ",
" .:=*++ ",
" :--=. "
},
/* 13: African milk tree */
{
" African milk tree ",
" AMT .. # ",
" . ==+*- . - AMT ",
" :++*====.# ",
" ==++-=#*+ AMT ",
" -%*=-#+#. ",
" .-+**#. AMT ",
" .**+#. ",
" .:=*++ ",
" :--=. "
},
/* 14: pine bonsai */
{
" pine bonsai ",
" .. # ^ ",
" . ==+*- . - ^ ",
" :++*====.# ",
" ==++-=#*+ ^ ",
" -%*=-#+#. ",
" ^ .-+**#. ",
" .**+#. ^ ",
" ^ .:=*++ ",
" :--=. "
},
/* 15: Lotus */
{
" Lotus ",
" .. # * ",
" . ==+*- . - * ",
" :++*====.# ",
" ==++-=#*+ * ",
" -%*=-#+#. ",
" .-+**#. ",
" * .**+#. ",
" .:=*++ * ",
" :--=. "
},
/* 16: Heart fern */
{
" Heart fern <3 ",
" .. # ",
" . ==+*- . - <3 ",
" :++*====.# <3 ",
" ==++-=#*+ ",
" <3 -%*=-#+#. ",
" .-+**#. <3 ",
" .**+#. ",
" <3 .:=*++ ",
" :--=. "
},
/* 17: Corkscrew rush */
{
" Corkscrew rush ~@ ",
" .. # ~@ ",
" . ==+*- . - ~@ ",
" :++*====.# ",
" ~@==++-=#*+ ",
" -%*=-#+#. ~@ ",
" .-+**#. ",
" .**+#. ~@ ",
" .:=*++ ",
" :--=. ~@ "
},
/* 18: Weeping fig */
{
" Weeping fig ~ ",
" .. # ~ ",
" . ==+*- . - ",
" ~ :++*====.# ",
" ==++-=#*+ ~ ",
" -%*=-#+#. ",
" .-+**#. ~ ",
" .**+#. ",
" ~ .:=*++ ",
" :--=. "
},
/* 19: Corkscrew albuca */
{
" Corkscrew albuca ",
" Cka .. # ~@ ",
" . ==+*- . - ~@ ",
" :++*====.# ",
" ~@==++-=#*+ ",
" -%*=-#+#. ~@ ",
" .-+**#. ",
" ~@ .**+#. ",
" .:=*++ ",
" :--=. ~@ "
},
/* 20: Fiddle leaf fig */
{
" Fiddle leaf fig F ",
" .. # (leaf) ",
" ==+*- . - F ",
" :++*====.# ",
" ==++-=#*+ (leaf) ",
" -%*=-#+#. ",
" .-+**#. F ",
" .**+#. ",
" .:=*++ (leaf) ",
" :--=. "
},
/* 21: Mikado */
{
" Mikado | ",
" .. # | ",
" . ==+*- . - | ",
" :++*====.# ",
" ==++-=#*+ | ",
" -%*=-#+#. ",
" .-+**#. ",
" .**+#. | ",
" .:=*++ ",
" :--=. | "
},
/* 22: Kebab bush */
{
" Kebab bush ~o ",
" .. # o ",
" . ==+*- . - o ",
" :++*====.# ~ ",
" ~ ==++-=#*+ o ",
" -%*=-#+#. ",
" .-+**#. ~ o ",
" .**+#. ",
" o .:=*++ ~ ",
" :--=. o "
},
/* 23: Dwarf Papyrus */
{
" Dwarf Papyrus ^ ",
" .. # ^ ",
" . ==+*- . - ^ ",
" :++*====.# ",
" ==++-=#*+ ^ ",
" ^ -%*=-#+#. ",
" .-+**#. ^ ",
" .**+#. ",
" ^ .:=*++ ",
" :--=. "
},
/* 24: Hobbit Crassula */
{
" Hobbit Crassula (H) ",
" .. # ",
" . ==+*- . - (H) ",
" :++*====.# ",
" ==++-=#*+ ",
" -%*=-#+#. (H) ",
" .-+**#. ",
" (H) .**+#. ",
" .:=*++ ",
" :--=. (H) "
},
/* 25: Bunny ear cactus */
{
" Bunny ear cactus * ",
" .. # (ears) ",
" . ==+*- . - * ",
" :++*====.# (ears) ",
" ==++-=#*+ * ",
" -%*=-#+#. ",
" .-+**#. (ears) ",
" .**+#. * ",
" .:=*++ ",
" :--=. * "
},
/* 26: ghost echoversia */
{
" ghost echoversia # ",
" g.. # # ",
" . ==+*- . - # ",
" :++*====.# ",
" ==++-=#*+ # ",
" -%*=-#+#. # ",
" .-+**#. # ",
" .**+#. # ",
" .:=*++ # ",
" :--=. "
},
/* 27: chinese lantern */
{
" chinese lantern ",
" .. # (lantern) ",
" . ==+*- . - ",
" :++*====.# (lamp) ",
" ==++-=#*+ ",
" -%*=-#+#. (lamp) ",
" .-+**#. ",
" .**+#. (lantern) ",
" .:=*++ ",
" :--=. "
},
/* 28: ginseng ficus */
{
" ginseng ficus Gf ",
" .. # Gf ",
" . ==+*- . - Gf ",
" :++*====.# ",
" ==++-=#*+ ",
" -%*=-#+#. Gf ",
" .-+**#. ",
" .**+#. Gf ",
" .:=*++ ",
" :--=. "
},
/* 29: venus flytrap */
{
" venus flytrap V ",
" .. # x ",
" . ==+*- . - x ",
" :++*====.# ",
" V ==++-=#*+ x ",
" -%*=-#+#. ",
" .-+**#. x ",
" V .**+#. ",
" .:=*++ ",
" :--=. x "
},
/* 30: Flamingo flower */
{
" Flamingo flower F ",
" .. # (fl) ",
" . ==+*- . - F ",
" :++*====.# ",
" (fl)==++-=#*+ F ",
" -%*=-#+#. ",
" .-+**#. (fl) ",
" .**+#. ",
" F .:=*++ ",
" :--=. (fl) "
},
/* 31: Japanese maple bonsai */
{
" Japanese maple bns ",
" .. # Jmb ",
" . ==+*- . - ",
" :++*====.# Jmb ",
" ==++-=#*+ ",
" -%*=-#+#. ",
" Jmb .-+**#. ",
" .**+#. ",
" .:=*++ Jmb ",
" :--=. "
},
/* 32: Fshbone cactus */
{
" Fshbone cactus ~^ ",
" .. # ~^ ",
" . ==+*- . - ~^ ",
" :++*====.# ",
" ==++-=#*+ ~^ ",
" -%*=-#+#. ",
" .-+**#. ~^ ",
" .**+#. ",
" .:=*++ ~^ ",
" :--=. "
},
/* 33: Paddle plant */
{
" Paddle plant ",
" .. # == ",
" . ==+*- . - == ",
" :++*====.# ",
" ==++-=#*+ == ",
" -%*=-#+#. ",
" .-+**#. ",
" == .**+#. ",
" .:=*++ == ",
" :--=. "
},
/* 34: Donkey's tail */
{
" Donkey's tail ~ ",
" .. # ~ ",
" . ==+*- . - ~ ",
" :++*====.# ",
" ~ ==++-=#*+ ",
" -%*=-#+#. ~ ",
" .-+**#. ",
" ~ .**+#. ",
" .:=*++ ",
" :--=. ~ "
},
/* 35: Common ivy */
{
" Common ivy ivy ",
" .. # iv ",
" . ==+*- . - iv ",
" iv:++*====.# ",
" ==++-=#*+ ",
" -%*=-#+#. iv ",
" .-+**#. ",
" .**+#. ",
" iv .:=*++ ",
" :--=. ivy "
},
/* 36: Chinese Crassula */
{
" Chinese Crassula C ",
" .. # (Cr) ",
" . ==+*- . - (Cr) ",
" :++*====.# ",
" ==++-=#*+ ",
" -%*=-#+#. (Cr) ",
" .-+**#. ",
" .**+#. (Cr) ",
" .:=*++ ",
" :--=. (Cr) "
},
/* 37: Blue Chalksticks */
{
" Blue Chalksticks B ",
" .. # Bc ",
" . ==+*- . - Bc ",
" :++*====.# ",
" ==++-=#*+ Bc ",
" -%*=-#+#. ",
" .-+**#. Bc ",
" .**+#. ",
" .:=*++ Bc ",
" :--=. "
},
/* 38: Angel's tears */
{
" Angel's tears @A ",
" .. # tears ",
" . ==+*- . - @A ",
" :++*====.# ",
" ==++-=#*+ ",
" -%*=-#+#. @A ",
" .-+**#. ",
" .**+#. tears ",
" .:=*++ @A ",
" :--=. "
},
/* 39: White clover */
{
" White clover (wc) ",
" .. # (wc) ",
" . ==+*- . - ",
" :++*====.# (wc) ",
" ==++-=#*+ ",
" -%*=-#+#. ",
" .-+**#. (wc) ",
" .**+#. ",
" .:=*++ (wc)",
" :--=. "
}
};
/******************************************************************************
* getPlantArt
*
* Returns the (line)-th line of ASCII art for plantIndex
******************************************************************************/
const char* getPlantArt(int plantIndex, int line) {
if(plantIndex < 0 || plantIndex >= 40) {
/* Safety: return empty string if out of range */
return " ";
}
if(line < 0 || line >= 10) {
return " ";
}
return PLANT_ART[plantIndex][line];
}

View File

@@ -0,0 +1,14 @@
/******************************************************************************
* ascii_plant_data.h
*
* Exposes function(s) to retrieve the 10-line ASCII art for each of the 40 plants.
******************************************************************************/
#ifndef ASCII_PLANT_DATA_H
#define ASCII_PLANT_DATA_H
/* Returns the line-th line (0..9) of ASCII art for plant index (0..39). */
const char* getPlantArt(int plantIndex, int line);
#endif

Binary file not shown.

View File

@@ -0,0 +1,259 @@
/******************************************************************************
* game.c
*
* Implements core game logic: initialization, update, rendering, input, etc.
******************************************************************************/
/*
* game.c - Contains the implementation of the Terrarium game logic
*/
#define _POSIX_C_SOURCE 200809L
#include "game.h"
/* System / standard headers */
#include <stdio.h>
#include <stdlib.h> /* for system() */
#include <time.h> /* for clock_gettime, etc. */
#include <unistd.h> /* for usleep() */
#include <fcntl.h>
#include <termios.h>
#include <string.h> /* for strncpy() */
/******************************************************************************
* Terminal-handling variables/functions (non-blocking input, etc.)
*****************************************************************************/
static struct termios old, current;
/* Initialize new terminal i/o settings */
void initTermios(int echo) {
tcgetattr(0, &old);
current = old;
current.c_lflag &= ~ICANON; /* disable buffered i/o */
if (echo) {
current.c_lflag |= ECHO;
} else {
current.c_lflag &= ~ECHO;
}
tcsetattr(0, TCSANOW, &current);
}
/* Restore old terminal i/o settings */
void resetTermios(void) {
tcsetattr(0, TCSANOW, &old);
}
/* Check if a key has been pressed (non-blocking) */
int kbhit(void) {
struct termios oldt, newt;
int ch;
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if(ch != EOF) {
ungetc(ch, stdin);
return 1;
}
return 0;
}
/* Read one character (like getch in Windows) */
char getch(void) {
return getchar();
}
/* Clear the screen (Unix-like) */
void clearScreen(void) {
system("clear");
}
/******************************************************************************
* Elapsed Time (we store the old time in a static variable)
*****************************************************************************/
double getElapsedTime(void) {
static struct timespec lastTime = {0, 0};
struct timespec now;
/* If first call, initialize lastTime */
if (lastTime.tv_sec == 0 && lastTime.tv_nsec == 0) {
clock_gettime(CLOCK_MONOTONIC, &now);
lastTime = now;
return 0.0;
}
/* current time */
clock_gettime(CLOCK_MONOTONIC, &now);
double elapsed = (now.tv_sec - lastTime.tv_sec) +
(now.tv_nsec - lastTime.tv_nsec) / 1000000000.0;
lastTime = now;
return elapsed;
}
/******************************************************************************
* Game Logic & Rendering
*****************************************************************************/
/* Forward-declarations (private to this file) */
static double calcTotalProduction(const GameState *gs);
static void buyPlant(GameState *gs);
/* initGame: brand-new game (if load fails or for fresh start) */
void initGame(GameState *gs) {
gs->oxygen = 0.0;
gs->plantCost = BASE_PLANT_COST;
gs->plantCount = 0;
/* The 40 plants (names). Adjust as you wish. */
const char* plantNames[MAX_PLANTS] = {
"Snake Plant", "Aloe Vera", "Rubber fig", "Maidenhair fern", "Zebra Plant",
"Jade plant", "Yucca", "Chain of Hearts", "ZZ plant", "Moonstones",
"Chinese Money plant", "String of pearls.", "Air plant", "African milk tree",
"pine bonsai", "Lotus", "Heart fern", "Corkscrew rush", "Weeping fig", "Corkscrew albuca",
"Fiddle leaf fig", "Mikado", "Kebab bush", "Dwarf Papyrus", "Hobbit Crassula",
"Bunny ear cactus", "ghost echoversia", "chinese lantern", "ginseng ficus", "venus flytrap",
"Flamingo flower", "Japanese maple bonsai", "Fshbone cactus", "Paddle plant",
"Donkey's tail", "Common ivy", "Chinese Crassula", "Blue Chalksticks", "Angel's tears", "White clover"
};
for(int i = 0; i < MAX_PLANTS; i++) {
strncpy(gs->plants[i].name, plantNames[i], sizeof(gs->plants[i].name) - 1);
gs->plants[i].name[sizeof(gs->plants[i].name)-1] = '\0';
gs->plants[i].active = 0;
gs->plants[i].productionRate = 0.0;
}
/* Start with the first plant active, if desired */
gs->plants[0].active = 1;
gs->plants[0].productionRate = BASE_OXYGEN_PER_SECOND;
gs->plantCount = 1;
}
/* loadGame: tries to read from a file. If it fails, calls initGame(). */
void loadGame(GameState *gs) {
FILE *f = fopen("terrarium_save.dat", "rb");
if(!f) {
initGame(gs);
return;
}
if(fread(gs, sizeof(GameState), 1, f) != 1) {
fclose(f);
initGame(gs);
return;
}
fclose(f);
/* Basic sanity check */
if(gs->plantCount < 1) {
initGame(gs);
}
}
/* saveGame: writes the current game state to a file. */
void saveGame(const GameState *gs) {
FILE *f = fopen("terrarium_save.dat", "wb");
if(!f) return;
fwrite(gs, sizeof(GameState), 1, f);
fclose(f);
}
/* updateGame: called each loop to add oxygen from production rate. */
void updateGame(GameState *gs, double elapsedSeconds) {
double totalProd = calcTotalProduction(gs);
gs->oxygen += totalProd * elapsedSeconds;
}
/* renderGame: draws shelves + plants in top 3/4, info in bottom 1/4. */
void renderGame(const GameState *gs) {
clearScreen();
printf("========================================\n");
printf(" T E R R A R I U M \n");
printf("========================================\n\n");
/*
* We have 5 shelves (SHELF_COUNT=5).
* Each shelf displays 8 plants => total 40 plants (PLANTS_PER_SHELF=8).
*/
int plantIdx = 0;
for(int shelf = 0; shelf < SHELF_COUNT; shelf++) {
for(int slot = 0; slot < PLANTS_PER_SHELF; slot++) {
if(plantIdx < MAX_PLANTS && gs->plants[plantIdx].active) {
/* Print the plant name (20 chars padded, e.g.) */
printf("[%2d] %-20s | ", plantIdx+1, gs->plants[plantIdx].name);
} else {
printf("[ ] %-20s | ", "(empty)");
}
plantIdx++;
}
printf("\n--------------------------------------------------------\n");
}
/* BOTTOM 1/4: info, oxygen, etc. */
printf("\nOxygen: %.2f O2\n", gs->oxygen);
printf("Total Production: %.3f O2/s\n", calcTotalProduction(gs));
printf("Next Plant Cost: %.2f O2\n\n", gs->plantCost);
printf("[SPACE] Tap the first plant for +%.1f O2 (if active)\n", TAP_BONUS_OXYGEN);
printf("[b] Buy a new plant\n");
printf("[q] Quit\n\n");
}
/* handleInput: user keystrokes (tap or buy, etc.) */
void handleInput(GameState *gs, char input) {
switch(input) {
case ' ':
/* Tap only works on the first plant if active */
if(gs->plants[0].active) {
gs->oxygen += TAP_BONUS_OXYGEN;
}
break;
case 'b':
case 'B':
buyPlant(gs);
break;
default:
break;
}
}
/* Internal helper: calculates sum of production rates */
static double calcTotalProduction(const GameState *gs) {
double total = 0.0;
for(int i = 0; i < MAX_PLANTS; i++) {
if(gs->plants[i].active) {
total += gs->plants[i].productionRate;
}
}
return total;
}
/* Internal helper: buy the next plant if we can afford it */
static void buyPlant(GameState *gs) {
if(gs->plantCount >= MAX_PLANTS) {
return; /* can't buy more than 40 */
}
if(gs->oxygen >= gs->plantCost) {
gs->oxygen -= gs->plantCost;
/* activate next plant in line */
int index = gs->plantCount;
gs->plants[index].active = 1;
gs->plants[index].productionRate = BASE_OXYGEN_PER_SECOND; /* or custom logic */
gs->plantCount++;
/* Increase cost for next time */
gs->plantCost *= COST_MULTIPLIER;
}
}

View File

@@ -0,0 +1,67 @@
/******************************************************************************
* game.h
*
* Exposes the core game logic and data.
******************************************************************************/
#ifndef GAME_H
#define GAME_H
/*
* This header declares functions/data for the Terrarium game.
* Include it from both main.c and game.c.
*/
#define MAX_PLANTS 40
#define SHELF_COUNT 5 /* We'll display 5 shelves total */
#define PLANTS_PER_SHELF 8 /* 8 plants per shelf => 40 plants total */
#define GAME_TICK_INTERVAL_MS 200 /* Loop tick (ms) */
#define BASE_OXYGEN_PER_SECOND 0.1
#define TAP_BONUS_OXYGEN 1.0
#define BASE_PLANT_COST 10.0
#define COST_MULTIPLIER 1.2
/*
* Data Structures
*/
typedef struct {
char name[64]; /* e.g. "Snake Plant" */
int active; /* 1 if purchased/visible, 0 if not */
double productionRate; /* O2 per second contributed by this plant */
} Plant;
/* Main Game State container */
typedef struct {
double oxygen; /* Current total oxygen currency */
double plantCost; /* Cost to buy the next plant */
Plant plants[MAX_PLANTS]; /* All 40 plants */
int plantCount; /* How many plants are active */
} GameState;
/*
* Function Prototypes
*/
/* Game lifecycle */
void initGame(GameState *gs);
void loadGame(GameState *gs);
void saveGame(const GameState *gs);
/* Game logic */
void updateGame(GameState *gs, double elapsedSeconds);
void renderGame(const GameState *gs);
void handleInput(GameState *gs, char input);
/* Timing: returns elapsed seconds since last call */
double getElapsedTime(void);
/* Terminal / I/O helpers */
void initTermios(int echo);
void resetTermios(void);
int kbhit(void);
char getch(void);
void clearScreen(void);
#endif /* GAME_H */

Binary file not shown.

View File

@@ -0,0 +1,57 @@
/*
* main.c - Entry point of the Terrarium program
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h> /* for system() */
#include <unistd.h> /* for usleep() */
#include "game.h"
int main(void) {
GameState gs;
/* Load a previous save or start fresh. */
loadGame(&gs);
/* Setup terminal for non-blocking input, no echo */
initTermios(0);
int running = 1;
while (running) {
/* Time since the last loop in seconds */
double elapsedSeconds = getElapsedTime();
/* Update game logic (oxygen accumulation, etc.) */
updateGame(&gs, elapsedSeconds);
/* Render the shelves and the info area */
renderGame(&gs);
/* Check user input (non-blocking) */
if (kbhit()) {
char c = getch();
if (c == 'q' || c == 'Q') {
running = 0; /* quit */
} else {
handleInput(&gs, c);
}
}
/* Control frame/tick rate (~5 times a second) */
usleep(GAME_TICK_INTERVAL_MS * 1000);
/* Auto-save each loop (optional) */
saveGame(&gs);
}
/* Cleanup */
resetTermios();
clearScreen();
printf("Thanks for playing Terrarium!\n");
return 0;
}

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More