//gui.c
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
/* main.c에 정의된 Todo 자료구조와 전역 데이터 */
#define MAX_TODO 100
typedef struct Todo {
char text[200];
int completed;
} Todo;
extern Todo todos[MAX_TODO];
extern int todoCount;
/* GUI 위젯 */
static GtkWidget* time_label;
static GtkWidget* cpu_label;
static GtkWidget* weather_label;
static GtkWidget* memo_view;
static GtkWidget* todo_entry;
static GtkWidget* todo_list;
static GtkWidget* status_label;
/* CPU 측정용 이전 값 */
static ULONGLONG previous_idle = 0;
static ULONGLONG previous_kernel = 0;
static ULONGLONG previous_user = 0;
static int cpu_initialized = 0;
/* 함수 선언 */
static void update_time(void);
static gboolean update_cpu(gpointer data);
static void update_todo_list(void);
static void show_status(const char* message);
static void on_add_todo(GtkButton* button, gpointer data);
static void on_complete_todo(GtkButton* button, gpointer data);
static void on_save_memo(GtkButton* button, gpointer data);
static void on_load_memo(GtkButton* button, gpointer data);
static gboolean update_time_timer(gpointer data)
{
(void)data;
update_time();
return G_SOURCE_CONTINUE;
}
/* ==============================
상태 메시지
============================== */
static void show_status(const char* message)
{
gtk_label_set_text(GTK_LABEL(status_label), message);
}
/* ==============================
현재 시간
============================== */
static void update_time(void)
{
time_t now = time(NULL);
struct tm* local = localtime(&now);
char buffer[128];
if (local == NULL) {
gtk_label_set_text(
GTK_LABEL(time_label),
"시간을 가져올 수 없습니다."
);
return;
}
snprintf(
buffer, sizeof(buffer),
"%04d-%02d-%02d %02d:%02d:%02d",
local->tm_year + 1900,
local->tm_mon + 1,
local->tm_mday,
local->tm_hour,
local->tm_min,
local->tm_sec
);
gtk_label_set_text(GTK_LABEL(time_label), buffer);
}
/* ==============================
CPU 사용률
1초마다 비차단 방식으로 갱신
============================== */
static gboolean update_cpu(gpointer data)
{
FILETIME idle_ft, kernel_ft, user_ft;
ULONGLONG idle, kernel, user;
ULONGLONG total_delta, idle_delta;
double usage;
char buffer[100];
(void)data;
if (!GetSystemTimes(&idle_ft, &kernel_ft, &user_ft)) {
gtk_label_set_text(
GTK_LABEL(cpu_label),
"CPU 정보를 가져올 수 없습니다."
);
return G_SOURCE_CONTINUE;
}
idle =
((ULONGLONG)idle_ft.dwHighDateTime << 32)
| idle_ft.dwLowDateTime;
kernel =
((ULONGLONG)kernel_ft.dwHighDateTime << 32)
| kernel_ft.dwLowDateTime;
user =
((ULONGLONG)user_ft.dwHighDateTime << 32)
| user_ft.dwLowDateTime;
if (!cpu_initialized) {
previous_idle = idle;
previous_kernel = kernel;
previous_user = user;
cpu_initialized = 1;
gtk_label_set_text(
GTK_LABEL(cpu_label),
"측정 중..."
);
return G_SOURCE_CONTINUE;
}
total_delta =
(kernel - previous_kernel)
+ (user - previous_user);
idle_delta = idle - previous_idle;
previous_idle = idle;
previous_kernel = kernel;
previous_user = user;
if (total_delta == 0) {
return G_SOURCE_CONTINUE;
}
usage = 100.0 *
(double)(total_delta - idle_delta)
/ (double)total_delta;
snprintf(
buffer, sizeof(buffer),
"%.1f%%", usage
);
gtk_label_set_text(GTK_LABEL(cpu_label), buffer);
return G_SOURCE_CONTINUE;
}
/* ==============================
Todo 목록 갱신
============================== */
static void update_todo_list(void)
{
GtkWidget* child;
/* 기존 목록 위젯 제거 */
while ((child = gtk_widget_get_first_child(todo_list)) != NULL) {
gtk_list_box_remove(GTK_LIST_BOX(todo_list), child);
}
for (int i = 0; i < todoCount; i++) {
char text[300];
snprintf(
text, sizeof(text),
"%s %s",
todos[i].completed ? "[완료]" : "[미완료]",
todos[i].text
);
GtkWidget* label = gtk_label_new(text);
gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
gtk_widget_set_margin_start(label, 8);
gtk_widget_set_margin_end(label, 8);
gtk_widget_set_margin_top(label, 6);
gtk_widget_set_margin_bottom(label, 6);
gtk_list_box_append(GTK_LIST_BOX(todo_list), label);
}
}
/* ==============================
Todo 추가
============================== */
static void on_add_todo(GtkButton* button, gpointer data)
{
const char* text;
(void)button;
(void)data;
text = gtk_editable_get_text(
GTK_EDITABLE(todo_entry)
);
if (text[0] == '\0') {
show_status("할 일을 입력해 주세요.");
return;
}
if (todoCount >= MAX_TODO) {
show_status("Todo 목록이 가득 찼습니다.");
return;
}
snprintf(
todos[todoCount].text,
sizeof(todos[todoCount].text),
"%s", text
);
todos[todoCount].completed = 0;
todoCount++;
gtk_editable_set_text(
GTK_EDITABLE(todo_entry), ""
);
update_todo_list();
show_status("Todo를 추가했습니다.");
}
/* ==============================
선택한 Todo 완료
============================== */
static void on_complete_todo(
GtkButton* button,
gpointer data
)
{
GtkListBoxRow* row;
int index;
(void)button;
(void)data;
row = gtk_list_box_get_selected_row(
GTK_LIST_BOX(todo_list)
);
if (row == NULL) {
show_status("완료할 Todo를 먼저 선택하세요.");
return;
}
index = gtk_list_box_row_get_index(row);
if (index < 0 || index >= todoCount) {
show_status("잘못된 Todo입니다.");
return;
}
todos[index].completed = 1;
update_todo_list();
show_status("Todo를 완료했습니다.");
}
/* ==============================
메모 저장
============================== */
static void on_save_memo(
GtkButton* button,
gpointer data
)
{
GtkTextBuffer* buffer;
GtkTextIter start, end;
char* text;
FILE* fp;
(void)button;
(void)data;
buffer = gtk_text_view_get_buffer(
GTK_TEXT_VIEW(memo_view)
);
gtk_text_buffer_get_bounds(buffer, &start, &end);
text = gtk_text_buffer_get_text(
buffer, &start, &end, FALSE
);
fp = fopen("memo.txt", "w");
if (fp == NULL) {
show_status("메모 파일을 저장할 수 없습니다.");
g_free(text);
return;
}
if (fputs(text, fp) == EOF) {
fclose(fp);
show_status("메모 저장에 실패했습니다.");
g_free(text);
return;
}
fclose(fp);
g_free(text);
show_status("메모를 저장했습니다.");
}
/* ==============================
메모 읽기
============================== */
static void on_load_memo(
GtkButton* button,
gpointer data
)
{
FILE* fp;
char buffer[1024];
GtkTextBuffer* text_buffer;
(void)button;
(void)data;
fp = fopen("memo.txt", "r");
if (fp == NULL) {
show_status("저장된 메모가 없습니다.");
return;
}
text_buffer = gtk_text_view_get_buffer(
GTK_TEXT_VIEW(memo_view)
);
gtk_text_buffer_set_text(text_buffer, "", 0);
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
GtkTextIter end;
gtk_text_buffer_get_end_iter(text_buffer, &end);
gtk_text_buffer_insert(
text_buffer, &end,
buffer, -1
);
}
fclose(fp);
show_status("메모를 불러왔습니다.");
}
/* ==============================
GTK 위젯 생성 보조 함수
============================== */
static GtkWidget* create_section_title(const char* text)
{
GtkWidget* label = gtk_label_new(text);
gtk_label_set_xalign(GTK_LABEL(label), 0.0f);
gtk_widget_add_css_class(label, "heading");
return label;
}
/* ==============================
GTK 화면 구성
============================== */
static void activate(
GtkApplication* app,
gpointer user_data
)
{
GtkWidget* window;
GtkWidget* root;
GtkWidget* grid;
GtkWidget* time_box;
GtkWidget* system_box;
GtkWidget* todo_box;
GtkWidget* memo_box;
GtkWidget* todo_buttons;
GtkWidget* memo_buttons;
GtkWidget* scrolled;
GtkWidget* button;
(void)user_data;
window = gtk_application_window_new(app);
gtk_window_set_title(
GTK_WINDOW(window), "PC Dashboard"
);
gtk_window_set_default_size(
GTK_WINDOW(window), 850, 600
);
root = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
gtk_widget_set_margin_top(root, 18);
gtk_widget_set_margin_bottom(root, 18);
gtk_widget_set_margin_start(root, 18);
gtk_widget_set_margin_end(root, 18);
gtk_window_set_child(GTK_WINDOW(window), root);
/* 상단 제목 */
GtkWidget* title = gtk_label_new("PC Dashboard");
gtk_widget_add_css_class(title, "title-1");
gtk_label_set_xalign(GTK_LABEL(title), 0.0f);
gtk_box_append(GTK_BOX(root), title);
/* 시간 및 시스템 정보 */
grid = gtk_grid_new();
gtk_grid_set_row_spacing(GTK_GRID(grid), 8);
gtk_grid_set_column_spacing(GTK_GRID(grid), 24);
gtk_box_append(GTK_BOX(root), grid);
time_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
system_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
gtk_grid_attach(GTK_GRID(grid), time_box, 0, 0, 1, 1);
gtk_grid_attach(GTK_GRID(grid), system_box, 1, 0, 1, 1);
gtk_box_append(
GTK_BOX(time_box),
create_section_title("현재 시간")
);
time_label = gtk_label_new("");
gtk_label_set_xalign(GTK_LABEL(time_label), 0.0f);
gtk_box_append(GTK_BOX(time_box), time_label);
gtk_box_append(
GTK_BOX(system_box),
create_section_title("CPU 사용률")
);
cpu_label = gtk_label_new("측정 중...");
gtk_label_set_xalign(GTK_LABEL(cpu_label), 0.0f);
gtk_box_append(GTK_BOX(system_box), cpu_label);
/* 날씨 */
weather_label = gtk_label_new(
"서울 · 맑음 · 22°C · 습도 45%\n"
"테스트 데이터"
);
gtk_label_set_xalign(GTK_LABEL(weather_label), 0.0f);
gtk_box_append(
GTK_BOX(system_box), weather_label
);
/* 콘텐츠 영역 */
grid = gtk_grid_new();
gtk_grid_set_row_spacing(GTK_GRID(grid), 16);
gtk_grid_set_column_spacing(GTK_GRID(grid), 20);
gtk_widget_set_vexpand(grid, TRUE);
gtk_box_append(GTK_BOX(root), grid);
/* Todo 영역 */
todo_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
gtk_widget_set_hexpand(todo_box, TRUE);
gtk_widget_set_vexpand(todo_box, TRUE);
gtk_grid_attach(GTK_GRID(grid), todo_box, 0, 0, 1, 1);
gtk_box_append(
GTK_BOX(todo_box),
create_section_title("Todo List")
);
todo_entry = gtk_entry_new();
gtk_entry_set_placeholder_text(
GTK_ENTRY(todo_entry), "할 일을 입력하세요"
);
gtk_box_append(GTK_BOX(todo_box), todo_entry);
button = gtk_button_new_with_label("Todo 추가");
g_signal_connect(
button, "clicked",
G_CALLBACK(on_add_todo), NULL
);
gtk_box_append(GTK_BOX(todo_box), button);
scrolled = gtk_scrolled_window_new();
gtk_widget_set_vexpand(scrolled, TRUE);
todo_list = gtk_list_box_new();
gtk_list_box_set_selection_mode(
GTK_LIST_BOX(todo_list),
GTK_SELECTION_SINGLE
);
gtk_scrolled_window_set_child(
GTK_SCROLLED_WINDOW(scrolled), todo_list
);
gtk_box_append(GTK_BOX(todo_box), scrolled);
button = gtk_button_new_with_label("선택한 Todo 완료");
g_signal_connect(
button, "clicked",
G_CALLBACK(on_complete_todo), NULL
);
gtk_box_append(GTK_BOX(todo_box), button);
/* 메모 영역 */
memo_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
gtk_widget_set_hexpand(memo_box, TRUE);
gtk_widget_set_vexpand(memo_box, TRUE);
gtk_grid_attach(GTK_GRID(grid), memo_box, 1, 0, 1, 1);
gtk_box_append(
GTK_BOX(memo_box),
create_section_title("메모장")
);
scrolled = gtk_scrolled_window_new();
gtk_widget_set_vexpand(scrolled, TRUE);
memo_view = gtk_text_view_new();
gtk_text_view_set_wrap_mode(
GTK_TEXT_VIEW(memo_view),
GTK_WRAP_WORD_CHAR
);
gtk_scrolled_window_set_child(
GTK_SCROLLED_WINDOW(scrolled), memo_view
);
gtk_box_append(GTK_BOX(memo_box), scrolled);
memo_buttons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
gtk_box_append(GTK_BOX(memo_box), memo_buttons);
button = gtk_button_new_with_label("메모 저장");
g_signal_connect(
button, "clicked",
G_CALLBACK(on_save_memo), NULL
);
gtk_box_append(GTK_BOX(memo_buttons), button);
button = gtk_button_new_with_label("메모 불러오기");
g_signal_connect(
button, "clicked",
G_CALLBACK(on_load_memo), NULL
);
gtk_box_append(GTK_BOX(memo_buttons), button);
/* 상태 표시 */
status_label = gtk_label_new("준비되었습니다.");
gtk_label_set_xalign(GTK_LABEL(status_label), 0.0f);
gtk_box_append(GTK_BOX(root), status_label);
/* 초기 화면 갱신 및 주기적 갱신 */
update_time();
update_todo_list();
/*g_timeout_add_seconds(1, (GSourceFunc)update_time, NULL);
g_timeout_add_seconds(1, update_cpu, NULL);*/
g_timeout_add_seconds(1, update_time_timer, NULL);
g_timeout_add_seconds(1, update_cpu, NULL);
gtk_window_present(GTK_WINDOW(window));
}
/* ==============================
프로그램 시작점
============================== */
int main(int argc, char** argv)
{
GtkApplication* app;
int status;
app = gtk_application_new(
"com.example.pcdashboard",
G_APPLICATION_DEFAULT_FLAGS
);
g_signal_connect(
app, "activate",
G_CALLBACK(activate), NULL
);
status = g_application_run(
G_APPLICATION(app), argc, argv
);
g_object_unref(app);
return status;
}
To embed this project on your website, copy the following code and paste it into your website's HTML: