From c76a620012fa4bbf0e4f1aa606b58b23dbc8e76b Mon Sep 17 00:00:00 2001 From: retoor Date: Sun, 28 Dec 2025 05:01:46 +0100 Subject: [PATCH] style: remove comments from header files --- include/ai.h | 15 +---- include/applauncher.h | 24 +++----- include/atoms.h | 18 +----- include/client.h | 13 +--- include/config.h | 13 +--- include/decorations.h | 9 +-- include/dwn.h | 62 ++++++------------- include/keys.h | 11 +--- include/layout.h | 5 +- include/news.h | 38 +++++------- include/notifications.h | 27 +++------ include/panel.h | 13 +--- include/systray.h | 38 ++++-------- include/util.h | 18 ++---- include/workspace.h | 11 +--- site/ai-features.html | 38 ------------ site/architecture.html | 44 -------------- site/configuration.html | 39 ------------ site/documentation.html | 28 --------- site/features.html | 29 --------- site/index.html | 74 +---------------------- site/installation.html | 51 ---------------- site/shortcuts.html | 21 ------- src/ai.c | 78 +----------------------- src/applauncher.c | 57 +----------------- src/atoms.c | 25 -------- src/client.c | 97 ++---------------------------- src/config.c | 23 ------- src/decorations.c | 42 +++---------- src/keys.c | 67 ++------------------- src/layout.c | 18 ------ src/main.c | 129 +++------------------------------------- src/news.c | 29 --------- src/notifications.c | 98 ++---------------------------- src/panel.c | 103 ++++++-------------------------- src/systray.c | 102 +++++-------------------------- src/util.c | 99 +++++++----------------------- src/workspace.c | 42 ------------- 38 files changed, 167 insertions(+), 1481 deletions(-) diff --git a/include/ai.h b/include/ai.h index 109c5c9..fe9ff7e 100644 --- a/include/ai.h +++ b/include/ai.h @@ -10,7 +10,6 @@ #include "dwn.h" #include -/* AI request states */ typedef enum { AI_STATE_IDLE, AI_STATE_PENDING, @@ -18,7 +17,6 @@ typedef enum { AI_STATE_ERROR } AIState; -/* AI request structure */ typedef struct AIRequest { char *prompt; char *response; @@ -28,7 +26,6 @@ typedef struct AIRequest { struct AIRequest *next; } AIRequest; -/* AI context for window analysis */ typedef struct { char focused_window[256]; char focused_class[64]; @@ -36,40 +33,32 @@ typedef struct { int window_count; } AIContext; -/* Initialization */ bool ai_init(void); void ai_cleanup(void); bool ai_is_available(void); -/* API calls */ AIRequest *ai_send_request(const char *prompt, void (*callback)(AIRequest *)); void ai_cancel_request(AIRequest *req); void ai_process_pending(void); -/* Context analysis */ void ai_update_context(void); const char *ai_analyze_task(void); const char *ai_suggest_window(void); const char *ai_suggest_app(void); -/* Command palette */ void ai_show_command_palette(void); void ai_execute_command(const char *command); -/* Smart features */ void ai_auto_organize_workspace(void); void ai_suggest_layout(void); void ai_analyze_workflow(void); -/* Notification intelligence */ bool ai_should_show_notification(const char *app, const char *summary); int ai_notification_priority(const char *app, const char *summary); -/* Performance monitoring */ void ai_monitor_performance(void); const char *ai_performance_suggestion(void); -/* Exa semantic search */ typedef struct { char title[256]; char url[512]; @@ -81,7 +70,7 @@ typedef struct ExaRequest { char *query; ExaSearchResult results[10]; int result_count; - int state; /* Uses AIState enum */ + int state; void (*callback)(struct ExaRequest *req); void *user_data; struct ExaRequest *next; @@ -92,4 +81,4 @@ ExaRequest *exa_search(const char *query, void (*callback)(ExaRequest *)); void exa_process_pending(void); void exa_show_app_launcher(void); -#endif /* DWN_AI_H */ +#endif diff --git a/include/applauncher.h b/include/applauncher.h index 1431845..9449ace 100644 --- a/include/applauncher.h +++ b/include/applauncher.h @@ -9,41 +9,33 @@ #include -/* Maximum applications to track */ #define MAX_APPS 512 #define MAX_RECENT_APPS 10 -/* Application entry from .desktop file */ typedef struct { - char name[128]; /* Display name */ - char exec[512]; /* Command to execute */ - char icon[128]; /* Icon name (unused for now) */ - char desktop_id[256]; /* Desktop file basename for tracking */ - bool terminal; /* Run in terminal */ - bool hidden; /* Should be hidden */ + char name[128]; + char exec[512]; + char icon[128]; + char desktop_id[256]; + bool terminal; + bool hidden; } AppEntry; -/* Application launcher state */ typedef struct { AppEntry apps[MAX_APPS]; int app_count; - char recent[MAX_RECENT_APPS][256]; /* Desktop IDs of recent apps */ + char recent[MAX_RECENT_APPS][256]; int recent_count; } AppLauncherState; -/* Initialize the app launcher (scans .desktop files) */ void applauncher_init(void); -/* Cleanup resources */ void applauncher_cleanup(void); -/* Rescan .desktop files */ void applauncher_refresh(void); -/* Show the application launcher (dmenu-based) */ void applauncher_show(void); -/* Launch an application by desktop_id */ void applauncher_launch(const char *desktop_id); -#endif /* DWN_APPLAUNCHER_H */ +#endif diff --git a/include/atoms.h b/include/atoms.h index eae169b..e05dd44 100644 --- a/include/atoms.h +++ b/include/atoms.h @@ -10,9 +10,7 @@ #include #include -/* EWMH (Extended Window Manager Hints) atoms */ typedef struct { - /* Root window properties */ Atom NET_SUPPORTED; Atom NET_SUPPORTING_WM_CHECK; Atom NET_CLIENT_LIST; @@ -25,7 +23,6 @@ typedef struct { Atom NET_ACTIVE_WINDOW; Atom NET_WORKAREA; - /* Client window properties */ Atom NET_WM_NAME; Atom NET_WM_VISIBLE_NAME; Atom NET_WM_DESKTOP; @@ -36,7 +33,6 @@ typedef struct { Atom NET_WM_STRUT_PARTIAL; Atom NET_WM_PID; - /* Window types */ Atom NET_WM_WINDOW_TYPE_DESKTOP; Atom NET_WM_WINDOW_TYPE_DOCK; Atom NET_WM_WINDOW_TYPE_TOOLBAR; @@ -47,7 +43,6 @@ typedef struct { Atom NET_WM_WINDOW_TYPE_NORMAL; Atom NET_WM_WINDOW_TYPE_NOTIFICATION; - /* Window states */ Atom NET_WM_STATE_MODAL; Atom NET_WM_STATE_STICKY; Atom NET_WM_STATE_MAXIMIZED_VERT; @@ -62,7 +57,6 @@ typedef struct { Atom NET_WM_STATE_DEMANDS_ATTENTION; Atom NET_WM_STATE_FOCUSED; - /* Actions */ Atom NET_WM_ACTION_MOVE; Atom NET_WM_ACTION_RESIZE; Atom NET_WM_ACTION_MINIMIZE; @@ -74,14 +68,12 @@ typedef struct { Atom NET_WM_ACTION_CHANGE_DESKTOP; Atom NET_WM_ACTION_CLOSE; - /* Client messages */ Atom NET_CLOSE_WINDOW; Atom NET_MOVERESIZE_WINDOW; Atom NET_WM_MOVERESIZE; Atom NET_REQUEST_FRAME_EXTENTS; Atom NET_FRAME_EXTENTS; - /* System tray */ Atom NET_SYSTEM_TRAY_OPCODE; Atom NET_SYSTEM_TRAY_S0; Atom MANAGER; @@ -89,7 +81,6 @@ typedef struct { Atom XEMBED_INFO; } EWMHAtoms; -/* ICCCM (Inter-Client Communication Conventions Manual) atoms */ typedef struct { Atom WM_PROTOCOLS; Atom WM_DELETE_WINDOW; @@ -103,7 +94,6 @@ typedef struct { Atom WM_WINDOW_ROLE; } ICCCMAtoms; -/* Other useful atoms */ typedef struct { Atom UTF8_STRING; Atom COMPOUND_TEXT; @@ -113,15 +103,12 @@ typedef struct { Atom DWN_RESTART; } MiscAtoms; -/* Global atom containers */ extern EWMHAtoms ewmh; extern ICCCMAtoms icccm; extern MiscAtoms misc_atoms; -/* Initialization */ void atoms_init(Display *display); -/* EWMH root window setup */ void atoms_setup_ewmh(void); void atoms_update_client_list(void); void atoms_update_desktop_names(void); @@ -129,7 +116,6 @@ void atoms_set_current_desktop(int desktop); void atoms_set_active_window(Window window); void atoms_set_number_of_desktops(int count); -/* Window property helpers */ bool atoms_get_window_type(Window window, Atom *type); bool atoms_get_window_state(Window window, Atom **states, int *count); bool atoms_set_window_state(Window window, Atom *states, int count); @@ -138,12 +124,10 @@ bool atoms_set_window_desktop(Window window, int desktop); char *atoms_get_window_name(Window window); bool atoms_get_wm_class(Window window, char *class_name, char *instance_name, size_t len); -/* Protocol helpers */ bool atoms_window_supports_protocol(Window window, Atom protocol); void atoms_send_protocol(Window window, Atom protocol, Time timestamp); -/* Client message sending */ void atoms_send_client_message(Window window, Atom message_type, long data0, long data1, long data2, long data3, long data4); -#endif /* DWN_ATOMS_H */ +#endif diff --git a/include/client.h b/include/client.h index 3954db5..6fa1a81 100644 --- a/include/client.h +++ b/include/client.h @@ -10,17 +10,14 @@ #include "dwn.h" #include -/* Client creation and destruction */ Client *client_create(Window window); void client_destroy(Client *client); -/* Client management */ Client *client_manage(Window window); void client_unmanage(Client *client); Client *client_find_by_window(Window window); Client *client_find_by_frame(Window frame); -/* Client state */ void client_focus(Client *client); void client_unfocus(Client *client); void client_raise(Client *client); @@ -28,14 +25,12 @@ void client_lower(Client *client); void client_minimize(Client *client); void client_restore(Client *client); -/* Client geometry */ void client_move(Client *client, int x, int y); void client_resize(Client *client, int width, int height); void client_move_resize(Client *client, int x, int y, int width, int height); void client_configure(Client *client); void client_apply_size_hints(Client *client, int *width, int *height); -/* Client properties */ void client_update_title(Client *client); void client_update_class(Client *client); void client_set_fullscreen(Client *client, bool fullscreen); @@ -43,7 +38,6 @@ void client_toggle_fullscreen(Client *client); void client_set_floating(Client *client, bool floating); void client_toggle_floating(Client *client); -/* Window type checking */ bool client_is_floating(Client *client); bool client_is_fullscreen(Client *client); bool client_is_minimized(Client *client); @@ -51,31 +45,26 @@ bool client_is_dialog(Window window); bool client_is_dock(Window window); bool client_is_desktop(Window window); -/* Frame management */ void client_create_frame(Client *client); void client_destroy_frame(Client *client); void client_reparent_to_frame(Client *client); void client_reparent_from_frame(Client *client); -/* Visibility */ void client_show(Client *client); void client_hide(Client *client); bool client_is_visible(Client *client); -/* Close handling */ void client_close(Client *client); void client_kill(Client *client); -/* List operations */ void client_add_to_list(Client *client); void client_remove_from_list(Client *client); int client_count(void); int client_count_on_workspace(int workspace); -/* Iteration */ Client *client_get_next(Client *client); Client *client_get_prev(Client *client); Client *client_get_first(void); Client *client_get_last(void); -#endif /* DWN_CLIENT_H */ +#endif diff --git a/include/config.h b/include/config.h index 3207293..60a34f8 100644 --- a/include/config.h +++ b/include/config.h @@ -10,7 +10,6 @@ #include "dwn.h" #include -/* Color configuration */ typedef struct { unsigned long panel_bg; unsigned long panel_fg; @@ -27,16 +26,13 @@ typedef struct { unsigned long notification_fg; } ColorScheme; -/* Configuration structure */ struct Config { - /* General */ char terminal[128]; char launcher[128]; char file_manager[128]; FocusMode focus_mode; bool show_decorations; - /* Appearance */ int border_width; int title_height; int panel_height; @@ -44,34 +40,28 @@ struct Config { char font_name[128]; ColorScheme colors; - /* Layout */ float default_master_ratio; int default_master_count; LayoutType default_layout; - /* Panels */ bool top_panel_enabled; bool bottom_panel_enabled; - /* AI */ char openrouter_api_key[256]; char exa_api_key[256]; char ai_model[64]; bool ai_enabled; - /* Paths */ char config_path[512]; char log_path[512]; }; -/* Configuration functions */ Config *config_create(void); void config_destroy(Config *cfg); bool config_load(Config *cfg, const char *path); bool config_reload(Config *cfg); void config_set_defaults(Config *cfg); -/* Getters for commonly used values */ const char *config_get_terminal(void); const char *config_get_launcher(void); int config_get_border_width(void); @@ -80,9 +70,8 @@ int config_get_panel_height(void); int config_get_gap(void); const ColorScheme *config_get_colors(void); -/* INI parsing helpers */ typedef void (*ConfigCallback)(const char *section, const char *key, const char *value, void *user_data); bool config_parse_ini(const char *path, ConfigCallback callback, void *user_data); -#endif /* DWN_CONFIG_H */ +#endif diff --git a/include/decorations.h b/include/decorations.h index 904ab68..d67f185 100644 --- a/include/decorations.h +++ b/include/decorations.h @@ -10,7 +10,6 @@ #include "dwn.h" #include -/* Button types */ typedef enum { BUTTON_CLOSE, BUTTON_MAXIMIZE, @@ -18,32 +17,26 @@ typedef enum { BUTTON_COUNT } ButtonType; -/* Button areas for hit testing */ typedef struct { int x, y; int width, height; } ButtonArea; -/* Decoration initialization */ void decorations_init(void); void decorations_cleanup(void); -/* Rendering */ void decorations_render(Client *client, bool focused); void decorations_render_title_bar(Client *client, bool focused); void decorations_render_buttons(Client *client, bool focused); void decorations_render_border(Client *client, bool focused); -/* Hit testing */ ButtonType decorations_hit_test_button(Client *client, int x, int y); bool decorations_hit_test_title_bar(Client *client, int x, int y); bool decorations_hit_test_resize_area(Client *client, int x, int y, int *direction); -/* Button actions */ void decorations_button_press(Client *client, ButtonType button); -/* Text rendering */ void decorations_draw_text(Window window, GC gc, int x, int y, const char *text, unsigned long color); -#endif /* DWN_DECORATIONS_H */ +#endif diff --git a/include/dwn.h b/include/dwn.h index 9305eee..acddbda 100644 --- a/include/dwn.h +++ b/include/dwn.h @@ -16,24 +16,20 @@ #include #include -/* Version */ #define DWN_VERSION "1.0.0" #define DWN_NAME "DWN" -/* Limits */ #define MAX_CLIENTS 256 #define MAX_WORKSPACES 9 #define MAX_MONITORS 8 #define MAX_NOTIFICATIONS 32 #define MAX_KEYBINDINGS 64 -/* Default dimensions */ #define DEFAULT_BORDER_WIDTH 2 #define DEFAULT_TITLE_HEIGHT 24 #define DEFAULT_PANEL_HEIGHT 28 #define DEFAULT_GAP 4 -/* Common error/status codes */ typedef enum { DWN_OK = 0, DWN_ERROR = -1, @@ -44,7 +40,6 @@ typedef enum { DWN_ERROR_IO = -6 } DwnStatus; -/* Layout types */ typedef enum { LAYOUT_TILING, LAYOUT_FLOATING, @@ -52,13 +47,11 @@ typedef enum { LAYOUT_COUNT } LayoutType; -/* Focus modes */ typedef enum { FOCUS_CLICK, FOCUS_FOLLOW } FocusMode; -/* Client state flags */ typedef enum { CLIENT_NORMAL = 0, CLIENT_FLOATING = (1 << 0), @@ -68,31 +61,28 @@ typedef enum { CLIENT_STICKY = (1 << 4) } ClientFlags; -/* Forward declarations */ typedef struct Client Client; typedef struct Workspace Workspace; typedef struct Monitor Monitor; typedef struct Panel Panel; typedef struct Config Config; -/* Client structure - represents a managed window */ struct Client { - Window window; /* Application window */ - Window frame; /* Frame window (decoration) */ - int x, y; /* Position */ - int width, height; /* Size */ - int old_x, old_y; /* Previous position (for floating restore) */ + Window window; + Window frame; + int x, y; + int width, height; + int old_x, old_y; int old_width, old_height; int border_width; - uint32_t flags; /* ClientFlags bitmask */ - unsigned int workspace; /* Current workspace (0-8) */ - char title[256]; /* Window title */ - char class[64]; /* Window class */ - Client *next; /* Linked list */ + uint32_t flags; + unsigned int workspace; + char title[256]; + char class[64]; + Client *next; Client *prev; }; -/* Monitor structure - represents a physical display */ struct Monitor { int x, y; int width, height; @@ -100,17 +90,15 @@ struct Monitor { bool primary; }; -/* Workspace structure */ struct Workspace { - Client *clients; /* Head of client list */ - Client *focused; /* Currently focused client */ - LayoutType layout; /* Current layout */ - float master_ratio; /* Ratio for master area in tiling */ - int master_count; /* Number of windows in master area */ - char name[32]; /* Workspace name */ + Client *clients; + Client *focused; + LayoutType layout; + float master_ratio; + int master_count; + char name[32]; }; -/* Panel widget types */ typedef enum { WIDGET_WORKSPACES, WIDGET_TASKBAR, @@ -120,7 +108,6 @@ typedef enum { WIDGET_SEPARATOR } WidgetType; -/* Global state - singleton pattern */ typedef struct { Display *display; int screen; @@ -128,36 +115,28 @@ typedef struct { int screen_width; int screen_height; - /* Monitors */ Monitor monitors[MAX_MONITORS]; int monitor_count; - /* Workspaces */ Workspace workspaces[MAX_WORKSPACES]; int current_workspace; - /* Clients */ - Client *client_list; /* All clients */ + Client *client_list; int client_count; - /* Panels */ Panel *top_panel; Panel *bottom_panel; - /* Configuration */ Config *config; - /* State */ bool running; bool ai_enabled; - /* Graphics contexts */ GC gc; XFontStruct *font; - XftFont *xft_font; /* Xft font for UTF-8 rendering */ + XftFont *xft_font; Colormap colormap; - /* Drag state */ Client *drag_client; int drag_start_x, drag_start_y; int drag_orig_x, drag_orig_y; @@ -165,16 +144,13 @@ typedef struct { bool resizing; } DWNState; -/* Global state accessor */ extern DWNState *dwn; -/* Core functions */ int dwn_init(void); void dwn_cleanup(void); void dwn_run(void); void dwn_quit(void); -/* Event handlers */ void dwn_handle_event(XEvent *ev); -#endif /* DWN_H */ +#endif diff --git a/include/keys.h b/include/keys.h index da05a54..abcfa67 100644 --- a/include/keys.h +++ b/include/keys.h @@ -11,16 +11,13 @@ #include #include -/* Modifier masks */ #define MOD_ALT Mod1Mask #define MOD_CTRL ControlMask #define MOD_SHIFT ShiftMask #define MOD_SUPER Mod4Mask -/* Key binding callback type */ typedef void (*KeyCallback)(void); -/* Key binding structure */ typedef struct { unsigned int modifiers; KeySym keysym; @@ -28,26 +25,21 @@ typedef struct { const char *description; } KeyBinding; -/* Initialization */ void keys_init(void); void keys_cleanup(void); void keys_grab_all(void); void keys_ungrab_all(void); -/* Key binding registration */ void keys_bind(unsigned int modifiers, KeySym keysym, KeyCallback callback, const char *description); void keys_unbind(unsigned int modifiers, KeySym keysym); void keys_clear_all(void); -/* Key event handling */ void keys_handle_press(XKeyEvent *ev); void keys_handle_release(XKeyEvent *ev); -/* Default key bindings */ void keys_setup_defaults(void); -/* Key binding callbacks */ void key_spawn_terminal(void); void key_spawn_launcher(void); void key_spawn_file_manager(void); @@ -89,11 +81,10 @@ void key_ai_command(void); void key_show_shortcuts(void); void key_start_tutorial(void); -/* Tutorial system */ void tutorial_start(void); void tutorial_stop(void); void tutorial_next_step(void); void tutorial_check_key(unsigned int modifiers, KeySym keysym); bool tutorial_is_active(void); -#endif /* DWN_KEYS_H */ +#endif diff --git a/include/layout.h b/include/layout.h index 579de8f..ca807c1 100644 --- a/include/layout.h +++ b/include/layout.h @@ -9,18 +9,15 @@ #include "dwn.h" -/* Layout arrangement */ void layout_arrange(int workspace); void layout_arrange_tiling(int workspace); void layout_arrange_floating(int workspace); void layout_arrange_monocle(int workspace); -/* Layout helpers */ int layout_get_usable_area(int *x, int *y, int *width, int *height); int layout_count_tiled_clients(int workspace); -/* Layout names */ const char *layout_get_name(LayoutType layout); const char *layout_get_symbol(LayoutType layout); -#endif /* DWN_LAYOUT_H */ +#endif diff --git a/include/news.h b/include/news.h index 96e2bea..a819bc4 100644 --- a/include/news.h +++ b/include/news.h @@ -10,18 +10,15 @@ #include "dwn.h" #include -/* Maximum articles to cache */ #define MAX_NEWS_ARTICLES 50 #define NEWS_API_URL "https://news.app.molodetz.nl/api" -/* Sentiment classification */ typedef enum { SENTIMENT_NEUTRAL = 0, SENTIMENT_POSITIVE, SENTIMENT_NEGATIVE } NewsSentiment; -/* News article */ typedef struct { char title[256]; char content[1024]; @@ -31,46 +28,39 @@ typedef struct { float sentiment_score; } NewsArticle; -/* News ticker state */ typedef struct { NewsArticle articles[MAX_NEWS_ARTICLES]; int article_count; - int current_article; /* Currently displayed article index */ - double scroll_offset; /* Sub-pixel offset for smooth scrolling */ - bool fetching; /* Currently fetching from API */ - bool has_error; /* Last fetch failed */ - long last_fetch; /* Timestamp of last fetch */ - long last_scroll_update; /* Timestamp of last scroll update */ - bool interactive_mode; /* User is navigating with up/down */ - int display_widths[MAX_NEWS_ARTICLES]; /* Cached text widths */ - int total_width; /* Total scrollable width */ - int render_x; /* X position where news starts rendering */ - int render_width; /* Width of news render area */ - bool widths_dirty; /* Need to recalculate widths */ + int current_article; + double scroll_offset; + bool fetching; + bool has_error; + long last_fetch; + long last_scroll_update; + bool interactive_mode; + int display_widths[MAX_NEWS_ARTICLES]; + int total_width; + int render_x; + int render_width; + bool widths_dirty; } NewsState; -/* Global state */ extern NewsState news_state; -/* Initialization */ void news_init(void); void news_cleanup(void); -/* Fetching */ void news_fetch_async(void); -void news_update(void); /* Called from main loop */ +void news_update(void); -/* Navigation */ void news_next_article(void); void news_prev_article(void); void news_open_current(void); -/* Rendering */ void news_render(Panel *panel, int x, int max_width, int *used_width); void news_handle_click(int x, int y); -/* Thread-safe access */ void news_lock(void); void news_unlock(void); -#endif /* DWN_NEWS_H */ +#endif diff --git a/include/notifications.h b/include/notifications.h index 22f669c..10488c4 100644 --- a/include/notifications.h +++ b/include/notifications.h @@ -11,42 +11,36 @@ #include #include -/* Notification urgency levels */ typedef enum { NOTIFY_URGENCY_LOW, NOTIFY_URGENCY_NORMAL, NOTIFY_URGENCY_CRITICAL } NotifyUrgency; -/* Notification structure */ typedef struct Notification { uint32_t id; char app_name[64]; - char summary[512]; /* Larger summary for AI responses */ - char *body; /* Dynamically allocated - unlimited size */ - size_t body_len; /* Length of body text */ + char summary[512]; + char *body; + size_t body_len; char icon[256]; - int timeout; /* -1 = default, 0 = never, >0 = milliseconds */ + int timeout; NotifyUrgency urgency; - long expire_time; /* Timestamp when notification should disappear */ - Window window; /* X11 window for rendering */ - int width; /* Dynamic width based on content */ - int height; /* Dynamic height based on content */ + long expire_time; + Window window; + int width; + int height; struct Notification *next; } Notification; -/* D-Bus connection */ extern DBusConnection *dbus_conn; -/* Initialization */ bool notifications_init(void); void notifications_cleanup(void); -/* D-Bus handling */ void notifications_process_messages(void); bool notifications_register_service(void); -/* Notification management */ uint32_t notification_show(const char *app_name, const char *summary, const char *body, const char *icon, int timeout); void notification_close(uint32_t id); @@ -54,21 +48,18 @@ void notification_close_all(void); Notification *notification_find(uint32_t id); Notification *notification_find_by_window(Window window); -/* Rendering */ void notification_render(Notification *notif); void notifications_render_all(void); void notifications_update(void); void notifications_position(void); void notifications_raise_all(void); -/* D-Bus method handlers */ DBusHandlerResult notifications_handle_message(DBusConnection *conn, DBusMessage *msg, void *user_data); -/* Server info */ void notifications_get_server_info(const char **name, const char **vendor, const char **version, const char **spec_version); void notifications_get_capabilities(const char ***caps, int *count); -#endif /* DWN_NOTIFICATIONS_H */ +#endif diff --git a/include/panel.h b/include/panel.h index 174a8f6..a58ebc7 100644 --- a/include/panel.h +++ b/include/panel.h @@ -10,29 +10,25 @@ #include "dwn.h" #include -/* Panel position */ typedef enum { PANEL_TOP, PANEL_BOTTOM } PanelPosition; -/* Panel structure */ struct Panel { Window window; PanelPosition position; int x, y; int width, height; bool visible; - Pixmap buffer; /* Double buffering */ + Pixmap buffer; }; -/* Panel initialization */ Panel *panel_create(PanelPosition position); void panel_destroy(Panel *panel); void panels_init(void); void panels_cleanup(void); -/* Panel rendering */ void panel_render(Panel *panel); void panel_render_all(void); void panel_render_workspaces(Panel *panel, int x, int *width); @@ -42,25 +38,20 @@ void panel_render_systray(Panel *panel, int x, int *width); void panel_render_layout_indicator(Panel *panel, int x, int *width); void panel_render_ai_status(Panel *panel, int x, int *width); -/* Panel interaction */ void panel_handle_click(Panel *panel, int x, int y, int button); int panel_hit_test_workspace(Panel *panel, int x, int y); Client *panel_hit_test_taskbar(Panel *panel, int x, int y); -/* Panel visibility */ void panel_show(Panel *panel); void panel_hide(Panel *panel); void panel_toggle(Panel *panel); -/* Clock updates */ void panel_update_clock(void); -/* System stats updates */ void panel_update_system_stats(void); -/* System tray */ void panel_init_systray(void); void panel_add_systray_icon(Window icon); void panel_remove_systray_icon(Window icon); -#endif /* DWN_PANEL_H */ +#endif diff --git a/include/systray.h b/include/systray.h index 9420c1d..2ffde53 100644 --- a/include/systray.h +++ b/include/systray.h @@ -10,18 +10,15 @@ #include "dwn.h" #include -/* Maximum number of WiFi networks to show */ #define MAX_WIFI_NETWORKS 20 -/* WiFi network info */ typedef struct { char ssid[64]; - int signal; /* Signal strength 0-100 */ - char security[32]; /* Security type (WPA2, etc.) */ + int signal; + char security[32]; bool connected; } WifiNetwork; -/* WiFi state */ typedef struct { bool enabled; bool connected; @@ -29,24 +26,21 @@ typedef struct { int signal_strength; WifiNetwork networks[MAX_WIFI_NETWORKS]; int network_count; - long last_scan; /* Timestamp of last scan */ + long last_scan; } WifiState; -/* Audio state */ typedef struct { - int volume; /* 0-100 */ + int volume; bool muted; } AudioState; -/* Battery state */ typedef struct { - bool present; /* Battery exists */ - bool charging; /* Currently charging */ - int percentage; /* 0-100 */ - int time_remaining; /* Minutes remaining */ + bool present; + bool charging; + int percentage; + int time_remaining; } BatteryState; -/* Volume slider popup */ typedef struct { Window window; int x, y; @@ -55,7 +49,6 @@ typedef struct { bool dragging; } VolumeSlider; -/* Dropdown menu */ typedef struct { Window window; int x, y; @@ -66,35 +59,29 @@ typedef struct { void (*on_select)(int index); } DropdownMenu; -/* System tray state */ extern WifiState wifi_state; extern AudioState audio_state; extern BatteryState battery_state; extern DropdownMenu *wifi_menu; extern VolumeSlider *volume_slider; -/* Initialization */ void systray_init(void); void systray_cleanup(void); -/* WiFi functions */ void wifi_update_state(void); void wifi_scan_networks(void); void wifi_connect(const char *ssid); void wifi_disconnect(void); const char *wifi_get_icon(void); -/* Audio functions */ void audio_update_state(void); void audio_set_volume(int volume); void audio_toggle_mute(void); const char *audio_get_icon(void); -/* Battery functions */ void battery_update_state(void); const char *battery_get_icon(void); -/* Volume slider functions */ VolumeSlider *volume_slider_create(int x, int y); void volume_slider_destroy(VolumeSlider *slider); void volume_slider_show(VolumeSlider *slider); @@ -104,7 +91,6 @@ void volume_slider_handle_click(VolumeSlider *slider, int x, int y); void volume_slider_handle_motion(VolumeSlider *slider, int x, int y); void volume_slider_handle_release(VolumeSlider *slider); -/* Dropdown menu functions */ DropdownMenu *dropdown_create(int x, int y, int width); void dropdown_destroy(DropdownMenu *menu); void dropdown_show(DropdownMenu *menu); @@ -115,21 +101,17 @@ int dropdown_hit_test(DropdownMenu *menu, int x, int y); void dropdown_handle_click(DropdownMenu *menu, int x, int y); void dropdown_handle_motion(DropdownMenu *menu, int x, int y); -/* Panel rendering for systray */ void systray_render(Panel *panel, int x, int *width); int systray_get_width(void); void systray_handle_click(int x, int y, int button); -int systray_hit_test(int x); /* Returns: 0=wifi, 1=audio, -1=none */ +int systray_hit_test(int x); -/* Periodic update */ void systray_update(void); -/* Thread-safe state access */ void systray_lock(void); void systray_unlock(void); -/* Thread-safe state snapshots (copies state under lock) */ BatteryState systray_get_battery_snapshot(void); AudioState systray_get_audio_snapshot(void); -#endif /* DWN_SYSTRAY_H */ +#endif diff --git a/include/util.h b/include/util.h index 0bbb5f0..f65f984 100644 --- a/include/util.h +++ b/include/util.h @@ -12,11 +12,9 @@ #include #include -/* Contract assertion macro - use for programmer errors */ #define DWN_ASSERT(cond) assert(cond) #define DWN_ASSERT_MSG(cond, msg) assert((cond) && (msg)) -/* Logging levels */ typedef enum { LOG_DEBUG, LOG_INFO, @@ -24,51 +22,43 @@ typedef enum { LOG_ERROR } LogLevel; -/* Async Logging - non-blocking with max file size (5MB) and rotation */ void log_init(const char *log_file); void log_close(void); void log_set_level(LogLevel level); -void log_flush(void); /* Force flush pending logs (call before exit/crash) */ +void log_flush(void); void log_msg(LogLevel level, const char *fmt, ...); -/* Convenience macros */ #define LOG_DEBUG(...) log_msg(LOG_DEBUG, __VA_ARGS__) #define LOG_INFO(...) log_msg(LOG_INFO, __VA_ARGS__) #define LOG_WARN(...) log_msg(LOG_WARN, __VA_ARGS__) #define LOG_ERROR(...) log_msg(LOG_ERROR, __VA_ARGS__) -/* Memory allocation with error checking */ void *dwn_malloc(size_t size); void *dwn_calloc(size_t nmemb, size_t size); void *dwn_realloc(void *ptr, size_t size); char *dwn_strdup(const char *s); void dwn_free(void *ptr); -void secure_wipe(void *ptr, size_t size); /* Securely wipe sensitive data */ +void secure_wipe(void *ptr, size_t size); -/* String utilities */ char *str_trim(char *str); bool str_starts_with(const char *str, const char *prefix); bool str_ends_with(const char *str, const char *suffix); int str_split(char *str, char delim, char **parts, int max_parts); -char *shell_escape(const char *str); /* Escape string for safe shell use */ +char *shell_escape(const char *str); -/* File utilities */ bool file_exists(const char *path); char *file_read_all(const char *path); bool file_write_all(const char *path, const char *content); char *expand_path(const char *path); -/* Color utilities */ unsigned long parse_color(const char *color_str); void color_to_rgb(unsigned long color, int *r, int *g, int *b); -/* Time utilities */ long get_time_ms(void); void sleep_ms(int ms); -/* Process utilities */ int spawn(const char *cmd); int spawn_async(const char *cmd); char *spawn_capture(const char *cmd); -#endif /* DWN_UTIL_H */ +#endif diff --git a/include/workspace.h b/include/workspace.h index 8e0837a..8960908 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -10,28 +10,23 @@ #include "dwn.h" #include -/* Workspace initialization */ void workspace_init(void); void workspace_cleanup(void); -/* Workspace access */ Workspace *workspace_get(int index); Workspace *workspace_get_current(void); int workspace_get_current_index(void); -/* Workspace switching */ void workspace_switch(int index); void workspace_switch_next(void); void workspace_switch_prev(void); -/* Client management within workspaces */ void workspace_add_client(int workspace, Client *client); void workspace_remove_client(int workspace, Client *client); void workspace_move_client(Client *client, int new_workspace); Client *workspace_get_first_client(int workspace); Client *workspace_get_focused_client(int workspace); -/* Layout */ void workspace_set_layout(int workspace, LayoutType layout); LayoutType workspace_get_layout(int workspace); void workspace_cycle_layout(int workspace); @@ -40,23 +35,19 @@ void workspace_adjust_master_ratio(int workspace, float delta); void workspace_set_master_count(int workspace, int count); void workspace_adjust_master_count(int workspace, int delta); -/* Arrangement */ void workspace_arrange(int workspace); void workspace_arrange_current(void); -/* Visibility */ void workspace_show(int workspace); void workspace_hide(int workspace); -/* Properties */ void workspace_set_name(int workspace, const char *name); const char *workspace_get_name(int workspace); int workspace_client_count(int workspace); bool workspace_is_empty(int workspace); -/* Focus cycling within workspace */ void workspace_focus_next(void); void workspace_focus_prev(void); void workspace_focus_master(void); -#endif /* DWN_WORKSPACE_H */ +#endif diff --git a/site/ai-features.html b/site/ai-features.html index 64b7c64..04e7761 100644 --- a/site/ai-features.html +++ b/site/ai-features.html @@ -37,7 +37,6 @@ -
@@ -47,7 +46,6 @@

