1162 lines
39 KiB
Plaintext
1162 lines
39 KiB
Plaintext
#include <gtk/gtk.h>
|
|
#include <vte/vte.h>
|
|
#include <sys/inotify.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <libgen.h>
|
|
#include <glib.h>
|
|
|
|
#define FILE_PATH_COLUMN 0
|
|
|
|
static GtkListStore *store;
|
|
static GtkWidget *tree_view, *window;
|
|
static VteTerminal *terminal;
|
|
static int inotify_fd;
|
|
static char *base_dir;
|
|
static char *current_dir;
|
|
|
|
// Function declarations
|
|
static void show_new_directory_dialog();
|
|
static void create_new_directory(const char *dir_name);
|
|
static void show_deletion_dialog();
|
|
static void delete_selected_item();
|
|
static int get_directory_depth(const char *dir);
|
|
static gboolean recursive_delete(const char *path);
|
|
static void make_file_executable(const char *path);
|
|
static void make_file_not_executable(const char *path);
|
|
static const char *get_language_from_path(const char *path);
|
|
static void compile_and_run(const char *path, const char *language);
|
|
static gboolean show_confirmation_dialog(const char *message);
|
|
static void create_new_file(const char *file_name);
|
|
static void show_new_file_dialog();
|
|
static void display_directory(const char *dir);
|
|
static void open_file_with_appropriate_application(const char *filepath);
|
|
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer userdata);
|
|
static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer userdata);
|
|
static void on_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata);
|
|
static GtkWidget* create_tree_view();
|
|
static void on_window_destroy(GtkWidget *widget, gpointer data);
|
|
static char *strip_html_markup(const char *input);
|
|
void run_executable(GtkMenuItem *menu_item, gpointer user_data);
|
|
void make_file_executable_menu(GtkMenuItem *menu_item, gpointer user_data);
|
|
void make_file_not_executable_menu(GtkMenuItem *menu_item, gpointer user_data);
|
|
|
|
static gboolean has_extension(const char *filename, const char *extension) {
|
|
const char *dot = strrchr(filename, '.');
|
|
return dot && !g_strcmp0(dot + 1, extension);
|
|
}
|
|
|
|
static gboolean show_confirmation_dialog(const char *message) {
|
|
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
|
|
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_MESSAGE_QUESTION,
|
|
GTK_BUTTONS_OK_CANCEL,
|
|
"%s", message);
|
|
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
gtk_widget_destroy(dialog);
|
|
return (response == GTK_RESPONSE_OK);
|
|
}
|
|
|
|
static void handle_file_open_error(const char *filepath) {
|
|
gchar *msg = g_strdup_printf("\nFailed to open '%s'\n", filepath);
|
|
vte_terminal_feed_child(VTE_TERMINAL(terminal), msg, -1);
|
|
g_free(msg);
|
|
}
|
|
|
|
static void open_file_with_appropriate_application(const char *filepath) {
|
|
struct stat path_stat;
|
|
if (stat(filepath, &path_stat) != 0 || S_ISDIR(path_stat.st_mode)) {
|
|
g_print("Attempted to open a directory or invalid path: %s\n", filepath);
|
|
return;
|
|
}
|
|
|
|
const char *const_argv[3] = {NULL, filepath, NULL};
|
|
char *argv[3];
|
|
|
|
if (has_extension(filepath, "jpg") || has_extension(filepath, "png")) {
|
|
const_argv[0] = "feh";
|
|
} else if (has_extension(filepath, "pdf")) {
|
|
const_argv[0] = "zathura";
|
|
} else {
|
|
const_argv[0] = "nvim";
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
argv[i] = (char *)const_argv[i];
|
|
}
|
|
|
|
vte_terminal_spawn_async(VTE_TERMINAL(terminal),
|
|
VTE_PTY_DEFAULT,
|
|
NULL,
|
|
argv,
|
|
NULL,
|
|
G_SPAWN_DEFAULT,
|
|
NULL, NULL,
|
|
NULL,
|
|
-1,
|
|
NULL, NULL,
|
|
NULL);
|
|
}
|
|
|
|
static void display_directory(const char *dir) {
|
|
DIR *d;
|
|
struct dirent *entry;
|
|
GtkTreeIter iter;
|
|
struct stat statbuf;
|
|
gboolean readme_found = FALSE;
|
|
char *readme_path = NULL;
|
|
|
|
if ((d = opendir(dir)) == NULL) {
|
|
fprintf(stderr, "Failed to open directory: %s\n", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
gtk_list_store_clear(store);
|
|
|
|
while ((entry = readdir(d)) != NULL) {
|
|
char *full_path = g_strdup_printf("%s/%s", dir, entry->d_name);
|
|
if (stat(full_path, &statbuf) == -1) {
|
|
g_free(full_path);
|
|
continue;
|
|
}
|
|
|
|
gchar *display_name;
|
|
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0 && strcmp(entry->d_name, "codeWS") != 0) {
|
|
if (S_ISDIR(statbuf.st_mode)) {
|
|
display_name = g_markup_printf_escaped("<span foreground='blue'>%s</span>", entry->d_name);
|
|
} else if (statbuf.st_mode & S_IXUSR) {
|
|
display_name = g_markup_printf_escaped("<span foreground='red'>%s</span>", entry->d_name);
|
|
} else {
|
|
display_name = g_strdup(entry->d_name);
|
|
}
|
|
|
|
gtk_list_store_append(store, &iter);
|
|
gtk_list_store_set(store, &iter, 0, display_name, 1, entry->d_name, -1);
|
|
g_free(display_name);
|
|
|
|
if (g_ascii_strcasecmp(entry->d_name, "readme.md") == 0) {
|
|
readme_found = TRUE;
|
|
readme_path = g_strdup_printf("%s/%s", dir, entry->d_name);
|
|
}
|
|
}
|
|
g_free(full_path);
|
|
}
|
|
closedir(d);
|
|
|
|
if (readme_found && readme_path) {
|
|
open_file_with_appropriate_application(readme_path);
|
|
g_free(readme_path);
|
|
}
|
|
}
|
|
|
|
static void navigate_up_directory() {
|
|
if (strcmp(current_dir, base_dir) == 0) {
|
|
return;
|
|
}
|
|
|
|
char *last_slash = strrchr(current_dir, '/');
|
|
if (last_slash != NULL) {
|
|
*last_slash = '\0';
|
|
if (strcmp(current_dir, base_dir) >= 0) {
|
|
display_directory(current_dir);
|
|
} else {
|
|
strcpy(current_dir, base_dir);
|
|
display_directory(current_dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *strip_html_markup(const char *input) {
|
|
if (input == NULL) return NULL;
|
|
|
|
GError *error = NULL;
|
|
GRegex *regex = g_regex_new("<[^>]*>", 0, 0, &error);
|
|
if (error != NULL) {
|
|
g_print("Regex error: %s\n", error->message);
|
|
g_error_free(error);
|
|
return NULL;
|
|
}
|
|
|
|
gchar *stripped = g_regex_replace_literal(regex, input, -1, 0, "", 0, NULL);
|
|
g_regex_unref(regex);
|
|
|
|
return stripped;
|
|
}
|
|
|
|
gboolean is_executable(const char *path) {
|
|
struct stat st;
|
|
if (stat(path, &st) == 0) {
|
|
return st.st_mode & S_IXUSR;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
|
|
if (event->keyval == GDK_KEY_BackSpace && !vte_terminal_get_has_selection(VTE_TERMINAL(terminal))) {
|
|
navigate_up_directory();
|
|
return TRUE;
|
|
}
|
|
if ((event->state & GDK_CONTROL_MASK) && event->keyval == GDK_KEY_n) {
|
|
show_new_directory_dialog();
|
|
return TRUE;
|
|
}
|
|
if ((event->state & GDK_CONTROL_MASK) && event->keyval == GDK_KEY_d) {
|
|
show_deletion_dialog();
|
|
return TRUE;
|
|
}
|
|
if ((event->state & GDK_CONTROL_MASK) && event->keyval == GDK_KEY_r) {
|
|
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
gchar *filename;
|
|
|
|
if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
|
|
gtk_tree_model_get(model, &iter, 0, &filename, -1);
|
|
char *clean_filename = strip_html_markup(filename);
|
|
char *path = g_strdup_printf("%s/%s", current_dir, clean_filename);
|
|
const char *language = get_language_from_path(path);
|
|
char *clean_path = strip_html_markup(path);
|
|
|
|
if (clean_path) {
|
|
g_print("Cleaned path: %s\n", clean_path); // Debug print
|
|
compile_and_run(clean_path, language);
|
|
g_free(clean_path);
|
|
} else {
|
|
g_print("Failed to clean path: %s\n", path); // Debug print
|
|
}
|
|
|
|
g_free(clean_filename);
|
|
g_free(filename);
|
|
g_free(path);
|
|
}
|
|
return TRUE;
|
|
}
|
|
if ((event->state & GDK_CONTROL_MASK) && event->keyval == GDK_KEY_e) {
|
|
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
gchar *filename;
|
|
|
|
if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
|
|
gtk_tree_model_get(model, &iter, 0, &filename, -1);
|
|
char *clean_filename = strip_html_markup(filename);
|
|
char *path = g_strdup_printf("%s/%s", current_dir, clean_filename);
|
|
make_file_executable(path);
|
|
display_directory(current_dir);
|
|
g_free(clean_filename);
|
|
g_free(filename);
|
|
g_free(path);
|
|
}
|
|
return TRUE;
|
|
}
|
|
if ((event->state & GDK_CONTROL_MASK) && event->keyval == GDK_KEY_f) {
|
|
show_new_file_dialog();
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer userdata) {
|
|
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
|
GtkTreePath *path;
|
|
GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
|
|
|
|
if (gtk_tree_view_get_path_at_pos(tree_view, (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL)) {
|
|
GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
|
|
GtkTreeIter iter;
|
|
if (gtk_tree_model_get_iter(model, &iter, path)) {
|
|
gchar *file_name;
|
|
gtk_tree_model_get(model, &iter, 1, &file_name, -1);
|
|
|
|
// Construct the full path
|
|
gchar *file_path = g_strdup_printf("%s/%s", current_dir, file_name);
|
|
|
|
struct stat path_stat;
|
|
if (stat(file_path, &path_stat) == 0 && !S_ISDIR(path_stat.st_mode)) { // Check if not a directory
|
|
GtkWidget *menu = gtk_menu_new();
|
|
|
|
// Create and add "Make File Executable" item
|
|
GtkWidget *make_exec_item = gtk_menu_item_new_with_label("Make File Executable");
|
|
g_signal_connect(make_exec_item, "activate", G_CALLBACK(make_file_executable_menu), g_strdup(file_path));
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), make_exec_item);
|
|
|
|
// Create and add "Make File Not Executable" item
|
|
GtkWidget *make_not_exec_item = gtk_menu_item_new_with_label("Make File Not Executable");
|
|
g_signal_connect(make_not_exec_item, "activate", G_CALLBACK(make_file_not_executable_menu), g_strdup(file_path));
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), make_not_exec_item);
|
|
|
|
// Create and add "Run" item if the file is executable
|
|
if (is_executable(file_path)) {
|
|
GtkWidget *run_item = gtk_menu_item_new_with_label("Run");
|
|
g_signal_connect(run_item, "activate", G_CALLBACK(run_executable), g_strdup(file_path));
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), run_item);
|
|
}
|
|
|
|
gtk_widget_show_all(menu);
|
|
gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
|
|
} else {
|
|
g_print("Right-clicked on a directory or invalid path: %s\n", file_path);
|
|
}
|
|
|
|
g_free(file_path);
|
|
g_free(file_name);
|
|
}
|
|
gtk_tree_path_free(path);
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void run_executable(GtkMenuItem *menu_item, gpointer user_data) {
|
|
const char *file_path = (const char *)user_data;
|
|
gchar *clean_path = g_strdup(file_path); // Duplicate the file path
|
|
gchar *command = g_strdup_printf("clear && '%s'", clean_path); // Use single quotes to handle spaces in paths
|
|
gchar *argv[] = {"bash", "-c", command, NULL};
|
|
|
|
g_print("Running executable: %s\n", clean_path); // Debug print
|
|
g_print("Command: %s\n", command); // Print the command for debugging
|
|
|
|
vte_terminal_spawn_async(
|
|
VTE_TERMINAL(terminal),
|
|
VTE_PTY_DEFAULT,
|
|
current_dir, // Set the working directory
|
|
argv,
|
|
NULL,
|
|
G_SPAWN_DEFAULT,
|
|
NULL, NULL, NULL, -1,
|
|
NULL, NULL, NULL);
|
|
|
|
g_free(command);
|
|
g_free(clean_path);
|
|
g_free((char *)file_path); // Free the strdup-ed file path
|
|
}
|
|
|
|
void make_file_executable_menu(GtkMenuItem *menu_item, gpointer user_data) {
|
|
const char *file_path = (const char *)user_data;
|
|
make_file_executable(file_path);
|
|
display_directory(current_dir); // Update the directory view immediately
|
|
g_free((char *)file_path); // Free the strdup-ed file path
|
|
}
|
|
|
|
void make_file_not_executable_menu(GtkMenuItem *menu_item, gpointer user_data) {
|
|
const char *file_path = (const char *)user_data;
|
|
make_file_not_executable(file_path);
|
|
display_directory(current_dir); // Update the directory view immediately
|
|
g_free((char *)file_path); // Free the strdup-ed file path
|
|
}
|
|
|
|
static void on_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata) {
|
|
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
|
|
GtkTreeIter iter;
|
|
gchar *actual_name;
|
|
|
|
if (gtk_tree_model_get_iter(model, &iter, path)) {
|
|
gtk_tree_model_get(model, &iter, 1, &actual_name, -1);
|
|
|
|
char *new_path = g_strdup_printf("%s/%s", current_dir, actual_name);
|
|
struct stat path_stat;
|
|
if (stat(new_path, &path_stat) == 0) {
|
|
if (S_ISDIR(path_stat.st_mode)) {
|
|
g_free(current_dir);
|
|
current_dir = new_path;
|
|
display_directory(current_dir);
|
|
} else {
|
|
open_file_with_appropriate_application(new_path);
|
|
g_free(new_path);
|
|
}
|
|
} else {
|
|
g_printerr("Failed to access %s: %s\n", new_path, strerror(errno));
|
|
g_free(new_path);
|
|
}
|
|
g_free(actual_name);
|
|
}
|
|
}
|
|
|
|
static GtkWidget* create_tree_view() {
|
|
tree_view = gtk_tree_view_new();
|
|
store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
|
|
gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(store));
|
|
|
|
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
|
|
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("Entries", renderer, "markup", 0, NULL);
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
|
|
|
|
g_signal_connect(tree_view, "row-activated", G_CALLBACK(on_row_activated), NULL);
|
|
g_signal_connect(tree_view, "key-press-event", G_CALLBACK(on_key_press), NULL);
|
|
g_signal_connect(tree_view, "button-press-event", G_CALLBACK(on_button_press), NULL);
|
|
|
|
return tree_view;
|
|
}
|
|
|
|
static void on_window_destroy(GtkWidget *widget, gpointer data) {
|
|
close(inotify_fd);
|
|
g_free(base_dir);
|
|
g_free(current_dir);
|
|
gtk_main_quit();
|
|
}
|
|
|
|
static char *extract_first_directory(const char *path) {
|
|
if (!path) return NULL;
|
|
path += strlen(base_dir);
|
|
if (*path == '/') path++;
|
|
|
|
const char *end = strchr(path, '/');
|
|
if (!end) end = path + strlen(path);
|
|
|
|
size_t len = end - path;
|
|
char *dir = malloc(len + 1);
|
|
if (dir) {
|
|
strncpy(dir, path, len);
|
|
dir[len] = '\0';
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
static const char *get_language_from_path(const char *path) {
|
|
if (!path) {
|
|
printf("Error: Path is NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
char *language_dir = extract_first_directory(path);
|
|
if (!language_dir) {
|
|
printf("No language directory found or error in path processing.\n");
|
|
return NULL;
|
|
}
|
|
|
|
const char *language = NULL;
|
|
if (strcmp(language_dir, "C") == 0) {
|
|
language = "C";
|
|
} else if (strcmp(language_dir, "Python3") == 0) {
|
|
language = "Python3";
|
|
} else if (strcmp(language_dir, "Asm") == 0) {
|
|
language = "Assembly";
|
|
}
|
|
|
|
if (language) {
|
|
printf("Detected language: %s\n", language);
|
|
} else {
|
|
printf("No recognized language found in the directory name.\n");
|
|
}
|
|
|
|
free(language_dir);
|
|
return language;
|
|
}
|
|
|
|
static void compile_and_run(const char *path, const char *language) {
|
|
if (path == NULL) {
|
|
g_print("Error: Path is NULL\n");
|
|
return;
|
|
}
|
|
|
|
char *clean_path = strip_html_markup(path);
|
|
if (clean_path == NULL) {
|
|
g_print("Failed to clean path from HTML markup.\n");
|
|
return;
|
|
}
|
|
|
|
gchar *compile_cmd = NULL;
|
|
gchar *cmd = g_strdup_printf("Are you sure you want to compile and run this %s program?", language);
|
|
if (show_confirmation_dialog(cmd)) {
|
|
gchar *dir_path = g_path_get_dirname(clean_path);
|
|
if (dir_path == NULL) {
|
|
g_print("Error: Unable to determine directory from path: %s\n", clean_path);
|
|
g_free(cmd);
|
|
g_free(clean_path);
|
|
return;
|
|
}
|
|
|
|
if (strcasecmp(language, "C") == 0) {
|
|
compile_cmd = g_strdup_printf("clear && cd '%s' && make clean && make", dir_path);
|
|
} else if (strcasecmp(language, "Python3") == 0) {
|
|
compile_cmd = g_strdup_printf("clear && cd %s && python3 %s\n", dir_path, clean_path);
|
|
} else if (strcasecmp(language, "Assembly") == 0) {
|
|
compile_cmd = g_strdup_printf("clear && cd %s && nasm -f elf64 %s -o %s.o && ld %s.o -o %s && ./%s\n", dir_path, clean_path, clean_path, clean_path, clean_path, clean_path);
|
|
}
|
|
|
|
g_free(dir_path);
|
|
|
|
if (compile_cmd) {
|
|
g_print("Compile command: %s\n", compile_cmd);
|
|
vte_terminal_spawn_async(
|
|
VTE_TERMINAL(terminal),
|
|
VTE_PTY_DEFAULT,
|
|
NULL,
|
|
(char *[]){ "bash", "-c", compile_cmd, NULL },
|
|
NULL,
|
|
G_SPAWN_DEFAULT,
|
|
NULL, NULL, NULL, -1,
|
|
NULL, NULL, NULL);
|
|
g_free(compile_cmd);
|
|
} else {
|
|
g_print("Unsupported language: %s\n", language);
|
|
}
|
|
}
|
|
g_free(cmd);
|
|
g_free(clean_path);
|
|
}
|
|
|
|
static void make_file_executable(const char *path) {
|
|
if (path == NULL) return;
|
|
gchar *command = g_strdup_printf("chmod +x \"%s\"", path);
|
|
gchar *argv[] = {"bash", "-c", command, NULL};
|
|
VteTerminal *vte_terminal = VTE_TERMINAL(terminal);
|
|
|
|
vte_terminal_spawn_async(
|
|
vte_terminal,
|
|
VTE_PTY_DEFAULT,
|
|
current_dir,
|
|
argv,
|
|
NULL,
|
|
G_SPAWN_DEFAULT,
|
|
NULL, NULL, NULL, -1,
|
|
NULL, NULL, NULL);
|
|
|
|
g_free(command);
|
|
}
|
|
|
|
static void make_file_not_executable(const char *path) {
|
|
if (path == NULL) return;
|
|
gchar *command = g_strdup_printf("chmod -x \"%s\"", path);
|
|
gchar *argv[] = {"bash", "-c", command, NULL};
|
|
VteTerminal *vte_terminal = VTE_TERMINAL(terminal);
|
|
|
|
vte_terminal_spawn_async(
|
|
vte_terminal,
|
|
VTE_PTY_DEFAULT,
|
|
current_dir,
|
|
argv,
|
|
NULL#include <gtk/gtk.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <ifaddrs.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <stdio.h>
|
|
|
|
#define MAX_COMMAND_LENGTH 256
|
|
#define SSH_LIST_FILE_PATH "/home/klein/codeWS/C/ssh_gui/ssh_list.txt"
|
|
#define PRIVATE_KEY_PATH "/home/klein/.ssh/id_rsa"
|
|
|
|
// Function to get local IP address
|
|
void get_local_ip(char *ip_buffer) {
|
|
struct ifaddrs *ifaddr, *ifa;
|
|
int family, s;
|
|
char host[NI_MAXHOST];
|
|
|
|
if (getifaddrs(&ifaddr) == -1) {
|
|
perror("getifaddrs");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
|
if (ifa->ifa_addr == NULL)
|
|
continue;
|
|
|
|
family = ifa->ifa_addr->sa_family;
|
|
|
|
if (family == AF_INET) {
|
|
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in),
|
|
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
|
|
if (s != 0) {
|
|
printf("getnameinfo() failed: %s\n", gai_strerror(s));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (strcmp(ifa->ifa_name, "lo") != 0) {
|
|
strncpy(ip_buffer, host, NI_MAXHOST);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
freeifaddrs(ifaddr);
|
|
}
|
|
|
|
// Function to get public IP address
|
|
void get_public_ip(char *ip_buffer) {
|
|
FILE *fp;
|
|
char path[1035];
|
|
|
|
fp = popen("curl -s ifconfig.me", "r");
|
|
if (fp == NULL) {
|
|
printf("Failed to run command\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (fgets(path, sizeof(path) - 1, fp) != NULL) {
|
|
strncpy(ip_buffer, path, 1035);
|
|
}
|
|
|
|
pclose(fp);
|
|
}
|
|
|
|
// Function to get SSID using nmcli
|
|
void get_ssid(char *ssid_buffer) {
|
|
FILE *fp;
|
|
char path[1035];
|
|
|
|
fp = popen("nmcli -t -f active,ssid dev wifi | egrep '^yes' | cut -d: -f2", "r");
|
|
if (fp == NULL) {
|
|
printf("Failed to run command\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (fgets(path, sizeof(path) - 1, fp) != NULL) {
|
|
strncpy(ssid_buffer, path, 1035);
|
|
}
|
|
|
|
pclose(fp);
|
|
}
|
|
|
|
// Function to get SSH service status
|
|
void get_ssh_service_status(char *ssh_service_status) {
|
|
FILE *fp;
|
|
char path[1035];
|
|
|
|
fp = popen("systemctl is-active ssh", "r");
|
|
if (fp == NULL) {
|
|
printf("Failed to run command\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (fgets(path, sizeof(path) - 1, fp) != NULL) {
|
|
strncpy(ssh_service_status, path, 1035);
|
|
}
|
|
|
|
pclose(fp);
|
|
}
|
|
|
|
// Function to check if port 22 is open and display relevant SSH information
|
|
void get_port_22_status(char *port_22_status) {
|
|
FILE *fp;
|
|
char path[1035];
|
|
|
|
fp = popen("ss -tunlp | grep ':22 '", "r");
|
|
if (fp == NULL) {
|
|
printf("Failed to run command\n");
|
|
exit(1);
|
|
}
|
|
|
|
// Clear the buffer before appending
|
|
port_22_status[0] = '\0';
|
|
|
|
while (fgets(path, sizeof(path) - 1, fp) != NULL) {
|
|
// Append the relevant lines to the status buffer
|
|
strcat(port_22_status, path);
|
|
}
|
|
|
|
pclose(fp);
|
|
}
|
|
|
|
// Function to get active connections using ss
|
|
void get_active_connections(char *conn_buffer) {
|
|
FILE *fp;
|
|
char path[1035];
|
|
|
|
fp = popen("ss -tunap", "r");
|
|
if (fp == NULL) {
|
|
printf("Failed to run command\n");
|
|
exit(1);
|
|
}
|
|
|
|
while (fgets(path, sizeof(path) - 1, fp) != NULL) {
|
|
strcat(conn_buffer, path);
|
|
}
|
|
|
|
pclose(fp);
|
|
}
|
|
|
|
// Function to execute SSH command
|
|
void execute_ssh(GtkWidget *widget, gpointer data) {
|
|
const char *command = (const char *)data;
|
|
|
|
char full_command[MAX_COMMAND_LENGTH];
|
|
snprintf(full_command, sizeof(full_command), "ssh -i %s %s", PRIVATE_KEY_PATH, command);
|
|
|
|
printf("Executing command: %s\n", full_command); // Debugging output
|
|
|
|
pid_t pid = fork();
|
|
if (pid == 0) {
|
|
// Child process
|
|
printf("Attempting to execute alacritty with command: %s\n", full_command);
|
|
execlp("alacritty", "alacritty", "-e", "bash", "-c", full_command, NULL);
|
|
printf("alacritty failed, attempting to execute st with command: %s\n", full_command);
|
|
execlp("st", "st", "-e", "bash", "-c", full_command, NULL);
|
|
perror("execlp");
|
|
exit(EXIT_FAILURE);
|
|
} else if (pid < 0) {
|
|
perror("fork");
|
|
} else {
|
|
// Parent process
|
|
gtk_main_quit(); // Close the GTK main loop
|
|
}
|
|
}
|
|
|
|
// Function to create buttons for each SSH command
|
|
void create_buttons(GtkWidget *box) {
|
|
FILE *file = fopen(SSH_LIST_FILE_PATH, "r");
|
|
if (!file) {
|
|
perror("Error opening file");
|
|
printf("Path attempted: %s\n", SSH_LIST_FILE_PATH); // Debugging output
|
|
return;
|
|
}
|
|
|
|
char line[MAX_COMMAND_LENGTH];
|
|
GtkWidget *prev_button = NULL;
|
|
|
|
while (fgets(line, sizeof(line), file)) {
|
|
// Remove newline character from the line
|
|
line[strcspn(line, "\n")] = 0;
|
|
|
|
// Find the first space to locate the start of the SSH command
|
|
char *command = strchr(line, ' ');
|
|
if (command && strlen(command) > 1) {
|
|
command++; // Skip the space to get the actual command
|
|
GtkWidget *button = gtk_button_new_with_label(command);
|
|
gtk_widget_set_name(button, "ssh-button"); // Set name for CSS
|
|
gtk_widget_set_can_focus(button, TRUE);
|
|
g_signal_connect(button, "clicked", G_CALLBACK(execute_ssh), g_strdup(command));
|
|
gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
|
|
}
|
|
}
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
// Function to open SSH instructions in Neovim
|
|
void open_ssh_instructions(GtkWidget *widget, gpointer data) {
|
|
const char *file_path = (const char *)data;
|
|
|
|
pid_t pid = fork();
|
|
if (pid == 0) {
|
|
// Child process
|
|
execlp("alacritty", "alacritty", "-e", "nvim", file_path, NULL);
|
|
perror("execlp");
|
|
exit(EXIT_FAILURE);
|
|
} else if (pid < 0) {
|
|
perror("fork");
|
|
} else {
|
|
// Parent process
|
|
gtk_main_quit(); // Close the GTK main loop
|
|
}
|
|
}
|
|
|
|
// Key press event handler for buttons
|
|
gboolean on_button_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) {
|
|
GtkWidget *parent = gtk_widget_get_parent(widget);
|
|
GList *children = gtk_container_get_children(GTK_CONTAINER(parent));
|
|
GList *current = g_list_find(children, widget);
|
|
|
|
switch (event->keyval) {
|
|
case GDK_KEY_Up:
|
|
case GDK_KEY_Left:
|
|
if (current->prev) {
|
|
gtk_widget_grab_focus(GTK_WIDGET(current->prev->data));
|
|
}
|
|
break;
|
|
case GDK_KEY_Down:
|
|
case GDK_KEY_Right:
|
|
if (current->next) {
|
|
gtk_widget_grab_focus(GTK_WIDGET(current->next->data));
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Key press event handler for window
|
|
gboolean on_window_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) {
|
|
if (event->keyval == GDK_KEY_q || event->keyval == GDK_KEY_Escape || event->keyval == GDK_KEY_Delete) {
|
|
gtk_main_quit();
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void apply_css(GtkWidget *widget, GtkStyleProvider *provider) {
|
|
gtk_style_context_add_provider(gtk_widget_get_style_context(widget), provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
if (GTK_IS_CONTAINER(widget)) {
|
|
gtk_container_forall(GTK_CONTAINER(widget), (GtkCallback)apply_css, provider);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
gtk_init(&argc, &argv);
|
|
|
|
// Get system info
|
|
char local_ip[NI_MAXHOST] = {0};
|
|
char public_ip[1035] = {0};
|
|
char ssid[1035] = {0};
|
|
char username[256] = {0};
|
|
char ssh_service_status[1035] = {0};
|
|
char port_22_status[2048] = {0};
|
|
char active_connections[2048] = {0};
|
|
char info[512]; // Declare the info variable
|
|
|
|
get_local_ip(local_ip);
|
|
get_public_ip(public_ip);
|
|
get_ssid(ssid);
|
|
getlogin_r(username, sizeof(username));
|
|
get_ssh_service_status(ssh_service_status);
|
|
get_port_22_status(port_22_status);
|
|
get_active_connections(active_connections);
|
|
|
|
// Create CSS provider and load CSS
|
|
GtkCssProvider *cssProvider = gtk_css_provider_new();
|
|
gtk_css_provider_load_from_data(cssProvider,
|
|
"* { font-family: 'Arial'; }\n"
|
|
"#app-title { font-size: 24px; font-weight: bold; color: #00FF00; }\n"
|
|
"#info-label { font-size: 14px; color: #FFFFFF; }\n"
|
|
"#ssh-button { font-size: 14px; background-color: #1E1E1E; color: #000000; border-radius: 4px; padding: 5px 10px; }\n"
|
|
"#ssh-button:hover { background-color: #333333; }\n"
|
|
"window { background-color: #2E2E2E; }\n",
|
|
-1, NULL);
|
|
|
|
// Create GTK window
|
|
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_title(GTK_WINDOW(window), "SSH Commands");
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
|
|
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
|
g_signal_connect(window, "key-press-event", G_CALLBACK(on_window_key_press), NULL);
|
|
|
|
// Create a horizontal box to organize sections
|
|
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
|
|
gtk_container_add(GTK_CONTAINER(window), hbox);
|
|
|
|
// Create left box for SSH and firewall info
|
|
GtkWidget *left_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
|
gtk_box_pack_start(GTK_BOX(hbox), left_box, FALSE, FALSE, 10);
|
|
|
|
// Add SSH service status to left box
|
|
GtkWidget *label = gtk_label_new("SSH Service Status:");
|
|
gtk_widget_set_name(label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(left_box), label, FALSE, FALSE, 5);
|
|
|
|
char ssh_status_markup[512];
|
|
if (strncmp(ssh_service_status, "active", 6) == 0) {
|
|
snprintf(ssh_status_markup, sizeof(ssh_status_markup), "<span foreground='green'>Active ✓</span>");
|
|
} else {
|
|
snprintf(ssh_status_markup, sizeof(ssh_status_markup), "<span foreground='red'>Inactive ✗</span>");
|
|
}
|
|
|
|
GtkWidget *ssh_service_label = gtk_label_new(NULL);
|
|
gtk_label_set_markup(GTK_LABEL(ssh_service_label), ssh_status_markup);
|
|
gtk_widget_set_name(ssh_service_label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(left_box), ssh_service_label, FALSE, FALSE, 5);
|
|
|
|
label = gtk_label_new("Port 22 Status:");
|
|
gtk_widget_set_name(label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(left_box), label, FALSE, FALSE, 5);
|
|
|
|
GtkWidget *port_22_text_view = gtk_text_view_new();
|
|
gtk_text_view_set_editable(GTK_TEXT_VIEW(port_22_text_view), FALSE);
|
|
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(port_22_text_view), FALSE);
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(port_22_text_view));
|
|
gtk_text_buffer_set_text(buffer, port_22_status, -1);
|
|
gtk_box_pack_start(GTK_BOX(left_box), port_22_text_view, TRUE, TRUE, 5);
|
|
|
|
// Create center box for user info and buttons
|
|
GtkWidget *center_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
|
gtk_box_pack_start(GTK_BOX(hbox), center_box, TRUE, TRUE, 10);
|
|
|
|
// Add application title to center box
|
|
label = gtk_label_new("SSH Command Center");
|
|
gtk_widget_set_name(label, "app-title");
|
|
gtk_box_pack_start(GTK_BOX(center_box), label, FALSE, FALSE, 10);
|
|
|
|
// Add system info labels to center box
|
|
snprintf(info, sizeof(info), "Username: %s", username);
|
|
label = gtk_label_new(info);
|
|
gtk_widget_set_name(label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(center_box), label, FALSE, FALSE, 5);
|
|
|
|
snprintf(info, sizeof(info), "Local IP: %s", local_ip);
|
|
label = gtk_label_new(info);
|
|
gtk_widget_set_name(label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(center_box), label, FALSE, FALSE, 5);
|
|
|
|
snprintf(info, sizeof(info), "Public IP: %s", public_ip);
|
|
label = gtk_label_new(info);
|
|
gtk_widget_set_name(label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(center_box), label, FALSE, FALSE, 5);
|
|
|
|
snprintf(info, sizeof(info), "SSID: %s", ssid);
|
|
label = gtk_label_new(info);
|
|
gtk_widget_set_name(label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(center_box), label, FALSE, FALSE, 5);
|
|
|
|
// Create buttons for SSH commands
|
|
GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
|
create_buttons(button_box);
|
|
gtk_box_pack_start(GTK_BOX(center_box), button_box, FALSE, FALSE, 5);
|
|
|
|
// Add button to open SSH instructions in Neovim
|
|
GtkWidget *edit_button = gtk_button_new_with_label("Edit SSH Instructions");
|
|
gtk_widget_set_name(edit_button, "ssh-button");
|
|
gtk_widget_set_can_focus(edit_button, TRUE);
|
|
g_signal_connect(edit_button, "clicked", G_CALLBACK(open_ssh_instructions), (gpointer)SSH_LIST_FILE_PATH);
|
|
gtk_box_pack_start(GTK_BOX(center_box), edit_button, FALSE, FALSE, 5);
|
|
|
|
// Create right box for active connections
|
|
GtkWidget *right_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
|
gtk_box_pack_start(GTK_BOX(hbox), right_box, TRUE, TRUE, 10);
|
|
|
|
// Add active connections to right box
|
|
label = gtk_label_new("Active Connections:");
|
|
gtk_widget_set_name(label, "info-label");
|
|
gtk_box_pack_start(GTK_BOX(right_box), label, FALSE, FALSE, 5);
|
|
|
|
GtkWidget *conn_text_view = gtk_text_view_new();
|
|
gtk_text_view_set_editable(GTK_TEXT_VIEW(conn_text_view), FALSE);
|
|
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(conn_text_view), FALSE);
|
|
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(conn_text_view));
|
|
gtk_text_buffer_set_text(buffer, active_connections, -1);
|
|
gtk_box_pack_start(GTK_BOX(right_box), conn_text_view, TRUE, TRUE, 5);
|
|
|
|
// Apply CSS to all widgets
|
|
apply_css(window, GTK_STYLE_PROVIDER(cssProvider));
|
|
|
|
// Set up key press event handlers for buttons
|
|
GList *buttons = gtk_container_get_children(GTK_CONTAINER(button_box));
|
|
for (GList *iter = buttons; iter != NULL; iter = iter->next) {
|
|
g_signal_connect(iter->data, "key-press-event", G_CALLBACK(on_button_key_press), NULL);
|
|
}
|
|
g_signal_connect(edit_button, "key-press-event", G_CALLBACK(on_button_key_press), NULL);
|
|
|
|
gtk_widget_show_all(window);
|
|
gtk_main();
|
|
|
|
return 0;
|
|
},
|
|
G_SPAWN_DEFAULT,
|
|
NULL, NULL, NULL, -1,
|
|
NULL, NULL, NULL);
|
|
|
|
g_free(command);
|
|
}
|
|
|
|
static void create_new_file(const char *file_name) {
|
|
if (file_name == NULL) return;
|
|
|
|
gchar *full_path = g_strdup_printf("%s/%s", current_dir, file_name);
|
|
FILE *file = fopen(full_path, "w");
|
|
if (file) {
|
|
fclose(file);
|
|
} else {
|
|
g_print("Error creating file: %s\n", strerror(errno));
|
|
}
|
|
g_free(full_path);
|
|
}
|
|
|
|
static void show_new_file_dialog() {
|
|
GtkWidget *dialog, *content_area, *entry;
|
|
dialog = gtk_dialog_new_with_buttons("New File", GTK_WINDOW(window),
|
|
GTK_DIALOG_MODAL,
|
|
"_OK", GTK_RESPONSE_OK,
|
|
"_Cancel", GTK_RESPONSE_CANCEL,
|
|
NULL);
|
|
content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
|
entry = gtk_entry_new();
|
|
gtk_container_add(GTK_CONTAINER(content_area), entry);
|
|
gtk_widget_show_all(dialog);
|
|
|
|
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
if (response == GTK_RESPONSE_OK) {
|
|
const char *file_name = gtk_entry_get_text(GTK_ENTRY(entry));
|
|
create_new_file(file_name);
|
|
display_directory(current_dir);
|
|
}
|
|
gtk_widget_destroy(dialog);
|
|
}
|
|
|
|
static void show_new_directory_dialog() {
|
|
GtkWidget *dialog, *content_area, *entry;
|
|
dialog = gtk_dialog_new_with_buttons("New Directory", GTK_WINDOW(window),
|
|
GTK_DIALOG_MODAL,
|
|
"_OK", GTK_RESPONSE_OK,
|
|
"_Cancel", GTK_RESPONSE_CANCEL,
|
|
NULL);
|
|
content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
|
entry = gtk_entry_new();
|
|
gtk_container_add(GTK_CONTAINER(content_area), entry);
|
|
gtk_widget_show_all(dialog);
|
|
|
|
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
if (response == GTK_RESPONSE_OK) {
|
|
const char *dir_name = gtk_entry_get_text(GTK_ENTRY(entry));
|
|
create_new_directory(dir_name);
|
|
display_directory(current_dir);
|
|
}
|
|
gtk_widget_destroy(dialog);
|
|
}
|
|
|
|
static void create_new_directory(const char *dir_name) {
|
|
if (dir_name == NULL) return;
|
|
|
|
gchar *full_path = g_strdup_printf("%s/%s", current_dir, dir_name);
|
|
if (mkdir(full_path, 0755) == -1) {
|
|
g_print("Error creating directory: %s\n", strerror(errno));
|
|
} else {
|
|
display_directory(current_dir);
|
|
}
|
|
g_free(full_path);
|
|
}
|
|
|
|
static int get_directory_depth(const char *dir) {
|
|
const char *base = base_dir;
|
|
const char *tmp = dir;
|
|
|
|
while (*base != '\0' && *tmp == *base) {
|
|
base++;
|
|
tmp++;
|
|
}
|
|
|
|
int depth = 0;
|
|
while (*tmp != '\0') {
|
|
if (*tmp == '/') {
|
|
depth++;
|
|
}
|
|
tmp++;
|
|
}
|
|
|
|
return depth;
|
|
}
|
|
|
|
static gboolean recursive_delete(const char *path) {
|
|
DIR *d = opendir(path);
|
|
if (d == NULL) {
|
|
g_printerr("Failed to open directory for deletion: %s\n", strerror(errno));
|
|
return FALSE;
|
|
}
|
|
|
|
struct dirent *entry;
|
|
gboolean result = TRUE;
|
|
|
|
while ((entry = readdir(d)) != NULL) {
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, "codeWS") == 0) {
|
|
continue;
|
|
}
|
|
|
|
char *full_path = g_strdup_printf("%s/%s", path, entry->d_name);
|
|
if (entry->d_type == DT_DIR) {
|
|
if (!recursive_delete(full_path)) {
|
|
result = FALSE;
|
|
g_free(full_path);
|
|
break;
|
|
}
|
|
} else {
|
|
if (remove(full_path) != 0) {
|
|
g_printerr("Failed to delete file: %s\n", strerror(errno));
|
|
result = FALSE;
|
|
g_free(full_path);
|
|
break;
|
|
}
|
|
}
|
|
g_free(full_path);
|
|
}
|
|
|
|
closedir(d);
|
|
|
|
if (result) {
|
|
if (rmdir(path) != 0) {
|
|
g_printerr("Failed to delete directory: %s\n", strerror(errno));
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void show_deletion_dialog() {
|
|
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_OK_CANCEL,
|
|
"Are you sure you want to delete the selected item?");
|
|
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
gtk_widget_destroy(dialog);
|
|
|
|
if (response == GTK_RESPONSE_OK) {
|
|
delete_selected_item();
|
|
}
|
|
}
|
|
|
|
static void delete_selected_item() {
|
|
GtkTreeModel *model;
|
|
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
|
|
GtkTreeIter iter;
|
|
gchar *actual_name;
|
|
|
|
if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
|
|
gtk_tree_model_get(model, &iter, 1, &actual_name, -1);
|
|
char *full_path = g_strdup_printf("%s/%s", current_dir, actual_name);
|
|
|
|
if (g_file_test(full_path, G_FILE_TEST_IS_DIR)) {
|
|
if (!recursive_delete(full_path)) {
|
|
g_printerr("Failed to delete directory: %s\n", strerror(errno));
|
|
}
|
|
} else {
|
|
if (remove(full_path) != 0) {
|
|
g_printerr("Failed to delete file: %s\n", strerror(errno));
|
|
}
|
|
}
|
|
|
|
g_free(full_path);
|
|
g_free(actual_name);
|
|
display_directory(current_dir);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
gtk_init(&argc, &argv);
|
|
|
|
char *home_dir = getenv("HOME");
|
|
if (home_dir == NULL) {
|
|
fprintf(stderr, "Environment variable HOME is not set.\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
base_dir = g_strdup_printf("%s/codeWS", home_dir);
|
|
current_dir = strdup(base_dir);
|
|
|
|
inotify_fd = inotify_init();
|
|
if (inotify_fd < 0) {
|
|
perror("inotify_init");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
inotify_add_watch(inotify_fd, current_dir, IN_CREATE | IN_DELETE | IN_MODIFY);
|
|
|
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL);
|
|
gtk_window_set_title(GTK_WINDOW(window), "Code Workshop");
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 1200, 600);
|
|
|
|
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
|
|
gtk_container_add(GTK_CONTAINER(window), hbox);
|
|
|
|
GtkWidget *view = create_tree_view();
|
|
gtk_box_pack_start(GTK_BOX(hbox), view, FALSE, FALSE, 5);
|
|
|
|
terminal = VTE_TERMINAL(vte_terminal_new());
|
|
gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(terminal), TRUE, TRUE, 5);
|
|
|
|
display_directory(current_dir);
|
|
|
|
gtk_widget_show_all(window);
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|