feat: add show desktop and move window to workspace features
build: enhance security with stricter compiler flags and sanitizer build refactor: replace standard memory functions with custom dwn_ variants chore: remove unused wifi and dropdown menu code from systray
This commit is contained in:
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
# Compiler settings
|
# Compiler settings
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -O2 -I./include
|
CFLAGS = -Wall -Wextra -Wpedantic -Wshadow -O2 -I./include
|
||||||
|
CFLAGS += -fstack-protector-strong -D_FORTIFY_SOURCE=2
|
||||||
LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread
|
LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread
|
||||||
|
|
||||||
# Directories
|
# Directories
|
||||||
@@ -42,7 +43,7 @@ endif
|
|||||||
# MAIN TARGETS
|
# MAIN TARGETS
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
.PHONY: all help clean install uninstall debug run test deps check-deps
|
.PHONY: all help clean install uninstall debug sanitize run test deps check-deps
|
||||||
|
|
||||||
# Default target - show help if first time, otherwise build
|
# Default target - show help if first time, otherwise build
|
||||||
all: check-deps $(TARGET)
|
all: check-deps $(TARGET)
|
||||||
@@ -69,6 +70,7 @@ help:
|
|||||||
@echo "ALL COMMANDS:"
|
@echo "ALL COMMANDS:"
|
||||||
@echo " make - Build DWN (release version)"
|
@echo " make - Build DWN (release version)"
|
||||||
@echo " make debug - Build with debug symbols"
|
@echo " make debug - Build with debug symbols"
|
||||||
|
@echo " make sanitize - Build with address/UB sanitizers"
|
||||||
@echo " make run - Test in Xephyr window (safe!)"
|
@echo " make run - Test in Xephyr window (safe!)"
|
||||||
@echo " make install - Install to $(BINDIR)"
|
@echo " make install - Install to $(BINDIR)"
|
||||||
@echo " make uninstall- Remove from system"
|
@echo " make uninstall- Remove from system"
|
||||||
@@ -91,6 +93,13 @@ debug: CFLAGS += -g -DDEBUG
|
|||||||
debug: clean $(TARGET)
|
debug: clean $(TARGET)
|
||||||
@echo "Debug build complete!"
|
@echo "Debug build complete!"
|
||||||
|
|
||||||
|
# Build with address and undefined behavior sanitizers
|
||||||
|
sanitize: CFLAGS += -g -fsanitize=address,undefined -fno-omit-frame-pointer
|
||||||
|
sanitize: LDFLAGS += -fsanitize=address,undefined
|
||||||
|
sanitize: clean $(TARGET)
|
||||||
|
@echo "Sanitizer build complete!"
|
||||||
|
@echo "Run with: ASAN_OPTIONS=detect_leaks=1 ./$(TARGET)"
|
||||||
|
|
||||||
# Link all object files into final binary
|
# Link all object files into final binary
|
||||||
$(TARGET): $(OBJS) | $(BIN_DIR)
|
$(TARGET): $(OBJS) | $(BIN_DIR)
|
||||||
@echo "Linking..."
|
@echo "Linking..."
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -110,6 +110,7 @@ extern ICCCMAtoms icccm;
|
|||||||
extern MiscAtoms misc_atoms;
|
extern MiscAtoms misc_atoms;
|
||||||
|
|
||||||
void atoms_init(Display *display);
|
void atoms_init(Display *display);
|
||||||
|
void atoms_cleanup(void);
|
||||||
|
|
||||||
void atoms_setup_ewmh(void);
|
void atoms_setup_ewmh(void);
|
||||||
void atoms_update_client_list(void);
|
void atoms_update_client_list(void);
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ typedef struct {
|
|||||||
|
|
||||||
bool is_alt_tabbing;
|
bool is_alt_tabbing;
|
||||||
Client *alt_tab_client;
|
Client *alt_tab_client;
|
||||||
|
|
||||||
|
bool desktop_shown;
|
||||||
|
Window desktop_minimized[MAX_CLIENTS];
|
||||||
|
int desktop_minimized_count;
|
||||||
} DWNState;
|
} DWNState;
|
||||||
|
|
||||||
extern DWNState *dwn;
|
extern DWNState *dwn;
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ void key_snap_right(void);
|
|||||||
void key_snap_up(void);
|
void key_snap_up(void);
|
||||||
void key_snap_down(void);
|
void key_snap_down(void);
|
||||||
void key_start_demo(void);
|
void key_start_demo(void);
|
||||||
|
void key_show_desktop(void);
|
||||||
|
void key_move_to_workspace_prev(void);
|
||||||
|
void key_move_to_workspace_next(void);
|
||||||
|
|
||||||
void tutorial_start(void);
|
void tutorial_start(void);
|
||||||
void tutorial_stop(void);
|
void tutorial_stop(void);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "dwn.h"
|
#include "dwn.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define MAX_WIFI_NETWORKS 20
|
|
||||||
#define MAX_TRAY_ICONS 32
|
#define MAX_TRAY_ICONS 32
|
||||||
#define TRAY_ICON_SIZE 22
|
#define TRAY_ICON_SIZE 22
|
||||||
#define TRAY_ICON_SPACING 4
|
#define TRAY_ICON_SPACING 4
|
||||||
@@ -41,21 +40,11 @@ extern TrayIcon tray_icons[MAX_TRAY_ICONS];
|
|||||||
extern int tray_icon_count;
|
extern int tray_icon_count;
|
||||||
extern Window tray_selection_owner;
|
extern Window tray_selection_owner;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char ssid[64];
|
|
||||||
int signal;
|
|
||||||
char security[32];
|
|
||||||
bool connected;
|
|
||||||
} WifiNetwork;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool connected;
|
bool connected;
|
||||||
char current_ssid[64];
|
char current_ssid[64];
|
||||||
int signal_strength;
|
int signal_strength;
|
||||||
WifiNetwork networks[MAX_WIFI_NETWORKS];
|
|
||||||
int network_count;
|
|
||||||
long last_scan;
|
|
||||||
} WifiState;
|
} WifiState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -78,29 +67,15 @@ typedef struct {
|
|||||||
bool dragging;
|
bool dragging;
|
||||||
} VolumeSlider;
|
} VolumeSlider;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Window window;
|
|
||||||
int x, y;
|
|
||||||
int width, height;
|
|
||||||
int item_count;
|
|
||||||
int hovered_item;
|
|
||||||
bool visible;
|
|
||||||
void (*on_select)(int index);
|
|
||||||
} DropdownMenu;
|
|
||||||
|
|
||||||
extern WifiState wifi_state;
|
extern WifiState wifi_state;
|
||||||
extern AudioState audio_state;
|
extern AudioState audio_state;
|
||||||
extern BatteryState battery_state;
|
extern BatteryState battery_state;
|
||||||
extern DropdownMenu *wifi_menu;
|
|
||||||
extern VolumeSlider *volume_slider;
|
extern VolumeSlider *volume_slider;
|
||||||
|
|
||||||
void systray_init(void);
|
void systray_init(void);
|
||||||
void systray_cleanup(void);
|
void systray_cleanup(void);
|
||||||
|
|
||||||
void wifi_update_state(void);
|
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);
|
const char *wifi_get_icon(void);
|
||||||
|
|
||||||
void audio_update_state(void);
|
void audio_update_state(void);
|
||||||
@@ -120,16 +95,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_motion(VolumeSlider *slider, int x, int y);
|
||||||
void volume_slider_handle_release(VolumeSlider *slider);
|
void volume_slider_handle_release(VolumeSlider *slider);
|
||||||
|
|
||||||
DropdownMenu *dropdown_create(int x, int y, int width);
|
|
||||||
void dropdown_destroy(DropdownMenu *menu);
|
|
||||||
void dropdown_show(DropdownMenu *menu);
|
|
||||||
void dropdown_hide(DropdownMenu *menu);
|
|
||||||
void dropdown_add_item(DropdownMenu *menu, const char *label);
|
|
||||||
void dropdown_render(DropdownMenu *menu);
|
|
||||||
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);
|
|
||||||
|
|
||||||
void systray_render(Panel *panel, int x, int *width);
|
void systray_render(Panel *panel, int x, int *width);
|
||||||
int systray_get_width(void);
|
int systray_get_width(void);
|
||||||
void systray_handle_click(int x, int y, int button);
|
void systray_handle_click(int x, int y, int button);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ static size_t write_callback(void *contents, size_t size, size_t nmemb, void *us
|
|||||||
size_t realsize = size * nmemb;
|
size_t realsize = size * nmemb;
|
||||||
ResponseBuffer *buf = (ResponseBuffer *)userp;
|
ResponseBuffer *buf = (ResponseBuffer *)userp;
|
||||||
|
|
||||||
char *ptr = realloc(buf->data, buf->size + realsize + 1);
|
char *ptr = dwn_realloc(buf->data, buf->size + realsize + 1);
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-10
@@ -244,9 +244,11 @@ static void add_to_recent(const char *desktop_id)
|
|||||||
for (int i = 0; i < launcher_state.recent_count; i++) {
|
for (int i = 0; i < launcher_state.recent_count; i++) {
|
||||||
if (strcmp(launcher_state.recent[i], desktop_id) == 0) {
|
if (strcmp(launcher_state.recent[i], desktop_id) == 0) {
|
||||||
for (int j = i; j > 0; j--) {
|
for (int j = i; j > 0; j--) {
|
||||||
strcpy(launcher_state.recent[j], launcher_state.recent[j-1]);
|
memcpy(launcher_state.recent[j], launcher_state.recent[j-1],
|
||||||
|
sizeof(launcher_state.recent[0]));
|
||||||
}
|
}
|
||||||
strcpy(launcher_state.recent[0], desktop_id);
|
snprintf(launcher_state.recent[0], sizeof(launcher_state.recent[0]),
|
||||||
|
"%s", desktop_id);
|
||||||
save_recent_apps();
|
save_recent_apps();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -257,9 +259,11 @@ static void add_to_recent(const char *desktop_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int i = launcher_state.recent_count - 1; i > 0; i--) {
|
for (int i = launcher_state.recent_count - 1; i > 0; i--) {
|
||||||
strcpy(launcher_state.recent[i], launcher_state.recent[i-1]);
|
memcpy(launcher_state.recent[i], launcher_state.recent[i-1],
|
||||||
|
sizeof(launcher_state.recent[0]));
|
||||||
}
|
}
|
||||||
strncpy(launcher_state.recent[0], desktop_id, sizeof(launcher_state.recent[0]) - 1);
|
snprintf(launcher_state.recent[0], sizeof(launcher_state.recent[0]),
|
||||||
|
"%s", desktop_id);
|
||||||
|
|
||||||
save_recent_apps();
|
save_recent_apps();
|
||||||
}
|
}
|
||||||
@@ -330,16 +334,16 @@ void applauncher_show(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t buf_size = launcher_state.app_count * 140;
|
size_t buf_size = launcher_state.app_count * 140;
|
||||||
char *choices = malloc(buf_size);
|
char *choices = dwn_malloc(buf_size);
|
||||||
if (choices == NULL) {
|
if (choices == NULL) {
|
||||||
LOG_ERROR("Failed to allocate memory for app list");
|
LOG_ERROR("Failed to allocate memory for app list");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
choices[0] = '\0';
|
choices[0] = '\0';
|
||||||
|
|
||||||
bool *added = calloc(launcher_state.app_count, sizeof(bool));
|
bool *added = dwn_calloc(launcher_state.app_count, sizeof(bool));
|
||||||
if (added == NULL) {
|
if (added == NULL) {
|
||||||
free(choices);
|
dwn_free(choices);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,19 +383,19 @@ void applauncher_show(void)
|
|||||||
choices[pos-1] = '\0';
|
choices[pos-1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
free(added);
|
dwn_free(added);
|
||||||
|
|
||||||
char tmp_path[] = "/tmp/dwn_apps_XXXXXX";
|
char tmp_path[] = "/tmp/dwn_apps_XXXXXX";
|
||||||
int tmp_fd = mkstemp(tmp_path);
|
int tmp_fd = mkstemp(tmp_path);
|
||||||
if (tmp_fd < 0) {
|
if (tmp_fd < 0) {
|
||||||
free(choices);
|
dwn_free(choices);
|
||||||
LOG_ERROR("Failed to create temp file for app list");
|
LOG_ERROR("Failed to create temp file for app list");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t written = write(tmp_fd, choices, strlen(choices));
|
ssize_t written = write(tmp_fd, choices, strlen(choices));
|
||||||
close(tmp_fd);
|
close(tmp_fd);
|
||||||
free(choices);
|
dwn_free(choices);
|
||||||
|
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
unlink(tmp_path);
|
unlink(tmp_path);
|
||||||
|
|||||||
+17
-1
@@ -16,6 +16,8 @@ EWMHAtoms ewmh;
|
|||||||
ICCCMAtoms icccm;
|
ICCCMAtoms icccm;
|
||||||
MiscAtoms misc_atoms;
|
MiscAtoms misc_atoms;
|
||||||
|
|
||||||
|
static Window ewmh_check_window = None;
|
||||||
|
|
||||||
#define ATOM(name) XInternAtom(display, name, False)
|
#define ATOM(name) XInternAtom(display, name, False)
|
||||||
|
|
||||||
void atoms_init(Display *display)
|
void atoms_init(Display *display)
|
||||||
@@ -124,7 +126,8 @@ void atoms_setup_ewmh(void)
|
|||||||
Display *dpy = dwn->display;
|
Display *dpy = dwn->display;
|
||||||
Window root = dwn->root;
|
Window root = dwn->root;
|
||||||
|
|
||||||
Window check = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
|
ewmh_check_window = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
|
||||||
|
Window check = ewmh_check_window;
|
||||||
|
|
||||||
XChangeProperty(dpy, root, ewmh.NET_SUPPORTING_WM_CHECK,
|
XChangeProperty(dpy, root, ewmh.NET_SUPPORTING_WM_CHECK,
|
||||||
XA_WINDOW, 32, PropModeReplace,
|
XA_WINDOW, 32, PropModeReplace,
|
||||||
@@ -510,3 +513,16 @@ bool atoms_update_wm_state(Window window, Atom state, bool add)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void atoms_cleanup(void)
|
||||||
|
{
|
||||||
|
if (dwn == NULL || dwn->display == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ewmh_check_window != None) {
|
||||||
|
XDestroyWindow(dwn->display, ewmh_check_window);
|
||||||
|
ewmh_check_window = None;
|
||||||
|
LOG_DEBUG("EWMH check window destroyed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -410,7 +410,8 @@ static void demo_phase_workspaces(void)
|
|||||||
"DWN provides 9 virtual workspaces:\n\n"
|
"DWN provides 9 virtual workspaces:\n\n"
|
||||||
"- F1 to F9: Switch workspace\n"
|
"- F1 to F9: Switch workspace\n"
|
||||||
"- Shift+F1-F9: Move window to workspace\n"
|
"- Shift+F1-F9: Move window to workspace\n"
|
||||||
"- Ctrl+Alt+Left/Right: Navigate\n\n"
|
"- Ctrl+Alt+Left/Right: Navigate\n"
|
||||||
|
"- Super+Shift+Left/Right: Move window\n\n"
|
||||||
"Watch the workspace indicators in the panel...", 0);
|
"Watch the workspace indicators in the panel...", 0);
|
||||||
demo_wait_for(DEMO_WAIT_TIME, demo_get_step_delay());
|
demo_wait_for(DEMO_WAIT_TIME, demo_get_step_delay());
|
||||||
demo_next_step();
|
demo_next_step();
|
||||||
|
|||||||
+103
-21
@@ -92,140 +92,161 @@ static const TutorialStep tutorial_steps[] = {
|
|||||||
MOD_SUPER, XK_t
|
MOD_SUPER, XK_t
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"1/20: Open Terminal",
|
"1/23: Open Terminal",
|
||||||
"The terminal is your command center.\n\n"
|
"The terminal is your command center.\n\n"
|
||||||
"Press: Ctrl + Alt + T",
|
"Press: Ctrl + Alt + T",
|
||||||
"Hold Ctrl and Alt, then press T",
|
"Hold Ctrl and Alt, then press T",
|
||||||
MOD_CTRL | MOD_ALT, XK_t
|
MOD_CTRL | MOD_ALT, XK_t
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"2/20: App Launcher",
|
"2/23: App Launcher",
|
||||||
"Launch any application by name.\n\n"
|
"Launch any application by name.\n\n"
|
||||||
"Press: Alt + F2",
|
"Press: Alt + F2",
|
||||||
"Type app name and press Enter",
|
"Type app name and press Enter",
|
||||||
MOD_ALT, XK_F2
|
MOD_ALT, XK_F2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"3/20: File Manager",
|
"3/23: File Manager",
|
||||||
"Open the file manager.\n\n"
|
"Open the file manager.\n\n"
|
||||||
"Press: Super + E",
|
"Press: Super + E",
|
||||||
"",
|
"",
|
||||||
MOD_SUPER, XK_e
|
MOD_SUPER, XK_e
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"4/20: Web Browser",
|
"4/23: Web Browser",
|
||||||
"Open your default web browser.\n\n"
|
"Open your default web browser.\n\n"
|
||||||
"Press: Super + B",
|
"Press: Super + B",
|
||||||
"",
|
"",
|
||||||
MOD_SUPER, XK_b
|
MOD_SUPER, XK_b
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"5/20: Switch Windows",
|
"5/23: Switch Windows",
|
||||||
"Cycle through open windows.\n\n"
|
"Cycle through open windows.\n\n"
|
||||||
"Press: Alt + Tab",
|
"Press: Alt + Tab",
|
||||||
"Hold Alt, press Tab repeatedly",
|
"Hold Alt, press Tab repeatedly",
|
||||||
MOD_ALT, XK_Tab
|
MOD_ALT, XK_Tab
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"6/20: Close Window",
|
"6/23: Close Window",
|
||||||
"Close the focused window.\n\n"
|
"Close the focused window.\n\n"
|
||||||
"Press: Alt + F4",
|
"Press: Alt + F4",
|
||||||
"",
|
"",
|
||||||
MOD_ALT, XK_F4
|
MOD_ALT, XK_F4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"7/20: Toggle Maximize",
|
"7/23: Show Desktop",
|
||||||
|
"Minimize all windows to show desktop.\n\n"
|
||||||
|
"Press: Super + D",
|
||||||
|
"Press again to restore windows",
|
||||||
|
MOD_SUPER, XK_d
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8/23: Toggle Maximize",
|
||||||
"Maximize or restore a window.\n\n"
|
"Maximize or restore a window.\n\n"
|
||||||
"Press: Alt + F10",
|
"Press: Alt + F10",
|
||||||
"",
|
"",
|
||||||
MOD_ALT, XK_F10
|
MOD_ALT, XK_F10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"8/20: Toggle Fullscreen",
|
"9/23: Toggle Fullscreen",
|
||||||
"Make a window fullscreen.\n\n"
|
"Make a window fullscreen.\n\n"
|
||||||
"Press: Alt + F11",
|
"Press: Alt + F11",
|
||||||
"Press again to exit",
|
"Press again to exit",
|
||||||
MOD_ALT, XK_F11
|
MOD_ALT, XK_F11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"9/20: Toggle Floating",
|
"10/23: Toggle Floating",
|
||||||
"Make window float above tiled windows.\n\n"
|
"Make window float above tiled windows.\n\n"
|
||||||
"Press: Super + F9",
|
"Press: Super + F9",
|
||||||
"",
|
"",
|
||||||
MOD_SUPER, XK_F9
|
MOD_SUPER, XK_F9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"10/20: Next Workspace",
|
"11/23: Next Workspace",
|
||||||
"Switch to the next virtual desktop.\n\n"
|
"Switch to the next virtual desktop.\n\n"
|
||||||
"Press: Ctrl + Alt + Right",
|
"Press: Ctrl + Alt + Right",
|
||||||
"",
|
"",
|
||||||
MOD_CTRL | MOD_ALT, XK_Right
|
MOD_CTRL | MOD_ALT, XK_Right
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"11/20: Previous Workspace",
|
"12/23: Previous Workspace",
|
||||||
"Switch to the previous workspace.\n\n"
|
"Switch to the previous workspace.\n\n"
|
||||||
"Press: Ctrl + Alt + Left",
|
"Press: Ctrl + Alt + Left",
|
||||||
"",
|
"",
|
||||||
MOD_CTRL | MOD_ALT, XK_Left
|
MOD_CTRL | MOD_ALT, XK_Left
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"12/20: Go to Workspace",
|
"13/23: Go to Workspace",
|
||||||
"Jump directly to workspace 1.\n\n"
|
"Jump directly to workspace 1.\n\n"
|
||||||
"Press: F1",
|
"Press: F1",
|
||||||
"Use F1-F9 for workspaces 1-9",
|
"Use F1-F9 for workspaces 1-9",
|
||||||
0, XK_F1
|
0, XK_F1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"13/20: Cycle Layout",
|
"14/23: Move Window to Prev Workspace",
|
||||||
|
"Send the focused window to previous workspace.\n\n"
|
||||||
|
"Press: Super + Shift + Left",
|
||||||
|
"Window moves, you stay",
|
||||||
|
MOD_SUPER | MOD_SHIFT, XK_Left
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15/23: Move Window to Next Workspace",
|
||||||
|
"Send the focused window to next workspace.\n\n"
|
||||||
|
"Press: Super + Shift + Right",
|
||||||
|
"Window moves, you stay",
|
||||||
|
MOD_SUPER | MOD_SHIFT, XK_Right
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16/23: Cycle Layout",
|
||||||
"Switch between tiling, floating, monocle.\n\n"
|
"Switch between tiling, floating, monocle.\n\n"
|
||||||
"Press: Super + Space",
|
"Press: Super + Space",
|
||||||
"Try it multiple times!",
|
"Try it multiple times!",
|
||||||
MOD_SUPER, XK_space
|
MOD_SUPER, XK_space
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"14/20: Expand Master",
|
"17/23: Expand Master",
|
||||||
"Make the master area larger.\n\n"
|
"Make the master area larger.\n\n"
|
||||||
"Press: Super + L",
|
"Press: Super + L",
|
||||||
"",
|
"",
|
||||||
MOD_SUPER, XK_l
|
MOD_SUPER, XK_l
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"15/20: Shrink Master",
|
"18/23: Shrink Master",
|
||||||
"Make the master area smaller.\n\n"
|
"Make the master area smaller.\n\n"
|
||||||
"Press: Super + H",
|
"Press: Super + H",
|
||||||
"",
|
"",
|
||||||
MOD_SUPER, XK_h
|
MOD_SUPER, XK_h
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"16/20: Add to Master",
|
"19/23: Add to Master",
|
||||||
"Increase windows in master area.\n\n"
|
"Increase windows in master area.\n\n"
|
||||||
"Press: Super + I",
|
"Press: Super + I",
|
||||||
"",
|
"",
|
||||||
MOD_SUPER, XK_i
|
MOD_SUPER, XK_i
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"17/20: AI Command",
|
"20/23: AI Command",
|
||||||
"Ask AI to run commands for you.\n\n"
|
"Ask AI to run commands for you.\n\n"
|
||||||
"Press: Super + A",
|
"Press: Super + A",
|
||||||
"Requires OPENROUTER_API_KEY",
|
"Requires OPENROUTER_API_KEY",
|
||||||
MOD_SUPER, XK_a
|
MOD_SUPER, XK_a
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"18/20: AI Context",
|
"21/23: AI Context",
|
||||||
"Show AI analysis of your current task.\n\n"
|
"Show AI analysis of your current task.\n\n"
|
||||||
"Press: Super + Shift + A",
|
"Press: Super + Shift + A",
|
||||||
"Requires OPENROUTER_API_KEY",
|
"Requires OPENROUTER_API_KEY",
|
||||||
MOD_SUPER | MOD_SHIFT, XK_a
|
MOD_SUPER | MOD_SHIFT, XK_a
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"19/20: Exa Search",
|
"22/23: Exa Search",
|
||||||
"Semantic web search.\n\n"
|
"Semantic web search.\n\n"
|
||||||
"Press: Super + Shift + E",
|
"Press: Super + Shift + E",
|
||||||
"Requires EXA_API_KEY",
|
"Requires EXA_API_KEY",
|
||||||
MOD_SUPER | MOD_SHIFT, XK_e
|
MOD_SUPER | MOD_SHIFT, XK_e
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"20/20: Show Shortcuts",
|
"23/23: Show Shortcuts",
|
||||||
"Display all keyboard shortcuts.\n\n"
|
"Display all keyboard shortcuts.\n\n"
|
||||||
"Press: Super + S",
|
"Press: Super + S",
|
||||||
"Shows complete reference",
|
"Shows complete reference",
|
||||||
@@ -519,10 +540,14 @@ void keys_setup_defaults(void)
|
|||||||
keys_bind(MOD_SUPER, XK_l, key_increase_master, "Increase master ratio");
|
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_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_i, key_increase_master_count, "Increase master count");
|
||||||
keys_bind(MOD_SUPER, XK_d, key_decrease_master_count, "Decrease master count");
|
keys_bind(MOD_SUPER, XK_minus, key_decrease_master_count, "Decrease master count");
|
||||||
|
|
||||||
|
keys_bind(MOD_SUPER, XK_d, key_show_desktop, "Show desktop");
|
||||||
|
|
||||||
keys_bind(MOD_SUPER, XK_Left, key_snap_left, "Snap window left");
|
keys_bind(MOD_SUPER, XK_Left, key_snap_left, "Snap window left");
|
||||||
keys_bind(MOD_SUPER, XK_Right, key_snap_right, "Snap window right");
|
keys_bind(MOD_SUPER, XK_Right, key_snap_right, "Snap window right");
|
||||||
|
keys_bind(MOD_SUPER | MOD_SHIFT, XK_Left, key_move_to_workspace_prev, "Move window to previous workspace");
|
||||||
|
keys_bind(MOD_SUPER | MOD_SHIFT, XK_Right, key_move_to_workspace_next, "Move window to next workspace");
|
||||||
|
|
||||||
keys_bind(MOD_SUPER, XK_a, key_ai_command, "AI command");
|
keys_bind(MOD_SUPER, XK_a, key_ai_command, "AI command");
|
||||||
keys_bind(MOD_SUPER | MOD_SHIFT, XK_a, key_toggle_ai, "Toggle AI context");
|
keys_bind(MOD_SUPER | MOD_SHIFT, XK_a, key_toggle_ai, "Toggle AI context");
|
||||||
@@ -725,6 +750,63 @@ void key_decrease_master_count(void)
|
|||||||
workspace_adjust_master_count(dwn->current_workspace, -1);
|
workspace_adjust_master_count(dwn->current_workspace, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void key_show_desktop(void)
|
||||||
|
{
|
||||||
|
if (dwn == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dwn->desktop_shown) {
|
||||||
|
dwn->desktop_minimized_count = 0;
|
||||||
|
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
|
||||||
|
if (c->workspace == (unsigned int)dwn->current_workspace &&
|
||||||
|
!client_is_minimized(c) &&
|
||||||
|
dwn->desktop_minimized_count < MAX_CLIENTS) {
|
||||||
|
dwn->desktop_minimized[dwn->desktop_minimized_count++] = c->window;
|
||||||
|
client_minimize(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dwn->desktop_shown = true;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < dwn->desktop_minimized_count; i++) {
|
||||||
|
Client *c = client_find_by_window(dwn->desktop_minimized[i]);
|
||||||
|
if (c != NULL && c->workspace == (unsigned int)dwn->current_workspace) {
|
||||||
|
client_restore(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dwn->desktop_minimized_count = 0;
|
||||||
|
dwn->desktop_shown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void key_move_to_workspace_prev(void)
|
||||||
|
{
|
||||||
|
if (dwn == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int target = dwn->current_workspace - 1;
|
||||||
|
if (target < 0) {
|
||||||
|
target = MAX_WORKSPACES - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
move_focused_to_workspace(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void key_move_to_workspace_next(void)
|
||||||
|
{
|
||||||
|
if (dwn == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int target = dwn->current_workspace + 1;
|
||||||
|
if (target >= MAX_WORKSPACES) {
|
||||||
|
target = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
move_focused_to_workspace(target);
|
||||||
|
}
|
||||||
|
|
||||||
void key_toggle_ai(void)
|
void key_toggle_ai(void)
|
||||||
{
|
{
|
||||||
if (dwn == NULL) {
|
if (dwn == NULL) {
|
||||||
|
|||||||
-14
@@ -416,15 +416,6 @@ static void handle_button_press(XButtonEvent *ev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wifi_menu != NULL && wifi_menu->visible) {
|
|
||||||
if (ev->window == wifi_menu->window) {
|
|
||||||
dropdown_handle_click(wifi_menu, ev->x, ev->y);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
dropdown_hide(wifi_menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification *notif = notification_find_by_window(ev->window);
|
Notification *notif = notification_find_by_window(ev->window);
|
||||||
if (notif != NULL) {
|
if (notif != NULL) {
|
||||||
notification_close(notif->id);
|
notification_close(notif->id);
|
||||||
@@ -531,11 +522,6 @@ static void handle_motion_notify(XMotionEvent *ev)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wifi_menu != NULL && wifi_menu->visible && ev->window == wifi_menu->window) {
|
|
||||||
dropdown_handle_motion(wifi_menu, ev->x, ev->y);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dwn->drag_client == NULL) {
|
if (dwn->drag_client == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -54,7 +54,7 @@ static size_t news_curl_write_cb(void *contents, size_t size, size_t nmemb, void
|
|||||||
size_t realsize = size * nmemb;
|
size_t realsize = size * nmemb;
|
||||||
CurlBuffer *buf = (CurlBuffer *)userp;
|
CurlBuffer *buf = (CurlBuffer *)userp;
|
||||||
|
|
||||||
char *ptr = realloc(buf->data, buf->size + realsize + 1);
|
char *ptr = dwn_realloc(buf->data, buf->size + realsize + 1);
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -374,7 +374,15 @@ static void *news_fetch_thread(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CurlBuffer buf = {0};
|
CurlBuffer buf = {0};
|
||||||
buf.data = malloc(1);
|
buf.data = dwn_malloc(1);
|
||||||
|
if (buf.data == NULL) {
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
pthread_mutex_lock(&news_mutex);
|
||||||
|
news_state.fetching = false;
|
||||||
|
news_state.has_error = true;
|
||||||
|
pthread_mutex_unlock(&news_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
buf.data[0] = '\0';
|
buf.data[0] = '\0';
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, NEWS_API_URL);
|
curl_easy_setopt(curl, CURLOPT_URL, NEWS_API_URL);
|
||||||
|
|||||||
+2
-2
@@ -552,8 +552,8 @@ void panel_handle_click(Panel *panel, int x, int y, int button)
|
|||||||
if (client_is_minimized(c)) {
|
if (client_is_minimized(c)) {
|
||||||
client_restore(c);
|
client_restore(c);
|
||||||
} else {
|
} else {
|
||||||
Workspace *ws = workspace_get(dwn->current_workspace);
|
Workspace *current = workspace_get(dwn->current_workspace);
|
||||||
if (ws != NULL && ws->focused == c) {
|
if (current != NULL && current->focused == c) {
|
||||||
client_minimize(c);
|
client_minimize(c);
|
||||||
} else {
|
} else {
|
||||||
client_focus(c, true);
|
client_focus(c, true);
|
||||||
|
|||||||
+1
-347
@@ -35,20 +35,14 @@
|
|||||||
#define ASCII_CHARGING "+"
|
#define ASCII_CHARGING "+"
|
||||||
|
|
||||||
#define SYSTRAY_SPACING 12
|
#define SYSTRAY_SPACING 12
|
||||||
#define DROPDOWN_ITEM_HEIGHT 28
|
|
||||||
#define DROPDOWN_WIDTH 400
|
|
||||||
#define DROPDOWN_PADDING 8
|
|
||||||
#define SLIDER_WIDTH 30
|
#define SLIDER_WIDTH 30
|
||||||
#define SLIDER_HEIGHT 120
|
#define SLIDER_HEIGHT 120
|
||||||
#define SLIDER_PADDING 8
|
#define SLIDER_PADDING 8
|
||||||
#define SLIDER_KNOB_HEIGHT 8
|
#define SLIDER_KNOB_HEIGHT 8
|
||||||
|
|
||||||
#define WIFI_SCAN_INTERVAL 10000
|
|
||||||
|
|
||||||
WifiState wifi_state = {0};
|
WifiState wifi_state = {0};
|
||||||
AudioState audio_state = {0};
|
AudioState audio_state = {0};
|
||||||
BatteryState battery_state = {0};
|
BatteryState battery_state = {0};
|
||||||
DropdownMenu *wifi_menu = NULL;
|
|
||||||
VolumeSlider *volume_slider = NULL;
|
VolumeSlider *volume_slider = NULL;
|
||||||
|
|
||||||
TrayIcon tray_icons[MAX_TRAY_ICONS];
|
TrayIcon tray_icons[MAX_TRAY_ICONS];
|
||||||
@@ -174,10 +168,6 @@ void systray_cleanup(void)
|
|||||||
pthread_join(update_thread, NULL);
|
pthread_join(update_thread, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wifi_menu != NULL) {
|
|
||||||
dropdown_destroy(wifi_menu);
|
|
||||||
wifi_menu = NULL;
|
|
||||||
}
|
|
||||||
if (volume_slider != NULL) {
|
if (volume_slider != NULL) {
|
||||||
volume_slider_destroy(volume_slider);
|
volume_slider_destroy(volume_slider);
|
||||||
volume_slider = NULL;
|
volume_slider = NULL;
|
||||||
@@ -309,82 +299,6 @@ void wifi_update_state(void)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void wifi_scan_networks(void)
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
char line[512];
|
|
||||||
|
|
||||||
WifiNetwork temp_networks[MAX_WIFI_NETWORKS];
|
|
||||||
int temp_count = 0;
|
|
||||||
long scan_time = get_time_ms();
|
|
||||||
|
|
||||||
fp = popen("nmcli -t -f SSID,SIGNAL,SECURITY,IN-USE dev wifi list 2>/dev/null", "r");
|
|
||||||
if (fp == NULL) {
|
|
||||||
LOG_WARN("Failed to scan WiFi networks");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), fp) != NULL && temp_count < MAX_WIFI_NETWORKS) {
|
|
||||||
line[strcspn(line, "\n")] = '\0';
|
|
||||||
|
|
||||||
char *ssid = strtok(line, ":");
|
|
||||||
char *signal = strtok(NULL, ":");
|
|
||||||
char *security = strtok(NULL, ":");
|
|
||||||
char *in_use = strtok(NULL, ":");
|
|
||||||
|
|
||||||
if (ssid != NULL && strlen(ssid) > 0) {
|
|
||||||
WifiNetwork *net = &temp_networks[temp_count];
|
|
||||||
strncpy(net->ssid, ssid, sizeof(net->ssid) - 1);
|
|
||||||
net->ssid[sizeof(net->ssid) - 1] = '\0';
|
|
||||||
net->signal = signal ? atoi(signal) : 0;
|
|
||||||
strncpy(net->security, security ? security : "", sizeof(net->security) - 1);
|
|
||||||
net->security[sizeof(net->security) - 1] = '\0';
|
|
||||||
net->connected = (in_use != NULL && in_use[0] == '*');
|
|
||||||
temp_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pclose(fp);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&state_mutex);
|
|
||||||
memcpy(wifi_state.networks, temp_networks, sizeof(temp_networks));
|
|
||||||
wifi_state.network_count = temp_count;
|
|
||||||
wifi_state.last_scan = scan_time;
|
|
||||||
pthread_mutex_unlock(&state_mutex);
|
|
||||||
|
|
||||||
LOG_DEBUG("Scanned %d WiFi networks", temp_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wifi_connect(const char *ssid)
|
|
||||||
{
|
|
||||||
if (ssid == NULL || strlen(ssid) == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char cmd[256];
|
|
||||||
snprintf(cmd, sizeof(cmd), "nmcli dev wifi connect \"%s\" &", ssid);
|
|
||||||
|
|
||||||
char msg[128];
|
|
||||||
snprintf(msg, sizeof(msg), "Connecting to %s...", ssid);
|
|
||||||
notification_show("WiFi", "Connecting", msg, NULL, 3000);
|
|
||||||
|
|
||||||
spawn_async(cmd);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&state_mutex);
|
|
||||||
wifi_state.connected = false;
|
|
||||||
pthread_mutex_unlock(&state_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wifi_disconnect(void)
|
|
||||||
{
|
|
||||||
spawn_async("nmcli dev disconnect wlan0 &");
|
|
||||||
|
|
||||||
pthread_mutex_lock(&state_mutex);
|
|
||||||
wifi_state.connected = false;
|
|
||||||
pthread_mutex_unlock(&state_mutex);
|
|
||||||
|
|
||||||
notification_show("WiFi", "Disconnected", "WiFi connection closed", NULL, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *wifi_get_icon(void)
|
const char *wifi_get_icon(void)
|
||||||
{
|
{
|
||||||
if (!wifi_state.enabled || !wifi_state.connected) {
|
if (!wifi_state.enabled || !wifi_state.connected) {
|
||||||
@@ -615,227 +529,6 @@ void volume_slider_handle_release(VolumeSlider *slider)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DropdownMenu *dropdown_create(int x, int y, int width)
|
|
||||||
{
|
|
||||||
if (dwn == NULL || dwn->display == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DropdownMenu *menu = dwn_calloc(1, sizeof(DropdownMenu));
|
|
||||||
menu->x = x;
|
|
||||||
menu->y = y;
|
|
||||||
menu->width = width;
|
|
||||||
menu->height = 0;
|
|
||||||
menu->item_count = 0;
|
|
||||||
menu->hovered_item = -1;
|
|
||||||
menu->visible = false;
|
|
||||||
menu->on_select = NULL;
|
|
||||||
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dropdown_destroy(DropdownMenu *menu)
|
|
||||||
{
|
|
||||||
if (menu == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (menu->window != None && dwn != NULL && dwn->display != NULL) {
|
|
||||||
XDestroyWindow(dwn->display, menu->window);
|
|
||||||
}
|
|
||||||
|
|
||||||
dwn_free(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dropdown_show(DropdownMenu *menu)
|
|
||||||
{
|
|
||||||
if (menu == NULL || dwn == NULL || dwn->display == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wifi_scan_networks();
|
|
||||||
|
|
||||||
menu->item_count = wifi_state.network_count;
|
|
||||||
menu->height = menu->item_count * DROPDOWN_ITEM_HEIGHT + 2 * DROPDOWN_PADDING;
|
|
||||||
|
|
||||||
if (menu->height < DROPDOWN_ITEM_HEIGHT + 2 * DROPDOWN_PADDING) {
|
|
||||||
menu->height = DROPDOWN_ITEM_HEIGHT + 2 * DROPDOWN_PADDING;
|
|
||||||
menu->item_count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int max_text_width = 0;
|
|
||||||
for (int i = 0; i < wifi_state.network_count; i++) {
|
|
||||||
WifiNetwork *net = &wifi_state.networks[i];
|
|
||||||
char label[128];
|
|
||||||
snprintf(label, sizeof(label), "%s %d%%", net->ssid, net->signal);
|
|
||||||
int text_width = get_text_width(label);
|
|
||||||
if (text_width > max_text_width) {
|
|
||||||
max_text_width = text_width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int min_width = 250;
|
|
||||||
int calculated_width = max_text_width + 2 * DROPDOWN_PADDING + 30;
|
|
||||||
menu->width = (calculated_width > min_width) ? calculated_width : min_width;
|
|
||||||
|
|
||||||
if (menu->window == None) {
|
|
||||||
XSetWindowAttributes swa;
|
|
||||||
swa.override_redirect = True;
|
|
||||||
swa.background_pixel = dwn->config->colors.panel_bg;
|
|
||||||
swa.border_pixel = dwn->config->colors.border_focused;
|
|
||||||
swa.event_mask = ExposureMask | ButtonPressMask | PointerMotionMask |
|
|
||||||
LeaveWindowMask | EnterWindowMask;
|
|
||||||
|
|
||||||
menu->window = XCreateWindow(dwn->display, dwn->root,
|
|
||||||
menu->x, menu->y,
|
|
||||||
menu->width, menu->height,
|
|
||||||
0,
|
|
||||||
CopyFromParent, InputOutput, CopyFromParent,
|
|
||||||
CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask,
|
|
||||||
&swa);
|
|
||||||
} else {
|
|
||||||
XMoveResizeWindow(dwn->display, menu->window,
|
|
||||||
menu->x, menu->y, menu->width, menu->height);
|
|
||||||
}
|
|
||||||
|
|
||||||
menu->visible = true;
|
|
||||||
XMapRaised(dwn->display, menu->window);
|
|
||||||
dropdown_render(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dropdown_hide(DropdownMenu *menu)
|
|
||||||
{
|
|
||||||
if (menu == NULL || menu->window == None) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu->visible = false;
|
|
||||||
XUnmapWindow(dwn->display, menu->window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dropdown_render(DropdownMenu *menu)
|
|
||||||
{
|
|
||||||
if (menu == NULL || menu->window == None || !menu->visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dwn == NULL || dwn->display == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Display *dpy = dwn->display;
|
|
||||||
const ColorScheme *colors = config_get_colors();
|
|
||||||
|
|
||||||
int font_height = 12;
|
|
||||||
if (dwn->xft_font != NULL) {
|
|
||||||
font_height = dwn->xft_font->ascent;
|
|
||||||
} else if (dwn->font != NULL) {
|
|
||||||
font_height = dwn->font->ascent;
|
|
||||||
}
|
|
||||||
int text_y_offset = (DROPDOWN_ITEM_HEIGHT + font_height) / 2;
|
|
||||||
|
|
||||||
XSetForeground(dpy, dwn->gc, colors->panel_bg);
|
|
||||||
XFillRectangle(dpy, menu->window, dwn->gc, 0, 0, menu->width, menu->height);
|
|
||||||
|
|
||||||
if (wifi_state.network_count == 0) {
|
|
||||||
draw_utf8_text(menu->window, DROPDOWN_PADDING, DROPDOWN_PADDING + text_y_offset,
|
|
||||||
"No networks found", colors->workspace_inactive);
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < wifi_state.network_count && i < MAX_WIFI_NETWORKS; i++) {
|
|
||||||
WifiNetwork *net = &wifi_state.networks[i];
|
|
||||||
int y = DROPDOWN_PADDING + i * DROPDOWN_ITEM_HEIGHT;
|
|
||||||
|
|
||||||
if (i == menu->hovered_item) {
|
|
||||||
XSetForeground(dpy, dwn->gc, colors->workspace_active);
|
|
||||||
XFillRectangle(dpy, menu->window, dwn->gc,
|
|
||||||
2, y, menu->width - 4, DROPDOWN_ITEM_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long text_color = (i == menu->hovered_item) ? colors->panel_bg : colors->panel_fg;
|
|
||||||
|
|
||||||
char label[80];
|
|
||||||
if (net->connected) {
|
|
||||||
snprintf(label, sizeof(label), "> %s", net->ssid);
|
|
||||||
} else {
|
|
||||||
snprintf(label, sizeof(label), " %s", net->ssid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen(label) > 25) {
|
|
||||||
label[22] = '.';
|
|
||||||
label[23] = '.';
|
|
||||||
label[24] = '.';
|
|
||||||
label[25] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_utf8_text(menu->window, DROPDOWN_PADDING, y + text_y_offset, label, text_color);
|
|
||||||
|
|
||||||
char signal[8];
|
|
||||||
snprintf(signal, sizeof(signal), "%d%%", net->signal);
|
|
||||||
int signal_width = get_text_width(signal);
|
|
||||||
int signal_x = menu->width - DROPDOWN_PADDING - signal_width;
|
|
||||||
|
|
||||||
unsigned long signal_color = (i == menu->hovered_item) ? colors->panel_bg : colors->workspace_inactive;
|
|
||||||
draw_utf8_text(menu->window, signal_x, y + text_y_offset, signal, signal_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XFlush(dpy);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dropdown_hit_test(DropdownMenu *menu, int x, int y)
|
|
||||||
{
|
|
||||||
(void)x;
|
|
||||||
|
|
||||||
if (menu == NULL || !menu->visible) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y < DROPDOWN_PADDING || y >= menu->height - DROPDOWN_PADDING) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int item = (y - DROPDOWN_PADDING) / DROPDOWN_ITEM_HEIGHT;
|
|
||||||
if (item >= 0 && item < menu->item_count) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dropdown_handle_click(DropdownMenu *menu, int x, int y)
|
|
||||||
{
|
|
||||||
if (menu == NULL || !menu->visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int item = dropdown_hit_test(menu, x, y);
|
|
||||||
if (item >= 0 && item < wifi_state.network_count) {
|
|
||||||
WifiNetwork *net = &wifi_state.networks[item];
|
|
||||||
|
|
||||||
if (net->connected) {
|
|
||||||
wifi_disconnect();
|
|
||||||
} else {
|
|
||||||
wifi_connect(net->ssid);
|
|
||||||
}
|
|
||||||
|
|
||||||
dropdown_hide(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dropdown_handle_motion(DropdownMenu *menu, int x, int y)
|
|
||||||
{
|
|
||||||
if (menu == NULL || !menu->visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int old_hovered = menu->hovered_item;
|
|
||||||
menu->hovered_item = dropdown_hit_test(menu, x, y);
|
|
||||||
|
|
||||||
if (menu->hovered_item != old_hovered) {
|
|
||||||
dropdown_render(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int systray_get_width(void)
|
int systray_get_width(void)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&state_mutex);
|
pthread_mutex_lock(&state_mutex);
|
||||||
@@ -955,34 +648,7 @@ void systray_handle_click(int x, int y, int button)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget == 0) {
|
if (widget == 1) {
|
||||||
if (volume_slider != NULL && volume_slider->visible) {
|
|
||||||
volume_slider_hide(volume_slider);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button == 1) {
|
|
||||||
if (wifi_menu == NULL) {
|
|
||||||
int panel_height = config_get_panel_height();
|
|
||||||
wifi_menu = dropdown_create(wifi_icon_x, panel_height, DROPDOWN_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wifi_menu->visible) {
|
|
||||||
dropdown_hide(wifi_menu);
|
|
||||||
} else {
|
|
||||||
wifi_menu->x = wifi_icon_x;
|
|
||||||
wifi_menu->y = config_get_panel_height();
|
|
||||||
dropdown_show(wifi_menu);
|
|
||||||
}
|
|
||||||
} else if (button == 3) {
|
|
||||||
if (wifi_state.connected) {
|
|
||||||
wifi_disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (widget == 1) {
|
|
||||||
if (wifi_menu != NULL && wifi_menu->visible) {
|
|
||||||
dropdown_hide(wifi_menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button == 1) {
|
if (button == 1) {
|
||||||
if (volume_slider == NULL) {
|
if (volume_slider == NULL) {
|
||||||
int panel_height = config_get_panel_height();
|
int panel_height = config_get_panel_height();
|
||||||
@@ -1019,18 +685,6 @@ void systray_handle_click(int x, int y, int button)
|
|||||||
|
|
||||||
void systray_update(void)
|
void systray_update(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (wifi_menu != NULL && wifi_menu->visible) {
|
|
||||||
long now = get_time_ms();
|
|
||||||
pthread_mutex_lock(&state_mutex);
|
|
||||||
long last_scan = wifi_state.last_scan;
|
|
||||||
pthread_mutex_unlock(&state_mutex);
|
|
||||||
|
|
||||||
if (now - last_scan >= WIFI_SCAN_INTERVAL) {
|
|
||||||
wifi_scan_networks();
|
|
||||||
dropdown_render(wifi_menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user