-
@@ -55,7 +53,6 @@

AI features are completely optional and require external API keys. DWN works perfectly without them.

-

Overview

DWN integrates with two AI services to provide intelligent desktop assistance: @@ -64,7 +61,6 @@

  • OpenRouter API - Powers the AI command palette and context analysis
  • Exa API - Provides semantic web search capabilities
  • -
    🤖
    @@ -88,14 +84,11 @@

    Super + Shift + E

    - -

    Setting Up OpenRouter

    OpenRouter provides access to multiple AI models through a single API. You can use free models or paid ones depending on your needs.

    -
    1
    @@ -105,7 +98,6 @@ and create a free account to get your API key.

    -
    2
    @@ -118,7 +110,6 @@
    export OPENROUTER_API_KEY="sk-or-v1-your-key-here"
    -
    3
    @@ -136,7 +127,6 @@ model = google/gemini-2.0-flash-exp:free
    -

    Recommended Free Models

    @@ -168,14 +158,11 @@ model = google/gemini-2.0-flash-exp:free
    - -

    AI Command Palette

    Press Super + Shift + A to open the command palette. Type natural language commands and press Enter.

    -

    Supported Commands

    @@ -215,19 +202,15 @@ model = google/gemini-2.0-flash-exp:free
    -
    Pro Tip

    The AI understands context. You can say "open browser" instead of remembering the exact application name - it will figure out what you mean.

    - -

    Context Analysis

    Press Super + A to see AI-powered analysis of your current workspace.

    -

    What It Shows

      @@ -237,13 +220,10 @@ model = google/gemini-2.0-flash-exp:free
    • Workspace Summary - Overview of open applications
    - -

    Setting Up Exa Search

    Exa provides semantic search - finding content based on meaning rather than exact keywords.

    -
    1
    @@ -253,7 +233,6 @@ model = google/gemini-2.0-flash-exp:free and create an account to get your API key.

    -
    2
    @@ -265,7 +244,6 @@ model = google/gemini-2.0-flash-exp:free
    export EXA_API_KEY="your-exa-key-here"
    -
    3
    @@ -274,13 +252,10 @@ model = google/gemini-2.0-flash-exp:free
    - -

    Unlike traditional search, Exa understands the meaning of your query.

    -

    Traditional Search

    @@ -307,7 +282,6 @@ model = google/gemini-2.0-flash-exp:free

    -

    Search Tips

    • Use natural, conversational queries
    • @@ -315,14 +289,11 @@ model = google/gemini-2.0-flash-exp:free
    • Results appear in a dmenu/rofi list - select to open in browser
    • Search includes articles, documentation, tutorials, and more
    - -

    Privacy Considerations

    Data Sent to External Services

    When using AI features, the following data is sent to external APIs:

    -
    @@ -351,7 +322,6 @@ model = google/gemini-2.0-flash-exp:free
    -

    If you're concerned about privacy, you can:

    @@ -360,10 +330,7 @@ model = google/gemini-2.0-flash-exp:free
  • Use OpenRouter with privacy-focused models
  • Only use AI features when needed
  • - -

    Troubleshooting

    -
    -
    -
    -
    -
    - diff --git a/site/architecture.html b/site/architecture.html index 561937c..b767aa0 100644 --- a/site/architecture.html +++ b/site/architecture.html @@ -37,7 +37,6 @@ -
    @@ -47,7 +46,6 @@

    -

    Overview

    @@ -56,7 +54,6 @@ A global DWNState singleton manages all state, and the main event loop dispatches X11 events to specialized modules.

    -

    Project Statistics

    @@ -78,8 +75,6 @@
    - -

    Directory Structure

    Project Layout @@ -104,8 +99,6 @@ ├── Makefile # Build system ├── CLAUDE.md # AI assistant context └── README.md # Project readme - -

    Core Modules

    @@ -185,8 +178,6 @@
    - -

    Module Dependencies

    main.c (orchestrator)
    @@ -208,14 +199,11 @@
         └── keys.c
             └── config.c
    - -

    Global State (DWNState)

    All window manager state is centralized in a single DWNState structure. This simplifies state management and makes the codebase easier to understand.

    -
    include/dwn.h (simplified)
    @@ -223,32 +211,23 @@ Display *display; // X11 connection Window root; // Root window int screen; // Default screen - Client *clients[MAX_CLIENTS]; // All managed windows int client_count; - Workspace workspaces[MAX_WORKSPACES]; // Virtual desktops int current_workspace; - Panel top_panel; Panel bottom_panel; - Config config; // User configuration KeyBinding keys[MAX_KEYBINDINGS]; - // EWMH atoms Atom atoms[ATOM_COUNT]; } DWNState; - extern DWNState *dwn; // Global singleton - -

    Event Loop

    DWN uses a traditional X11 event loop with XNextEvent. Events are dispatched to appropriate handlers based on type.

    -
    main.c (simplified)
    @@ -256,11 +235,9 @@ extern DWNState *dwn; // Global singleton dwn_init(); // Initialize X11, atoms, config setup_keybindings(); // Register keyboard shortcuts setup_panels(); // Create panel windows - XEvent event; while (running) { XNextEvent(dwn->display, &event); - switch (event.type) { case MapRequest: handle_map_request(&event.xmaprequest); @@ -280,12 +257,9 @@ extern DWNState *dwn; // Global singleton // ... more event types } } - dwn_cleanup(); return 0; } - -

    Key Constants

    @@ -325,8 +299,6 @@ extern DWNState *dwn; // Global singleton
    - -

    Coding Conventions

    @@ -348,33 +320,26 @@ extern DWNState *dwn; // Global singleton
    -
    Example Function
    void client_focus(Client *c) {
         if (!c) return;
    -
         // Unfocus previous
         if (dwn->focused && dwn->focused != c) {
             client_unfocus(dwn->focused);
         }
    -
         dwn->focused = c;
         XSetInputFocus(dwn->display, c->window, RevertToPointerRoot, CurrentTime);
         XRaiseWindow(dwn->display, c->frame);
    -
         decorations_update(c);
         atoms_set_active_window(c->window);
     }
    - -

    EWMH/ICCCM Support

    DWN implements key Extended Window Manager Hints and ICCCM protocols for compatibility with modern applications.

    -

    EWMH Atoms

    @@ -407,13 +372,10 @@ extern DWNState *dwn; // Global singleton
    - -

    Build System

    DWN uses a simple Makefile-based build system with pkg-config for dependency detection.

    -
    @@ -467,13 +429,10 @@ extern DWNState *dwn; // Global singleton
    - -

    Contributing

    Contributions are welcome! Here's how to get started:

    -
    1
    @@ -511,11 +470,9 @@ extern DWNState *dwn; // Global singleton
    -
    -
    - diff --git a/site/configuration.html b/site/configuration.html index a340781..d2f4d1a 100644 --- a/site/configuration.html +++ b/site/configuration.html @@ -37,7 +37,6 @@ -
    @@ -47,7 +46,6 @@

    -

    Configuration File

    @@ -55,19 +53,15 @@ DWN reads its configuration from ~/.config/dwn/config using an INI-style format. Changes take effect on restart (or you can reload in a future version).

    -
    First Run

    DWN creates a default configuration file on first run if one doesn't exist. You can also copy the example config from the source repository.

    - -

    [general] - Core Settings

    Basic behavior settings for applications and focus handling.

    -
    @@ -106,7 +100,6 @@
    -
    Example @@ -117,13 +110,10 @@ launcher = rofi -show run file_manager = nautilus focus_mode = click decorations = true - -

    [appearance] - Visual Settings

    Control the visual appearance of windows, panels, and gaps.

    -
    @@ -168,7 +158,6 @@ decorations = true
    -
    Example @@ -179,13 +168,10 @@ title_height = 28 panel_height = 32 gap = 8 font = DejaVu Sans-10 - -

    [layout] - Layout Behavior

    Configure the default layout mode and tiling parameters.

    -
    @@ -218,7 +204,6 @@ font = DejaVu Sans-10
    -
    Example @@ -227,13 +212,10 @@ font = DejaVu Sans-10 default = tiling master_ratio = 0.60 master_count = 1 - -

    [panels] - Panel Visibility

    Control which panels are displayed.

    -
    @@ -257,7 +239,6 @@ master_count = 1
    -
    Example - Minimal Setup @@ -265,13 +246,10 @@ master_count = 1
    [panels]
     top = true
     bottom = false
    - -

    [colors] - Color Scheme

    Customize all colors using hex format (#RRGGBB).

    -
    @@ -350,7 +328,6 @@ bottom = false
    -
    Example - Nord Theme @@ -369,13 +346,10 @@ border_focused = #88c0d0 border_unfocused = #3b4252 notification_bg = #3b4252 notification_fg = #eceff4 - -

    [ai] - AI Integration

    Configure AI features. See AI Features for full setup instructions.

    -
    @@ -400,7 +374,6 @@ notification_fg = #eceff4
    -
    Example @@ -409,14 +382,11 @@ notification_fg = #eceff4 model = google/gemini-2.0-flash-exp:free openrouter_api_key = sk-or-v1-your-key-here exa_api_key = your-exa-key-here -
    Security Note

    For better security, use environment variables instead of storing API keys in the config file: export OPENROUTER_API_KEY=sk-or-v1-...

    - -

    Complete Configuration Example

    ~/.config/dwn/config @@ -424,30 +394,25 @@ exa_api_key = your-exa-key-here
    # DWN Window Manager Configuration
     # https://dwn.github.io
    -
     [general]
     terminal = alacritty
     launcher = rofi -show drun
     file_manager = thunar
     focus_mode = click
     decorations = true
    -
     [appearance]
     border_width = 2
     title_height = 24
     panel_height = 28
     gap = 6
     font = DejaVu Sans-10
    -
     [layout]
     default = tiling
     master_ratio = 0.55
     master_count = 1
    -
     [panels]
     top = true
     bottom = true
    -
     [colors]
     panel_bg = #1a1a2e
     panel_fg = #e0e0e0
    @@ -462,15 +427,12 @@ border_focused = #4a90d9
     border_unfocused = #333333
     notification_bg = #2a2a3e
     notification_fg = #ffffff
    -
     [ai]
     model = google/gemini-2.0-flash-exp:free
     # API keys via environment variables recommended
    -
    -
    - diff --git a/site/documentation.html b/site/documentation.html index 1e9fc46..072d5b4 100644 --- a/site/documentation.html +++ b/site/documentation.html @@ -37,7 +37,6 @@ -
    -

    Getting Started with DWN

    Learn the fundamentals of DWN and become productive in minutes.

    -

    First Steps

    After installing DWN and starting your session, you'll see a clean desktop with two panels: a top panel with workspace indicators, taskbar, and system tray, and a bottom panel showing the clock.

    -

    Opening Your First Application

    Start by launching a terminal and application launcher:

    -
    @@ -115,28 +110,23 @@
    -
    Tip: Run the Tutorial

    Press Super + T to start an interactive tutorial that will guide you through all essential shortcuts.

    -

    Basic Concepts

    -

    The Super Key

    Most DWN shortcuts use the Super key (often the Windows key or Command key). This keeps shortcuts separate from application shortcuts that typically use Ctrl or Alt.

    -

    Focus Model

    By default, DWN uses "click to focus" - you click on a window to focus it. You can change this to "focus follows mouse" (sloppy focus) in the configuration.

    -

    Window Decorations

    Each window has a title bar showing its name. The title bar color indicates focus: @@ -145,7 +135,6 @@

  • Blue title bar - Focused window
  • Gray title bar - Unfocused window
  • -

    Interactive Tutorial

    DWN includes a built-in interactive tutorial that teaches you essential shortcuts @@ -157,14 +146,11 @@

  • Automatically advances when you complete each step
  • Covers all essential shortcuts from basic to advanced
  • -

    Start the Tutorial

    Press Super + T at any time to start or resume the tutorial.

    -

    Managing Windows

    -

    Window Operations

    @@ -202,20 +188,17 @@
    -

    Moving and Resizing

    In floating mode, you can move and resize windows with the mouse:

    • Move - Click and drag the title bar
    • Resize - Drag any window edge or corner
    -

    Using Workspaces

    DWN provides 9 virtual workspaces to organize your windows. You can see which workspaces are active in the top panel.

    -

    Workspace Navigation

    @@ -245,7 +228,6 @@
    -

    Workspace Organization Tips

    • Workspace 1 - Main work (editor, terminal)
    • @@ -253,13 +235,11 @@
    • Workspace 3 - Communication (email, chat)
    • Workspace 4-9 - Project-specific contexts
    -

    Layout Modes

    DWN supports three layout modes. Press Super + Space to cycle through them.

    -

    Tiling

    @@ -283,7 +263,6 @@

    -

    Tiling Layout Controls

    @@ -313,9 +292,7 @@
    -

    Panels & System Tray

    -

    Top Panel

    The top panel contains:

      @@ -323,7 +300,6 @@
    • Taskbar - Shows windows on current workspace
    • System tray - Battery, volume, WiFi (see below)
    -

    System Tray

    @@ -357,7 +333,6 @@
    -

    Bottom Panel

    The bottom panel shows the current time and a scrolling news ticker. Navigate @@ -365,7 +340,6 @@ the current article with Super + Return. Both panels can be hidden in the configuration if you prefer a minimal setup.

    -

    Next Steps

    Now that you know the basics, explore these topics:

      @@ -375,7 +349,6 @@
    -
    - diff --git a/site/features.html b/site/features.html index 605678c..20a2079 100644 --- a/site/features.html +++ b/site/features.html @@ -37,7 +37,6 @@ -
    @@ -47,8 +46,6 @@

    - -

    Window Management

    @@ -56,7 +53,6 @@ DWN provides flexible window management that adapts to your workflow, whether you prefer the precision of tiling or the freedom of floating windows.

    -

    Tiling Layout

    @@ -92,7 +88,6 @@
    -
    Pro Tip

    Switch layouts instantly with Super + Space. @@ -100,8 +95,6 @@

    - -

    Virtual Workspaces

    @@ -109,7 +102,6 @@ Nine virtual desktops give you unlimited room to organize your work. Each workspace maintains its own window state and layout preferences.

    -
    1-9
    @@ -135,8 +127,6 @@
    - -

    Panels & System Tray

    @@ -144,7 +134,6 @@ Built-in panels provide essential information and quick access to common functions without needing external tools or status bars.

    -

    Top Panel

    @@ -165,7 +154,6 @@
    -

    News Ticker

    The bottom panel includes a scrolling news ticker that displays headlines from a news feed. @@ -180,7 +168,6 @@ at 80 pixels per second keeps you informed without distraction.

    -

    System Tray Features

    @@ -213,8 +200,6 @@
    - -

    Notification System

    @@ -222,7 +207,6 @@ Built-in D-Bus notification daemon following freedesktop.org standards. No need for external notification tools like dunst or notify-osd.

    -

    Standards Compliant

    @@ -235,7 +219,6 @@ Notifications match your overall color scheme automatically.

    -
    Capacity

    DWN can display up to 32 notifications simultaneously, @@ -243,8 +226,6 @@

    - -

    AI Integration

    @@ -252,7 +233,6 @@ Optional AI features powered by OpenRouter API and Exa semantic search. Control your desktop with natural language and get intelligent assistance.

    -

    🤖 AI Command Palette

    @@ -275,15 +255,12 @@
    - -

    Standards Compliance

    DWN implements EWMH and ICCCM protocols for maximum compatibility with X11 applications.

    -

    EWMH Support

    @@ -310,8 +287,6 @@
    - -

    Technical Specifications

    @@ -361,8 +336,6 @@
    - -

    Ready to Try DWN?

    @@ -376,7 +349,6 @@
    -
    - diff --git a/site/index.html b/site/index.html index 48e4cdc..1c0494d 100644 --- a/site/index.html +++ b/site/index.html @@ -38,9 +38,7 @@ -
    -

    Modern Window Management for X11

    @@ -54,8 +52,6 @@
    - -

    Why Choose DWN?

    @@ -103,8 +99,6 @@
    - -
    @@ -127,8 +121,6 @@
    - -

    Get Up and Running in Minutes

    @@ -136,7 +128,6 @@ DWN is designed for easy installation and immediate productivity. Build from source or use your distribution's package manager.

    -
    Terminal @@ -145,25 +136,19 @@
    # Clone the repository
     git clone https://retoor.molodetz.nl/retoor/dwn.git
     cd dwn
    -
     # Install dependencies (auto-detects your distro)
     make deps
    -
     # Build and install
     make
     sudo make install
    -
     # Add to your .xinitrc
     echo "exec dwn" >> ~/.xinitrc
    -

    Full Installation Guide

    - -

    See DWN in Action

    @@ -192,8 +177,6 @@ echo "exec dwn" >> ~/.xinitrc
    - -

    How DWN Compares

    @@ -239,62 +222,9 @@ echo "exec dwn" >> ~/.xinitrc
    - - -
    -
    -

    What Users Say

    -
    -
    -

    - "Finally, a window manager that doesn't make me choose between - productivity and usability. DWN just works." -

    -
    -
    JD
    -
    - John Developer - Senior Software Engineer -
    -
    -
    -
    -

    - "The AI command palette changed how I interact with my desktop. - It's like having an assistant for window management." -

    -
    -
    SA
    -
    - Sarah Admin - System Administrator -
    -
    -
    -
    -

    - "Coming from i3, the learning curve was minimal. The built-in - tutorial had me productive in under 10 minutes." -

    -
    -
    ML
    -
    - Mike Linux - DevOps Engineer -
    -
    -
    -
    -
    -
    - -
    -

    Ready to Transform Your Desktop?

    -

    - Join thousands of developers and power users who have made DWN their window manager of choice. -

    +

    Ready to Try DWN?

    Install DWN Read the Docs @@ -302,7 +232,6 @@ echo "exec dwn" >> ~/.xinitrc
    -
    - diff --git a/site/installation.html b/site/installation.html index 9b621bc..203c7b3 100644 --- a/site/installation.html +++ b/site/installation.html @@ -37,7 +37,6 @@ -
    @@ -47,15 +46,12 @@

    - -

    Requirements

    DWN requires X11 and a few common libraries. Most Linux distributions include these by default.

    -

    Required Dependencies

    @@ -114,15 +110,12 @@
    - -

    Quick Installation

    The fastest way to get started. Our build system auto-detects your distribution.

    -
    1
    @@ -137,7 +130,6 @@ cd dwn
    -
    2
    @@ -153,7 +145,6 @@ cd dwn

    -
    3
    @@ -166,7 +157,6 @@ cd dwn
    make
    -
    4
    @@ -182,7 +172,6 @@ cd dwn

    -
    5
    @@ -198,19 +187,15 @@ cd dwn
    - -

    Distribution-Specific Instructions

    -
    -

    Debian / Ubuntu / Linux Mint

    @@ -230,14 +215,12 @@ sudo apt install -y \ libdbus-1-dev \ libcurl4-openssl-dev \ pkg-config - # Build and install git clone https://retoor.molodetz.nl/retoor/dwn.git cd dwn make sudo make install
    -

    Fedora / RHEL / CentOS

    @@ -257,14 +240,12 @@ sudo dnf install -y \ dbus-devel \ libcurl-devel \ pkg-config - # Build and install git clone https://retoor.molodetz.nl/retoor/dwn.git cd dwn make sudo make install
    -

    Arch Linux / Manjaro

    @@ -283,7 +264,6 @@ sudo pacman -S --needed \ dbus \ curl \ pkg-config - # Build and install git clone https://retoor.molodetz.nl/retoor/dwn.git cd dwn @@ -295,7 +275,6 @@ sudo make install yay -S dwn-git

    -

    Void Linux

    @@ -314,7 +293,6 @@ sudo xbps-install -S \ dbus-devel \ libcurl-devel \ pkg-config - # Build and install git clone https://retoor.molodetz.nl/retoor/dwn.git cd dwn @@ -323,15 +301,12 @@ sudo make install
    - -

    Session Setup

    Configure your display manager or xinit to start DWN.

    -

    Using xinit / startx

    @@ -342,10 +317,8 @@ sudo make install
    # Optional: set display settings
     xrandr --output DP-1 --mode 2560x1440
    -
     # Optional: set wallpaper
     feh --bg-fill ~/wallpaper.jpg
    -
     # Start DWN
     exec dwn

    @@ -372,15 +345,12 @@ DesktopNames=DWN

    - -

    Testing in a Nested X Server

    Test DWN without leaving your current session using Xephyr.

    -

    Using make run

    The easiest way to test DWN safely:

    @@ -392,14 +362,12 @@ DesktopNames=DWN # Debian/Ubuntu: sudo apt install xserver-xephyr # Fedora: sudo dnf install xorg-x11-server-Xephyr # Arch: sudo pacman -S xorg-server-xephyr - # Run DWN in a nested window make run

    This opens a 1280x720 window running DWN. Perfect for experimenting with configuration changes.

    -

    Manual Xephyr Setup

    For more control over the test environment:

    @@ -409,21 +377,16 @@ make run
    # Start Xephyr on display :1
     Xephyr :1 -screen 1920x1080 &
    -
     # Run DWN on that display
     DISPLAY=:1 ./dwn
    -
     # Open a terminal in the test environment
     DISPLAY=:1 xterm &
    - -

    Post-Installation

    -
    1
    @@ -437,7 +400,6 @@ DISPLAY=:1 xterm &
    mkdir -p ~/.config/dwn
    -
    2
    @@ -446,7 +408,6 @@ DISPLAY=:1 xterm & that will teach you all the essential shortcuts.

    -
    3
    @@ -454,7 +415,6 @@ DISPLAY=:1 xterm &

    Press Super + S to see a complete list of keyboard shortcuts.

    -
    4
    @@ -465,12 +425,9 @@ DISPLAY=:1 xterm &
    - -

    Troubleshooting

    -
    -
    -
    -
    -
    - -

    Installation Complete?

    @@ -560,7 +511,6 @@ sudo dnf install dejavu-fonts-all liberation-fonts # Fedora
    -
    - diff --git a/site/shortcuts.html b/site/shortcuts.html index 513d4e2..688f3af 100644 --- a/site/shortcuts.html +++ b/site/shortcuts.html @@ -37,7 +37,6 @@ -
    @@ -48,14 +47,11 @@

    -
    - -

    Application Launchers

    @@ -89,8 +85,6 @@
    - -

    Window Management

    @@ -128,8 +122,6 @@
    - -

    Workspace Navigation

    @@ -223,8 +215,6 @@
    - -

    Layout Control

    @@ -258,8 +248,6 @@
    - -

    AI Features

    These shortcuts require API keys to be configured. See AI Features for setup. @@ -288,8 +276,6 @@

    - -

    News Ticker

    Navigate the scrolling news ticker displayed in the bottom panel. @@ -318,8 +304,6 @@ - -

    Help & System

    @@ -345,12 +329,9 @@
    - -

    Printable Quick Reference

    Essential shortcuts to memorize when starting with DWN:

    -

    Must Know

    @@ -397,7 +378,6 @@
    -
    - diff --git a/src/ai.c b/src/ai.c index 3111c5a..753b377 100644 --- a/src/ai.c +++ b/src/ai.c @@ -17,21 +17,17 @@ #include #include -/* API endpoints */ #define OPENROUTER_URL "https://openrouter.ai/api/v1/chat/completions" -/* Request queue */ static AIRequest *request_queue = NULL; static CURLM *curl_multi = NULL; static AIContext current_context; -/* Response buffer for curl */ typedef struct { char *data; size_t size; } ResponseBuffer; -/* ========== CURL callbacks ========== */ static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { @@ -51,7 +47,6 @@ static size_t write_callback(void *contents, size_t size, size_t nmemb, void *us return realsize; } -/* ========== Initialization ========== */ bool ai_init(void) { @@ -62,10 +57,9 @@ bool ai_init(void) if (dwn->config->openrouter_api_key[0] == '\0') { LOG_INFO("AI features disabled (no OPENROUTER_API_KEY)"); dwn->ai_enabled = false; - return true; /* Not an error, just disabled */ + return true; } - /* Initialize curl */ curl_global_init(CURL_GLOBAL_DEFAULT); curl_multi = curl_multi_init(); @@ -82,7 +76,6 @@ bool ai_init(void) void ai_cleanup(void) { - /* Cancel all pending requests */ while (request_queue != NULL) { AIRequest *next = request_queue->next; if (request_queue->prompt) free(request_queue->prompt); @@ -104,7 +97,6 @@ bool ai_is_available(void) return dwn != NULL && dwn->ai_enabled; } -/* ========== API calls ========== */ AIRequest *ai_send_request(const char *prompt, void (*callback)(AIRequest *)) { @@ -117,11 +109,9 @@ AIRequest *ai_send_request(const char *prompt, void (*callback)(AIRequest *)) req->state = AI_STATE_PENDING; req->callback = callback; - /* Build JSON request body */ char *json_prompt = dwn_malloc(strlen(prompt) * 2 + 256); char *escaped_prompt = dwn_malloc(strlen(prompt) * 2 + 1); - /* Escape special characters in prompt */ const char *src = prompt; char *dst = escaped_prompt; while (*src) { @@ -144,7 +134,6 @@ AIRequest *ai_send_request(const char *prompt, void (*callback)(AIRequest *)) dwn_free(escaped_prompt); - /* Create curl easy handle */ CURL *easy = curl_easy_init(); if (easy == NULL) { dwn_free(json_prompt); @@ -153,10 +142,8 @@ AIRequest *ai_send_request(const char *prompt, void (*callback)(AIRequest *)) return NULL; } - /* Response buffer */ ResponseBuffer *response = dwn_calloc(1, sizeof(ResponseBuffer)); - /* Set curl options */ struct curl_slist *headers = NULL; char auth_header[300]; snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", @@ -175,19 +162,15 @@ AIRequest *ai_send_request(const char *prompt, void (*callback)(AIRequest *)) curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, 2L); - /* Add to multi handle */ curl_multi_add_handle(curl_multi, easy); - /* Add to queue */ req->next = request_queue; request_queue = req; - /* Store response buffer pointer for cleanup */ req->user_data = response; LOG_DEBUG("AI request sent: %.50s...", prompt); - /* Note: json_prompt and headers will be freed after request completes */ return req; } @@ -198,7 +181,6 @@ void ai_cancel_request(AIRequest *req) return; } - /* Remove from queue */ AIRequest **pp = &request_queue; while (*pp != NULL) { if (*pp == req) { @@ -229,7 +211,6 @@ void ai_process_pending(void) int running_handles; curl_multi_perform(curl_multi, &running_handles); - /* Check for completed requests */ CURLMsg *msg; int msgs_left; @@ -244,8 +225,6 @@ void ai_process_pending(void) ResponseBuffer *buf = (ResponseBuffer *)req->user_data; if (msg->data.result == CURLE_OK && buf != NULL && buf->data != NULL) { - /* Parse response using cJSON */ - /* OpenRouter format: {"choices":[{"message":{"content":"..."}}]} */ cJSON *root = cJSON_Parse(buf->data); if (root != NULL) { cJSON *choices = cJSON_GetObjectItemCaseSensitive(root, "choices"); @@ -260,7 +239,6 @@ void ai_process_pending(void) } } } - /* Check for error in response */ if (req->state != AI_STATE_COMPLETED) { cJSON *error = cJSON_GetObjectItemCaseSensitive(root, "error"); if (error != NULL) { @@ -276,7 +254,6 @@ void ai_process_pending(void) } if (req->state != AI_STATE_COMPLETED && req->state != AI_STATE_ERROR) { - /* Fallback: return raw response for debugging */ req->response = dwn_strdup(buf->data); req->state = AI_STATE_COMPLETED; LOG_WARN("Could not parse AI response, returning raw"); @@ -286,12 +263,10 @@ void ai_process_pending(void) LOG_ERROR("AI request failed: %s", curl_easy_strerror(msg->data.result)); } - /* Call callback */ if (req->callback != NULL) { req->callback(req); } - /* Cleanup */ if (buf != NULL) { if (buf->data) free(buf->data); dwn_free(buf); @@ -304,7 +279,6 @@ void ai_process_pending(void) } } -/* ========== Context analysis ========== */ void ai_update_context(void) { @@ -318,7 +292,6 @@ void ai_update_context(void) "%s", ws->focused->class); } - /* Build list of windows on current workspace */ int offset = 0; for (Client *c = dwn->client_list; c != NULL; c = c->next) { if (c->workspace == (unsigned int)dwn->current_workspace) { @@ -335,7 +308,6 @@ void ai_update_context(void) const char *ai_analyze_task(void) { - /* Analyze based on focused window class */ const char *class = current_context.focused_class; if (strstr(class, "code") || strstr(class, "Code") || @@ -359,7 +331,6 @@ const char *ai_analyze_task(void) const char *ai_suggest_window(void) { - /* Simple heuristic suggestion */ const char *task = ai_analyze_task(); if (strcmp(task, "coding") == 0) { @@ -374,12 +345,10 @@ const char *ai_suggest_window(void) const char *ai_suggest_app(void) { - return NULL; /* Would require more context */ + return NULL; } -/* ========== Command palette ========== */ -/* Callback for AI command response */ static void ai_command_response_callback(AIRequest *req) { if (req == NULL) { @@ -387,15 +356,12 @@ static void ai_command_response_callback(AIRequest *req) } if (req->state == AI_STATE_COMPLETED && req->response != NULL) { - /* Check if response contains a command to execute */ - /* Format: [RUN: command] or [EXEC: command] */ char *run_cmd = strstr(req->response, "[RUN:"); if (run_cmd == NULL) { run_cmd = strstr(req->response, "[EXEC:"); } if (run_cmd != NULL) { - /* Extract command */ char *cmd_start = strchr(run_cmd, ':'); if (cmd_start != NULL) { cmd_start++; @@ -408,7 +374,6 @@ static void ai_command_response_callback(AIRequest *req) strncpy(cmd, cmd_start, cmd_len); cmd[cmd_len] = '\0'; - /* Trim trailing spaces */ while (cmd_len > 0 && cmd[cmd_len - 1] == ' ') { cmd[--cmd_len] = '\0'; } @@ -420,15 +385,12 @@ static void ai_command_response_callback(AIRequest *req) } } } else { - /* No command, just show response */ notification_show("DWN AI", "Response", req->response, NULL, 8000); } } else { notification_show("DWN AI", "Error", "Failed to get AI response", NULL, 3000); } - /* Cleanup - don't free req itself, it's managed by the queue */ - /* The queue will be cleaned up separately */ } void ai_show_command_palette(void) @@ -440,10 +402,8 @@ void ai_show_command_palette(void) return; } - /* Check if dmenu or rofi is available */ char *input = NULL; - /* Try dmenu first, then rofi */ if (spawn("command -v dmenu >/dev/null 2>&1") == 0) { input = spawn_capture("echo '' | dmenu -p 'Ask AI:'"); } else if (spawn("command -v rofi >/dev/null 2>&1") == 0) { @@ -466,10 +426,8 @@ void ai_show_command_palette(void) LOG_DEBUG("AI command palette input: %s", input); - /* Show "thinking" notification */ notification_show("DWN AI", "Processing...", input, NULL, 2000); - /* Build context-aware prompt */ ai_update_context(); const char *task = ai_analyze_task(); @@ -492,7 +450,6 @@ void ai_show_command_palette(void) dwn_free(input); - /* Send request */ ai_send_request(prompt, ai_command_response_callback); } @@ -504,7 +461,6 @@ void ai_execute_command(const char *command) LOG_DEBUG("AI executing command: %s", command); - /* Send to AI for interpretation */ char prompt[512]; snprintf(prompt, sizeof(prompt), "User command: %s\nCurrent task: %s\nRespond with a single action to take.", @@ -513,7 +469,6 @@ void ai_execute_command(const char *command) ai_send_request(prompt, NULL); } -/* ========== Smart features ========== */ void ai_auto_organize_workspace(void) { @@ -530,19 +485,16 @@ void ai_analyze_workflow(void) LOG_DEBUG("AI workflow analysis (placeholder)"); } -/* ========== Notification intelligence ========== */ bool ai_should_show_notification(const char *app, const char *summary) { - /* Simple filtering - could be enhanced with AI */ (void)app; (void)summary; - return true; /* Show all by default */ + return true; } int ai_notification_priority(const char *app, const char *summary) { - /* Simple priority assignment */ if (strstr(summary, "urgent") || strstr(summary, "Urgent") || strstr(summary, "error") || strstr(summary, "Error")) { return 3; @@ -554,11 +506,9 @@ int ai_notification_priority(const char *app, const char *summary) return 1; } -/* ========== Performance monitoring ========== */ void ai_monitor_performance(void) { - /* Read from /proc for basic metrics */ LOG_DEBUG("AI performance monitoring (placeholder)"); } @@ -567,7 +517,6 @@ const char *ai_performance_suggestion(void) return NULL; } -/* ========== Exa Semantic Search ========== */ #define EXA_API_URL "https://api.exa.ai/search" @@ -579,7 +528,6 @@ bool exa_is_available(void) dwn->config->exa_api_key[0] != '\0'; } -/* Parse Exa JSON response using cJSON */ static void exa_parse_response(ExaRequest *req, const char *json) { if (req == NULL || json == NULL) { @@ -607,21 +555,18 @@ static void exa_parse_response(ExaRequest *req, const char *json) ExaSearchResult *res = &req->results[req->result_count]; - /* Extract title */ cJSON *title = cJSON_GetObjectItemCaseSensitive(item, "title"); if (cJSON_IsString(title) && title->valuestring != NULL) { strncpy(res->title, title->valuestring, sizeof(res->title) - 1); res->title[sizeof(res->title) - 1] = '\0'; } - /* Extract URL */ cJSON *url = cJSON_GetObjectItemCaseSensitive(item, "url"); if (cJSON_IsString(url) && url->valuestring != NULL) { strncpy(res->url, url->valuestring, sizeof(res->url) - 1); res->url[sizeof(res->url) - 1] = '\0'; } - /* Extract text/snippet if available */ cJSON *text = cJSON_GetObjectItemCaseSensitive(item, "text"); if (cJSON_IsString(text) && text->valuestring != NULL) { strncpy(res->snippet, text->valuestring, sizeof(res->snippet) - 1); @@ -647,11 +592,9 @@ ExaRequest *exa_search(const char *query, void (*callback)(ExaRequest *)) req->callback = callback; req->result_count = 0; - /* Build JSON request */ char *json_query = dwn_malloc(strlen(query) * 2 + 256); char *escaped = dwn_malloc(strlen(query) * 2 + 1); - /* Escape query string */ const char *src = query; char *dst = escaped; while (*src) { @@ -667,7 +610,6 @@ ExaRequest *exa_search(const char *query, void (*callback)(ExaRequest *)) escaped); dwn_free(escaped); - /* Create curl handle */ CURL *easy = curl_easy_init(); if (easy == NULL) { dwn_free(json_query); @@ -676,10 +618,8 @@ ExaRequest *exa_search(const char *query, void (*callback)(ExaRequest *)) return NULL; } - /* Response buffer */ ResponseBuffer *response = dwn_calloc(1, sizeof(ResponseBuffer)); - /* Set headers */ struct curl_slist *headers = NULL; char api_header[300]; snprintf(api_header, sizeof(api_header), "x-api-key: %s", @@ -698,13 +638,11 @@ ExaRequest *exa_search(const char *query, void (*callback)(ExaRequest *)) curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, 2L); - /* Add to multi handle */ if (curl_multi == NULL) { curl_multi = curl_multi_init(); } curl_multi_add_handle(curl_multi, easy); - /* Add to queue */ req->next = exa_queue; exa_queue = req; req->user_data = response; @@ -733,7 +671,6 @@ void exa_process_pending(void) curl_easy_getinfo(easy, CURLINFO_PRIVATE, &req); - /* Check if this is an Exa request (in exa_queue) */ bool is_exa = false; for (ExaRequest *r = exa_queue; r != NULL; r = r->next) { if (r == req) { @@ -757,13 +694,11 @@ void exa_process_pending(void) req->callback(req); } - /* Cleanup buffer */ if (buf != NULL) { if (buf->data) free(buf->data); dwn_free(buf); } - /* Remove from queue */ ExaRequest **pp = &exa_queue; while (*pp != NULL) { if (*pp == req) { @@ -780,7 +715,6 @@ void exa_process_pending(void) } } -/* Callback for app launcher search */ static void exa_launcher_callback(ExaRequest *req) { if (req == NULL || req->state != AI_STATE_COMPLETED) { @@ -793,7 +727,6 @@ static void exa_launcher_callback(ExaRequest *req) return; } - /* Show results via dmenu/rofi - use bounded string operations */ size_t choices_size = req->result_count * 300; char *choices = dwn_malloc(choices_size); size_t offset = 0; @@ -807,7 +740,6 @@ static void exa_launcher_callback(ExaRequest *req) } } - /* Show in dmenu - escape choices to prevent command injection */ char *escaped_choices = shell_escape(choices); char *cmd = dwn_malloc(strlen(escaped_choices) + 64); snprintf(cmd, strlen(escaped_choices) + 64, "echo %s | dmenu -l 10 -p 'Results:'", escaped_choices); @@ -817,10 +749,8 @@ static void exa_launcher_callback(ExaRequest *req) dwn_free(escaped_choices); if (selected != NULL && selected[0] != '\0') { - /* Find which result was selected and open URL */ for (int i = 0; i < req->result_count; i++) { if (strncmp(selected, req->results[i].title, strlen(req->results[i].title)) == 0) { - /* Escape URL to prevent command injection */ char *escaped_url = shell_escape(req->results[i].url); char *open_cmd = dwn_malloc(strlen(escaped_url) + 32); snprintf(open_cmd, strlen(escaped_url) + 32, "xdg-open %s &", escaped_url); @@ -847,7 +777,6 @@ void exa_show_app_launcher(void) return; } - /* Get search query from user */ char *query = NULL; if (spawn("command -v dmenu >/dev/null 2>&1") == 0) { @@ -865,7 +794,6 @@ void exa_show_app_launcher(void) return; } - /* Remove trailing newline */ query[strcspn(query, "\n")] = '\0'; notification_show("Exa", "Searching...", query, NULL, 2000); diff --git a/src/applauncher.c b/src/applauncher.c index 4c91df5..094e9b0 100644 --- a/src/applauncher.c +++ b/src/applauncher.c @@ -17,13 +17,10 @@ #include #include -/* Global state */ static AppLauncherState launcher_state = {0}; -/* Path to recent apps file */ static char recent_file_path[512] = {0}; -/* Forward declarations */ static void scan_desktop_dir(const char *dir); static int parse_desktop_file(const char *path, AppEntry *entry); static void load_recent_apps(void); @@ -32,13 +29,11 @@ static void add_to_recent(const char *desktop_id); static char *strip_field_codes(const char *exec); static int compare_apps(const void *a, const void *b); -/* ========== Initialization ========== */ void applauncher_init(void) { memset(&launcher_state, 0, sizeof(launcher_state)); - /* Set up recent file path */ const char *home = getenv("HOME"); if (home == NULL) { struct passwd *pw = getpwuid(getuid()); @@ -47,15 +42,12 @@ void applauncher_init(void) snprintf(recent_file_path, sizeof(recent_file_path), "%s/.local/share/dwn/recent_apps", home); - /* Ensure directory exists */ char dir_path[512]; snprintf(dir_path, sizeof(dir_path), "%s/.local/share/dwn", home); mkdir(dir_path, 0755); - /* Load recent apps first */ load_recent_apps(); - /* Scan application directories */ applauncher_refresh(); LOG_INFO("App launcher initialized with %d applications", launcher_state.app_count); @@ -70,10 +62,8 @@ void applauncher_refresh(void) { launcher_state.app_count = 0; - /* Scan system applications */ scan_desktop_dir("/usr/share/applications"); - /* Scan user applications (takes precedence) */ const char *home = getenv("HOME"); if (home) { char user_apps[512]; @@ -81,14 +71,12 @@ void applauncher_refresh(void) scan_desktop_dir(user_apps); } - /* Sort apps alphabetically by name */ qsort(launcher_state.apps, launcher_state.app_count, sizeof(AppEntry), compare_apps); LOG_DEBUG("Refreshed app list: %d applications found", launcher_state.app_count); } -/* ========== Directory Scanning ========== */ static void scan_desktop_dir(const char *dir) { @@ -103,17 +91,14 @@ static void scan_desktop_dir(const char *dir) break; } - /* Only process .desktop files */ size_t len = strlen(entry->d_name); if (len < 9 || strcmp(entry->d_name + len - 8, ".desktop") != 0) { continue; } - /* Build full path */ char path[1024]; snprintf(path, sizeof(path), "%s/%s", dir, entry->d_name); - /* Check if already exists (user apps override system) */ bool exists = false; for (int i = 0; i < launcher_state.app_count; i++) { if (strcmp(launcher_state.apps[i].desktop_id, entry->d_name) == 0) { @@ -125,7 +110,6 @@ static void scan_desktop_dir(const char *dir) continue; } - /* Parse the desktop file */ AppEntry app = {0}; if (parse_desktop_file(path, &app) == 0) { snprintf(app.desktop_id, sizeof(app.desktop_id), "%s", entry->d_name); @@ -136,7 +120,6 @@ static void scan_desktop_dir(const char *dir) closedir(d); } -/* ========== Desktop File Parsing ========== */ static int parse_desktop_file(const char *path, AppEntry *entry) { @@ -154,14 +137,12 @@ static int parse_desktop_file(const char *path, AppEntry *entry) entry->terminal = false; while (fgets(line, sizeof(line), f) != NULL) { - /* Remove trailing whitespace/newline */ size_t len = strlen(line); while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r' || line[len-1] == ' ' || line[len-1] == '\t')) { line[--len] = '\0'; } - /* Check for section header */ if (line[0] == '[') { in_desktop_entry = (strcmp(line, "[Desktop Entry]") == 0); continue; @@ -171,7 +152,6 @@ static int parse_desktop_file(const char *path, AppEntry *entry) continue; } - /* Parse key=value */ char *eq = strchr(line, '='); if (eq == NULL) { continue; @@ -181,7 +161,6 @@ static int parse_desktop_file(const char *path, AppEntry *entry) char *key = line; char *value = eq + 1; - /* Skip localized entries (Name[en]=...) */ if (strchr(key, '[') != NULL) { continue; } @@ -197,7 +176,6 @@ static int parse_desktop_file(const char *path, AppEntry *entry) } else if (strcmp(key, "Terminal") == 0) { entry->terminal = (strcasecmp(value, "true") == 0); } else if (strcmp(key, "Type") == 0) { - /* Skip non-Application types */ if (strcmp(value, "Application") != 0) { fclose(f); return -1; @@ -211,7 +189,6 @@ static int parse_desktop_file(const char *path, AppEntry *entry) fclose(f); - /* Must have name and exec, and not be hidden */ if (!has_name || !has_exec || entry->hidden) { return -1; } @@ -219,7 +196,6 @@ static int parse_desktop_file(const char *path, AppEntry *entry) return 0; } -/* ========== Recent Apps Management ========== */ static void load_recent_apps(void) { @@ -233,7 +209,6 @@ static void load_recent_apps(void) char line[256]; while (fgets(line, sizeof(line), f) != NULL && launcher_state.recent_count < MAX_RECENT_APPS) { - /* Remove newline */ size_t len = strlen(line); if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; @@ -266,10 +241,8 @@ static void save_recent_apps(void) static void add_to_recent(const char *desktop_id) { - /* Remove if already in list */ for (int i = 0; i < launcher_state.recent_count; i++) { if (strcmp(launcher_state.recent[i], desktop_id) == 0) { - /* Shift everything down */ for (int j = i; j > 0; j--) { strcpy(launcher_state.recent[j], launcher_state.recent[j-1]); } @@ -279,7 +252,6 @@ static void add_to_recent(const char *desktop_id) } } - /* Add to front, shift others */ if (launcher_state.recent_count < MAX_RECENT_APPS) { launcher_state.recent_count++; } @@ -292,7 +264,6 @@ static void add_to_recent(const char *desktop_id) save_recent_apps(); } -/* ========== Helper Functions ========== */ static char *strip_field_codes(const char *exec) { @@ -301,13 +272,11 @@ static char *strip_field_codes(const char *exec) for (size_t i = 0; exec[i] && j < sizeof(result) - 1; i++) { if (exec[i] == '%' && exec[i+1]) { - /* Skip field codes: %f, %F, %u, %U, %d, %D, %n, %N, %i, %c, %k */ char code = exec[i+1]; if (code == 'f' || code == 'F' || code == 'u' || code == 'U' || code == 'd' || code == 'D' || code == 'n' || code == 'N' || code == 'i' || code == 'c' || code == 'k') { - i++; /* Skip the code character */ - /* Also skip trailing space if any */ + i++; if (exec[i+1] == ' ') { i++; } @@ -318,7 +287,6 @@ static char *strip_field_codes(const char *exec) } result[j] = '\0'; - /* Trim trailing whitespace */ while (j > 0 && (result[j-1] == ' ' || result[j-1] == '\t')) { result[--j] = '\0'; } @@ -333,7 +301,6 @@ static int compare_apps(const void *a, const void *b) return strcasecmp(app_a->name, app_b->name); } -/* Find app by desktop_id */ static AppEntry *find_app_by_id(const char *desktop_id) { for (int i = 0; i < launcher_state.app_count; i++) { @@ -344,7 +311,6 @@ static AppEntry *find_app_by_id(const char *desktop_id) return NULL; } -/* Find app by name */ static AppEntry *find_app_by_name(const char *name) { for (int i = 0; i < launcher_state.app_count; i++) { @@ -355,7 +321,6 @@ static AppEntry *find_app_by_name(const char *name) return NULL; } -/* ========== Launcher Display ========== */ void applauncher_show(void) { @@ -364,10 +329,6 @@ void applauncher_show(void) return; } - /* Build the list for dmenu: - * - Recent apps first (if any) - * - Then all apps alphabetically - */ size_t buf_size = launcher_state.app_count * 140; char *choices = malloc(buf_size); if (choices == NULL) { @@ -376,7 +337,6 @@ void applauncher_show(void) } choices[0] = '\0'; - /* Track which apps we've added (to avoid duplicates) */ bool *added = calloc(launcher_state.app_count, sizeof(bool)); if (added == NULL) { free(choices); @@ -385,11 +345,9 @@ void applauncher_show(void) size_t pos = 0; - /* Add recent apps first */ for (int i = 0; i < launcher_state.recent_count; i++) { AppEntry *app = find_app_by_id(launcher_state.recent[i]); if (app != NULL) { - /* Mark as added */ for (int j = 0; j < launcher_state.app_count; j++) { if (&launcher_state.apps[j] == app) { added[j] = true; @@ -405,7 +363,6 @@ void applauncher_show(void) } } - /* Add remaining apps */ for (int i = 0; i < launcher_state.app_count; i++) { if (added[i]) { continue; @@ -419,12 +376,11 @@ void applauncher_show(void) } if (pos > 0) { - choices[pos-1] = '\0'; /* Remove trailing newline */ + choices[pos-1] = '\0'; } free(added); - /* Write choices to a temp file to avoid shell escaping issues */ char tmp_path[] = "/tmp/dwn_apps_XXXXXX"; int tmp_fd = mkstemp(tmp_path); if (tmp_fd < 0) { @@ -443,7 +399,6 @@ void applauncher_show(void) return; } - /* Run dmenu */ char cmd[1024]; snprintf(cmd, sizeof(cmd), "cat '%s' | dmenu -i -l 10 -p 'Run:'; rm -f '%s'", @@ -458,7 +413,6 @@ void applauncher_show(void) char selected[256] = {0}; if (fgets(selected, sizeof(selected), p) != NULL) { - /* Remove trailing newline */ size_t len = strlen(selected); if (len > 0 && selected[len-1] == '\n') { selected[len-1] = '\0'; @@ -467,15 +421,13 @@ void applauncher_show(void) pclose(p); if (selected[0] == '\0') { - return; /* User cancelled */ + return; } - /* Find and launch the selected app */ AppEntry *app = find_app_by_name(selected); if (app != NULL) { applauncher_launch(app->desktop_id); } else { - /* User typed a custom command */ spawn_async(selected); } } @@ -488,10 +440,8 @@ void applauncher_launch(const char *desktop_id) return; } - /* Strip field codes from exec */ char *exec_cmd = strip_field_codes(app->exec); - /* Build command */ char cmd[1024]; if (app->terminal) { const char *terminal = config_get_terminal(); @@ -503,6 +453,5 @@ void applauncher_launch(const char *desktop_id) LOG_INFO("Launching: %s (%s)", app->name, cmd); spawn_async(cmd); - /* Add to recent apps */ add_to_recent(desktop_id); } diff --git a/src/atoms.c b/src/atoms.c index b83d625..70b2d74 100644 --- a/src/atoms.c +++ b/src/atoms.c @@ -12,19 +12,16 @@ #include #include -/* Global atom containers */ EWMHAtoms ewmh; ICCCMAtoms icccm; MiscAtoms misc_atoms; -/* Helper macro for atom initialization */ #define ATOM(name) XInternAtom(display, name, False) void atoms_init(Display *display) { LOG_DEBUG("Initializing X11 atoms"); - /* EWMH root window properties */ ewmh.NET_SUPPORTED = ATOM("_NET_SUPPORTED"); ewmh.NET_SUPPORTING_WM_CHECK = ATOM("_NET_SUPPORTING_WM_CHECK"); ewmh.NET_CLIENT_LIST = ATOM("_NET_CLIENT_LIST"); @@ -37,7 +34,6 @@ void atoms_init(Display *display) ewmh.NET_ACTIVE_WINDOW = ATOM("_NET_ACTIVE_WINDOW"); ewmh.NET_WORKAREA = ATOM("_NET_WORKAREA"); - /* EWMH client window properties */ ewmh.NET_WM_NAME = ATOM("_NET_WM_NAME"); ewmh.NET_WM_VISIBLE_NAME = ATOM("_NET_WM_VISIBLE_NAME"); ewmh.NET_WM_DESKTOP = ATOM("_NET_WM_DESKTOP"); @@ -48,7 +44,6 @@ void atoms_init(Display *display) ewmh.NET_WM_STRUT_PARTIAL = ATOM("_NET_WM_STRUT_PARTIAL"); ewmh.NET_WM_PID = ATOM("_NET_WM_PID"); - /* Window types */ ewmh.NET_WM_WINDOW_TYPE_DESKTOP = ATOM("_NET_WM_WINDOW_TYPE_DESKTOP"); ewmh.NET_WM_WINDOW_TYPE_DOCK = ATOM("_NET_WM_WINDOW_TYPE_DOCK"); ewmh.NET_WM_WINDOW_TYPE_TOOLBAR = ATOM("_NET_WM_WINDOW_TYPE_TOOLBAR"); @@ -59,7 +54,6 @@ void atoms_init(Display *display) ewmh.NET_WM_WINDOW_TYPE_NORMAL = ATOM("_NET_WM_WINDOW_TYPE_NORMAL"); ewmh.NET_WM_WINDOW_TYPE_NOTIFICATION = ATOM("_NET_WM_WINDOW_TYPE_NOTIFICATION"); - /* Window states */ ewmh.NET_WM_STATE_MODAL = ATOM("_NET_WM_STATE_MODAL"); ewmh.NET_WM_STATE_STICKY = ATOM("_NET_WM_STATE_STICKY"); ewmh.NET_WM_STATE_MAXIMIZED_VERT = ATOM("_NET_WM_STATE_MAXIMIZED_VERT"); @@ -74,7 +68,6 @@ void atoms_init(Display *display) ewmh.NET_WM_STATE_DEMANDS_ATTENTION = ATOM("_NET_WM_STATE_DEMANDS_ATTENTION"); ewmh.NET_WM_STATE_FOCUSED = ATOM("_NET_WM_STATE_FOCUSED"); - /* Actions */ ewmh.NET_WM_ACTION_MOVE = ATOM("_NET_WM_ACTION_MOVE"); ewmh.NET_WM_ACTION_RESIZE = ATOM("_NET_WM_ACTION_RESIZE"); ewmh.NET_WM_ACTION_MINIMIZE = ATOM("_NET_WM_ACTION_MINIMIZE"); @@ -86,21 +79,18 @@ void atoms_init(Display *display) ewmh.NET_WM_ACTION_CHANGE_DESKTOP = ATOM("_NET_WM_ACTION_CHANGE_DESKTOP"); ewmh.NET_WM_ACTION_CLOSE = ATOM("_NET_WM_ACTION_CLOSE"); - /* Client messages */ ewmh.NET_CLOSE_WINDOW = ATOM("_NET_CLOSE_WINDOW"); ewmh.NET_MOVERESIZE_WINDOW = ATOM("_NET_MOVERESIZE_WINDOW"); ewmh.NET_WM_MOVERESIZE = ATOM("_NET_WM_MOVERESIZE"); ewmh.NET_REQUEST_FRAME_EXTENTS = ATOM("_NET_REQUEST_FRAME_EXTENTS"); ewmh.NET_FRAME_EXTENTS = ATOM("_NET_FRAME_EXTENTS"); - /* System tray */ ewmh.NET_SYSTEM_TRAY_OPCODE = ATOM("_NET_SYSTEM_TRAY_OPCODE"); ewmh.NET_SYSTEM_TRAY_S0 = ATOM("_NET_SYSTEM_TRAY_S0"); ewmh.MANAGER = ATOM("MANAGER"); ewmh.XEMBED = ATOM("_XEMBED"); ewmh.XEMBED_INFO = ATOM("_XEMBED_INFO"); - /* ICCCM atoms */ icccm.WM_PROTOCOLS = ATOM("WM_PROTOCOLS"); icccm.WM_DELETE_WINDOW = ATOM("WM_DELETE_WINDOW"); icccm.WM_TAKE_FOCUS = ATOM("WM_TAKE_FOCUS"); @@ -112,7 +102,6 @@ void atoms_init(Display *display) icccm.WM_CLIENT_LEADER = ATOM("WM_CLIENT_LEADER"); icccm.WM_WINDOW_ROLE = ATOM("WM_WINDOW_ROLE"); - /* Misc atoms */ misc_atoms.UTF8_STRING = ATOM("UTF8_STRING"); misc_atoms.COMPOUND_TEXT = ATOM("COMPOUND_TEXT"); misc_atoms.MOTIF_WM_HINTS = ATOM("_MOTIF_WM_HINTS"); @@ -123,7 +112,6 @@ void atoms_init(Display *display) LOG_DEBUG("X11 atoms initialized"); } -/* ========== EWMH Setup ========== */ void atoms_setup_ewmh(void) { @@ -134,10 +122,8 @@ void atoms_setup_ewmh(void) Display *dpy = dwn->display; Window root = dwn->root; - /* Create a check window for _NET_SUPPORTING_WM_CHECK */ Window check = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); - /* Set _NET_SUPPORTING_WM_CHECK on root and check window */ XChangeProperty(dpy, root, ewmh.NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&check, 1); @@ -145,12 +131,10 @@ void atoms_setup_ewmh(void) XA_WINDOW, 32, PropModeReplace, (unsigned char *)&check, 1); - /* Set _NET_WM_NAME on check window */ XChangeProperty(dpy, check, ewmh.NET_WM_NAME, misc_atoms.UTF8_STRING, 8, PropModeReplace, (unsigned char *)DWN_NAME, strlen(DWN_NAME)); - /* Set supported atoms */ Atom supported[] = { ewmh.NET_SUPPORTED, ewmh.NET_SUPPORTING_WM_CHECK, @@ -176,13 +160,10 @@ void atoms_setup_ewmh(void) XA_ATOM, 32, PropModeReplace, (unsigned char *)supported, sizeof(supported) / sizeof(Atom)); - /* Set number of desktops */ atoms_set_number_of_desktops(MAX_WORKSPACES); - /* Set current desktop */ atoms_set_current_desktop(0); - /* Update desktop names */ atoms_update_desktop_names(); LOG_INFO("EWMH compliance initialized"); @@ -216,7 +197,6 @@ void atoms_update_desktop_names(void) return; } - /* Build null-separated list of workspace names */ char names[MAX_WORKSPACES * 32]; int offset = 0; @@ -270,7 +250,6 @@ void atoms_set_number_of_desktops(int count) (unsigned char *)&data, 1); } -/* ========== Window property helpers ========== */ bool atoms_get_window_type(Window window, Atom *type) { @@ -343,7 +322,6 @@ char *atoms_get_window_name(Window window) unsigned long nitems, bytes_after; unsigned char *data = NULL; - /* Try _NET_WM_NAME first (UTF-8) */ if (XGetWindowProperty(dpy, window, ewmh.NET_WM_NAME, 0, 256, False, misc_atoms.UTF8_STRING, &actual_type, &actual_format, &nitems, &bytes_after, @@ -353,7 +331,6 @@ char *atoms_get_window_name(Window window) return name; } - /* Fall back to WM_NAME */ if (XGetWindowProperty(dpy, window, icccm.WM_NAME, 0, 256, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, @@ -363,7 +340,6 @@ char *atoms_get_window_name(Window window) return name; } - /* Last resort: XFetchName */ char *name = NULL; if (XFetchName(dpy, window, &name) && name != NULL) { char *result = dwn_strdup(name); @@ -398,7 +374,6 @@ bool atoms_get_wm_class(Window window, char *class_name, char *instance_name, si return false; } -/* ========== Protocol helpers ========== */ bool atoms_window_supports_protocol(Window window, Atom protocol) { diff --git a/src/client.c b/src/client.c index d0771f8..921b205 100644 --- a/src/client.c +++ b/src/client.c @@ -17,11 +17,9 @@ #include #include -/* ========== Client creation/destruction ========== */ Client *client_create(Window window) { - /* Defensive: validate global state before proceeding */ if (dwn == NULL || dwn->display == NULL) { LOG_ERROR("client_create: dwn or display is NULL"); return NULL; @@ -46,7 +44,6 @@ Client *client_create(Window window) client->next = NULL; client->prev = NULL; - /* Get initial geometry from window */ XWindowAttributes wa; int orig_width = 640, orig_height = 480; if (XGetWindowAttributes(dwn->display, window, &wa)) { @@ -54,7 +51,6 @@ Client *client_create(Window window) orig_height = wa.height; } - /* Calculate work area (account for panels) */ int work_x = 0; int work_y = 0; int work_width = dwn->screen_width; @@ -70,28 +66,23 @@ Client *client_create(Window window) } } - /* Set size to 75% of work area or original size, whichever is larger */ int target_width = (work_width * 75) / 100; int target_height = (work_height * 75) / 100; client->width = (orig_width > target_width) ? orig_width : target_width; client->height = (orig_height > target_height) ? orig_height : target_height; - /* Clamp to work area */ if (client->width > work_width - 20) client->width = work_width - 20; if (client->height > work_height - 20) client->height = work_height - 20; - /* Center on screen */ client->x = work_x + (work_width - client->width) / 2; client->y = work_y + (work_height - client->height) / 2; - /* Save original geometry for floating restore */ client->old_x = client->x; client->old_y = client->y; client->old_width = client->width; client->old_height = client->height; - /* Update properties */ client_update_title(client); client_update_class(client); @@ -111,13 +102,11 @@ void client_destroy(Client *client) dwn_free(client); } -/* Sync log for debugging - disabled in production */ static inline void client_sync_log(const char *msg) { - (void)msg; /* No-op - enable for debugging */ + (void)msg; } -/* ========== Client management ========== */ Client *client_manage(Window window) { @@ -128,14 +117,12 @@ Client *client_manage(Window window) return NULL; } - /* Check if already managed */ Client *existing = client_find_by_window(window); if (existing != NULL) { client_sync_log("client_manage: already managed"); return existing; } - /* Check window type - don't manage docks, desktops */ if (client_is_dock(window) || client_is_desktop(window)) { LOG_DEBUG("Skipping dock/desktop window: %lu", window); client_sync_log("client_manage: skip dock/desktop"); @@ -145,7 +132,6 @@ Client *client_manage(Window window) LOG_DEBUG("Managing window: %lu", window); client_sync_log("client_manage: creating client"); - /* Create client */ Client *client = client_create(window); if (client == NULL) { LOG_ERROR("client_manage: failed to create client for window %lu", window); @@ -155,19 +141,16 @@ Client *client_manage(Window window) client_sync_log("client_manage: checking dialog"); - /* Check if it should be floating (dialogs, etc.) */ if (client_is_dialog(window)) { client->flags |= CLIENT_FLOATING; } client_sync_log("client_manage: creating frame"); - /* Create frame with decorations */ client_create_frame(client); client_sync_log("client_manage: verifying window"); - /* Verify window still exists before reparenting */ XWindowAttributes wa; if (!XGetWindowAttributes(dwn->display, window, &wa)) { LOG_WARN("client_manage: window %lu disappeared before reparenting", window); @@ -179,9 +162,8 @@ Client *client_manage(Window window) client_sync_log("client_manage: reparenting"); client_reparent_to_frame(client); - /* Verify both window AND frame still exist after reparent */ client_sync_log("client_manage: verifying after reparent"); - XSync(dwn->display, False); /* Flush any pending errors */ + XSync(dwn->display, False); if (client->frame == None) { client_sync_log("client_manage: frame is None after reparent"); @@ -207,24 +189,19 @@ Client *client_manage(Window window) } client_sync_log("client_manage: configuring"); - /* Resize client window to match frame dimensions */ client_configure(client); client_sync_log("client_manage: adding to list"); - /* Add to client list */ client_add_to_list(client); client_sync_log("client_manage: adding to workspace"); - /* Add to current workspace */ workspace_add_client(dwn->current_workspace, client); client_sync_log("client_manage: setting EWMH"); - /* Set EWMH properties */ atoms_set_window_desktop(window, client->workspace); atoms_update_client_list(); client_sync_log("client_manage: verifying window again"); - /* Verify window still exists before subscribing to events */ if (!XGetWindowAttributes(dwn->display, window, &wa)) { LOG_WARN("client_manage: window %lu disappeared before event setup", window); client_sync_log("client_manage: window gone before events"); @@ -236,22 +213,18 @@ Client *client_manage(Window window) } client_sync_log("client_manage: selecting input"); - /* Subscribe to window events */ XSelectInput(dwn->display, window, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask); client_sync_log("client_manage: grabbing button"); - /* Grab button for click-to-focus (will replay event to app after focusing) */ XGrabButton(dwn->display, Button1, AnyModifier, window, False, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); client_sync_log("client_manage: syncing X"); - /* Sync to ensure all operations completed */ XSync(dwn->display, False); client_sync_log("client_manage: showing"); - /* Map and focus */ client_show(client); client_sync_log("client_manage: focusing"); @@ -273,27 +246,21 @@ void client_unmanage(Client *client) LOG_DEBUG("Unmanaging window: %lu", client->window); client_sync_log("client_unmanage: remove from workspace"); - /* Remove from workspace */ workspace_remove_client(client->workspace, client); client_sync_log("client_unmanage: remove from list"); - /* Remove from client list */ client_remove_from_list(client); client_sync_log("client_unmanage: reparent from frame"); - /* Reparent back to root */ client_reparent_from_frame(client); client_sync_log("client_unmanage: update EWMH"); - /* Update EWMH */ atoms_update_client_list(); client_sync_log("client_unmanage: destroy client"); - /* Destroy client */ client_destroy(client); client_sync_log("client_unmanage: focus next"); - /* Focus next client if needed */ Workspace *ws = workspace_get_current(); if (ws != NULL && ws->focused == NULL) { Client *next = workspace_get_first_client(dwn->current_workspace); @@ -333,7 +300,6 @@ Client *client_find_by_frame(Window frame) return NULL; } -/* ========== Client state ========== */ void client_focus(Client *client) { @@ -344,14 +310,12 @@ void client_focus(Client *client) return; } - /* Defensive: verify window is still valid */ if (client->window == None) { LOG_WARN("client_focus: client has invalid window (None)"); client_sync_log("client_focus: window is None"); return; } - /* Verify window still exists before focusing */ XWindowAttributes wa; if (!XGetWindowAttributes(dwn->display, client->window, &wa)) { LOG_WARN("client_focus: window %lu no longer exists", client->window); @@ -361,7 +325,6 @@ void client_focus(Client *client) client_sync_log("client_focus: unfocusing previous"); - /* Unfocus previous */ Workspace *ws = workspace_get(client->workspace); if (ws != NULL && ws->focused != NULL && ws->focused != client) { client_unfocus(ws->focused); @@ -369,30 +332,25 @@ void client_focus(Client *client) client_sync_log("client_focus: XSetInputFocus"); - /* Set focus with error handling */ XSetInputFocus(dwn->display, client->window, RevertToPointerRoot, CurrentTime); - XSync(dwn->display, False); /* Catch any X errors immediately */ + XSync(dwn->display, False); client_sync_log("client_focus: updating workspace"); - /* Update workspace */ if (ws != NULL) { ws->focused = client; } client_sync_log("client_focus: raising"); - /* Raise window */ client_raise(client); client_sync_log("client_focus: decorations"); - /* Update decorations */ decorations_render(client, true); client_sync_log("client_focus: EWMH"); - /* Update EWMH */ atoms_set_active_window(client->window); client_sync_log("client_focus: DONE"); @@ -405,7 +363,6 @@ void client_unfocus(Client *client) return; } - /* Update decorations */ decorations_render(client, false); } @@ -426,7 +383,6 @@ void client_raise(Client *client) XRaiseWindow(dwn->display, win); - /* Keep notifications on top */ notifications_raise_all(); } @@ -468,7 +424,6 @@ void client_restore(Client *client) client_focus(client); } -/* ========== Client geometry ========== */ void client_move(Client *client, int x, int y) { @@ -517,7 +472,6 @@ void client_configure(Client *client) return; } - /* Verify window still exists before configuring */ XWindowAttributes wa; if (!XGetWindowAttributes(dwn->display, client->window, &wa)) { LOG_DEBUG("client_configure: window no longer exists"); @@ -529,14 +483,12 @@ void client_configure(Client *client) int border = client->border_width; if (client->frame != None) { - /* Move/resize frame */ XMoveResizeWindow(dwn->display, client->frame, client->x - border, client->y - title_height - border, client->width + 2 * border, client->height + title_height + 2 * border); - /* Move/resize client window within frame */ XMoveResizeWindow(dwn->display, client->window, border, title_height + border, client->width, client->height); @@ -546,9 +498,8 @@ void client_configure(Client *client) client->width, client->height); } - /* Send configure notify to client */ XConfigureEvent ce; - memset(&ce, 0, sizeof(ce)); /* Zero-initialize */ + memset(&ce, 0, sizeof(ce)); ce.type = ConfigureNotify; ce.event = client->window; ce.window = client->window; @@ -576,19 +527,16 @@ void client_apply_size_hints(Client *client, int *width, int *height) return; } - /* Minimum size */ if (hints.flags & PMinSize) { if (*width < hints.min_width) *width = hints.min_width; if (*height < hints.min_height) *height = hints.min_height; } - /* Maximum size */ if (hints.flags & PMaxSize) { if (*width > hints.max_width) *width = hints.max_width; if (*height > hints.max_height) *height = hints.max_height; } - /* Size increments */ if (hints.flags & PResizeInc) { int base_w = (hints.flags & PBaseSize) ? hints.base_width : 0; int base_h = (hints.flags & PBaseSize) ? hints.base_height : 0; @@ -598,7 +546,6 @@ void client_apply_size_hints(Client *client, int *width, int *height) } } -/* ========== Client properties ========== */ void client_update_title(Client *client) { @@ -615,7 +562,6 @@ void client_update_title(Client *client) strncpy(client->title, "Untitled", sizeof(client->title) - 1); } - /* Redraw decorations */ if (client->frame != None) { Workspace *ws = workspace_get(client->workspace); bool focused = (ws != NULL && ws->focused == client); @@ -638,7 +584,6 @@ void client_set_fullscreen(Client *client, bool fullscreen) return; } - /* Verify window still exists */ if (client->window == None) { return; } @@ -650,7 +595,6 @@ void client_set_fullscreen(Client *client, bool fullscreen) } if (fullscreen) { - /* Save current geometry */ if (!(client->flags & CLIENT_FULLSCREEN)) { client->old_x = client->x; client->old_y = client->y; @@ -660,13 +604,11 @@ void client_set_fullscreen(Client *client, bool fullscreen) client->flags |= CLIENT_FULLSCREEN; - /* Get monitor dimensions */ client->x = 0; client->y = 0; client->width = dwn->screen_width; client->height = dwn->screen_height; - /* Reparent window to root and hide frame */ if (client->frame != None) { XUnmapWindow(dwn->display, client->frame); XSync(dwn->display, False); @@ -680,13 +622,11 @@ void client_set_fullscreen(Client *client, bool fullscreen) } else { client->flags &= ~CLIENT_FULLSCREEN; - /* Restore geometry */ client->x = client->old_x; client->y = client->old_y; client->width = client->old_width; client->height = client->old_height; - /* Reparent back to frame and show it */ if (client->frame != None) { int title_height = config_get_title_height(); int border = client->border_width; @@ -729,7 +669,6 @@ void client_toggle_floating(Client *client) client_set_floating(client, !(client->flags & CLIENT_FLOATING)); } -/* ========== Window type checking ========== */ bool client_is_floating(Client *client) { @@ -753,7 +692,6 @@ bool client_is_dialog(Window window) return type == ewmh.NET_WM_WINDOW_TYPE_DIALOG; } - /* Check transient hint */ Window transient_for = None; if (XGetTransientForHint(dwn->display, window, &transient_for)) { return transient_for != None; @@ -780,7 +718,6 @@ bool client_is_desktop(Window window) return false; } -/* ========== Frame management ========== */ void client_create_frame(Client *client) { @@ -793,7 +730,6 @@ void client_create_frame(Client *client) return; } - /* Defensive: verify client window is valid */ if (client->window == None) { LOG_ERROR("client_create_frame: client has no valid window"); return; @@ -802,7 +738,6 @@ void client_create_frame(Client *client) int title_height = config_get_title_height(); int border = client->border_width; - /* Ensure reasonable dimensions */ int frame_width = client->width + 2 * border; int frame_height = client->height + title_height + 2 * border; if (frame_width <= 0 || frame_height <= 0) { @@ -810,9 +745,8 @@ void client_create_frame(Client *client) return; } - /* Create frame window */ XSetWindowAttributes swa; - memset(&swa, 0, sizeof(swa)); /* Defensive: zero-initialize */ + memset(&swa, 0, sizeof(swa)); swa.override_redirect = True; swa.background_pixel = dwn->config->colors.title_unfocused_bg; swa.border_pixel = dwn->config->colors.border_unfocused; @@ -830,14 +764,13 @@ void client_create_frame(Client *client) CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask, &swa); - /* Verify frame was created successfully */ if (client->frame == None) { LOG_ERROR("client_create_frame: XCreateWindow failed for window %lu", client->window); return; } XSetWindowBorderWidth(dwn->display, client->frame, border); - XSync(dwn->display, False); /* Catch any X errors */ + XSync(dwn->display, False); LOG_DEBUG("Created frame %lu for window %lu", client->frame, client->window); } @@ -862,7 +795,6 @@ void client_reparent_to_frame(Client *client) return; } - /* Verify window still exists before reparenting */ if (client->window == None) { LOG_WARN("client_reparent_to_frame: client has no valid window"); return; @@ -877,16 +809,13 @@ void client_reparent_to_frame(Client *client) int title_height = config_get_title_height(); int border = client->border_width; - /* Sync before reparent to catch any pending errors */ XSync(dwn->display, False); XReparentWindow(dwn->display, client->window, client->frame, border, title_height + border); - /* Sync after reparent to catch errors immediately */ XSync(dwn->display, False); - /* Save the frame window association */ XSaveContext(dwn->display, client->window, XUniqueContext(), (XPointer)client); } @@ -900,24 +829,20 @@ void client_reparent_from_frame(Client *client) return; } - /* Verify window still exists before reparenting */ XWindowAttributes wa; if (!XGetWindowAttributes(dwn->display, client->window, &wa)) { LOG_WARN("client_reparent_from_frame: window %lu no longer exists", client->window); return; } - /* Sync before reparent to catch any pending errors */ XSync(dwn->display, False); XReparentWindow(dwn->display, client->window, dwn->root, client->x, client->y); - /* Sync after reparent to catch errors immediately */ XSync(dwn->display, False); } -/* ========== Visibility ========== */ void client_show(Client *client) { @@ -929,7 +854,6 @@ void client_show(Client *client) return; } - /* Verify window still exists before mapping */ XWindowAttributes wa; if (!XGetWindowAttributes(dwn->display, client->window, &wa)) { LOG_DEBUG("client_show: window no longer exists"); @@ -952,7 +876,6 @@ void client_hide(Client *client) return; } - /* Verify window still exists before unmapping */ XWindowAttributes wa; if (!XGetWindowAttributes(dwn->display, client->window, &wa)) { LOG_DEBUG("client_hide: window no longer exists"); @@ -971,12 +894,10 @@ bool client_is_visible(Client *client) return false; } - /* Hidden windows are not visible */ if (client->flags & CLIENT_MINIMIZED) { return false; } - /* Only visible if on current workspace (or sticky) */ if (!(client->flags & CLIENT_STICKY) && client->workspace != (unsigned int)dwn->current_workspace) { return false; @@ -985,7 +906,6 @@ bool client_is_visible(Client *client) return true; } -/* ========== Close handling ========== */ void client_close(Client *client) { @@ -997,18 +917,15 @@ void client_close(Client *client) return; } - /* Verify window still exists */ XWindowAttributes wa; if (!XGetWindowAttributes(dwn->display, client->window, &wa)) { LOG_DEBUG("client_close: window no longer exists"); return; } - /* Try WM_DELETE_WINDOW first */ if (atoms_window_supports_protocol(client->window, icccm.WM_DELETE_WINDOW)) { atoms_send_protocol(client->window, icccm.WM_DELETE_WINDOW, CurrentTime); } else { - /* Force kill */ client_kill(client); } } @@ -1026,7 +943,6 @@ void client_kill(Client *client) XKillClient(dwn->display, client->window); } -/* ========== List operations ========== */ void client_add_to_list(Client *client) { @@ -1080,7 +996,6 @@ int client_count_on_workspace(int workspace) return count; } -/* ========== Iteration ========== */ Client *client_get_next(Client *client) { diff --git a/src/config.c b/src/config.c index 4f341a7..008716d 100644 --- a/src/config.c +++ b/src/config.c @@ -12,7 +12,6 @@ #include #include -/* Default color values (dark modern theme) */ #define DEFAULT_PANEL_BG "#1a1a2e" #define DEFAULT_PANEL_FG "#e0e0e0" #define DEFAULT_WS_ACTIVE "#4a90d9" @@ -27,7 +26,6 @@ #define DEFAULT_NOTIFICATION_BG "#2a2a3e" #define DEFAULT_NOTIFICATION_FG "#ffffff" -/* ========== Configuration creation/destruction ========== */ Config *config_create(void) { @@ -39,7 +37,6 @@ Config *config_create(void) void config_destroy(Config *cfg) { if (cfg != NULL) { - /* Securely wipe API keys before freeing */ secure_wipe(cfg->openrouter_api_key, sizeof(cfg->openrouter_api_key)); secure_wipe(cfg->exa_api_key, sizeof(cfg->exa_api_key)); dwn_free(cfg); @@ -52,41 +49,34 @@ void config_set_defaults(Config *cfg) return; } - /* General */ strncpy(cfg->terminal, "xfce4-terminal", sizeof(cfg->terminal) - 1); strncpy(cfg->launcher, "dmenu_run", sizeof(cfg->launcher) - 1); strncpy(cfg->file_manager, "thunar", sizeof(cfg->file_manager) - 1); cfg->focus_mode = FOCUS_CLICK; cfg->show_decorations = true; - /* Appearance */ cfg->border_width = DEFAULT_BORDER_WIDTH; cfg->title_height = DEFAULT_TITLE_HEIGHT; cfg->panel_height = DEFAULT_PANEL_HEIGHT; cfg->gap = DEFAULT_GAP; strncpy(cfg->font_name, "fixed", sizeof(cfg->font_name) - 1); - /* Layout */ cfg->default_master_ratio = 0.55f; cfg->default_master_count = 1; cfg->default_layout = LAYOUT_TILING; - /* Panels */ cfg->top_panel_enabled = true; cfg->bottom_panel_enabled = true; - /* AI (disabled by default, enabled if API key found) */ cfg->openrouter_api_key[0] = '\0'; cfg->exa_api_key[0] = '\0'; strncpy(cfg->ai_model, "google/gemini-2.0-flash-exp:free", sizeof(cfg->ai_model) - 1); cfg->ai_enabled = false; - /* Paths */ strncpy(cfg->config_path, "~/.config/dwn/config", sizeof(cfg->config_path) - 1); strncpy(cfg->log_path, "~/.local/share/dwn/dwn.log", sizeof(cfg->log_path) - 1); } -/* ========== INI Parser ========== */ typedef struct { Config *cfg; @@ -181,12 +171,10 @@ bool config_parse_ini(const char *path, ConfigCallback callback, void *user_data while (fgets(line, sizeof(line), f) != NULL) { char *trimmed = str_trim(line); - /* Skip empty lines and comments */ if (trimmed[0] == '\0' || trimmed[0] == '#' || trimmed[0] == ';') { continue; } - /* Section header */ if (trimmed[0] == '[') { char *end = strchr(trimmed, ']'); if (end != NULL) { @@ -196,14 +184,12 @@ bool config_parse_ini(const char *path, ConfigCallback callback, void *user_data continue; } - /* Key = value */ char *equals = strchr(trimmed, '='); if (equals != NULL) { *equals = '\0'; char *key = str_trim(trimmed); char *value = str_trim(equals + 1); - /* Remove quotes from value */ size_t vlen = strlen(value); if (vlen >= 2 && ((value[0] == '"' && value[vlen-1] == '"') || (value[0] == '\'' && value[vlen-1] == '\''))) { @@ -219,7 +205,6 @@ bool config_parse_ini(const char *path, ConfigCallback callback, void *user_data return true; } -/* ========== Load configuration ========== */ bool config_load(Config *cfg, const char *path) { @@ -227,16 +212,13 @@ bool config_load(Config *cfg, const char *path) return false; } - /* Set defaults first */ config_set_defaults(cfg); - /* Determine config path */ const char *config_path = path; if (config_path == NULL) { config_path = cfg->config_path; } - /* Parse config file first (environment variables override below) */ ParseContext ctx = { .cfg = cfg }; char *expanded = expand_path(config_path); @@ -249,7 +231,6 @@ bool config_load(Config *cfg, const char *path) dwn_free(expanded); - /* Check for API keys in environment (override config file) */ const char *openrouter_key = getenv("OPENROUTER_API_KEY"); if (openrouter_key != NULL && openrouter_key[0] != '\0') { strncpy(cfg->openrouter_api_key, openrouter_key, sizeof(cfg->openrouter_api_key) - 1); @@ -262,7 +243,6 @@ bool config_load(Config *cfg, const char *path) strncpy(cfg->exa_api_key, exa_key, sizeof(cfg->exa_api_key) - 1); } - /* Log AI status */ if (cfg->ai_enabled) { LOG_INFO("AI features enabled"); } @@ -275,13 +255,11 @@ bool config_reload(Config *cfg) if (cfg == NULL) { return false; } - /* Securely wipe existing API keys before reloading */ secure_wipe(cfg->openrouter_api_key, sizeof(cfg->openrouter_api_key)); secure_wipe(cfg->exa_api_key, sizeof(cfg->exa_api_key)); return config_load(cfg, cfg->config_path); } -/* ========== Getters ========== */ const char *config_get_terminal(void) { @@ -339,7 +317,6 @@ const ColorScheme *config_get_colors(void) return NULL; } -/* Initialize colors (must be called after display is open) */ void config_init_colors(Config *cfg) { if (cfg == NULL || dwn == NULL || dwn->display == NULL) { diff --git a/src/decorations.c b/src/decorations.c index 3ba91b1..7b7c350 100644 --- a/src/decorations.c +++ b/src/decorations.c @@ -13,14 +13,11 @@ #include #include -/* Button dimensions */ #define BUTTON_SIZE 16 #define BUTTON_PADDING 4 -/* Resize edge size */ #define RESIZE_EDGE 8 -/* ========== Initialization ========== */ void decorations_init(void) { @@ -29,10 +26,8 @@ void decorations_init(void) void decorations_cleanup(void) { - /* Nothing to clean up */ } -/* ========== Rendering ========== */ void decorations_render(Client *client, bool focused) { @@ -64,25 +59,21 @@ void decorations_render_title_bar(Client *client, bool focused) int title_height = config_get_title_height(); int border = client->border_width; - /* Set colors based on focus */ unsigned long bg_color = focused ? colors->title_focused_bg : colors->title_unfocused_bg; unsigned long fg_color = focused ? colors->title_focused_fg : colors->title_unfocused_fg; - /* Draw title bar background */ XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, border, border, client->width, title_height); - /* Draw title text */ if (client->title[0] != '\0' && dwn->xft_font != NULL) { int text_y = border + (title_height + dwn->xft_font->ascent) / 2; int text_x = border + BUTTON_PADDING; - /* Truncate title if too long */ int max_width = client->width - 3 * (BUTTON_SIZE + BUTTON_PADDING) - 2 * BUTTON_PADDING; char display_title[256]; - strncpy(display_title, client->title, sizeof(display_title) - 4); /* Leave room for "..." */ + strncpy(display_title, client->title, sizeof(display_title) - 4); display_title[sizeof(display_title) - 4] = '\0'; XGlyphInfo extents; @@ -90,16 +81,14 @@ void decorations_render_title_bar(Client *client, bool focused) (const FcChar8 *)display_title, strlen(display_title), &extents); int text_width = extents.xOff; - /* Truncate UTF-8 aware: find valid UTF-8 boundary */ bool title_truncated = false; while (text_width > max_width && strlen(display_title) > 3) { size_t len = strlen(display_title); - /* Move back to find UTF-8 character boundary */ size_t cut = len - 1; while (cut > 0 && (display_title[cut] & 0xC0) == 0x80) { - cut--; /* Skip continuation bytes */ + cut--; } - if (cut > 0) cut--; /* Remove one more character for ellipsis space */ + if (cut > 0) cut--; while (cut > 0 && (display_title[cut] & 0xC0) == 0x80) { cut--; } @@ -114,7 +103,6 @@ void decorations_render_title_bar(Client *client, bool focused) strncat(display_title, "...", sizeof(display_title) - strlen(display_title) - 1); } - /* Draw with Xft for UTF-8 support */ XftDraw *xft_draw = XftDrawCreate(dpy, client->frame, DefaultVisual(dpy, dwn->screen), dwn->colormap); @@ -155,7 +143,6 @@ void decorations_render_buttons(Client *client, bool focused) int title_height = config_get_title_height(); int border = client->border_width; - /* Button positions (right-aligned) */ int button_y = border + (title_height - BUTTON_SIZE) / 2; int close_x = border + client->width - BUTTON_SIZE - BUTTON_PADDING; int max_x = close_x - BUTTON_SIZE - BUTTON_PADDING; @@ -163,10 +150,9 @@ void decorations_render_buttons(Client *client, bool focused) unsigned long bg_color = focused ? colors->title_focused_bg : colors->title_unfocused_bg; - /* Close button (red X) */ XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, close_x, button_y, BUTTON_SIZE, BUTTON_SIZE); - XSetForeground(dpy, dwn->gc, 0xcc4444); /* Red */ + XSetForeground(dpy, dwn->gc, 0xcc4444); XDrawLine(dpy, client->frame, dwn->gc, close_x + 3, button_y + 3, close_x + BUTTON_SIZE - 4, button_y + BUTTON_SIZE - 4); @@ -174,18 +160,16 @@ void decorations_render_buttons(Client *client, bool focused) close_x + BUTTON_SIZE - 4, button_y + 3, close_x + 3, button_y + BUTTON_SIZE - 4); - /* Maximize button (square) */ XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, max_x, button_y, BUTTON_SIZE, BUTTON_SIZE); - XSetForeground(dpy, dwn->gc, 0x44cc44); /* Green */ + XSetForeground(dpy, dwn->gc, 0x44cc44); XDrawRectangle(dpy, client->frame, dwn->gc, max_x + 3, button_y + 3, BUTTON_SIZE - 7, BUTTON_SIZE - 7); - /* Minimize button (line) */ XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, min_x, button_y, BUTTON_SIZE, BUTTON_SIZE); - XSetForeground(dpy, dwn->gc, 0xcccc44); /* Yellow */ + XSetForeground(dpy, dwn->gc, 0xcccc44); XDrawLine(dpy, client->frame, dwn->gc, min_x + 3, button_y + BUTTON_SIZE - 5, min_x + BUTTON_SIZE - 4, button_y + BUTTON_SIZE - 5); @@ -207,18 +191,16 @@ void decorations_render_border(Client *client, bool focused) XSetWindowBorder(dwn->display, client->frame, border_color); } -/* ========== Hit testing ========== */ ButtonType decorations_hit_test_button(Client *client, int x, int y) { if (client == NULL) { - return BUTTON_COUNT; /* No button hit */ + return BUTTON_COUNT; } int title_height = config_get_title_height(); int border = client->border_width; - /* Check if in title bar area */ if (y < border || y > border + title_height) { return BUTTON_COUNT; } @@ -228,19 +210,16 @@ ButtonType decorations_hit_test_button(Client *client, int x, int y) int max_x = close_x - BUTTON_SIZE - BUTTON_PADDING; int min_x = max_x - BUTTON_SIZE - BUTTON_PADDING; - /* Check close button */ if (x >= close_x && x < close_x + BUTTON_SIZE && y >= button_y && y < button_y + BUTTON_SIZE) { return BUTTON_CLOSE; } - /* Check maximize button */ if (x >= max_x && x < max_x + BUTTON_SIZE && y >= button_y && y < button_y + BUTTON_SIZE) { return BUTTON_MAXIMIZE; } - /* Check minimize button */ if (x >= min_x && x < min_x + BUTTON_SIZE && y >= button_y && y < button_y + BUTTON_SIZE) { return BUTTON_MINIMIZE; @@ -258,7 +237,6 @@ bool decorations_hit_test_title_bar(Client *client, int x, int y) int title_height = config_get_title_height(); int border = client->border_width; - /* In title bar but not on a button */ if (y >= border && y < border + title_height) { ButtonType btn = decorations_hit_test_button(client, x, y); return btn == BUTTON_COUNT; @@ -280,7 +258,6 @@ bool decorations_hit_test_resize_area(Client *client, int x, int y, int *directi *direction = 0; - /* Check edges */ bool left = (x < RESIZE_EDGE); bool right = (x > frame_width - RESIZE_EDGE); bool top = (y < RESIZE_EDGE); @@ -290,7 +267,6 @@ bool decorations_hit_test_resize_area(Client *client, int x, int y, int *directi return false; } - /* Encode direction as bitmask */ if (left) *direction |= 1; if (right) *direction |= 2; if (top) *direction |= 4; @@ -299,7 +275,6 @@ bool decorations_hit_test_resize_area(Client *client, int x, int y, int *directi return true; } -/* ========== Button actions ========== */ void decorations_button_press(Client *client, ButtonType button) { @@ -328,7 +303,6 @@ void decorations_button_press(Client *client, ButtonType button) } } -/* ========== Text rendering ========== */ void decorations_draw_text(Window window, GC gc, int x, int y, const char *text, unsigned long color) @@ -337,7 +311,6 @@ void decorations_draw_text(Window window, GC gc, int x, int y, return; } - /* Use Xft for UTF-8 support */ if (dwn->xft_font != NULL) { XftDraw *xft_draw = XftDrawCreate(dwn->display, window, DefaultVisual(dwn->display, dwn->screen), @@ -363,7 +336,6 @@ void decorations_draw_text(Window window, GC gc, int x, int y, } } - /* Fallback to legacy X11 text */ XSetForeground(dwn->display, gc, color); XDrawString(dwn->display, window, gc, x, y, text, strlen(text)); } diff --git a/src/keys.c b/src/keys.c index a8b4ba0..d073024 100644 --- a/src/keys.c +++ b/src/keys.c @@ -22,7 +22,6 @@ static bool super_pressed = false; static bool super_used_in_combo = false; -/* Forward declarations for key callbacks */ void key_spawn_terminal(void); void key_spawn_launcher(void); void key_spawn_file_manager(void); @@ -69,11 +68,9 @@ void key_show_shortcuts(void); void key_start_tutorial(void); void key_screenshot(void); -/* Key bindings storage */ static KeyBinding bindings[MAX_KEYBINDINGS]; static int binding_count = 0; -/* ========== Tutorial System ========== */ typedef struct { const char *title; @@ -91,7 +88,6 @@ static const TutorialStep tutorial_steps[] = { "(Super = Windows/Meta key)", MOD_SUPER, XK_t }, - /* === Applications === */ { "1/20: Open Terminal", "The terminal is your command center.\n\n" @@ -120,7 +116,6 @@ static const TutorialStep tutorial_steps[] = { "", MOD_SUPER, XK_b }, - /* === Window Management === */ { "5/20: Switch Windows", "Cycle through open windows.\n\n" @@ -156,7 +151,6 @@ static const TutorialStep tutorial_steps[] = { "", MOD_SUPER, XK_F9 }, - /* === Workspaces === */ { "10/20: Next Workspace", "Switch to the next virtual desktop.\n\n" @@ -178,7 +172,6 @@ static const TutorialStep tutorial_steps[] = { "Use F1-F9 for workspaces 1-9", 0, XK_F1 }, - /* === Layout === */ { "13/20: Cycle Layout", "Switch between tiling, floating, monocle.\n\n" @@ -207,7 +200,6 @@ static const TutorialStep tutorial_steps[] = { "", MOD_SUPER, XK_i }, - /* === AI Features === */ { "17/20: AI Context", "Show AI analysis of your current task.\n\n" @@ -229,7 +221,6 @@ static const TutorialStep tutorial_steps[] = { "Requires EXA_API_KEY", MOD_SUPER | MOD_SHIFT, XK_e }, - /* === Help & System === */ { "20/20: Show Shortcuts", "Display all keyboard shortcuts.\n\n" @@ -266,7 +257,7 @@ void tutorial_start(void) const TutorialStep *step = &tutorial_steps[0]; char msg[512]; snprintf(msg, sizeof(msg), "%s\n\n%s", step->instruction, step->hint); - notification_show("DWN Tutorial", step->title, msg, NULL, 0); /* No timeout */ + notification_show("DWN Tutorial", step->title, msg, NULL, 0); } void tutorial_stop(void) @@ -301,14 +292,11 @@ void tutorial_check_key(unsigned int modifiers, KeySym keysym) const TutorialStep *step = &tutorial_steps[tutorial_current_step]; - /* Check if the pressed key matches the expected key */ if (modifiers == step->modifiers && keysym == step->keysym) { - /* Correct key! Move to next step */ tutorial_next_step(); } } -/* ========== Initialization ========== */ void keys_init(void) { @@ -336,10 +324,8 @@ void keys_grab_all(void) Display *dpy = dwn->display; Window root = dwn->root; - /* Ungrab first to avoid errors */ XUngrabKey(dpy, AnyKey, AnyModifier, root); - /* Grab all registered bindings */ for (int i = 0; i < binding_count; i++) { KeyCode code = XKeysymToKeycode(dpy, bindings[i].keysym); if (code == 0) { @@ -347,11 +333,10 @@ void keys_grab_all(void) continue; } - /* Grab with and without NumLock/CapsLock */ unsigned int modifiers[] = { bindings[i].modifiers, - bindings[i].modifiers | Mod2Mask, /* NumLock */ - bindings[i].modifiers | LockMask, /* CapsLock */ + bindings[i].modifiers | Mod2Mask, + bindings[i].modifiers | LockMask, bindings[i].modifiers | Mod2Mask | LockMask }; @@ -382,7 +367,6 @@ void keys_ungrab_all(void) XUngrabKey(dwn->display, AnyKey, AnyModifier, dwn->root); } -/* ========== Key binding registration ========== */ void keys_bind(unsigned int modifiers, KeySym keysym, KeyCallback callback, const char *description) @@ -404,7 +388,6 @@ void keys_unbind(unsigned int modifiers, KeySym keysym) for (int i = 0; i < binding_count; i++) { if (bindings[i].modifiers == modifiers && bindings[i].keysym == keysym) { - /* Shift remaining bindings */ memmove(&bindings[i], &bindings[i + 1], (binding_count - i - 1) * sizeof(KeyBinding)); binding_count--; @@ -419,7 +402,6 @@ void keys_clear_all(void) memset(bindings, 0, sizeof(bindings)); } -/* ========== Key event handling ========== */ void keys_handle_press(XKeyEvent *ev) { @@ -439,10 +421,8 @@ void keys_handle_press(XKeyEvent *ev) super_used_in_combo = true; } - /* Clean modifiers (remove NumLock, CapsLock) */ unsigned int clean_mask = ev->state & ~(Mod2Mask | LockMask); - /* Check tutorial progress */ if (tutorial_is_active()) { tutorial_check_key(clean_mask, keysym); } @@ -476,45 +456,32 @@ void keys_handle_release(XKeyEvent *ev) } } -/* ========== Default key bindings (XFCE-style) ========== */ void keys_setup_defaults(void) { - /* ===== XFCE-style shortcuts ===== */ - /* Terminal: Ctrl+Alt+T (XFCE default) */ keys_bind(MOD_CTRL | MOD_ALT, XK_t, key_spawn_terminal, "Spawn terminal"); - /* Application finder: Alt+F2 (XFCE default) */ keys_bind(MOD_ALT, XK_F2, key_spawn_launcher, "Application finder"); - /* File manager: Super+E (XFCE default) */ keys_bind(MOD_SUPER, XK_e, key_spawn_file_manager, "File manager"); - /* Browser: Super+B */ keys_bind(MOD_SUPER, XK_b, key_spawn_browser, "Web browser"); - /* Close window: Alt+F4 (XFCE default) */ keys_bind(MOD_ALT, XK_F4, key_close_window, "Close window"); - /* Maximize toggle: Alt+F10 (XFCE default) */ keys_bind(MOD_ALT, XK_F10, key_toggle_maximize, "Toggle maximize"); - /* Fullscreen: Alt+F11 (XFCE default) */ keys_bind(MOD_ALT, XK_F11, key_toggle_fullscreen, "Toggle fullscreen"); - /* Cycle windows: Alt+Tab (XFCE default) */ keys_bind(MOD_ALT, XK_Tab, key_focus_next, "Cycle windows"); keys_bind(MOD_ALT | MOD_SHIFT, XK_Tab, key_focus_prev, "Cycle windows reverse"); - /* Toggle floating: Super+F9 */ keys_bind(MOD_SUPER, XK_F9, key_toggle_floating, "Toggle floating"); - /* Next/Previous workspace: Ctrl+Alt+Right/Left (XFCE default) */ keys_bind(MOD_CTRL | MOD_ALT, XK_Right, key_workspace_next, "Next workspace"); keys_bind(MOD_CTRL | MOD_ALT, XK_Left, key_workspace_prev, "Previous workspace"); - /* Switch to workspace: F1-F9 */ keys_bind(0, XK_F1, key_workspace_1, "Switch to workspace 1"); keys_bind(0, XK_F2, key_workspace_2, "Switch to workspace 2"); keys_bind(0, XK_F3, key_workspace_3, "Switch to workspace 3"); @@ -525,7 +492,6 @@ void keys_setup_defaults(void) keys_bind(0, XK_F8, key_workspace_8, "Switch to workspace 8"); keys_bind(0, XK_F9, key_workspace_9, "Switch to workspace 9"); - /* Move to workspace: Shift+F1-F9 */ keys_bind(MOD_SHIFT, XK_F1, key_move_to_workspace_1, "Move to workspace 1"); keys_bind(MOD_SHIFT, XK_F2, key_move_to_workspace_2, "Move to workspace 2"); keys_bind(MOD_SHIFT, XK_F3, key_move_to_workspace_3, "Move to workspace 3"); @@ -536,48 +502,34 @@ void keys_setup_defaults(void) keys_bind(MOD_SHIFT, XK_F8, key_move_to_workspace_8, "Move to workspace 8"); keys_bind(MOD_SHIFT, XK_F9, key_move_to_workspace_9, "Move to workspace 9"); - /* ===== DWN-specific shortcuts (all use Super key) ===== */ - /* Quit DWN: Super+BackSpace */ keys_bind(MOD_SUPER, XK_BackSpace, key_quit_dwn, "Quit DWN"); - /* Cycle layout mode: Super+Space */ keys_bind(MOD_SUPER, XK_space, key_cycle_layout, "Cycle layout"); - /* Master area adjustments: Super+H/L/I/D */ keys_bind(MOD_SUPER, XK_l, key_increase_master, "Increase master ratio"); keys_bind(MOD_SUPER, XK_h, key_decrease_master, "Decrease master ratio"); keys_bind(MOD_SUPER, XK_i, key_increase_master_count, "Increase master count"); keys_bind(MOD_SUPER, XK_d, key_decrease_master_count, "Decrease master count"); - /* AI: Super+A */ keys_bind(MOD_SUPER, XK_a, key_toggle_ai, "Toggle AI context"); keys_bind(MOD_SUPER | MOD_SHIFT, XK_a, key_ai_command, "AI command"); - /* Exa semantic search: Super+Shift+E */ keys_bind(MOD_SUPER | MOD_SHIFT, XK_e, key_exa_search, "Exa web search"); - /* Show shortcuts: Super+S */ keys_bind(MOD_SUPER, XK_s, key_show_shortcuts, "Show shortcuts"); - /* Tutorial: Super+T */ keys_bind(MOD_SUPER, XK_t, key_start_tutorial, "Start tutorial"); - /* ===== News ticker navigation ===== */ - /* Super+Down: Next news article */ keys_bind(MOD_SUPER, XK_Down, key_news_next, "Next news article"); - /* Super+Up: Previous news article */ keys_bind(MOD_SUPER, XK_Up, key_news_prev, "Previous news article"); - /* Super+Return: Open current news article */ keys_bind(MOD_SUPER, XK_Return, key_news_open, "Open news article"); - /* Screenshot: Print Screen (XFCE default) */ keys_bind(0, XK_Print, key_screenshot, "Take screenshot"); } -/* ========== Key binding callbacks ========== */ void key_spawn_terminal(void) { @@ -647,8 +599,6 @@ void key_toggle_maximize(void) { Workspace *ws = workspace_get_current(); if (ws != NULL && ws->focused != NULL) { - /* Toggle between maximized and normal state */ - /* For now, use fullscreen as maximize equivalent */ client_toggle_fullscreen(ws->focused); } } @@ -670,7 +620,7 @@ void key_workspace_next(void) } int next = dwn->current_workspace + 1; if (next >= MAX_WORKSPACES) { - next = 0; /* Wrap around */ + next = 0; } workspace_switch(next); } @@ -682,12 +632,11 @@ void key_workspace_prev(void) } int prev = dwn->current_workspace - 1; if (prev < 0) { - prev = MAX_WORKSPACES - 1; /* Wrap around */ + prev = MAX_WORKSPACES - 1; } workspace_switch(prev); } -/* Workspace switching */ void key_workspace_1(void) { workspace_switch(0); } void key_workspace_2(void) { workspace_switch(1); } void key_workspace_3(void) { workspace_switch(2); } @@ -698,7 +647,6 @@ void key_workspace_7(void) { workspace_switch(6); } void key_workspace_8(void) { workspace_switch(7); } void key_workspace_9(void) { workspace_switch(8); } -/* Move to workspace */ static void move_focused_to_workspace(int ws) { Workspace *current = workspace_get_current(); @@ -717,7 +665,6 @@ void key_move_to_workspace_7(void) { move_focused_to_workspace(6); } void key_move_to_workspace_8(void) { move_focused_to_workspace(7); } void key_move_to_workspace_9(void) { move_focused_to_workspace(8); } -/* Master area adjustments */ void key_increase_master(void) { if (dwn == NULL) { @@ -750,7 +697,6 @@ void key_decrease_master_count(void) workspace_adjust_master_count(dwn->current_workspace, -1); } -/* AI functions */ void key_toggle_ai(void) { if (dwn == NULL) { @@ -764,7 +710,6 @@ void key_toggle_ai(void) return; } - /* Update and show AI context */ ai_update_context(); const char *task = ai_analyze_task(); const char *suggestion = ai_suggest_window(); @@ -850,14 +795,12 @@ void key_show_shortcuts(void) void key_start_tutorial(void) { if (tutorial_is_active()) { - /* If already in tutorial, this acts as "next" */ tutorial_next_step(); } else { tutorial_start(); } } -/* ========== News ticker callbacks ========== */ void key_news_next(void) { diff --git a/src/layout.c b/src/layout.c index f7bbbfd..ff486db 100644 --- a/src/layout.c +++ b/src/layout.c @@ -10,7 +10,6 @@ #include "config.h" #include "util.h" -/* Layout names and symbols */ static const char *layout_names[] = { "Tiling", "Floating", @@ -23,7 +22,6 @@ static const char *layout_symbols[] = { "[M]" }; -/* ========== Main arrangement function ========== */ void layout_arrange(int workspace) { @@ -48,7 +46,6 @@ void layout_arrange(int workspace) } } -/* ========== Tiling layout ========== */ void layout_arrange_tiling(int workspace) { @@ -57,7 +54,6 @@ void layout_arrange_tiling(int workspace) return; } - /* Get usable screen area (excluding panels) */ int area_x, area_y, area_width, area_height; layout_get_usable_area(&area_x, &area_y, &area_width, &area_height); @@ -65,7 +61,6 @@ void layout_arrange_tiling(int workspace) int title_height = config_get_title_height(); int border = config_get_border_width(); - /* Count tiled (non-floating) clients */ int n = layout_count_tiled_clients(workspace); if (n == 0) { return; @@ -76,7 +71,6 @@ void layout_arrange_tiling(int workspace) master_count = n; } - /* Calculate master area width */ int master_width; if (n <= master_count) { master_width = area_width - 2 * gap; @@ -86,7 +80,6 @@ void layout_arrange_tiling(int workspace) int stack_width = area_width - master_width - 3 * gap; - /* Arrange windows */ int i = 0; int master_y = area_y + gap; int stack_y = area_y + gap; @@ -102,7 +95,6 @@ void layout_arrange_tiling(int workspace) int x, y, w, h; if (i < master_count) { - /* Master area */ int master_h = (area_height - 2 * gap - (master_count - 1) * gap) / master_count; x = area_x + gap; @@ -112,7 +104,6 @@ void layout_arrange_tiling(int workspace) master_y += h + gap; } else { - /* Stack area */ int stack_count = n - master_count; int stack_h = (area_height - 2 * gap - (stack_count - 1) * gap) / stack_count; @@ -124,7 +115,6 @@ void layout_arrange_tiling(int workspace) stack_y += h + gap; } - /* Account for decorations */ int actual_h = h - title_height - 2 * border; int actual_w = w - 2 * border; @@ -138,12 +128,9 @@ void layout_arrange_tiling(int workspace) } } -/* ========== Floating layout ========== */ void layout_arrange_floating(int workspace) { - /* In floating mode, we don't rearrange windows automatically. - Just make sure all clients are configured properly. */ for (Client *c = dwn->client_list; c != NULL; c = c->next) { if (c->workspace != (unsigned int)workspace) { @@ -153,7 +140,6 @@ void layout_arrange_floating(int workspace) continue; } - /* Ensure window is within screen bounds */ int area_x, area_y, area_width, area_height; layout_get_usable_area(&area_x, &area_y, &area_width, &area_height); @@ -170,11 +156,9 @@ void layout_arrange_floating(int workspace) } } -/* ========== Monocle layout ========== */ void layout_arrange_monocle(int workspace) { - /* Get usable screen area */ int area_x, area_y, area_width, area_height; layout_get_usable_area(&area_x, &area_y, &area_width, &area_height); @@ -190,7 +174,6 @@ void layout_arrange_monocle(int workspace) continue; } - /* All tiled windows take the full usable area */ int x = area_x + gap; int y = area_y + gap; int w = area_width - 2 * gap - 2 * border; @@ -203,7 +186,6 @@ void layout_arrange_monocle(int workspace) } } -/* ========== Helpers ========== */ int layout_get_usable_area(int *x, int *y, int *width, int *height) { diff --git a/src/main.c b/src/main.c index 178aa7b..122f2bf 100644 --- a/src/main.c +++ b/src/main.c @@ -30,11 +30,9 @@ #include #include -/* Global state instance */ DWNState *dwn = NULL; static DWNState dwn_state; -/* Signal handling */ static volatile sig_atomic_t received_signal = 0; static void signal_handler(int sig) @@ -42,29 +40,23 @@ static void signal_handler(int sig) received_signal = sig; } -/* Crash signal handler - flush logs before dying */ static void crash_signal_handler(int sig) { - /* Flush logs synchronously before crashing */ log_flush(); - /* Re-raise the signal with default handler to get proper crash behavior */ signal(sig, SIG_DFL); raise(sig); } -/* X11 error handlers */ -static int last_x_error = 0; /* Track last error for checking */ +static int last_x_error = 0; static int x_error_handler(Display *dpy, XErrorEvent *ev) { char error_text[256]; XGetErrorText(dpy, ev->error_code, error_text, sizeof(error_text)); - /* Store last error code for functions that want to check */ last_x_error = ev->error_code; - /* Write all X errors to crash log for debugging */ FILE *crash = fopen("/tmp/dwn_crash.log", "a"); if (crash) { fprintf(crash, "[X_ERROR] code=%d request=%d resource=%lu: %s\n", @@ -74,22 +66,19 @@ static int x_error_handler(Display *dpy, XErrorEvent *ev) fclose(crash); } - /* BadWindow errors are common and recoverable - just log and continue */ if (ev->error_code == BadWindow) { LOG_DEBUG("X11 BadWindow error (request %d, resource %lu) - window was destroyed", ev->request_code, ev->resourceid); - return 0; /* Continue - this is not fatal */ + return 0; } - /* BadMatch, BadValue, BadDrawable are also often recoverable */ if (ev->error_code == BadMatch || ev->error_code == BadValue || ev->error_code == BadDrawable || ev->error_code == BadPixmap) { LOG_WARN("X11 error: %s (request %d, resource %lu) - continuing", error_text, ev->request_code, ev->resourceid); - return 0; /* Continue - these are recoverable */ + return 0; } - /* Log other errors but don't crash */ LOG_WARN("X11 error: %s (request %d, resource %lu)", error_text, ev->request_code, ev->resourceid); return 0; @@ -99,7 +88,6 @@ static int x_io_error_handler(Display *dpy) { (void)dpy; - /* Write directly to crash log - do not rely on async logging */ FILE *crash = fopen("/tmp/dwn_crash.log", "a"); if (crash) { fprintf(crash, "[IO_ERROR] Fatal X11 I/O error - X server connection lost\n"); @@ -110,16 +98,13 @@ static int x_io_error_handler(Display *dpy) fprintf(stderr, "[IO_ERROR] Fatal X11 I/O error - X server connection lost\n"); fflush(stderr); - /* I/O errors mean the X server connection is broken - we must exit */ - /* But first, try to flush any pending logs */ log_flush(); LOG_ERROR("Fatal X11 I/O error - X server connection lost"); log_flush(); - _exit(EXIT_FAILURE); /* Use _exit to avoid cleanup that might touch X */ - return 0; /* Never reached */ + _exit(EXIT_FAILURE); + return 0; } -/* Check if another WM is running */ static int wm_detected = 0; static int wm_detect_error_handler(Display *dpy, XErrorEvent *ev) @@ -143,11 +128,9 @@ static bool check_other_wm(void) return wm_detected != 0; } -/* Initialize multi-monitor support */ static void init_monitors(void) { if (!XineramaIsActive(dwn->display)) { - /* Single monitor */ dwn->monitors[0].x = 0; dwn->monitors[0].y = 0; dwn->monitors[0].width = dwn->screen_width; @@ -188,7 +171,6 @@ static void init_monitors(void) LOG_INFO("Detected %d monitor(s)", dwn->monitor_count); } -/* Scan for existing windows */ static void scan_existing_windows(void) { Window root_return, parent_return; @@ -213,11 +195,9 @@ static void scan_existing_windows(void) LOG_INFO("Scanned %d existing window(s)", client_count()); } -/* ========== Event handlers ========== */ static void handle_map_request(XMapRequestEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL || dwn == NULL || dwn->display == NULL) { return; } @@ -242,7 +222,6 @@ static void handle_map_request(XMapRequestEvent *ev) static void handle_unmap_notify(XUnmapEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL || dwn == NULL) { return; } @@ -252,15 +231,10 @@ static void handle_unmap_notify(XUnmapEvent *ev) return; } - /* Ignore synthetic events */ if (ev->send_event) { return; } - /* Don't unmanage windows that are intentionally hidden: - * - Windows on a different workspace (hidden during workspace switch) - * - Minimized windows - * These windows are still managed, just not visible */ if (c->workspace != (unsigned int)dwn->current_workspace) { return; } @@ -268,13 +242,11 @@ static void handle_unmap_notify(XUnmapEvent *ev) return; } - /* Window was actually closed/withdrawn - unmanage it */ client_unmanage(c); } static void handle_destroy_notify(XDestroyWindowEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL) { return; } @@ -287,7 +259,6 @@ static void handle_destroy_notify(XDestroyWindowEvent *ev) static void handle_configure_request(XConfigureRequestEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL || dwn == NULL || dwn->display == NULL) { return; } @@ -295,7 +266,6 @@ static void handle_configure_request(XConfigureRequestEvent *ev) Client *c = client_find_by_window(ev->window); if (c != NULL) { - /* Managed window - respect some requests for floating windows */ if (client_is_floating(c) || client_is_fullscreen(c)) { if (ev->value_mask & CWX) c->x = ev->x; if (ev->value_mask & CWY) c->y = ev->y; @@ -303,11 +273,9 @@ static void handle_configure_request(XConfigureRequestEvent *ev) if (ev->value_mask & CWHeight) c->height = ev->height; client_configure(c); } else { - /* Just send configure notify with current geometry */ client_configure(c); } } else { - /* Unmanaged window - pass through */ XWindowChanges wc; wc.x = ev->x; wc.y = ev->y; @@ -323,7 +291,6 @@ static void handle_configure_request(XConfigureRequestEvent *ev) static void handle_property_notify(XPropertyEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL || dwn == NULL) { return; } @@ -341,17 +308,14 @@ static void handle_property_notify(XPropertyEvent *ev) static void handle_expose(XExposeEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL || dwn == NULL) { return; } - /* Only handle final expose in a sequence */ if (ev->count != 0) { return; } - /* Check if it's a panel */ if (dwn->top_panel != NULL && ev->window == dwn->top_panel->window) { panel_render(dwn->top_panel); return; @@ -361,14 +325,12 @@ static void handle_expose(XExposeEvent *ev) return; } - /* Check if it's a notification */ Notification *notif = notification_find_by_window(ev->window); if (notif != NULL) { notification_render(notif); return; } - /* Check if it's a frame */ Client *c = client_find_by_frame(ev->window); if (c != NULL) { Workspace *ws = workspace_get(c->workspace); @@ -379,7 +341,6 @@ static void handle_expose(XExposeEvent *ev) static void handle_enter_notify(XCrossingEvent *ev) { - /* Defensive: validate all pointers */ if (ev == NULL || dwn == NULL || dwn->config == NULL) { return; } @@ -388,7 +349,6 @@ static void handle_enter_notify(XCrossingEvent *ev) return; } - /* Focus on enter for follow-mouse mode */ Client *c = client_find_by_frame(ev->window); if (c == NULL) { c = client_find_by_window(ev->window); @@ -401,41 +361,34 @@ static void handle_enter_notify(XCrossingEvent *ev) static void handle_button_press(XButtonEvent *ev) { - /* Defensive: validate all pointers */ if (ev == NULL || dwn == NULL || dwn->display == NULL) { return; } - /* Check volume slider first */ if (volume_slider != NULL && volume_slider->visible) { if (ev->window == volume_slider->window) { volume_slider_handle_click(volume_slider, ev->x, ev->y); return; } else { - /* Clicked outside slider - close it */ volume_slider_hide(volume_slider); } } - /* Check WiFi dropdown menu */ if (wifi_menu != NULL && wifi_menu->visible) { if (ev->window == wifi_menu->window) { dropdown_handle_click(wifi_menu, ev->x, ev->y); return; } else { - /* Clicked outside dropdown - close it */ dropdown_hide(wifi_menu); } } - /* Check notifications first - clicking dismisses them */ Notification *notif = notification_find_by_window(ev->window); if (notif != NULL) { notification_close(notif->id); return; } - /* Check panels first */ if (dwn->top_panel != NULL && ev->window == dwn->top_panel->window) { panel_handle_click(dwn->top_panel, ev->x, ev->y, ev->button); return; @@ -445,7 +398,6 @@ static void handle_button_press(XButtonEvent *ev) return; } - /* Find client */ Client *c = client_find_by_frame(ev->window); bool is_client_window = false; if (c == NULL) { @@ -457,20 +409,14 @@ static void handle_button_press(XButtonEvent *ev) return; } - /* If click was on client window content, replay the event to the application FIRST - * before any other X operations. The synchronous grab freezes pointer events until - * XAllowEvents is called. Calling XAllowEvents before client_focus ensures the - * click reaches the application (tabs, buttons, etc.) without timing issues. */ if (is_client_window) { XAllowEvents(dwn->display, ReplayPointer, ev->time); client_focus(c); return; } - /* Focus on click */ client_focus(c); - /* Check for button clicks in decorations */ if (c->frame != None && ev->window == c->frame) { ButtonType btn = decorations_hit_test_button(c, ev->x, ev->y); if (btn != BUTTON_COUNT) { @@ -478,10 +424,8 @@ static void handle_button_press(XButtonEvent *ev) return; } - /* Check for title bar drag */ if (decorations_hit_test_title_bar(c, ev->x, ev->y)) { if (ev->button == 1) { - /* Start move */ dwn->drag_client = c; dwn->drag_start_x = ev->x_root; dwn->drag_start_y = ev->y_root; @@ -496,7 +440,6 @@ static void handle_button_press(XButtonEvent *ev) return; } - /* Check for resize */ int direction; if (decorations_hit_test_resize_area(c, ev->x, ev->y, &direction)) { if (ev->button == 1) { @@ -519,12 +462,10 @@ static void handle_button_press(XButtonEvent *ev) static void handle_button_release(XButtonEvent *ev) { - /* Defensive: validate pointers */ if (dwn == NULL || dwn->display == NULL) { return; } - /* Handle volume slider release */ if (volume_slider != NULL && volume_slider->visible && volume_slider->dragging) { volume_slider_handle_release(volume_slider); } @@ -539,18 +480,15 @@ static void handle_button_release(XButtonEvent *ev) static void handle_motion_notify(XMotionEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL || dwn == NULL || dwn->display == NULL) { return; } - /* Check volume slider drag */ if (volume_slider != NULL && volume_slider->visible && ev->window == volume_slider->window) { volume_slider_handle_motion(volume_slider, ev->x, ev->y); return; } - /* Check WiFi dropdown hover */ if (wifi_menu != NULL && wifi_menu->visible && ev->window == wifi_menu->window) { dropdown_handle_motion(wifi_menu, ev->x, ev->y); return; @@ -562,7 +500,7 @@ static void handle_motion_notify(XMotionEvent *ev) Client *c = dwn->drag_client; if (c == NULL) { - dwn->drag_client = NULL; /* Reset invalid drag state */ + dwn->drag_client = NULL; return; } @@ -582,7 +520,6 @@ static void handle_motion_notify(XMotionEvent *ev) static void handle_client_message(XClientMessageEvent *ev) { - /* Defensive: validate pointers */ if (ev == NULL || dwn == NULL || dwn->display == NULL) { return; } @@ -626,7 +563,6 @@ static void handle_client_message(XClientMessageEvent *ev) } } -/* Sync log - disabled in production (enable for debugging crashes) */ #if 0 static FILE *crash_log_file = NULL; @@ -645,16 +581,13 @@ static void sync_log(const char *msg) } #endif -/* Main event dispatcher */ void dwn_handle_event(XEvent *ev) { - /* Defensive: validate event pointer */ if (ev == NULL) { LOG_WARN("dwn_handle_event: received NULL event"); return; } - /* Defensive: validate global state */ if (dwn == NULL || dwn->display == NULL) { LOG_ERROR("dwn_handle_event: dwn or display is NULL"); return; @@ -705,15 +638,12 @@ void dwn_handle_event(XEvent *ev) } } -/* ========== Core functions ========== */ int dwn_init(void) { - /* Initialize global state */ dwn = &dwn_state; memset(dwn, 0, sizeof(DWNState)); - /* Open display */ dwn->display = XOpenDisplay(NULL); if (dwn->display == NULL) { fprintf(stderr, "Cannot open X display\n"); @@ -726,36 +656,29 @@ int dwn_init(void) dwn->screen_height = DisplayHeight(dwn->display, dwn->screen); dwn->colormap = DefaultColormap(dwn->display, dwn->screen); - /* Set error handlers */ XSetErrorHandler(x_error_handler); XSetIOErrorHandler(x_io_error_handler); - /* Check for other WM */ if (check_other_wm()) { fprintf(stderr, "Another window manager is already running\n"); XCloseDisplay(dwn->display); return -1; } - /* Initialize logging */ log_init("~/.local/share/dwn/dwn.log"); LOG_INFO("DWN %s starting", DWN_VERSION); - /* Load configuration */ dwn->config = config_create(); config_load(dwn->config, NULL); - /* Initialize colors (after display is open) */ extern void config_init_colors(Config *cfg); config_init_colors(dwn->config); - /* Load font */ dwn->font = XLoadQueryFont(dwn->display, dwn->config->font_name); if (dwn->font == NULL) { dwn->font = XLoadQueryFont(dwn->display, "fixed"); } - /* Load Xft font for UTF-8 support */ dwn->xft_font = XftFontOpenName(dwn->display, dwn->screen, "monospace:size=10:antialias=true"); if (dwn->xft_font == NULL) { @@ -766,7 +689,6 @@ int dwn_init(void) LOG_INFO("Loaded Xft font for UTF-8 support"); } - /* Create GC */ XGCValues gcv; gcv.foreground = dwn->config->colors.panel_fg; gcv.background = dwn->config->colors.panel_bg; @@ -774,59 +696,42 @@ int dwn_init(void) dwn->gc = XCreateGC(dwn->display, dwn->root, GCForeground | GCBackground | (dwn->font ? GCFont : 0), &gcv); - /* Initialize atoms */ atoms_init(dwn->display); - /* Initialize monitors */ init_monitors(); - /* Initialize workspaces */ workspace_init(); - /* Initialize decorations */ decorations_init(); - /* Initialize panels */ panels_init(); - /* Initialize system tray (WiFi, Audio indicators) */ systray_init(); - /* Initialize news ticker */ news_init(); - /* Initialize app launcher */ applauncher_init(); - /* Initialize keyboard shortcuts */ keys_init(); - /* Initialize D-Bus notifications */ notifications_init(); - /* Initialize AI */ ai_init(); - /* Setup EWMH */ atoms_setup_ewmh(); - /* Select events on root window */ XSelectInput(dwn->display, dwn->root, SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask | PropertyChangeMask | ButtonPressMask | PointerMotionMask); - /* Set cursor */ Cursor cursor = XCreateFontCursor(dwn->display, XC_left_ptr); XDefineCursor(dwn->display, dwn->root, cursor); - /* Scan existing windows */ scan_existing_windows(); - /* Arrange initial workspace */ workspace_arrange_current(); - /* Render panels */ panel_render_all(); dwn->running = true; @@ -840,7 +745,6 @@ void dwn_cleanup(void) { LOG_INFO("DWN shutting down"); - /* Cleanup subsystems */ ai_cleanup(); notifications_cleanup(); news_cleanup(); @@ -851,12 +755,10 @@ void dwn_cleanup(void) decorations_cleanup(); workspace_cleanup(); - /* Unmanage all clients */ while (dwn->client_list != NULL) { client_unmanage(dwn->client_list); } - /* Free resources */ if (dwn->gc != None) { XFreeGC(dwn->display, dwn->gc); } @@ -870,7 +772,6 @@ void dwn_cleanup(void) config_destroy(dwn->config); } - /* Close display */ if (dwn->display != NULL) { XCloseDisplay(dwn->display); } @@ -893,35 +794,28 @@ void dwn_run(void) long last_news_update = 0; while (dwn->running && received_signal == 0) { - /* Handle pending X events */ while (XPending(dwn->display)) { XEvent ev; XNextEvent(dwn->display, &ev); dwn_handle_event(&ev); } - /* Process D-Bus messages */ notifications_process_messages(); - /* Process AI requests */ ai_process_pending(); - /* Process Exa requests */ exa_process_pending(); - /* Update notifications (check for expired) */ notifications_update(); long now = get_time_ms(); - /* Update news ticker frequently for smooth scrolling (~60fps) */ if (now - last_news_update >= 16) { news_update(); panel_render_all(); last_news_update = now; } - /* Update clock and system stats every second */ if (now - last_clock_update >= 1000) { panel_update_clock(); panel_update_system_stats(); @@ -929,7 +823,6 @@ void dwn_run(void) last_clock_update = now; } - /* Wait for events with timeout - short for smooth animation */ fd_set fds; FD_ZERO(&fds); FD_SET(x11_fd, &fds); @@ -939,7 +832,7 @@ void dwn_run(void) struct timeval tv; tv.tv_sec = 0; - tv.tv_usec = 16000; /* ~16ms for 60fps smooth scrolling */ + tv.tv_usec = 16000; int max_fd = x11_fd; if (dbus_fd > max_fd) max_fd = dbus_fd; @@ -947,7 +840,6 @@ void dwn_run(void) select(max_fd + 1, &fds, NULL, NULL, &tv); } - /* Handle signal */ if (received_signal != 0) { LOG_INFO("Received signal %d", received_signal); } @@ -958,7 +850,6 @@ void dwn_quit(void) dwn->running = false; } -/* ========== Main ========== */ static void print_usage(const char *program) { @@ -982,7 +873,6 @@ static void print_version(void) int main(int argc, char *argv[]) { - /* Parse arguments */ for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { print_usage(argv[0]); @@ -994,27 +884,22 @@ int main(int argc, char *argv[]) } } - /* Setup signal handlers */ signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); signal(SIGHUP, signal_handler); - /* Setup crash signal handlers to flush logs before dying */ signal(SIGSEGV, crash_signal_handler); signal(SIGABRT, crash_signal_handler); signal(SIGFPE, crash_signal_handler); signal(SIGBUS, crash_signal_handler); signal(SIGILL, crash_signal_handler); - /* Initialize */ if (dwn_init() != 0) { return EXIT_FAILURE; } - /* Run event loop */ dwn_run(); - /* Cleanup */ dwn_cleanup(); return EXIT_SUCCESS; diff --git a/src/news.c b/src/news.c index e6b0a86..72b0d46 100644 --- a/src/news.c +++ b/src/news.c @@ -20,13 +20,10 @@ #include #include -/* Scroll speed in pixels per second */ #define SCROLL_SPEED_PPS 80 -/* Fetch interval in milliseconds (5 minutes) */ #define FETCH_INTERVAL 300000 -/* Sentiment colors (RGB hex values) */ #define SENTIMENT_COLOR_POSITIVE 0x81C784 #define SENTIMENT_COLOR_NEUTRAL 0xB0BEC5 #define SENTIMENT_COLOR_NEGATIVE 0xE57373 @@ -40,15 +37,12 @@ static unsigned long news_sentiment_color(NewsSentiment sentiment) } } -/* Global state */ NewsState news_state = {0}; -/* Thread safety */ static pthread_mutex_t news_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t fetch_thread; static volatile int fetch_running = 0; -/* CURL response buffer */ typedef struct { char *data; size_t size; @@ -72,7 +66,6 @@ static size_t news_curl_write_cb(void *contents, size_t size, size_t nmemb, void return realsize; } -/* Decode numeric HTML entity ({ or ) */ static int decode_numeric_entity(const char *src, char *out, size_t max_out) { if (src[0] != '&' || src[1] != '#') return 0; @@ -106,7 +99,6 @@ static int decode_numeric_entity(const char *src, char *out, size_t max_out) return 0; } -/* Strip HTML tags and decode entities */ static void strip_html(char *dst, const char *src, size_t max_len) { size_t j = 0; @@ -119,7 +111,6 @@ static void strip_html(char *dst, const char *src, size_t max_len) in_tag = false; } else if (!in_tag) { if (src[i] == '&') { - /* Named entities */ if (strncmp(&src[i], "&", 5) == 0) { dst[j++] = '&'; i += 4; } else if (strncmp(&src[i], "<", 4) == 0) { @@ -143,7 +134,6 @@ static void strip_html(char *dst, const char *src, size_t max_len) } else if (strncmp(&src[i], "“", 7) == 0 || strncmp(&src[i], "”", 7) == 0) { dst[j++] = '"'; i += 6; } else if (src[i+1] == '#') { - /* Numeric entity */ char decoded[4] = {0}; int consumed = decode_numeric_entity(&src[i], decoded, sizeof(decoded)); if (consumed > 0) { @@ -166,7 +156,6 @@ static void strip_html(char *dst, const char *src, size_t max_len) } dst[j] = '\0'; - /* Collapse multiple spaces */ char *read = dst, *write = dst; bool last_space = false; while (*read) { @@ -184,7 +173,6 @@ static void strip_html(char *dst, const char *src, size_t max_len) *write = '\0'; } -/* Parse JSON response and populate articles */ static int parse_news_json(const char *json_str) { cJSON *root = cJSON_Parse(json_str); @@ -218,7 +206,6 @@ static int parse_news_json(const char *json_str) strip_html(art->title, title->valuestring, sizeof(art->title)); } - /* Prefer content, fallback to description */ const char *text = NULL; if (cJSON_IsString(content) && content->valuestring && strlen(content->valuestring) > 0) { text = content->valuestring; @@ -229,7 +216,6 @@ static int parse_news_json(const char *json_str) if (text) { strip_html(art->content, text, sizeof(art->content)); } else { - /* Use title as fallback */ strncpy(art->content, art->title, sizeof(art->content) - 1); } @@ -269,14 +255,11 @@ static int parse_news_json(const char *json_str) return count; } -/* Separator between articles */ #define NEWS_SEPARATOR " • " #define NEWS_SEPARATOR_LEN 5 -/* Forward declarations */ static int news_find_article_at_x(int click_x); -/* Get UTF-8 text width using Xft */ static int news_text_width(const char *text) { if (dwn == NULL || text == NULL) return 0; @@ -295,7 +278,6 @@ static int news_text_width(const char *text) return strlen(text) * 8; } -/* Draw UTF-8 text using Xft with clipping */ static void news_draw_text_clipped(Drawable d, int x, int y, const char *text, unsigned long color, XRectangle *clip) { @@ -346,7 +328,6 @@ static void news_draw_text_clipped(Drawable d, int x, int y, const char *text, } } -/* Get display text for an article */ static void get_article_display_text(int index, char *buf, size_t buf_size) { if (index < 0 || index >= news_state.article_count) { @@ -361,7 +342,6 @@ static void get_article_display_text(int index, char *buf, size_t buf_size) } } -/* Recalculate cached widths after fetching */ static void news_recalc_widths(void) { if (dwn == NULL) { @@ -379,7 +359,6 @@ static void news_recalc_widths(void) } } -/* Background fetch thread */ static void *news_fetch_thread(void *arg) { (void)arg; @@ -436,14 +415,12 @@ static void *news_fetch_thread(void *arg) return NULL; } -/* ========== Public API ========== */ void news_init(void) { memset(&news_state, 0, sizeof(news_state)); news_state.last_fetch = 0; - /* Start initial fetch */ news_fetch_async(); LOG_INFO("News ticker initialized"); @@ -451,7 +428,6 @@ void news_init(void) void news_cleanup(void) { - /* Wait for any pending fetch */ if (fetch_running) { pthread_join(fetch_thread, NULL); fetch_running = 0; @@ -468,7 +444,6 @@ void news_fetch_async(void) news_state.fetching = true; pthread_mutex_unlock(&news_mutex); - /* Wait for previous thread if still running */ if (fetch_running) { pthread_join(fetch_thread, NULL); } @@ -489,18 +464,15 @@ void news_update(void) pthread_mutex_lock(&news_mutex); - /* Auto-refresh if interval passed */ if (!news_state.fetching && (now - news_state.last_fetch) >= FETCH_INTERVAL) { pthread_mutex_unlock(&news_mutex); news_fetch_async(); pthread_mutex_lock(&news_mutex); } - /* Time-based smooth scrolling */ if (!news_state.interactive_mode && news_state.article_count > 0) { if (news_state.last_scroll_update > 0) { long delta_ms = now - news_state.last_scroll_update; - /* Calculate smooth sub-pixel scroll amount based on elapsed time */ double scroll_amount = (SCROLL_SPEED_PPS * delta_ms) / 1000.0; news_state.scroll_offset += scroll_amount; } @@ -567,7 +539,6 @@ void news_open_current(void) } } -/* Find article at given x position within news area */ static int news_find_article_at_x(int click_x) { if (news_state.article_count == 0 || news_state.total_width == 0) { diff --git a/src/notifications.c b/src/notifications.c index e3edf19..d8ec8b5 100644 --- a/src/notifications.c +++ b/src/notifications.c @@ -12,41 +12,33 @@ #include #include -/* D-Bus connection */ DBusConnection *dbus_conn = NULL; -/* Notification list */ static Notification *notification_list = NULL; static uint32_t next_notification_id = 1; -/* Notification dimensions - dynamic based on screen size */ #define NOTIFICATION_MIN_WIDTH 280 #define NOTIFICATION_PADDING 12 #define NOTIFICATION_MARGIN 10 -/* Get max width: 33% of screen width */ static int notification_max_width(void) { if (dwn == NULL) return 500; return dwn->screen_width / 3; } -/* Get max height: 50% of screen height */ static int notification_max_height(void) { if (dwn == NULL) return 600; return dwn->screen_height / 2; } -/* Default timeout */ #define DEFAULT_TIMEOUT 5000 -/* ========== UTF-8 text helpers ========== */ -/* Get text width using Xft (UTF-8 aware) */ static int notif_text_width(const char *text, int len) { - if (text == NULL || dwn == NULL) return len * 7; /* Fallback estimate */ + if (text == NULL || dwn == NULL) return len * 7; if (dwn->xft_font != NULL) { XGlyphInfo extents; @@ -59,10 +51,9 @@ static int notif_text_width(const char *text, int len) return XTextWidth(dwn->font, text, len); } - return len * 7; /* Fallback estimate */ + return len * 7; } -/* Get line height */ static int notif_line_height(void) { if (dwn == NULL) return 14; @@ -78,7 +69,6 @@ static int notif_line_height(void) return 14; } -/* Get font ascent */ static int notif_font_ascent(void) { if (dwn == NULL) return 12; @@ -94,13 +84,11 @@ static int notif_font_ascent(void) return 12; } -/* Draw text using Xft (UTF-8 aware) */ static void notif_draw_text(Window win, int x, int y, const char *text, int len, unsigned long color) { if (text == NULL || dwn == NULL || dwn->display == NULL) return; - /* Use Xft for UTF-8 support */ if (dwn->xft_font != NULL) { XftDraw *xft_draw = XftDrawCreate(dwn->display, win, DefaultVisual(dwn->display, dwn->screen), @@ -126,19 +114,16 @@ static void notif_draw_text(Window win, int x, int y, const char *text, } } - /* Fallback to legacy X11 text */ XSetForeground(dwn->display, dwn->gc, color); XDrawString(dwn->display, win, dwn->gc, x, y, text, len); } -/* ========== Initialization ========== */ bool notifications_init(void) { DBusError err; dbus_error_init(&err); - /* Connect to session bus */ dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { LOG_ERROR("D-Bus connection error: %s", err.message); @@ -151,10 +136,8 @@ bool notifications_init(void) return false; } - /* Register notification service */ if (!notifications_register_service()) { LOG_WARN("Could not register notification service (another daemon running?)"); - /* Don't fail - we can still function as a WM */ } LOG_INFO("Notification daemon initialized"); @@ -163,14 +146,11 @@ bool notifications_init(void) void notifications_cleanup(void) { - /* Close all notifications */ notification_close_all(); - /* D-Bus connection is managed by libdbus */ dbus_conn = NULL; } -/* ========== D-Bus handling ========== */ bool notifications_register_service(void) { @@ -181,7 +161,6 @@ bool notifications_register_service(void) DBusError err; dbus_error_init(&err); - /* Request the notification service name */ int result = dbus_bus_request_name(dbus_conn, "org.freedesktop.Notifications", DBUS_NAME_FLAG_REPLACE_EXISTING, &err); @@ -196,7 +175,6 @@ bool notifications_register_service(void) return false; } - /* Add message filter */ dbus_connection_add_filter(dbus_conn, notifications_handle_message, NULL, NULL); LOG_INFO("Registered as org.freedesktop.Notifications"); @@ -209,11 +187,9 @@ void notifications_process_messages(void) return; } - /* Process pending D-Bus messages (non-blocking) */ dbus_connection_read_write(dbus_conn, 0); while (dbus_connection_dispatch(dbus_conn) == DBUS_DISPATCH_DATA_REMAINS) { - /* Keep dispatching */ } } @@ -234,7 +210,6 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - /* Handle Notify method */ if (strcmp(member, "Notify") == 0) { DBusMessageIter args; if (!dbus_message_iter_init(msg, &args)) { @@ -248,7 +223,6 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, char *body = ""; int32_t timeout = -1; - /* Parse arguments */ if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&args, &app_name); dbus_message_iter_next(&args); @@ -270,12 +244,10 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, dbus_message_iter_next(&args); } - /* Skip actions array */ if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_ARRAY) { dbus_message_iter_next(&args); } - /* Skip hints dict */ if (dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_ARRAY) { dbus_message_iter_next(&args); } @@ -284,10 +256,8 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, dbus_message_iter_get_basic(&args, &timeout); } - /* Show notification */ uint32_t id = notification_show(app_name, summary, body, icon, timeout); - /* Send reply */ DBusMessage *reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID); dbus_connection_send(conn, reply, NULL); @@ -296,7 +266,6 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } - /* Handle CloseNotification method */ if (strcmp(member, "CloseNotification") == 0) { DBusMessageIter args; if (dbus_message_iter_init(msg, &args) && @@ -313,7 +282,6 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } - /* Handle GetCapabilities method */ if (strcmp(member, "GetCapabilities") == 0) { DBusMessage *reply = dbus_message_new_method_return(msg); DBusMessageIter args_iter, array_iter; @@ -333,7 +301,6 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } - /* Handle GetServerInformation method */ if (strcmp(member, "GetServerInformation") == 0) { DBusMessage *reply = dbus_message_new_method_return(msg); @@ -358,11 +325,9 @@ DBusHandlerResult notifications_handle_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -/* ========== Notification management ========== */ #define MAX_VISIBLE_NOTIFICATIONS 3 -/* Calculate notification size based on content - accounts for word wrapping */ static void notification_calculate_size(const char *summary, const char *body, int *out_width, int *out_height) { @@ -375,7 +340,6 @@ static void notification_calculate_size(const char *summary, const char *body, int height = NOTIFICATION_PADDING * 2; int line_height = notif_line_height(); - /* Add space for summary */ if (summary != NULL && summary[0] != '\0') { int summary_width = NOTIFICATION_PADDING * 2; summary_width += notif_text_width(summary, strlen(summary)); @@ -383,41 +347,34 @@ static void notification_calculate_size(const char *summary, const char *body, height += line_height + 4; } - /* Count wrapped lines in body */ if (body != NULL && body[0] != '\0') { const char *p = body; int wrapped_line_count = 0; while (*p != '\0') { - /* Find end of this logical line (newline) */ const char *line_start = p; while (*p != '\0' && *p != '\n') p++; size_t logical_line_len = p - line_start; if (logical_line_len == 0) { - /* Empty line */ wrapped_line_count++; } else { - /* Count how many wrapped lines this logical line needs */ const char *lp = line_start; while (lp < line_start + logical_line_len) { size_t remaining = (line_start + logical_line_len) - lp; size_t fit_len = 0; - /* Find how much fits on one line */ for (size_t i = 0; i < remaining; i++) { - if ((lp[i] & 0xC0) == 0x80) continue; /* Skip continuation bytes */ + if ((lp[i] & 0xC0) == 0x80) continue; int w = notif_text_width(lp, i + 1); if (w > content_max_width) break; fit_len = i + 1; } - /* Include trailing UTF-8 continuation bytes */ while (fit_len < remaining && (lp[fit_len] & 0xC0) == 0x80) { fit_len++; } - /* Force at least one character */ if (fit_len == 0 && remaining > 0) { fit_len = 1; while (fit_len < remaining && (lp[fit_len] & 0xC0) == 0x80) { @@ -428,12 +385,10 @@ static void notification_calculate_size(const char *summary, const char *body, if (fit_len == 0) break; lp += fit_len; - /* Skip leading spaces on continuation */ while (lp < line_start + logical_line_len && *lp == ' ') lp++; wrapped_line_count++; - /* Stop if we'd exceed max height */ if (height + (wrapped_line_count * line_height) > max_height - line_height - NOTIFICATION_PADDING) { goto done_counting; } @@ -447,13 +402,10 @@ done_counting: height += wrapped_line_count * line_height; } - /* Add space for app name at bottom */ height += line_height + NOTIFICATION_PADDING; - /* Use full max_width since we're wrapping, not truncating */ width = max_width; - /* Clamp to min/max */ if (width < NOTIFICATION_MIN_WIDTH) width = NOTIFICATION_MIN_WIDTH; if (width > max_width) width = max_width; if (height < min_height) height = min_height; @@ -466,15 +418,13 @@ done_counting: uint32_t notification_show(const char *app_name, const char *summary, const char *body, const char *icon, int timeout) { - /* Count existing notifications and close oldest if we have too many */ int count = 0; Notification *oldest = NULL; for (Notification *n = notification_list; n != NULL; n = n->next) { count++; - oldest = n; /* Last in list is oldest (we prepend new ones) */ + oldest = n; } - /* Close oldest notification if we're at the limit */ if (count >= MAX_VISIBLE_NOTIFICATIONS && oldest != NULL) { notification_close(oldest->id); } @@ -486,7 +436,6 @@ uint32_t notification_show(const char *app_name, const char *summary, if (app_name) strncpy(notif->app_name, app_name, sizeof(notif->app_name) - 1); if (summary) strncpy(notif->summary, summary, sizeof(notif->summary) - 1); - /* Dynamically allocate body - unlimited size */ if (body != NULL && body[0] != '\0') { notif->body_len = strlen(body); notif->body = dwn_malloc(notif->body_len + 1); @@ -505,13 +454,11 @@ uint32_t notification_show(const char *app_name, const char *summary, notif->expire_time = (notif->timeout > 0) ? get_time_ms() + notif->timeout : 0; - /* Calculate size based on content */ int notif_width, notif_height; notification_calculate_size(summary, body, ¬if_width, ¬if_height); notif->width = notif_width; notif->height = notif_height; - /* Create notification window */ if (dwn != NULL && dwn->display != NULL) { XSetWindowAttributes swa; swa.override_redirect = True; @@ -530,11 +477,9 @@ uint32_t notification_show(const char *app_name, const char *summary, dwn->config->colors.border_focused); } - /* Add to list */ notif->next = notification_list; notification_list = notif; - /* Position and show */ notifications_position(); notification_render(notif); @@ -555,19 +500,16 @@ void notification_close(uint32_t id) while (notif != NULL) { if (notif->id == id) { - /* Remove from list */ if (prev != NULL) { prev->next = notif->next; } else { notification_list = notif->next; } - /* Destroy window */ if (notif->window != None && dwn != NULL && dwn->display != NULL) { XDestroyWindow(dwn->display, notif->window); } - /* Free dynamically allocated body */ if (notif->body != NULL) { dwn_free(notif->body); notif->body = NULL; @@ -575,7 +517,6 @@ void notification_close(uint32_t id) dwn_free(notif); - /* Reposition remaining notifications */ notifications_position(); return; } @@ -612,7 +553,6 @@ Notification *notification_find_by_window(Window window) return NULL; } -/* ========== Rendering ========== */ void notification_render(Notification *notif) { @@ -642,66 +582,54 @@ void notification_render(Notification *notif) LOG_DEBUG("Rendering notification: summary='%s', body_len=%zu", notif->summary, notif->body_len); - /* Set legacy font in GC if available (for fallback) */ if (dwn->font != NULL) { XSetFont(dpy, dwn->gc, dwn->font->fid); } - /* Clear background using dynamic size */ XSetForeground(dpy, dwn->gc, colors->notification_bg); XFillRectangle(dpy, notif->window, dwn->gc, 0, 0, notif->width, notif->height); - /* Draw summary (title) */ int line_height = notif_line_height(); int y = NOTIFICATION_PADDING + notif_font_ascent(); notif_draw_text(notif->window, NOTIFICATION_PADDING, y, notif->summary, strlen(notif->summary), colors->notification_fg); - /* Draw body - handle multiple lines */ y += line_height + 4; int max_width = notif->width - 2 * NOTIFICATION_PADDING; int max_y = notif->height - line_height - NOTIFICATION_PADDING; - /* Only process body if it exists */ if (notif->body != NULL && notif->body_len > 0) { - /* Dynamically allocate body copy for tokenization */ char *body_copy = dwn_malloc(notif->body_len + 1); if (body_copy != NULL) { memcpy(body_copy, notif->body, notif->body_len + 1); - /* Split by newlines and draw each line */ char *line = body_copy; char *next; while (line != NULL && *line != '\0' && y < max_y) { - /* Find next newline */ next = strchr(line, '\n'); if (next != NULL) { *next = '\0'; next++; } - /* Skip empty lines but count them for spacing */ if (*line == '\0') { - y += line_height / 2; /* Half-height for empty lines */ + y += line_height / 2; line = next; continue; } - /* Word wrap long lines instead of truncating */ size_t line_len = strlen(line); const char *p = line; while (*p != '\0' && y < max_y) { - /* Find how much text fits on this line */ size_t fit_len = 0; size_t last_space = 0; for (size_t i = 0; p[i] != '\0'; i++) { - /* Skip UTF-8 continuation bytes for character counting */ if ((p[i] & 0xC0) == 0x80) continue; int width = notif_text_width(p, i + 1); @@ -710,18 +638,15 @@ void notification_render(Notification *notif) } fit_len = i + 1; - /* Track last space for word wrapping */ if (p[i] == ' ') { last_space = i; } } - /* Adjust to include full UTF-8 characters */ while (fit_len < line_len && (p[fit_len] & 0xC0) == 0x80) { fit_len++; } - /* If we couldn't fit anything, force at least one character */ if (fit_len == 0 && *p != '\0') { fit_len = 1; while (fit_len < line_len && (p[fit_len] & 0xC0) == 0x80) { @@ -729,12 +654,10 @@ void notification_render(Notification *notif) } } - /* Prefer breaking at word boundary if possible */ if (fit_len < strlen(p) && last_space > 0 && last_space > fit_len / 2) { - fit_len = last_space + 1; /* Include the space */ + fit_len = last_space + 1; } - /* Draw this segment */ if (fit_len > 0) { char *segment = dwn_malloc(fit_len + 1); if (segment != NULL) { @@ -748,7 +671,6 @@ void notification_render(Notification *notif) } p += fit_len; - /* Skip leading spaces on new line */ while (*p == ' ') p++; y += line_height; @@ -764,7 +686,6 @@ void notification_render(Notification *notif) } } - /* Draw app name at bottom */ if (notif->app_name[0] != '\0') { y = notif->height - NOTIFICATION_PADDING; notif_draw_text(notif->window, NOTIFICATION_PADDING, y, @@ -772,7 +693,6 @@ void notification_render(Notification *notif) colors->workspace_inactive); } - /* Force the drawing to be sent to X server */ XFlush(dpy); } @@ -787,7 +707,6 @@ void notifications_update(void) { long now = get_time_ms(); - /* Check for expired notifications */ Notification *notif = notification_list; while (notif != NULL) { Notification *next = notif->next; @@ -808,18 +727,15 @@ void notifications_position(void) int y = NOTIFICATION_MARGIN; - /* Account for top panel */ if (dwn->config && dwn->config->top_panel_enabled) { y += config_get_panel_height(); } for (Notification *n = notification_list; n != NULL; n = n->next) { if (n->window != None) { - /* Position from right edge using notification's own width */ int x = dwn->screen_width - n->width - NOTIFICATION_MARGIN; XMoveWindow(dwn->display, n->window, x, y); } - /* Stack using notification's own height */ y += n->height + NOTIFICATION_MARGIN; } } @@ -830,7 +746,6 @@ void notifications_raise_all(void) return; } - /* Raise all notification windows to the top */ for (Notification *n = notification_list; n != NULL; n = n->next) { if (n->window != None) { XRaiseWindow(dwn->display, n->window); @@ -838,7 +753,6 @@ void notifications_raise_all(void) } } -/* ========== Server info ========== */ void notifications_get_server_info(const char **name, const char **vendor, const char **version, const char **spec_version) diff --git a/src/panel.c b/src/panel.c index 3257de9..174378b 100644 --- a/src/panel.c +++ b/src/panel.c @@ -21,43 +21,35 @@ #include #include -/* Panel padding and spacing */ #define PANEL_PADDING 8 #define WIDGET_SPACING 12 #define WORKSPACE_WIDTH 28 #define TASKBAR_ITEM_WIDTH 150 -/* Clock format */ #define CLOCK_FORMAT "%H:%M:%S" #define DATE_FORMAT "%Y-%m-%d" -/* Static clock buffer */ static char clock_buffer[32] = ""; static char date_buffer[32] = ""; -/* System stats */ typedef struct { - int cpu_percent; /* CPU usage percentage */ - int mem_percent; /* Memory usage percentage */ - int mem_used_mb; /* Memory used in MB */ - int mem_total_mb; /* Total memory in MB */ - float load_1min; /* 1-minute load average */ - float load_5min; /* 5-minute load average */ - float load_15min; /* 15-minute load average */ - /* CPU calculation state */ + int cpu_percent; + int mem_percent; + int mem_used_mb; + int mem_total_mb; + float load_1min; + float load_5min; + float load_15min; unsigned long long prev_idle; unsigned long long prev_total; } SystemStats; static SystemStats sys_stats = {0}; -/* Forward declarations */ static void panel_render_system_stats(Panel *panel, int x, int *width); static int panel_calculate_stats_width(void); -/* ========== UTF-8 text helpers ========== */ -/* Get text width using Xft (UTF-8 aware) */ static int panel_text_width(const char *text, int len) { if (text == NULL || dwn == NULL) return 0; @@ -69,7 +61,6 @@ static int panel_text_width(const char *text, int len) return extents.xOff; } - /* Fallback to legacy font */ if (dwn->font != NULL) { return XTextWidth(dwn->font, text, len); } @@ -77,13 +68,11 @@ static int panel_text_width(const char *text, int len) return 0; } -/* Draw text using Xft (UTF-8 aware) */ static void panel_draw_text(Drawable d, int x, int y, const char *text, int len, unsigned long color) { if (text == NULL || dwn == NULL || dwn->display == NULL) return; - /* Use Xft for UTF-8 support */ if (dwn->xft_font != NULL) { XftDraw *xft_draw = XftDrawCreate(dwn->display, d, DefaultVisual(dwn->display, dwn->screen), @@ -109,12 +98,10 @@ static void panel_draw_text(Drawable d, int x, int y, const char *text, } } - /* Fallback to legacy X11 text */ XSetForeground(dwn->display, dwn->gc, color); XDrawString(dwn->display, d, dwn->gc, x, y, text, len); } -/* Get text Y position for vertical centering */ static int panel_text_y(int panel_height) { if (dwn->xft_font != NULL) { @@ -126,7 +113,6 @@ static int panel_text_y(int panel_height) return panel_height / 2; } -/* ========== Panel creation/destruction ========== */ Panel *panel_create(PanelPosition position) { @@ -147,7 +133,6 @@ Panel *panel_create(PanelPosition position) panel->y = dwn->screen_height - panel->height; } - /* Create panel window */ XSetWindowAttributes swa; swa.override_redirect = True; swa.background_pixel = dwn->config->colors.panel_bg; @@ -161,12 +146,10 @@ Panel *panel_create(PanelPosition position) CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); - /* Create double buffer */ panel->buffer = XCreatePixmap(dwn->display, panel->window, panel->width, panel->height, DefaultDepth(dwn->display, dwn->screen)); - /* Set EWMH strut to reserve space */ long strut[4] = { 0, 0, 0, 0 }; if (position == PANEL_TOP) { strut[2] = panel->height; @@ -221,7 +204,6 @@ void panels_init(void) } } - /* Initial clock and stats update */ panel_update_clock(); panel_update_system_stats(); @@ -241,7 +223,6 @@ void panels_cleanup(void) } } -/* ========== Panel rendering ========== */ void panel_render(Panel *panel) { @@ -252,7 +233,6 @@ void panel_render(Panel *panel) Display *dpy = dwn->display; const ColorScheme *colors = config_get_colors(); - /* Clear buffer with background color */ XSetForeground(dpy, dwn->gc, colors->panel_bg); XFillRectangle(dpy, panel->buffer, dwn->gc, 0, 0, panel->width, panel->height); @@ -260,33 +240,25 @@ void panel_render(Panel *panel) int width; if (panel->position == PANEL_TOP) { - /* Top panel: workspaces, layout indicator, taskbar, systray */ - /* Workspace indicators */ panel_render_workspaces(panel, x, &width); x += width + WIDGET_SPACING; - /* Layout indicator */ panel_render_layout_indicator(panel, x, &width); x += width + WIDGET_SPACING; - /* Taskbar (takes remaining space, but leave room for systray) */ panel_render_taskbar(panel, x, &width); - /* System tray (right side) - WiFi, Audio, etc. */ int systray_actual_width = systray_get_width(); int systray_x = panel->width - systray_actual_width - PANEL_PADDING; systray_render(panel, systray_x, &width); - /* AI status (left of systray) */ if (dwn->ai_enabled) { int ai_x = systray_x - 60; panel_render_ai_status(panel, ai_x, &width); } } else { - /* Bottom panel: date (left), news ticker (center), system stats + clock (right) */ - /* Date (left side) */ int date_width = 0; if (dwn->xft_font != NULL || dwn->font != NULL) { int text_y = panel_text_y(panel->height); @@ -295,28 +267,23 @@ void panel_render(Panel *panel) date_width = panel_text_width(date_buffer, strlen(date_buffer)); } - /* Calculate positions from right edge */ int clock_width = panel_text_width(clock_buffer, strlen(clock_buffer)); int stats_width = panel_calculate_stats_width(); - /* Clock at rightmost position */ int clock_x = panel->width - clock_width - PANEL_PADDING; panel_render_clock(panel, clock_x, &width); - /* Stats immediately left of clock */ int stats_x = clock_x - stats_width - WIDGET_SPACING; panel_render_system_stats(panel, stats_x, &width); - /* News ticker between date and stats */ int news_start = PANEL_PADDING + date_width + WIDGET_SPACING * 2; int news_max_width = stats_x - news_start - WIDGET_SPACING; - if (news_max_width > 100) { /* Only show if there's reasonable space */ + if (news_max_width > 100) { int news_width = 0; news_render(panel, news_start, news_max_width, &news_width); } } - /* Copy buffer to window */ XCopyArea(dpy, panel->buffer, panel->window, dwn->gc, 0, 0, panel->width, panel->height, 0, 0); @@ -349,14 +316,12 @@ void panel_render_workspaces(Panel *panel, int x, int *width) bool active = (i == dwn->current_workspace); bool has_clients = !workspace_is_empty(i); - /* Background */ unsigned long bg = active ? colors->workspace_active : colors->panel_bg; XSetForeground(dpy, dwn->gc, bg); XFillRectangle(dpy, panel->buffer, dwn->gc, x + i * WORKSPACE_WIDTH, 2, WORKSPACE_WIDTH - 2, panel->height - 4); - /* Workspace number */ char num[4]; snprintf(num, sizeof(num), "%d", i + 1); @@ -384,7 +349,6 @@ void panel_render_taskbar(Panel *panel, int x, int *width) int available_width = panel->width - x - 100 - PANEL_PADDING; int item_count = 0; - /* Count visible clients */ for (Client *c = dwn->client_list; c != NULL; c = c->next) { if (c->workspace == (unsigned int)dwn->current_workspace && !client_is_minimized(c)) { item_count++; @@ -401,7 +365,6 @@ void panel_render_taskbar(Panel *panel, int x, int *width) item_width = TASKBAR_ITEM_WIDTH; } - /* Render each client */ Workspace *ws = workspace_get_current(); for (Client *c = dwn->client_list; c != NULL; c = c->next) { @@ -411,32 +374,27 @@ void panel_render_taskbar(Panel *panel, int x, int *width) bool focused = (ws != NULL && ws->focused == c); - /* Background */ unsigned long bg = focused ? colors->workspace_active : colors->panel_bg; XSetForeground(dpy, dwn->gc, bg); XFillRectangle(dpy, panel->buffer, dwn->gc, current_x, 2, item_width - 2, panel->height - 4); - /* Title - leave room for "..." (4 bytes including null) */ char title[64]; - strncpy(title, c->title, sizeof(title) - 4); /* Leave room for "..." */ + strncpy(title, c->title, sizeof(title) - 4); title[sizeof(title) - 4] = '\0'; - /* Truncate UTF-8 aware if necessary */ int max_text_width = item_width - 8; bool truncated = false; while (panel_text_width(title, strlen(title)) > max_text_width && strlen(title) > 3) { size_t len = strlen(title); - /* Move back to find UTF-8 character boundary */ size_t cut = len - 1; while (cut > 0 && (title[cut] & 0xC0) == 0x80) { - cut--; /* Skip continuation bytes */ + cut--; } if (cut > 0) cut--; while (cut > 0 && (title[cut] & 0xC0) == 0x80) { cut--; } - /* Ensure we have room for "..." */ if (cut > sizeof(title) - 4) { cut = sizeof(title) - 4; } @@ -476,8 +434,6 @@ void panel_render_clock(Panel *panel, int x, int *width) void panel_render_systray(Panel *panel, int x, int *width) { - /* System tray placeholder - actual implementation requires - handling _NET_SYSTEM_TRAY protocol */ (void)panel; (void)x; *width = 0; @@ -524,7 +480,6 @@ void panel_render_ai_status(Panel *panel, int x, int *width) *width = panel_text_width(status, strlen(status)); } -/* ========== Panel interaction ========== */ void panel_handle_click(Panel *panel, int x, int y, int button) { @@ -533,7 +488,6 @@ void panel_handle_click(Panel *panel, int x, int y, int button) } if (panel->position == PANEL_TOP) { - /* Check systray click first (right side) */ int systray_actual_width = systray_get_width(); int systray_start = panel->width - systray_actual_width - PANEL_PADDING; if (x >= systray_start) { @@ -541,27 +495,24 @@ void panel_handle_click(Panel *panel, int x, int y, int button) return; } - /* Check workspace click */ int ws = panel_hit_test_workspace(panel, x, y); if (ws >= 0 && ws < MAX_WORKSPACES) { - if (button == 1) { /* Left click */ + if (button == 1) { workspace_switch(ws); } return; } - /* Check taskbar click */ Client *c = panel_hit_test_taskbar(panel, x, y); if (c != NULL) { if (button == 1) { client_focus(c); - } else if (button == 3) { /* Right click */ + } else if (button == 3) { client_close(c); } return; } } else if (panel->position == PANEL_BOTTOM) { - /* Bottom panel - click on news opens article in browser */ if (button == 1) { news_handle_click(x, y); } @@ -629,7 +580,6 @@ Client *panel_hit_test_taskbar(Panel *panel, int x, int y) return NULL; } -/* ========== Panel visibility ========== */ void panel_show(Panel *panel) { @@ -665,7 +615,6 @@ void panel_toggle(Panel *panel) } } -/* ========== Clock updates ========== */ void panel_update_clock(void) { @@ -676,14 +625,12 @@ void panel_update_clock(void) strftime(date_buffer, sizeof(date_buffer), DATE_FORMAT, tm_info); } -/* ========== System stats ========== */ void panel_update_system_stats(void) { FILE *fp; char line[256]; - /* Read CPU stats from /proc/stat */ fp = fopen("/proc/stat", "r"); if (fp != NULL) { if (fgets(line, sizeof(line), fp) != NULL) { @@ -708,7 +655,6 @@ void panel_update_system_stats(void) fclose(fp); } - /* Read memory stats from /proc/meminfo */ fp = fopen("/proc/meminfo", "r"); if (fp != NULL) { unsigned long mem_total = 0, mem_free = 0, buffers = 0, cached = 0; @@ -722,7 +668,7 @@ void panel_update_system_stats(void) sscanf(line + 8, " %lu", &buffers); } else if (strncmp(line, "Cached:", 7) == 0) { sscanf(line + 7, " %lu", &cached); - break; /* Got all we need */ + break; } } fclose(fp); @@ -735,7 +681,6 @@ void panel_update_system_stats(void) } } - /* Read load average from /proc/loadavg */ fp = fopen("/proc/loadavg", "r"); if (fp != NULL) { if (fscanf(fp, "%f %f %f", @@ -750,7 +695,6 @@ void panel_update_system_stats(void) } } -/* Calculate stats width without rendering */ static int panel_calculate_stats_width(void) { if (dwn->xft_font == NULL && dwn->font == NULL) return 0; @@ -758,11 +702,9 @@ static int panel_calculate_stats_width(void) char buf[256]; int total = 0; - /* CPU */ int len = snprintf(buf, sizeof(buf), "CPU:%2d%%", sys_stats.cpu_percent); total += panel_text_width(buf, len) + WIDGET_SPACING; - /* Memory */ if (sys_stats.mem_total_mb >= 1024) { len = snprintf(buf, sizeof(buf), "MEM:%.1fG/%dG", sys_stats.mem_used_mb / 1024.0f, sys_stats.mem_total_mb / 1024); @@ -772,12 +714,10 @@ static int panel_calculate_stats_width(void) } total += panel_text_width(buf, len) + WIDGET_SPACING; - /* Load */ len = snprintf(buf, sizeof(buf), "Load:%.2f %.2f %.2f", sys_stats.load_1min, sys_stats.load_5min, sys_stats.load_15min); total += panel_text_width(buf, len) + WIDGET_SPACING; - /* Battery - use thread-safe snapshot */ BatteryState bat_snap = systray_get_battery_snapshot(); if (bat_snap.present) { const char *bat_icon = bat_snap.charging ? "[+]" : "[=]"; @@ -803,24 +743,21 @@ static void panel_render_system_stats(Panel *panel, int x, int *width) int text_y = panel_text_y(panel->height); int current_x = x; - /* Format: "CPU: 25% | MEM: 4.2G/16G | Load: 1.23 0.98 0.76 | BAT: 85%" */ char stats_buf[256]; int len; - /* CPU with color coding */ unsigned long cpu_color = colors->panel_fg; if (sys_stats.cpu_percent >= 90) { - cpu_color = colors->workspace_urgent; /* Red for high CPU */ + cpu_color = colors->workspace_urgent; } else if (sys_stats.cpu_percent >= 70) { - cpu_color = 0xFFA500; /* Orange for medium-high */ + cpu_color = 0xFFA500; } len = snprintf(stats_buf, sizeof(stats_buf), "CPU:%2d%%", sys_stats.cpu_percent); panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, cpu_color); current_x += panel_text_width(stats_buf, len) + WIDGET_SPACING; - /* Memory */ unsigned long mem_color = colors->panel_fg; if (sys_stats.mem_percent >= 90) { mem_color = colors->workspace_urgent; @@ -838,7 +775,6 @@ static void panel_render_system_stats(Panel *panel, int x, int *width) panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, mem_color); current_x += panel_text_width(stats_buf, len) + WIDGET_SPACING; - /* Load average */ unsigned long load_color = colors->panel_fg; if (sys_stats.load_1min >= 4.0f) { load_color = colors->workspace_urgent; @@ -851,16 +787,15 @@ static void panel_render_system_stats(Panel *panel, int x, int *width) panel_draw_text(panel->buffer, current_x, text_y, stats_buf, len, load_color); current_x += panel_text_width(stats_buf, len) + WIDGET_SPACING; - /* Battery (if present) - use thread-safe snapshot */ BatteryState bat_snap = systray_get_battery_snapshot(); if (bat_snap.present) { unsigned long bat_color = colors->panel_fg; if (bat_snap.percentage <= 20 && !bat_snap.charging) { - bat_color = colors->workspace_urgent; /* Red for low */ + bat_color = colors->workspace_urgent; } else if (bat_snap.percentage <= 40 && !bat_snap.charging) { - bat_color = 0xFFA500; /* Orange for medium-low */ + bat_color = 0xFFA500; } else if (bat_snap.charging) { - bat_color = colors->workspace_active; /* Blue for charging */ + bat_color = colors->workspace_active; } const char *bat_icon = bat_snap.charging ? "[+]" : "[=]"; @@ -872,11 +807,9 @@ static void panel_render_system_stats(Panel *panel, int x, int *width) *width = current_x - x; } -/* ========== System tray ========== */ void panel_init_systray(void) { - /* System tray initialization - requires _NET_SYSTEM_TRAY protocol */ LOG_DEBUG("System tray initialization (placeholder)"); } diff --git a/src/systray.c b/src/systray.c index 07e6867..a2ead31 100644 --- a/src/systray.c +++ b/src/systray.c @@ -17,17 +17,15 @@ #include #include -/* UTF-8 Icons for system tray */ -#define ICON_WIFI_FULL "\xE2\x96\x82\xE2\x96\x84\xE2\x96\x86\xE2\x96\x88" /* Full signal bars */ -#define ICON_WIFI_OFF "\xE2\x9C\x97" /* X mark */ -#define ICON_VOLUME_HIGH "\xF0\x9F\x94\x8A" /* Speaker high */ -#define ICON_VOLUME_MED "\xF0\x9F\x94\x89" /* Speaker medium */ -#define ICON_VOLUME_LOW "\xF0\x9F\x94\x88" /* Speaker low */ -#define ICON_VOLUME_MUTE "\xF0\x9F\x94\x87" /* Speaker muted */ -#define ICON_BATTERY_FULL "\xF0\x9F\x94\x8B" /* Battery full */ -#define ICON_BATTERY_CHARGE "\xE2\x9A\xA1" /* Lightning bolt */ +#define ICON_WIFI_FULL "\xE2\x96\x82\xE2\x96\x84\xE2\x96\x86\xE2\x96\x88" +#define ICON_WIFI_OFF "\xE2\x9C\x97" +#define ICON_VOLUME_HIGH "\xF0\x9F\x94\x8A" +#define ICON_VOLUME_MED "\xF0\x9F\x94\x89" +#define ICON_VOLUME_LOW "\xF0\x9F\x94\x88" +#define ICON_VOLUME_MUTE "\xF0\x9F\x94\x87" +#define ICON_BATTERY_FULL "\xF0\x9F\x94\x8B" +#define ICON_BATTERY_CHARGE "\xE2\x9A\xA1" -/* Simple ASCII fallback icons */ #define ASCII_WIFI_ON "W" #define ASCII_WIFI_OFF "-" #define ASCII_VOL_HIGH "V" @@ -35,7 +33,6 @@ #define ASCII_BATTERY "B" #define ASCII_CHARGING "+" -/* Widget dimensions */ #define SYSTRAY_SPACING 12 #define DROPDOWN_ITEM_HEIGHT 28 #define DROPDOWN_WIDTH 400 @@ -45,32 +42,26 @@ #define SLIDER_PADDING 8 #define SLIDER_KNOB_HEIGHT 8 -/* Scan interval in milliseconds */ #define WIFI_SCAN_INTERVAL 10000 -/* Global state */ WifiState wifi_state = {0}; AudioState audio_state = {0}; BatteryState battery_state = {0}; DropdownMenu *wifi_menu = NULL; VolumeSlider *volume_slider = NULL; -/* Async update thread */ static pthread_t update_thread; static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER; static volatile int thread_running = 0; -/* Systray position tracking */ static int systray_x = 0; static int systray_width = 0; static int wifi_icon_x = 0; static int audio_icon_x = 0; -/* Forward declarations for internal update functions */ static void wifi_update_state_internal(void); static void audio_update_state_internal(void); -/* ========== UTF-8 Text Drawing ========== */ static void draw_utf8_text(Drawable d, int x, int y, const char *text, unsigned long color) { @@ -79,7 +70,6 @@ static void draw_utf8_text(Drawable d, int x, int y, const char *text, unsigned } if (dwn->xft_font != NULL) { - /* Use Xft for proper UTF-8 rendering */ XftDraw *xft_draw = XftDrawCreate(dwn->display, d, DefaultVisual(dwn->display, dwn->screen), dwn->colormap); @@ -87,7 +77,6 @@ static void draw_utf8_text(Drawable d, int x, int y, const char *text, unsigned XftColor xft_color; XRenderColor render_color; - /* Convert X11 color to XRender color */ render_color.red = ((color >> 16) & 0xFF) * 257; render_color.green = ((color >> 8) & 0xFF) * 257; render_color.blue = (color & 0xFF) * 257; @@ -108,7 +97,6 @@ static void draw_utf8_text(Drawable d, int x, int y, const char *text, unsigned } } - /* Fallback to basic X11 drawing */ XSetForeground(dwn->display, dwn->gc, color); XDrawString(dwn->display, d, dwn->gc, x, y, text, strlen(text)); } @@ -130,31 +118,25 @@ static int get_text_width(const char *text) return XTextWidth(dwn->font, text, strlen(text)); } - return strlen(text) * 8; /* Rough estimate */ + return strlen(text) * 8; } -/* ========== Initialization ========== */ -/* Background thread for async state updates */ static void *systray_update_thread(void *arg) { (void)arg; while (thread_running) { - /* Update WiFi state (slow - uses popen) */ wifi_update_state_internal(); - /* Update audio state (slow - uses popen) */ audio_update_state_internal(); - /* Update battery state (fast - reads /sys files) */ pthread_mutex_lock(&state_mutex); battery_update_state(); pthread_mutex_unlock(&state_mutex); - /* Sleep for 2 seconds between updates */ for (int i = 0; i < 20 && thread_running; i++) { - usleep(100000); /* 100ms chunks for responsive shutdown */ + usleep(100000); } } @@ -167,11 +149,9 @@ void systray_init(void) memset(&audio_state, 0, sizeof(audio_state)); memset(&battery_state, 0, sizeof(battery_state)); - /* Set default states for instant display */ audio_state.volume = 50; wifi_state.enabled = true; - /* Start background update thread - all updates happen async */ thread_running = 1; if (pthread_create(&update_thread, NULL, systray_update_thread, NULL) != 0) { LOG_WARN("Failed to create systray update thread"); @@ -183,7 +163,6 @@ void systray_init(void) void systray_cleanup(void) { - /* Stop the update thread */ if (thread_running) { thread_running = 0; pthread_join(update_thread, NULL); @@ -199,7 +178,6 @@ void systray_cleanup(void) } } -/* ========== Battery Functions ========== */ void battery_update_state(void) { @@ -213,7 +191,6 @@ void battery_update_state(void) battery_state.charging = false; battery_state.percentage = 0; - /* Look for battery in /sys/class/power_supply */ dir = opendir("/sys/class/power_supply"); if (dir == NULL) { return; @@ -224,7 +201,6 @@ void battery_update_state(void) continue; } - /* Check if this is a battery (not AC adapter) */ snprintf(path, sizeof(path), "/sys/class/power_supply/%s/type", entry->d_name); fp = fopen(path, "r"); if (fp != NULL) { @@ -233,7 +209,6 @@ void battery_update_state(void) if (strcmp(value, "Battery") == 0) { battery_state.present = true; - /* Get capacity */ snprintf(path, sizeof(path), "/sys/class/power_supply/%s/capacity", entry->d_name); FILE *cap_fp = fopen(path, "r"); if (cap_fp != NULL) { @@ -243,7 +218,6 @@ void battery_update_state(void) fclose(cap_fp); } - /* Get status (charging/discharging) */ snprintf(path, sizeof(path), "/sys/class/power_supply/%s/status", entry->d_name); FILE *status_fp = fopen(path, "r"); if (status_fp != NULL) { @@ -256,7 +230,7 @@ void battery_update_state(void) } fclose(fp); - break; /* Found a battery, stop searching */ + break; } } fclose(fp); @@ -268,7 +242,7 @@ void battery_update_state(void) const char *battery_get_icon(void) { if (!battery_state.present) { - return ""; /* No battery = no icon */ + return ""; } if (battery_state.charging) { @@ -278,16 +252,13 @@ const char *battery_get_icon(void) return ASCII_BATTERY; } -/* ========== WiFi Functions ========== */ -/* Internal update function - called from background thread */ static void wifi_update_state_internal(void) { FILE *fp; char line[256]; WifiState temp_state = {0}; - /* Check if WiFi is connected and get current SSID */ fp = popen("nmcli -t -f GENERAL.STATE,GENERAL.CONNECTION dev show 2>/dev/null | head -2", "r"); if (fp != NULL) { while (fgets(line, sizeof(line), fp) != NULL) { @@ -310,7 +281,6 @@ static void wifi_update_state_internal(void) pclose(fp); } - /* Get signal strength if connected */ if (temp_state.connected) { fp = popen("nmcli -t -f IN-USE,SIGNAL dev wifi list 2>/dev/null | grep '^\\*' | cut -d: -f2", "r"); if (fp != NULL) { @@ -321,7 +291,6 @@ static void wifi_update_state_internal(void) } } - /* Copy to global state with mutex protection */ pthread_mutex_lock(&state_mutex); wifi_state.connected = temp_state.connected; wifi_state.enabled = temp_state.enabled; @@ -330,11 +299,8 @@ static void wifi_update_state_internal(void) pthread_mutex_unlock(&state_mutex); } -/* Public function - now just reads cached state (non-blocking) */ void wifi_update_state(void) { - /* State is updated by background thread - this is now a no-op */ - /* Kept for API compatibility */ } void wifi_scan_networks(void) @@ -342,7 +308,6 @@ void wifi_scan_networks(void) FILE *fp; char line[512]; - /* Build network list in temporary storage first */ WifiNetwork temp_networks[MAX_WIFI_NETWORKS]; int temp_count = 0; long scan_time = get_time_ms(); @@ -374,7 +339,6 @@ void wifi_scan_networks(void) } pclose(fp); - /* Copy to global state under lock */ pthread_mutex_lock(&state_mutex); memcpy(wifi_state.networks, temp_networks, sizeof(temp_networks)); wifi_state.network_count = temp_count; @@ -423,9 +387,7 @@ const char *wifi_get_icon(void) return ASCII_WIFI_ON; } -/* ========== Audio Functions ========== */ -/* Internal update function - called from background thread */ static void audio_update_state_internal(void) { FILE *fp; @@ -449,18 +411,14 @@ static void audio_update_state_internal(void) pclose(fp); } - /* Copy to global state with mutex protection */ pthread_mutex_lock(&state_mutex); audio_state.volume = volume; audio_state.muted = muted; pthread_mutex_unlock(&state_mutex); } -/* Public function - now just reads cached state (non-blocking) */ void audio_update_state(void) { - /* State is updated by background thread - this is now a no-op */ - /* Kept for API compatibility */ } void audio_set_volume(int volume) @@ -495,7 +453,6 @@ const char *audio_get_icon(void) return ASCII_VOL_HIGH; } -/* ========== Volume Slider ========== */ VolumeSlider *volume_slider_create(int x, int y) { @@ -575,11 +532,9 @@ void volume_slider_render(VolumeSlider *slider) Display *dpy = dwn->display; const ColorScheme *colors = config_get_colors(); - /* Clear background */ XSetForeground(dpy, dwn->gc, colors->panel_bg); XFillRectangle(dpy, slider->window, dwn->gc, 0, 0, slider->width, slider->height); - /* Draw track */ int track_x = slider->width / 2 - 2; int track_y = SLIDER_PADDING; int track_height = slider->height - 2 * SLIDER_PADDING; @@ -587,14 +542,12 @@ void volume_slider_render(VolumeSlider *slider) XSetForeground(dpy, dwn->gc, colors->workspace_inactive); XFillRectangle(dpy, slider->window, dwn->gc, track_x, track_y, 4, track_height); - /* Draw filled portion */ int fill_height = (audio_state.volume * track_height) / 100; int fill_y = track_y + track_height - fill_height; XSetForeground(dpy, dwn->gc, colors->workspace_active); XFillRectangle(dpy, slider->window, dwn->gc, track_x, fill_y, 4, fill_height); - /* Draw knob */ int knob_y = fill_y - SLIDER_KNOB_HEIGHT / 2; if (knob_y < track_y) knob_y = track_y; if (knob_y > track_y + track_height - SLIDER_KNOB_HEIGHT) { @@ -605,7 +558,6 @@ void volume_slider_render(VolumeSlider *slider) XFillRectangle(dpy, slider->window, dwn->gc, track_x - 4, knob_y, 12, SLIDER_KNOB_HEIGHT); - /* Draw percentage text at bottom */ char vol_text[8]; snprintf(vol_text, sizeof(vol_text), "%d%%", audio_state.volume); int text_width = get_text_width(vol_text); @@ -626,7 +578,6 @@ void volume_slider_handle_click(VolumeSlider *slider, int x, int y) slider->dragging = true; - /* Calculate volume from y position */ int track_y = SLIDER_PADDING; int track_height = slider->height - 2 * SLIDER_PADDING; @@ -657,7 +608,6 @@ void volume_slider_handle_release(VolumeSlider *slider) slider->dragging = false; } -/* ========== Dropdown Menu ========== */ DropdownMenu *dropdown_create(int x, int y, int width) { @@ -707,7 +657,6 @@ void dropdown_show(DropdownMenu *menu) menu->item_count = 1; } - /* Calculate auto-width based on longest network name + signal */ int max_text_width = 0; for (int i = 0; i < wifi_state.network_count; i++) { WifiNetwork *net = &wifi_state.networks[i]; @@ -778,7 +727,6 @@ void dropdown_render(DropdownMenu *menu) } int text_y_offset = (DROPDOWN_ITEM_HEIGHT + font_height) / 2; - /* Clear background */ XSetForeground(dpy, dwn->gc, colors->panel_bg); XFillRectangle(dpy, menu->window, dwn->gc, 0, 0, menu->width, menu->height); @@ -790,7 +738,6 @@ void dropdown_render(DropdownMenu *menu) WifiNetwork *net = &wifi_state.networks[i]; int y = DROPDOWN_PADDING + i * DROPDOWN_ITEM_HEIGHT; - /* Highlight hovered item */ if (i == menu->hovered_item) { XSetForeground(dpy, dwn->gc, colors->workspace_active); XFillRectangle(dpy, menu->window, dwn->gc, @@ -799,7 +746,6 @@ void dropdown_render(DropdownMenu *menu) unsigned long text_color = (i == menu->hovered_item) ? colors->panel_bg : colors->panel_fg; - /* Network name */ char label[80]; if (net->connected) { snprintf(label, sizeof(label), "> %s", net->ssid); @@ -807,7 +753,6 @@ void dropdown_render(DropdownMenu *menu) snprintf(label, sizeof(label), " %s", net->ssid); } - /* Truncate if needed */ if (strlen(label) > 25) { label[22] = '.'; label[23] = '.'; @@ -817,7 +762,6 @@ void dropdown_render(DropdownMenu *menu) draw_utf8_text(menu->window, DROPDOWN_PADDING, y + text_y_offset, label, text_color); - /* Signal strength */ char signal[8]; snprintf(signal, sizeof(signal), "%d%%", net->signal); int signal_width = get_text_width(signal); @@ -885,7 +829,6 @@ void dropdown_handle_motion(DropdownMenu *menu, int x, int y) } } -/* ========== System Tray Rendering ========== */ int systray_get_width(void) { @@ -922,7 +865,6 @@ void systray_render(Panel *panel, int x, int *width) const ColorScheme *colors = config_get_colors(); - /* Take thread-safe snapshots of state */ pthread_mutex_lock(&state_mutex); AudioState audio_snap = audio_state; WifiState wifi_snap; @@ -943,7 +885,6 @@ void systray_render(Panel *panel, int x, int *width) systray_x = x; int current_x = x; - /* Audio indicator */ audio_icon_x = current_x; char audio_label[32]; const char *audio_icon = (audio_snap.muted || audio_snap.volume == 0) ? ASCII_VOL_MUTE : ASCII_VOL_HIGH; @@ -953,7 +894,6 @@ void systray_render(Panel *panel, int x, int *width) draw_utf8_text(panel->buffer, current_x, text_y, audio_label, audio_color); current_x += get_text_width(audio_label) + SYSTRAY_SPACING; - /* WiFi indicator - show SSID and signal strength */ wifi_icon_x = current_x; char wifi_label[128]; const char *wifi_icon_str = (!wifi_snap.enabled || !wifi_snap.connected) ? ASCII_WIFI_OFF : ASCII_WIFI_ON; @@ -976,10 +916,10 @@ void systray_render(Panel *panel, int x, int *width) int systray_hit_test(int x) { if (x >= wifi_icon_x) { - return 0; /* WiFi - extends to end of systray */ + return 0; } if (x >= audio_icon_x && x < wifi_icon_x) { - return 1; /* Audio */ + return 1; } return -1; } @@ -988,8 +928,7 @@ void systray_handle_click(int x, int y, int button) { int widget = systray_hit_test(x); - if (widget == 0) { /* WiFi clicked */ - /* Hide volume slider if open */ + if (widget == 0) { if (volume_slider != NULL && volume_slider->visible) { volume_slider_hide(volume_slider); } @@ -1012,14 +951,12 @@ void systray_handle_click(int x, int y, int button) wifi_disconnect(); } } - } else if (widget == 1) { /* Audio clicked */ - /* Hide wifi menu if open */ + } else if (widget == 1) { if (wifi_menu != NULL && wifi_menu->visible) { dropdown_hide(wifi_menu); } if (button == 1) { - /* Show volume slider */ if (volume_slider == NULL) { int panel_height = config_get_panel_height(); volume_slider = volume_slider_create(audio_icon_x, panel_height); @@ -1033,18 +970,15 @@ void systray_handle_click(int x, int y, int button) volume_slider_show(volume_slider); } } else if (button == 3) { - /* Right-click: toggle mute */ audio_toggle_mute(); panel_render_all(); } else if (button == 4) { - /* Scroll up: increase volume */ audio_set_volume(audio_state.volume + 5); if (volume_slider != NULL && volume_slider->visible) { volume_slider_render(volume_slider); } panel_render_all(); } else if (button == 5) { - /* Scroll down: decrease volume */ audio_set_volume(audio_state.volume - 5); if (volume_slider != NULL && volume_slider->visible) { volume_slider_render(volume_slider); @@ -1058,10 +992,7 @@ void systray_handle_click(int x, int y, int button) void systray_update(void) { - /* State is updated asynchronously by the background thread. - * This function now only handles the WiFi menu re-scan. */ - /* Re-scan WiFi networks periodically if menu is open */ if (wifi_menu != NULL && wifi_menu->visible) { long now = get_time_ms(); pthread_mutex_lock(&state_mutex); @@ -1075,7 +1006,6 @@ void systray_update(void) } } -/* ========== Thread-safe access functions ========== */ void systray_lock(void) { diff --git a/src/util.c b/src/util.c index b44ed76..e8b13e8 100644 --- a/src/util.c +++ b/src/util.c @@ -21,30 +21,27 @@ #include #include -/* ========== Async Logging Configuration ========== */ -#define LOG_RING_SIZE 256 /* Number of log entries in ring buffer */ -#define LOG_MSG_MAX_LEN 512 /* Max length of a single log message */ -#define LOG_MAX_FILE_SIZE (5 * 1024 * 1024) /* 5 MB max log file size */ -#define LOG_FLUSH_INTERVAL_MS 100 /* Flush to disk every 100ms */ +#define LOG_RING_SIZE 256 +#define LOG_MSG_MAX_LEN 512 +#define LOG_MAX_FILE_SIZE (5 * 1024 * 1024) +#define LOG_FLUSH_INTERVAL_MS 100 -/* Log entry in ring buffer */ typedef struct { char message[LOG_MSG_MAX_LEN]; LogLevel level; - _Atomic int ready; /* 0 = empty, 1 = ready to write */ + _Atomic int ready; } LogEntry; -/* Static state for async logging */ static LogEntry log_ring[LOG_RING_SIZE]; -static _Atomic size_t log_write_idx = 0; /* Next slot to write to */ -static _Atomic size_t log_read_idx = 0; /* Next slot to read from */ -static _Atomic int log_running = 0; /* Log thread running flag */ +static _Atomic size_t log_write_idx = 0; +static _Atomic size_t log_read_idx = 0; +static _Atomic int log_running = 0; static pthread_t log_thread; -static int log_fd = -1; /* File descriptor for log file */ -static char *log_path = NULL; /* Path to log file */ +static int log_fd = -1; +static char *log_path = NULL; static LogLevel min_level = LOG_INFO; -static _Atomic size_t log_file_size = 0; /* Current log file size */ +static _Atomic size_t log_file_size = 0; static const char *level_names[] = { "DEBUG", @@ -54,19 +51,16 @@ static const char *level_names[] = { }; static const char *level_colors[] = { - "\033[36m", /* Cyan for DEBUG */ - "\033[32m", /* Green for INFO */ - "\033[33m", /* Yellow for WARN */ - "\033[31m" /* Red for ERROR */ + "\033[36m", + "\033[32m", + "\033[33m", + "\033[31m" }; -/* Forward declarations for internal log functions */ static void log_rotate_if_needed(void); static void *log_writer_thread(void *arg); -/* ========== Async Logging Implementation ========== */ -/* Rotate log file if it exceeds max size */ static void log_rotate_if_needed(void) { if (log_fd < 0 || log_path == NULL) { @@ -78,27 +72,22 @@ static void log_rotate_if_needed(void) return; } - /* Close current file */ close(log_fd); log_fd = -1; - /* Create backup filename */ size_t path_len = strlen(log_path); char *backup_path = malloc(path_len + 8); if (backup_path != NULL) { snprintf(backup_path, path_len + 8, "%s.old", log_path); - /* Remove old backup if exists, rename current to backup */ unlink(backup_path); rename(log_path, backup_path); free(backup_path); } - /* Reopen fresh log file */ log_fd = open(log_path, O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, 0644); atomic_store(&log_file_size, 0); } -/* Background thread that writes log entries to file */ static void *log_writer_thread(void *arg) { (void)arg; @@ -107,22 +96,18 @@ static void *log_writer_thread(void *arg) size_t read_idx = atomic_load(&log_read_idx); size_t write_idx = atomic_load(&log_write_idx); - /* Process all available entries */ while (read_idx != write_idx) { size_t idx = read_idx % LOG_RING_SIZE; LogEntry *entry = &log_ring[idx]; - /* Wait for entry to be ready (spin briefly) */ int attempts = 0; while (!atomic_load(&entry->ready) && attempts < 100) { attempts++; - /* Brief yield */ - struct timespec ts = {0, 1000}; /* 1 microsecond */ + struct timespec ts = {0, 1000}; nanosleep(&ts, NULL); } if (atomic_load(&entry->ready)) { - /* Write to file if open */ if (log_fd >= 0) { log_rotate_if_needed(); if (log_fd >= 0) { @@ -133,7 +118,6 @@ static void *log_writer_thread(void *arg) } } - /* Mark entry as consumed */ atomic_store(&entry->ready, 0); } @@ -141,7 +125,6 @@ static void *log_writer_thread(void *arg) atomic_store(&log_read_idx, read_idx); } - /* Sleep before next check */ struct timespec ts = {0, LOG_FLUSH_INTERVAL_MS * 1000000L}; nanosleep(&ts, NULL); } @@ -151,7 +134,6 @@ static void *log_writer_thread(void *arg) void log_init(const char *path) { - /* Initialize ring buffer */ memset(log_ring, 0, sizeof(log_ring)); atomic_store(&log_write_idx, 0); atomic_store(&log_read_idx, 0); @@ -159,42 +141,36 @@ void log_init(const char *path) if (path != NULL) { char *expanded = expand_path(path); if (expanded != NULL) { - /* Ensure directory exists */ char *dir = strdup(expanded); if (dir != NULL) { char *last_slash = strrchr(dir, '/'); if (last_slash != NULL) { *last_slash = '\0'; - /* Create directory recursively using mkdir */ struct stat st; if (stat(dir, &st) != 0) { - /* Directory doesn't exist, try to create it */ char cmd[512]; snprintf(cmd, sizeof(cmd), "mkdir -p '%s' 2>/dev/null", dir); int ret = system(cmd); - (void)ret; /* Ignore result - file open will fail if dir creation fails */ + (void)ret; } } free(dir); } - /* Open log file with O_APPEND for atomic writes */ log_fd = open(expanded, O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, 0644); if (log_fd < 0) { fprintf(stderr, "Warning: Could not open log file: %s\n", expanded); } else { - /* Get current file size */ struct stat st; if (fstat(log_fd, &st) == 0) { atomic_store(&log_file_size, (size_t)st.st_size); } } - log_path = expanded; /* Keep for rotation */ + log_path = expanded; } } - /* Start background writer thread */ atomic_store(&log_running, 1); if (pthread_create(&log_thread, NULL, log_writer_thread, NULL) != 0) { fprintf(stderr, "Warning: Could not create log writer thread\n"); @@ -204,23 +180,19 @@ void log_init(const char *path) void log_close(void) { - /* Signal thread to stop */ if (!atomic_load(&log_running)) { goto cleanup; } atomic_store(&log_running, 0); - /* Wait for thread to finish - give it time to flush */ pthread_join(log_thread, NULL); cleanup: - /* Close file */ if (log_fd >= 0) { close(log_fd); log_fd = -1; } - /* Free path */ if (log_path != NULL) { free(log_path); log_path = NULL; @@ -234,7 +206,6 @@ void log_set_level(LogLevel level) void log_flush(void) { - /* Force flush all pending log entries synchronously */ if (!atomic_load(&log_running) || log_fd < 0) { return; } @@ -258,7 +229,6 @@ void log_flush(void) atomic_store(&log_read_idx, read_idx); } - /* Sync to disk */ fsync(log_fd); } @@ -268,43 +238,35 @@ void log_msg(LogLevel level, const char *fmt, ...) return; } - /* Get timestamp */ time_t now = time(NULL); struct tm tm_info; - localtime_r(&now, &tm_info); /* Thread-safe version */ + localtime_r(&now, &tm_info); char time_buf[32]; strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", &tm_info); - /* Format the log message */ - char msg_buf[LOG_MSG_MAX_LEN - 64]; /* Leave room for prefix */ + char msg_buf[LOG_MSG_MAX_LEN - 64]; va_list args; va_start(args, fmt); vsnprintf(msg_buf, sizeof(msg_buf), fmt, args); va_end(args); - /* Print to stderr (always, for immediate visibility) */ fprintf(stderr, "%s[%s] %s: %s%s\n", level_colors[level], time_buf, level_names[level], "\033[0m", msg_buf); - /* Add to ring buffer for async file write (non-blocking) */ if (atomic_load(&log_running)) { size_t write_idx = atomic_fetch_add(&log_write_idx, 1); size_t idx = write_idx % LOG_RING_SIZE; LogEntry *entry = &log_ring[idx]; - /* Check if slot is available (not overwriting unread entry) */ - /* If buffer is full, we drop the message rather than block */ if (!atomic_load(&entry->ready)) { entry->level = level; snprintf(entry->message, sizeof(entry->message), "[%s] %s: %s\n", time_buf, level_names[level], msg_buf); atomic_store(&entry->ready, 1); } - /* If entry->ready is true, buffer is full - message is dropped (non-blocking) */ } } -/* ========== Memory allocation ========== */ void *dwn_malloc(size_t size) { @@ -360,10 +322,8 @@ void secure_wipe(void *ptr, size_t size) return; } #if defined(__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 25) - /* Use explicit_bzero if available (glibc 2.25+) */ explicit_bzero(ptr, size); #else - /* Fallback: Use volatile to prevent compiler optimization */ volatile unsigned char *p = (volatile unsigned char *)ptr; while (size--) { *p++ = 0; @@ -371,7 +331,6 @@ void secure_wipe(void *ptr, size_t size) #endif } -/* ========== String utilities ========== */ char *str_trim(char *str) { @@ -379,7 +338,6 @@ char *str_trim(char *str) return NULL; } - /* Trim leading whitespace */ while (isspace((unsigned char)*str)) { str++; } @@ -388,7 +346,6 @@ char *str_trim(char *str) return str; } - /* Trim trailing whitespace */ char *end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) { end--; @@ -450,13 +407,11 @@ char *shell_escape(const char *str) return dwn_strdup(""); } - /* Count single quotes to determine buffer size */ size_t quotes = 0; for (const char *p = str; *p; p++) { if (*p == '\'') quotes++; } - /* Each single quote becomes '\'' (4 chars), plus 2 for surrounding quotes */ size_t len = strlen(str) + quotes * 3 + 3; char *escaped = dwn_malloc(len); char *dst = escaped; @@ -464,7 +419,6 @@ char *shell_escape(const char *str) *dst++ = '\''; for (const char *src = str; *src; src++) { if (*src == '\'') { - /* Close quote, add escaped quote, reopen quote: '\'' */ *dst++ = '\''; *dst++ = '\\'; *dst++ = '\''; @@ -479,7 +433,6 @@ char *shell_escape(const char *str) return escaped; } -/* ========== File utilities ========== */ bool file_exists(const char *path) { @@ -548,7 +501,6 @@ char *expand_path(const char *path) return dwn_strdup(path); } -/* ========== Color utilities ========== */ unsigned long parse_color(const char *color_str) { @@ -561,7 +513,6 @@ unsigned long parse_color(const char *color_str) return color.pixel; } - /* Try parsing as hex */ if (color_str[0] == '#') { unsigned int r, g, b; if (sscanf(color_str + 1, "%02x%02x%02x", &r, &g, &b) == 3) { @@ -589,7 +540,6 @@ void color_to_rgb(unsigned long color, int *r, int *g, int *b) *b = xc.blue >> 8; } -/* ========== Time utilities ========== */ long get_time_ms(void) { @@ -606,7 +556,6 @@ void sleep_ms(int ms) nanosleep(&ts, NULL); } -/* ========== Process utilities ========== */ int spawn(const char *cmd) { @@ -618,7 +567,6 @@ int spawn(const char *cmd) pid_t pid = fork(); if (pid == 0) { - /* Child process */ setsid(); execl("/bin/sh", "sh", "-c", cmd, NULL); _exit(EXIT_FAILURE); @@ -627,7 +575,6 @@ int spawn(const char *cmd) return -1; } - /* Wait for child */ int status; waitpid(pid, &status, 0); return WEXITSTATUS(status); @@ -643,10 +590,8 @@ int spawn_async(const char *cmd) pid_t pid = fork(); if (pid == 0) { - /* Child process */ setsid(); - /* Double fork to avoid zombies */ pid_t pid2 = fork(); if (pid2 == 0) { execl("/bin/sh", "sh", "-c", cmd, NULL); @@ -658,7 +603,6 @@ int spawn_async(const char *cmd) return -1; } - /* Wait for first child (which exits immediately) */ int status; waitpid(pid, &status, 0); return 0; @@ -678,7 +622,6 @@ char *spawn_capture(const char *cmd) return NULL; } - /* Read output */ size_t buf_size = 1024; size_t len = 0; char *output = dwn_malloc(buf_size); @@ -691,7 +634,6 @@ char *spawn_capture(const char *cmd) buf_size *= 2; output = dwn_realloc(output, buf_size); } - /* Use memcpy with explicit bounds instead of strcpy */ memcpy(output + len, line, line_len + 1); len += line_len; } @@ -701,7 +643,6 @@ char *spawn_capture(const char *cmd) LOG_DEBUG("Command exited with status %d", status); } - /* Trim trailing newline */ if (len > 0 && output[len - 1] == '\n') { output[len - 1] = '\0'; } diff --git a/src/workspace.c b/src/workspace.c index eed8ec4..1565bea 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -14,7 +14,6 @@ #include #include -/* ========== Initialization ========== */ void workspace_init(void) { @@ -36,7 +35,6 @@ void workspace_init(void) ws->master_count = (dwn->config != NULL) ? dwn->config->default_master_count : 1; - /* Default names: "1", "2", ..., "9" */ snprintf(ws->name, sizeof(ws->name), "%d", i + 1); } @@ -45,10 +43,8 @@ void workspace_init(void) void workspace_cleanup(void) { - /* Clients are cleaned up separately */ } -/* ========== Workspace access ========== */ Workspace *workspace_get(int index) { @@ -68,7 +64,6 @@ int workspace_get_current_index(void) return dwn != NULL ? dwn->current_workspace : 0; } -/* ========== Workspace switching ========== */ void workspace_switch(int index) { @@ -83,23 +78,17 @@ void workspace_switch(int index) LOG_DEBUG("Switching from workspace %d to %d", dwn->current_workspace + 1, index + 1); - /* Grab server to batch all X operations - prevents flickering and improves speed */ XGrabServer(dwn->display); - /* Hide current workspace windows */ workspace_hide(dwn->current_workspace); - /* Update current workspace */ int old_workspace = dwn->current_workspace; dwn->current_workspace = index; - /* Show new workspace windows */ workspace_show(index); - /* Arrange windows on new workspace */ workspace_arrange(index); - /* Focus appropriate window */ Workspace *ws = workspace_get(index); if (ws != NULL) { if (ws->focused != NULL) { @@ -109,17 +98,14 @@ void workspace_switch(int index) if (first != NULL) { client_focus(first); } else { - /* No windows, focus root */ XSetInputFocus(dwn->display, dwn->root, RevertToPointerRoot, CurrentTime); atoms_set_active_window(None); } } } - /* Update EWMH */ atoms_set_current_desktop(index); - /* Release server and sync - all changes appear atomically */ XUngrabServer(dwn->display); XSync(dwn->display, False); @@ -138,7 +124,6 @@ void workspace_switch_prev(void) workspace_switch(prev); } -/* ========== Client management ========== */ void workspace_add_client(int workspace, Client *client) { @@ -151,11 +136,8 @@ void workspace_add_client(int workspace, Client *client) return; } - /* Add to workspace client list (at head) */ client->workspace = workspace; - /* We don't maintain a separate linked list per workspace, - just use the client's workspace field */ } void workspace_remove_client(int workspace, Client *client) @@ -169,7 +151,6 @@ void workspace_remove_client(int workspace, Client *client) return; } - /* If this was the focused client, clear it */ if (ws->focused == client) { ws->focused = NULL; } @@ -190,30 +171,23 @@ void workspace_move_client(Client *client, int new_workspace) LOG_DEBUG("Moving window '%s' from workspace %d to %d", client->title, old_workspace + 1, new_workspace + 1); - /* Grab server to batch all X operations */ XGrabServer(dwn->display); - /* Remove from old workspace */ workspace_remove_client(old_workspace, client); - /* Add to new workspace */ workspace_add_client(new_workspace, client); - /* Update EWMH */ atoms_set_window_desktop(client->window, new_workspace); - /* Hide if moving to non-current workspace */ if (new_workspace != dwn->current_workspace) { client_hide(client); } else { client_show(client); } - /* Rearrange both workspaces */ workspace_arrange(old_workspace); workspace_arrange(new_workspace); - /* Release server and sync */ XUngrabServer(dwn->display); XSync(dwn->display, False); } @@ -239,7 +213,6 @@ Client *workspace_get_focused_client(int workspace) return ws != NULL ? ws->focused : NULL; } -/* ========== Layout ========== */ void workspace_set_layout(int workspace, LayoutType layout) { @@ -250,7 +223,6 @@ void workspace_set_layout(int workspace, LayoutType layout) ws->layout = layout; - /* Batch the arrangement */ XGrabServer(dwn->display); workspace_arrange(workspace); XUngrabServer(dwn->display); @@ -274,7 +246,6 @@ void workspace_cycle_layout(int workspace) ws->layout = (ws->layout + 1) % LAYOUT_COUNT; - /* Batch the arrangement for smoother transition */ XGrabServer(dwn->display); workspace_arrange(workspace); XUngrabServer(dwn->display); @@ -291,13 +262,11 @@ void workspace_set_master_ratio(int workspace, float ratio) return; } - /* Clamp ratio */ if (ratio < 0.1f) ratio = 0.1f; if (ratio > 0.9f) ratio = 0.9f; ws->master_ratio = ratio; - /* Batch the arrangement */ XGrabServer(dwn->display); workspace_arrange(workspace); XUngrabServer(dwn->display); @@ -322,7 +291,6 @@ void workspace_set_master_count(int workspace, int count) if (count < 1) count = 1; ws->master_count = count; - /* Batch the arrangement */ XGrabServer(dwn->display); workspace_arrange(workspace); XUngrabServer(dwn->display); @@ -337,7 +305,6 @@ void workspace_adjust_master_count(int workspace, int delta) } } -/* ========== Arrangement ========== */ void workspace_arrange(int workspace) { @@ -346,7 +313,6 @@ void workspace_arrange(int workspace) return; } - /* Only arrange visible workspace */ if (workspace != dwn->current_workspace) { return; } @@ -356,14 +322,12 @@ void workspace_arrange(int workspace) void workspace_arrange_current(void) { - /* Batch the arrangement */ XGrabServer(dwn->display); workspace_arrange(dwn->current_workspace); XUngrabServer(dwn->display); XSync(dwn->display, False); } -/* ========== Visibility ========== */ void workspace_show(int workspace) { @@ -391,7 +355,6 @@ void workspace_hide(int workspace) } } -/* ========== Properties ========== */ void workspace_set_name(int workspace, const char *name) { @@ -422,7 +385,6 @@ bool workspace_is_empty(int workspace) return workspace_client_count(workspace) == 0; } -/* ========== Focus cycling ========== */ void workspace_focus_next(void) { @@ -431,13 +393,11 @@ void workspace_focus_next(void) return; } - /* Find next client on same workspace */ Client *next = ws->focused->next; while (next != NULL && next->workspace != (unsigned int)dwn->current_workspace) { next = next->next; } - /* Wrap around */ if (next == NULL) { next = workspace_get_first_client(dwn->current_workspace); } @@ -454,13 +414,11 @@ void workspace_focus_prev(void) return; } - /* Find previous client on same workspace */ Client *prev = ws->focused->prev; while (prev != NULL && prev->workspace != (unsigned int)dwn->current_workspace) { prev = prev->prev; } - /* Wrap around */ if (prev == NULL) { for (Client *c = client_get_last(); c != NULL; c = c->prev) { if (c->workspace == (unsigned int)dwn->current_workspace) {