diff --git a/backups/bat0daemonbak/Makefile b/backups/bat0daemonbak/Makefile new file mode 100644 index 0000000..63be530 --- /dev/null +++ b/backups/bat0daemonbak/Makefile @@ -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 diff --git a/backups/bat0daemonbak/README.md b/backups/bat0daemonbak/README.md new file mode 100644 index 0000000..e69de29 diff --git a/backups/bat0daemonbak/battery_daemon.sh b/backups/bat0daemonbak/battery_daemon.sh new file mode 100755 index 0000000..13fbf9a --- /dev/null +++ b/backups/bat0daemonbak/battery_daemon.sh @@ -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 + diff --git a/backups/bat0daemonbak/battery_monitor b/backups/bat0daemonbak/battery_monitor new file mode 100755 index 0000000..1364d40 Binary files /dev/null and b/backups/bat0daemonbak/battery_monitor differ diff --git a/backups/bat0daemonbak/battery_monitor.service b/backups/bat0daemonbak/battery_monitor.service new file mode 100755 index 0000000..d7a23b4 --- /dev/null +++ b/backups/bat0daemonbak/battery_monitor.service @@ -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 + diff --git a/backups/bat0daemonbak/docs/battery_monitor.log b/backups/bat0daemonbak/docs/battery_monitor.log new file mode 100644 index 0000000..6e8306c --- /dev/null +++ b/backups/bat0daemonbak/docs/battery_monitor.log @@ -0,0 +1,4 @@ +Starting battery monitor script +DISPLAY=:0 +XAUTHORITY=/home/klein/.Xauthority +Failed to open log file: Permission denied diff --git a/backups/bat0daemonbak/docs/procress_list.txt b/backups/bat0daemonbak/docs/procress_list.txt new file mode 100644 index 0000000..e69de29 diff --git a/backups/bat0daemonbak/include/battery_monitor.h b/backups/bat0daemonbak/include/battery_monitor.h new file mode 100644 index 0000000..e7e52b0 --- /dev/null +++ b/backups/bat0daemonbak/include/battery_monitor.h @@ -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 diff --git a/backups/bat0daemonbak/include/process_monitor.h b/backups/bat0daemonbak/include/process_monitor.h new file mode 100644 index 0000000..7b55b05 --- /dev/null +++ b/backups/bat0daemonbak/include/process_monitor.h @@ -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 + diff --git a/backups/bat0daemonbak/install.sh b/backups/bat0daemonbak/install.sh new file mode 100755 index 0000000..e97ee39 --- /dev/null +++ b/backups/bat0daemonbak/install.sh @@ -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 diff --git a/backups/bat0daemonbak/obj/battery_monitor.o b/backups/bat0daemonbak/obj/battery_monitor.o new file mode 100644 index 0000000..9391284 Binary files /dev/null and b/backups/bat0daemonbak/obj/battery_monitor.o differ diff --git a/backups/bat0daemonbak/obj/notification.o b/backups/bat0daemonbak/obj/notification.o new file mode 100644 index 0000000..8de1b7b Binary files /dev/null and b/backups/bat0daemonbak/obj/notification.o differ diff --git a/backups/bat0daemonbak/obj/process_monitor.o b/backups/bat0daemonbak/obj/process_monitor.o new file mode 100644 index 0000000..cf8f05c Binary files /dev/null and b/backups/bat0daemonbak/obj/process_monitor.o differ diff --git a/backups/bat0daemonbak/src/battery_monitor.c b/backups/bat0daemonbak/src/battery_monitor.c new file mode 100644 index 0000000..b4e2f0a --- /dev/null +++ b/backups/bat0daemonbak/src/battery_monitor.c @@ -0,0 +1,70 @@ +#include +#include +#include +#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; +} diff --git a/backups/bat0daemonbak/src/notification.c b/backups/bat0daemonbak/src/notification.c new file mode 100644 index 0000000..e675567 --- /dev/null +++ b/backups/bat0daemonbak/src/notification.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include +#include +#include +#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 +} diff --git a/backups/bat0daemonbak/src/notification.c.bak b/backups/bat0daemonbak/src/notification.c.bak new file mode 100644 index 0000000..38a0526 --- /dev/null +++ b/backups/bat0daemonbak/src/notification.c.bak @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/backups/bat0daemonbak/src/process_monitor.c b/backups/bat0daemonbak/src/process_monitor.c new file mode 100644 index 0000000..6c3d785 --- /dev/null +++ b/backups/bat0daemonbak/src/process_monitor.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#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]); + } +} diff --git a/brightness_control_two/Makefile b/brightness_control_two/Makefile new file mode 100644 index 0000000..7a23987 --- /dev/null +++ b/brightness_control_two/Makefile @@ -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 diff --git a/brightness_control_two/obj/brightness.d b/brightness_control_two/obj/brightness.d new file mode 100644 index 0000000..31ebd53 --- /dev/null +++ b/brightness_control_two/obj/brightness.d @@ -0,0 +1,2 @@ +obj/brightness.o: src/brightness.c src/brightness.h src/display.h \ + src/utils.h diff --git a/brightness_control_two/obj/brightness.o b/brightness_control_two/obj/brightness.o new file mode 100644 index 0000000..bc62203 Binary files /dev/null and b/brightness_control_two/obj/brightness.o differ diff --git a/brightness_control_two/obj/display.d b/brightness_control_two/obj/display.d new file mode 100644 index 0000000..8921907 --- /dev/null +++ b/brightness_control_two/obj/display.d @@ -0,0 +1 @@ +obj/display.o: src/display.c src/display.h src/brightness.h src/utils.h diff --git a/brightness_control_two/obj/display.o b/brightness_control_two/obj/display.o new file mode 100644 index 0000000..e767b66 Binary files /dev/null and b/brightness_control_two/obj/display.o differ diff --git a/brightness_control_two/obj/main.d b/brightness_control_two/obj/main.d new file mode 100644 index 0000000..2d8da03 --- /dev/null +++ b/brightness_control_two/obj/main.d @@ -0,0 +1 @@ +obj/main.o: src/main.c src/brightness.h diff --git a/brightness_control_two/src/brightness.c b/brightness_control_two/src/brightness.c new file mode 100644 index 0000000..d8fb52d --- /dev/null +++ b/brightness_control_two/src/brightness.c @@ -0,0 +1,184 @@ +#include "brightness.h" +#include "display.h" +#include "utils.h" + +#include +#include +#include // 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; +} diff --git a/brightness_control_two/src/brightness.h b/brightness_control_two/src/brightness.h new file mode 100644 index 0000000..9252300 --- /dev/null +++ b/brightness_control_two/src/brightness.h @@ -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 diff --git a/brightness_control_two/src/display.c b/brightness_control_two/src/display.c new file mode 100644 index 0000000..51b38ef --- /dev/null +++ b/brightness_control_two/src/display.c @@ -0,0 +1,95 @@ +#include "display.h" +#include "brightness.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/brightness_control_two/src/display.h b/brightness_control_two/src/display.h new file mode 100644 index 0000000..52c38c6 --- /dev/null +++ b/brightness_control_two/src/display.h @@ -0,0 +1,9 @@ +#ifndef DISPLAY_H +#define DISPLAY_H + +#include // Include X11 headers for Display, Window, and GC types + +void draw_icon(Display *d, Window w, GC gc); +void display_brightness(int brightness); + +#endif diff --git a/brightness_control_two/src/main.c b/brightness_control_two/src/main.c new file mode 100644 index 0000000..7ecf95f --- /dev/null +++ b/brightness_control_two/src/main.c @@ -0,0 +1,113 @@ +#include "brightness.h" + +#include +#include +#include +#include + +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; +} diff --git a/brightness_control_two/src/utils.c b/brightness_control_two/src/utils.c new file mode 100644 index 0000000..d0edacb --- /dev/null +++ b/brightness_control_two/src/utils.c @@ -0,0 +1,29 @@ +#include "utils.h" + +#include +#include + +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; +} diff --git a/brightness_control_two/src/utils.h b/brightness_control_two/src/utils.h new file mode 100644 index 0000000..4e16e88 --- /dev/null +++ b/brightness_control_two/src/utils.h @@ -0,0 +1,6 @@ +#ifndef UTILS_H +#define UTILS_H + +char *read_file_to_string(const char *filepath); + +#endif diff --git a/brightness_menu/Makefile b/brightness_menu/Makefile new file mode 100644 index 0000000..30b97e4 --- /dev/null +++ b/brightness_menu/Makefile @@ -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 diff --git a/brightness_menu/README.md b/brightness_menu/README.md new file mode 100644 index 0000000..e69de29 diff --git a/brightness_menu/build/brightness.o b/brightness_menu/build/brightness.o new file mode 100644 index 0000000..dd45c47 Binary files /dev/null and b/brightness_menu/build/brightness.o differ diff --git a/brightness_menu/build/color_temperature.o b/brightness_menu/build/color_temperature.o new file mode 100644 index 0000000..54d0410 Binary files /dev/null and b/brightness_menu/build/color_temperature.o differ diff --git a/brightness_menu/build/display.o b/brightness_menu/build/display.o new file mode 100644 index 0000000..fe98dc3 Binary files /dev/null and b/brightness_menu/build/display.o differ diff --git a/brightness_menu/build/main.o b/brightness_menu/build/main.o new file mode 100644 index 0000000..21ac4ce Binary files /dev/null and b/brightness_menu/build/main.o differ diff --git a/brightness_menu/build/refresh_rate.o b/brightness_menu/build/refresh_rate.o new file mode 100644 index 0000000..2d94381 Binary files /dev/null and b/brightness_menu/build/refresh_rate.o differ diff --git a/brightness_menu/build/resolution.o b/brightness_menu/build/resolution.o new file mode 100644 index 0000000..ae39e5d Binary files /dev/null and b/brightness_menu/build/resolution.o differ diff --git a/brightness_menu/build/screen_control b/brightness_menu/build/screen_control new file mode 100755 index 0000000..87de689 Binary files /dev/null and b/brightness_menu/build/screen_control differ diff --git a/brightness_menu/include/brightness.h b/brightness_menu/include/brightness.h new file mode 100644 index 0000000..24d82ff --- /dev/null +++ b/brightness_menu/include/brightness.h @@ -0,0 +1,6 @@ +#ifndef BRIGHTNESS_H +#define BRIGHTNESS_H + +void adjust_brightness(); + +#endif // BRIGHTNESS_H diff --git a/brightness_menu/include/color_temperature.h b/brightness_menu/include/color_temperature.h new file mode 100644 index 0000000..f1eabf7 --- /dev/null +++ b/brightness_menu/include/color_temperature.h @@ -0,0 +1,6 @@ +#ifndef COLOR_TEMPERATURE_H +#define COLOR_TEMPERATURE_H + +void adjust_color_temperature(); + +#endif // COLOR_TEMPERATURE_H diff --git a/brightness_menu/include/display.h b/brightness_menu/include/display.h new file mode 100644 index 0000000..bddf868 --- /dev/null +++ b/brightness_menu/include/display.h @@ -0,0 +1,7 @@ +#ifndef DISPLAY_H +#define DISPLAY_H + +void display_main_screen(); +void handle_option(int option); + +#endif // DISPLAY_H diff --git a/brightness_menu/include/refresh_rate.h b/brightness_menu/include/refresh_rate.h new file mode 100644 index 0000000..66ddde2 --- /dev/null +++ b/brightness_menu/include/refresh_rate.h @@ -0,0 +1,6 @@ +#ifndef REFRESH_RATE_H +#define REFRESH_RATE_H + +void adjust_refresh_rate(); + +#endif // REFRESH_RATE_H diff --git a/brightness_menu/include/resolution.h b/brightness_menu/include/resolution.h new file mode 100644 index 0000000..9cdd0a6 --- /dev/null +++ b/brightness_menu/include/resolution.h @@ -0,0 +1,6 @@ +#ifndef RESOLUTION_H +#define RESOLUTION_H + +void adjust_resolution(); + +#endif // RESOLUTION_H diff --git a/brightness_menu/src/brightness.c b/brightness_menu/src/brightness.c new file mode 100644 index 0000000..63f59af --- /dev/null +++ b/brightness_menu/src/brightness.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#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(); +} diff --git a/brightness_menu/src/brightness.o b/brightness_menu/src/brightness.o new file mode 100644 index 0000000..dd45c47 Binary files /dev/null and b/brightness_menu/src/brightness.o differ diff --git a/brightness_menu/src/color_temperature.c b/brightness_menu/src/color_temperature.c new file mode 100644 index 0000000..56d927c --- /dev/null +++ b/brightness_menu/src/color_temperature.c @@ -0,0 +1,26 @@ +#include +#include +#include +#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(); +} diff --git a/brightness_menu/src/display.c b/brightness_menu/src/display.c new file mode 100644 index 0000000..5481848 --- /dev/null +++ b/brightness_menu/src/display.c @@ -0,0 +1,49 @@ +#include +#include +#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(); +} + diff --git a/brightness_menu/src/display.o b/brightness_menu/src/display.o new file mode 100644 index 0000000..c3c7178 Binary files /dev/null and b/brightness_menu/src/display.o differ diff --git a/brightness_menu/src/main.c b/brightness_menu/src/main.c new file mode 100644 index 0000000..4de3933 --- /dev/null +++ b/brightness_menu/src/main.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#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(); +} diff --git a/brightness_menu/src/main.o b/brightness_menu/src/main.o new file mode 100644 index 0000000..cc6eae7 Binary files /dev/null and b/brightness_menu/src/main.o differ diff --git a/brightness_menu/src/refresh_rate.c b/brightness_menu/src/refresh_rate.c new file mode 100644 index 0000000..fe37395 --- /dev/null +++ b/brightness_menu/src/refresh_rate.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#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(); +} diff --git a/brightness_menu/src/resolution.c b/brightness_menu/src/resolution.c new file mode 100644 index 0000000..d567a3c --- /dev/null +++ b/brightness_menu/src/resolution.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#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(); +} diff --git a/mpvd/build/Install b/mpvd/build/Install new file mode 100644 index 0000000..cfffc91 --- /dev/null +++ b/mpvd/build/Install @@ -0,0 +1 @@ +libasound2-dev diff --git a/mpvd/build/Makefile b/mpvd/build/Makefile new file mode 100644 index 0000000..f88e318 --- /dev/null +++ b/mpvd/build/Makefile @@ -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 diff --git a/mpvd/build/mpvd b/mpvd/build/mpvd new file mode 100755 index 0000000..dee475d Binary files /dev/null and b/mpvd/build/mpvd differ diff --git a/mpvd/include/audio.h b/mpvd/include/audio.h new file mode 100644 index 0000000..40ebd25 --- /dev/null +++ b/mpvd/include/audio.h @@ -0,0 +1,6 @@ +#ifndef AUDIO_H +#define AUDIO_H + +void play_audio(const char *file_path); + +#endif diff --git a/mpvd/include/dir.h b/mpvd/include/dir.h new file mode 100644 index 0000000..d7c8d3b --- /dev/null +++ b/mpvd/include/dir.h @@ -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 diff --git a/mpvd/include/ui.h b/mpvd/include/ui.h new file mode 100644 index 0000000..0697565 --- /dev/null +++ b/mpvd/include/ui.h @@ -0,0 +1,14 @@ +#ifndef UI_H +#define UI_H + +#include + +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 diff --git a/mpvd/obj/audio.o b/mpvd/obj/audio.o new file mode 100644 index 0000000..76e00a4 Binary files /dev/null and b/mpvd/obj/audio.o differ diff --git a/mpvd/obj/dir.o b/mpvd/obj/dir.o new file mode 100644 index 0000000..7a17d22 Binary files /dev/null and b/mpvd/obj/dir.o differ diff --git a/mpvd/obj/main.o b/mpvd/obj/main.o new file mode 100644 index 0000000..dfcc0fe Binary files /dev/null and b/mpvd/obj/main.o differ diff --git a/mpvd/obj/ui.o b/mpvd/obj/ui.o new file mode 100644 index 0000000..4b73df0 Binary files /dev/null and b/mpvd/obj/ui.o differ diff --git a/mpvd/src/audio.c b/mpvd/src/audio.c new file mode 100644 index 0000000..9742397 --- /dev/null +++ b/mpvd/src/audio.c @@ -0,0 +1,41 @@ +#include "audio.h" +#include + +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(¶ms); + 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); +} diff --git a/mpvd/src/dir.c b/mpvd/src/dir.c new file mode 100644 index 0000000..56dbea3 --- /dev/null +++ b/mpvd/src/dir.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#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 +} diff --git a/mpvd/src/main.c b/mpvd/src/main.c new file mode 100644 index 0000000..ef0295d --- /dev/null +++ b/mpvd/src/main.c @@ -0,0 +1,55 @@ +// src/main.c + +#include +#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; +} diff --git a/mpvd/src/mpvd.c b/mpvd/src/mpvd.c new file mode 100644 index 0000000..a51cd1f --- /dev/null +++ b/mpvd/src/mpvd.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include + +#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(¶ms); + + // 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(); +} diff --git a/mpvd/src/ui.c b/mpvd/src/ui.c new file mode 100644 index 0000000..c222a0c --- /dev/null +++ b/mpvd/src/ui.c @@ -0,0 +1,87 @@ +#include +#include +#include +#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); +} diff --git a/ncursescalander/backup/Makefile b/ncursescalander/backup/Makefile new file mode 100644 index 0000000..d9d6f3b --- /dev/null +++ b/ncursescalander/backup/Makefile @@ -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 diff --git a/ncursescalander/backup/calendar b/ncursescalander/backup/calendar new file mode 100755 index 0000000..341d78d Binary files /dev/null and b/ncursescalander/backup/calendar differ diff --git a/ncursescalander/backup/main.c b/ncursescalander/backup/main.c new file mode 100644 index 0000000..bdbd60e --- /dev/null +++ b/ncursescalander/backup/main.c @@ -0,0 +1,326 @@ +#include +#include +#include + +#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 + } + } +} diff --git a/ncursescalander/backup/main.o b/ncursescalander/backup/main.o new file mode 100644 index 0000000..81ea559 Binary files /dev/null and b/ncursescalander/backup/main.o differ diff --git a/ncursescalander/build/Makefile b/ncursescalander/build/Makefile new file mode 100644 index 0000000..7b50c34 --- /dev/null +++ b/ncursescalander/build/Makefile @@ -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 + diff --git a/ncursescalander/build/add_event.o b/ncursescalander/build/add_event.o new file mode 100644 index 0000000..a674200 Binary files /dev/null and b/ncursescalander/build/add_event.o differ diff --git a/ncursescalander/build/calendar.o b/ncursescalander/build/calendar.o new file mode 100644 index 0000000..7982007 Binary files /dev/null and b/ncursescalander/build/calendar.o differ diff --git a/ncursescalander/build/calendar_app b/ncursescalander/build/calendar_app new file mode 100755 index 0000000..4d123a2 Binary files /dev/null and b/ncursescalander/build/calendar_app differ diff --git a/ncursescalander/build/day_view.o b/ncursescalander/build/day_view.o new file mode 100644 index 0000000..17010cf Binary files /dev/null and b/ncursescalander/build/day_view.o differ diff --git a/ncursescalander/build/main.o b/ncursescalander/build/main.o new file mode 100644 index 0000000..f3ac65f Binary files /dev/null and b/ncursescalander/build/main.o differ diff --git a/ncursescalander/build/repeat_tui.o b/ncursescalander/build/repeat_tui.o new file mode 100644 index 0000000..8355804 Binary files /dev/null and b/ncursescalander/build/repeat_tui.o differ diff --git a/ncursescalander/databases/calendar_database.db b/ncursescalander/databases/calendar_database.db new file mode 100644 index 0000000..f707725 Binary files /dev/null and b/ncursescalander/databases/calendar_database.db differ diff --git a/ncursescalander/include/add_event.h b/ncursescalander/include/add_event.h new file mode 100644 index 0000000..07688f5 --- /dev/null +++ b/ncursescalander/include/add_event.h @@ -0,0 +1,29 @@ +#ifndef ADD_EVENT_H +#define ADD_EVENT_H + +#include +#include + +#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 diff --git a/ncursescalander/include/calendar.h b/ncursescalander/include/calendar.h new file mode 100644 index 0000000..877f57c --- /dev/null +++ b/ncursescalander/include/calendar.h @@ -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 diff --git a/ncursescalander/include/day_view.h b/ncursescalander/include/day_view.h new file mode 100644 index 0000000..a224fbc --- /dev/null +++ b/ncursescalander/include/day_view.h @@ -0,0 +1,7 @@ +#ifndef DAY_VIEW_H +#define DAY_VIEW_H + +void draw_day_view(int *day, int *month, int *year); + +#endif + diff --git a/ncursescalander/include/repeat_tui.h b/ncursescalander/include/repeat_tui.h new file mode 100644 index 0000000..52282c9 --- /dev/null +++ b/ncursescalander/include/repeat_tui.h @@ -0,0 +1,7 @@ +#ifndef REPEAT_TUI_H +#define REPEAT_TUI_H + +void open_repeat_tui(); + +#endif + diff --git a/ncursescalander/src/add_event.c b/ncursescalander/src/add_event.c new file mode 100644 index 0000000..d38a50a --- /dev/null +++ b/ncursescalander/src/add_event.c @@ -0,0 +1,292 @@ +#include "add_event.h" +#include "repeat_tui.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 +} diff --git a/ncursescalander/src/add_event.c.bak b/ncursescalander/src/add_event.c.bak new file mode 100644 index 0000000..718710e --- /dev/null +++ b/ncursescalander/src/add_event.c.bak @@ -0,0 +1,225 @@ +#include "add_event.h" +#include "repeat_tui.h" +#include +#include +#include +#include + +#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 +} diff --git a/ncursescalander/src/calendar.c b/ncursescalander/src/calendar.c new file mode 100644 index 0000000..aeff0ac --- /dev/null +++ b/ncursescalander/src/calendar.c @@ -0,0 +1,56 @@ +#include "calendar.h" +#include + +// 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); + } + } +} diff --git a/ncursescalander/src/day_view.c b/ncursescalander/src/day_view.c new file mode 100644 index 0000000..aac91fa --- /dev/null +++ b/ncursescalander/src/day_view.c @@ -0,0 +1,127 @@ +#include "day_view.h" +#include +#include +#include +#include + +#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", ¤t_day, ¤t_month, ¤t_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); +} + diff --git a/ncursescalander/src/main.c b/ncursescalander/src/main.c new file mode 100644 index 0000000..dcd439f --- /dev/null +++ b/ncursescalander/src/main.c @@ -0,0 +1,106 @@ +#include +#include +#include +#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; +} diff --git a/ncursescalander/src/repeat_tui.c b/ncursescalander/src/repeat_tui.c new file mode 100644 index 0000000..cfe264d --- /dev/null +++ b/ncursescalander/src/repeat_tui.c @@ -0,0 +1,48 @@ +#include +#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 + } + } +} + diff --git a/ssh_gui b/ssh_gui new file mode 160000 index 0000000..ed35ae7 --- /dev/null +++ b/ssh_gui @@ -0,0 +1 @@ +Subproject commit ed35ae7f3a1e71e4667064bc18b9d15582924a9a diff --git a/terrarium_bk/terrarium/Makefile b/terrarium_bk/terrarium/Makefile new file mode 100644 index 0000000..900737e --- /dev/null +++ b/terrarium_bk/terrarium/Makefile @@ -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 + diff --git a/terrarium_bk/terrarium/ascii_plant_data.c b/terrarium_bk/terrarium/ascii_plant_data.c new file mode 100644 index 0000000..79536c7 --- /dev/null +++ b/terrarium_bk/terrarium/ascii_plant_data.c @@ -0,0 +1,581 @@ +/****************************************************************************** + * ascii_plant_data.c + * + * Defines 40 unique ASCII plants, each 10 lines of up to 20 chars. + * + * For demonstration, they’ll 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): + " . " + " .. # " + " . ==+*- . - " + " :++*====.# " + " ==++-=#*+ " + " -%*=-#+#. " + " .-+**#. " + " .**+#. " + " .:=*++ " + " :--=. " + We’ll 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]; +} + diff --git a/terrarium_bk/terrarium/ascii_plant_data.h b/terrarium_bk/terrarium/ascii_plant_data.h new file mode 100644 index 0000000..519d610 --- /dev/null +++ b/terrarium_bk/terrarium/ascii_plant_data.h @@ -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 + diff --git a/terrarium_bk/terrarium/ascii_plant_data.o b/terrarium_bk/terrarium/ascii_plant_data.o new file mode 100644 index 0000000..f013472 Binary files /dev/null and b/terrarium_bk/terrarium/ascii_plant_data.o differ diff --git a/terrarium_bk/terrarium/game.c b/terrarium_bk/terrarium/game.c new file mode 100644 index 0000000..b6f57a9 --- /dev/null +++ b/terrarium_bk/terrarium/game.c @@ -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 +#include /* for system() */ +#include /* for clock_gettime, etc. */ +#include /* for usleep() */ +#include +#include +#include /* 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, ¤t); +} + +/* 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; + } +} + diff --git a/terrarium_bk/terrarium/game.h b/terrarium_bk/terrarium/game.h new file mode 100644 index 0000000..497857f --- /dev/null +++ b/terrarium_bk/terrarium/game.h @@ -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 */ + diff --git a/terrarium_bk/terrarium/game.o b/terrarium_bk/terrarium/game.o new file mode 100644 index 0000000..f9ef473 Binary files /dev/null and b/terrarium_bk/terrarium/game.o differ diff --git a/terrarium_bk/terrarium/main.c b/terrarium_bk/terrarium/main.c new file mode 100644 index 0000000..5558a6e --- /dev/null +++ b/terrarium_bk/terrarium/main.c @@ -0,0 +1,57 @@ +/* + * main.c - Entry point of the Terrarium program + */ + +#define _POSIX_C_SOURCE 200809L + +#include +#include /* for system() */ +#include /* 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; +} + diff --git a/terrarium_bk/terrarium/main.o b/terrarium_bk/terrarium/main.o new file mode 100644 index 0000000..5311d60 Binary files /dev/null and b/terrarium_bk/terrarium/main.o differ diff --git a/terrarium_bk/terrarium/terrarium b/terrarium_bk/terrarium/terrarium new file mode 100755 index 0000000..e52103e Binary files /dev/null and b/terrarium_bk/terrarium/terrarium differ diff --git a/terrarium_bk/terrarium/terrarium.c b/terrarium_bk/terrarium/terrarium.c new file mode 100644 index 0000000..73dff5a --- /dev/null +++ b/terrarium_bk/terrarium/terrarium.c @@ -0,0 +1,388 @@ +/****************************************************************************** + * Terrarium - An ASCII-based terminal idle game in C + * + * - Top 3/4 of the screen: shelves to visualize plants with their names. + * - Bottom 1/4 of the screen: oxygen total, controls, etc. + * - Tapping (space bar) works only for the first plant to give bonus oxygen. + * - Press 'b' to buy new plants, which appear on the next available shelf. + * - Press 'q' to quit. + * - Auto-saves and auto-loads from "terrarium_save.dat". + * + * Compile (Linux/Unix): + * gcc terrarium.c -o terrarium -lm + * + * Run: + * ./terrarium + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************** + * Platform/Terminal-Handling Code (Non-Blocking Input, Clear Screen, etc.) + *****************************************************************************/ + +static struct termios old, current; + +/* Initialize new terminal i/o settings */ +void initTermios(int echo) { + tcgetattr(0, &old); /* grab old terminal i/o settings */ + current = old; + current.c_lflag &= ~ICANON; /* disable buffered i/o */ + if (echo) { + current.c_lflag |= ECHO; /* set echo mode */ + } else { + current.c_lflag &= ~ECHO; /* set no echo mode */ + } + tcsetattr(0, TCSANOW, ¤t); /* apply the new settings */ +} + +/* 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 without echo */ +char getch() { + return getchar(); +} + +/* Clear the screen (Unix-like) */ +void clearScreen() { + system("clear"); +} + +/****************************************************************************** + * Game Constants + *****************************************************************************/ + +#define MAX_PLANTS 10 /* Max number of plants we can display */ +#define SHELF_COUNT 5 /* How many shelves to visualize */ +#define GAME_TICK_INTERVAL_MS 200 /* Game loop tick interval in 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 + +/* Save file name */ +#define SAVE_FILE "terrarium_save.dat" + +/****************************************************************************** + * Data Structures + *****************************************************************************/ + +/* Each Plant has a name and a productionRate. We could expand this in the future. */ +typedef struct { + char name[32]; + int active; /* 1 if this plant is purchased and displayed, 0 otherwise */ + double productionRate; /* Oxygen production contribution from this plant */ +} Plant; + +typedef struct { + double oxygen; /* Current total oxygen */ + double plantCost; /* Cost to buy the next plant */ + Plant plants[MAX_PLANTS]; /* Our plants (0th is the "first" plant that can be tapped) */ + int plantCount; /* How many plants are active/purchased */ +} GameState; + +/****************************************************************************** + * Function Declarations + *****************************************************************************/ + +void initGame(GameState *gs); +void loadGame(GameState *gs); +void saveGame(const GameState *gs); + +void updateGame(GameState *gs, double elapsedSeconds); +void renderGame(const GameState *gs); + +void handleInput(GameState *gs, char input); +void buyPlant(GameState *gs); + +double calcTotalProduction(const GameState *gs); +double getElapsedTime(struct timespec *lastTick); + +/****************************************************************************** + * Main Function + *****************************************************************************/ + +int main(void) { + GameState gs; + + /* Attempt to load a previous save. If no save file is found, initialize. */ + loadGame(&gs); + + /* Terminal setup for non-blocking input */ + initTermios(0); // 0 = no echo + + /* Timing for the game loop */ + struct timespec lastTick; + clock_gettime(CLOCK_MONOTONIC, &lastTick); + + int running = 1; + while(running) { + /* 1) Compute elapsed time */ + double elapsedSeconds = getElapsedTime(&lastTick); + + /* 2) Update game logic */ + updateGame(&gs, elapsedSeconds); + + /* 3) Render the game */ + clearScreen(); + renderGame(&gs); + + /* 4) Check user input (non-blocking) */ + if(kbhit()) { + char input = getch(); + if(input == 'q' || input == 'Q') { + running = 0; + } else { + handleInput(&gs, input); + } + } + + /* 5) Sleep to control game speed */ + usleep(GAME_TICK_INTERVAL_MS * 1000); + + /* 6) Auto-save occasionally (for example, every loop). + * You could also do it less frequently to avoid disk writes. */ + saveGame(&gs); + } + + /* Cleanup */ + resetTermios(); + clearScreen(); + printf("Thanks for playing Terrarium!\n"); + return 0; +} + +/****************************************************************************** + * Initialize Game State (default if no save is found) + *****************************************************************************/ +void initGame(GameState *gs) { + gs->oxygen = 0.0; + gs->plantCost = BASE_PLANT_COST; + + /* Initialize all plants as inactive, except the first one. */ + for(int i = 0; i < MAX_PLANTS; i++){ + snprintf(gs->plants[i].name, sizeof(gs->plants[i].name), "Plant #%d", i+1); + gs->plants[i].active = 0; + gs->plants[i].productionRate = 0.0; + } + + /* Start with one plant active (the first one) */ + gs->plants[0].active = 1; + gs->plants[0].productionRate = BASE_OXYGEN_PER_SECOND; + gs->plantCount = 1; +} + +/****************************************************************************** + * Load Game State from SAVE_FILE (if it exists). Otherwise, init fresh. + *****************************************************************************/ +void loadGame(GameState *gs) { + FILE *f = fopen(SAVE_FILE, "rb"); + if(!f) { + /* No save found, start a new game */ + initGame(gs); + return; + } + + /* Read the saved struct. For a real game, you'd want version checks, etc. */ + fread(gs, sizeof(GameState), 1, f); + fclose(f); + + /* Sanity check that at least one plant is active. If not, re-init. */ + if(gs->plantCount < 1) { + initGame(gs); + } +} + +/****************************************************************************** + * Save Game State to SAVE_FILE + *****************************************************************************/ +void saveGame(const GameState *gs) { + FILE *f = fopen(SAVE_FILE, "wb"); + if(!f) { + return; /* if we can't open it, just skip saving */ + } + fwrite(gs, sizeof(GameState), 1, f); + fclose(f); +} + +/****************************************************************************** + * Update Game State + * - Accumulate oxygen based on the total production rate * elapsed time + *****************************************************************************/ +void updateGame(GameState *gs, double elapsedSeconds) { + double totalProd = calcTotalProduction(gs); + gs->oxygen += totalProd * elapsedSeconds; +} + +/****************************************************************************** + * Render/Draw the game to the terminal + * + * We’ll reserve the top 3/4 of the screen for shelves (with ASCII pots/plants), + * and the bottom 1/4 for stats, cost, etc. + * + * For simplicity, let's assume a typical 24- or 25-line terminal height. + * We'll do 15-16 lines for shelves, then ~8-9 lines for the bottom portion. + * + *****************************************************************************/ +void renderGame(const GameState *gs) { + /* We'll define how many lines we want for shelves, ignoring actual + terminal height detection for simplicity. Adjust as you like. */ + const int shelfLines = 15; + + /* Each shelf is represented by two lines: + 1) The plant pot / name + 2) The "--------" shelf line + We'll show up to SHELF_COUNT shelves from top to bottom. + */ + int shelfIndex = 0; + int plantIndex = 0; + + /* Print the top banner */ + printf("========================================\n"); + printf(" T E R R A R I U M \n"); + printf("========================================\n\n"); + + /* We have SHELF_COUNT shelves to display. Let’s map each shelf to a plant index if available. */ + for(shelfIndex = 0; shelfIndex < SHELF_COUNT; shelfIndex++) { + /* If there's a plant for this shelfIndex, show it. + We'll just go in the order plants are purchased (0,1,2,...) + and display them as long as they are active, up to the number of shelves. */ + if(plantIndex < MAX_PLANTS && gs->plants[plantIndex].active) { + /* Show an ASCII pot and the plant’s name */ + printf(" [%s]\n", gs->plants[plantIndex].name); + printf(" ---------------------- (shelf #%d)\n", shelfIndex+1); + plantIndex++; + } else { + /* Empty shelf */ + printf(" [empty]\n"); + printf(" ---------------------- (shelf #%d)\n", shelfIndex+1); + } + } + + /* Now we display the bottom portion (the UI area). We'll show the last 1/4 lines. */ + /* Let's just put a few blank lines to ensure separation. */ + printf("\n"); + + /* Bottom section with info and controls */ + printf("Oxygen: %.2f O2\n", gs->oxygen); + printf("Total Production Rate: %.3f O2/sec\n", calcTotalProduction(gs)); + printf("Next Plant Cost: %.2f O2\n\n", gs->plantCost); + + printf("[SPACE] Tap the first plant for +%.1f O2 (only if the first plant exists)\n", TAP_BONUS_OXYGEN); + printf("[b] Buy a new plant\n"); + printf("[q] Quit\n"); + printf("\n"); +} + +/****************************************************************************** + * Handle User Input + *****************************************************************************/ +void handleInput(GameState *gs, char input) { + switch(input) { + case ' ': + /* Space bar: Tapping only affects the first plant if active. */ + if(gs->plants[0].active) { + gs->oxygen += TAP_BONUS_OXYGEN; + } + break; + case 'b': + case 'B': + /* Buy a new plant if possible */ + buyPlant(gs); + break; + default: + /* ignore other keys */ + break; + } +} + +/****************************************************************************** + * Buy a new plant (if we can afford it and haven't reached MAX_PLANTS) + *****************************************************************************/ +void buyPlant(GameState *gs) { + if(gs->plantCount >= MAX_PLANTS) { + /* No more plants can be bought */ + return; + } + + /* Check if we have enough oxygen for the next plant */ + if(gs->oxygen >= gs->plantCost) { + gs->oxygen -= gs->plantCost; + + /* Activate the next plant and set its production rate */ + int index = gs->plantCount; /* next available slot */ + gs->plants[index].active = 1; + /* For demonstration: each new plant has the same base production, + or you could do something fancier. */ + gs->plants[index].productionRate = BASE_OXYGEN_PER_SECOND; + + gs->plantCount++; + + /* Increase the cost for the next plant */ + gs->plantCost *= COST_MULTIPLIER; + } +} + +/****************************************************************************** + * Calculate the Total Production from all active plants + *****************************************************************************/ +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; +} + +/****************************************************************************** + * Get elapsed time in seconds since the last tick + *****************************************************************************/ +double getElapsedTime(struct timespec *lastTick) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + double elapsed = (now.tv_sec - lastTick->tv_sec) + + (now.tv_nsec - lastTick->tv_nsec) / 1000000000.0; + *lastTick = now; + return elapsed; +} + diff --git a/terrarium_bk/terrarium/terrarium_save.dat b/terrarium_bk/terrarium/terrarium_save.dat new file mode 100644 index 0000000..840a214 Binary files /dev/null and b/terrarium_bk/terrarium/terrarium_save.dat differ diff --git a/testfunctions/readme.md b/testfunctions/readme.md new file mode 100644 index 0000000..53b8f8b --- /dev/null +++ b/testfunctions/readme.md @@ -0,0 +1,3 @@ +# Project README + +Details about the project. \ No newline at end of file diff --git a/testfunctions/source/test.c b/testfunctions/source/test.c new file mode 100755 index 0000000..e69de29 diff --git a/website-tests b/website-tests new file mode 160000 index 0000000..2d7b920 --- /dev/null +++ b/website-tests @@ -0,0 +1 @@ +Subproject commit 2d7b9204a5786c538bcf9efe93330ef95148fe7a