diff --git a/bin/dwn b/bin/dwn index 378bfdf..29d8666 100755 Binary files a/bin/dwn and b/bin/dwn differ diff --git a/build/ai.o b/build/ai.o index f148591..e19f321 100644 Binary files a/build/ai.o and b/build/ai.o differ diff --git a/build/applauncher.o b/build/applauncher.o index 4ec792a..23c909c 100644 Binary files a/build/applauncher.o and b/build/applauncher.o differ diff --git a/build/atoms.o b/build/atoms.o index fb32c32..f22dd27 100644 Binary files a/build/atoms.o and b/build/atoms.o differ diff --git a/build/autostart.o b/build/autostart.o index ff49f41..755debf 100644 Binary files a/build/autostart.o and b/build/autostart.o differ diff --git a/build/cJSON.o b/build/cJSON.o index 267611e..9c189f7 100644 Binary files a/build/cJSON.o and b/build/cJSON.o differ diff --git a/build/client.o b/build/client.o index cb40d94..0f61583 100644 Binary files a/build/client.o and b/build/client.o differ diff --git a/build/config.o b/build/config.o index 69be0b0..12db018 100644 Binary files a/build/config.o and b/build/config.o differ diff --git a/build/decorations.o b/build/decorations.o index 963fcd0..3b449e5 100644 Binary files a/build/decorations.o and b/build/decorations.o differ diff --git a/build/demo.o b/build/demo.o index 8ddffa5..81e0dce 100644 Binary files a/build/demo.o and b/build/demo.o differ diff --git a/build/keys.d b/build/keys.d index 235a29a..17b6fa8 100644 --- a/build/keys.d +++ b/build/keys.d @@ -19,7 +19,7 @@ build/keys.o: src/keys.c include/keys.h include/dwn.h include/client.h \ /usr/include/dbus-1.0/dbus/dbus-syntax.h \ /usr/include/dbus-1.0/dbus/dbus-threads.h include/news.h \ include/applauncher.h include/decorations.h include/demo.h \ - include/layout.h include/api.h include/marks.h + include/layout.h include/api.h include/marks.h include/panel.h include/keys.h: include/dwn.h: include/client.h: @@ -53,3 +53,4 @@ include/demo.h: include/layout.h: include/api.h: include/marks.h: +include/panel.h: diff --git a/build/keys.o b/build/keys.o index 8e1227f..3b5bf20 100644 Binary files a/build/keys.o and b/build/keys.o differ diff --git a/build/layout.o b/build/layout.o index c3727fd..edcf9d7 100644 Binary files a/build/layout.o and b/build/layout.o differ diff --git a/build/main.o b/build/main.o index f63fdd6..868164b 100644 Binary files a/build/main.o and b/build/main.o differ diff --git a/build/news.o b/build/news.o index 0f59896..67cf19d 100644 Binary files a/build/news.o and b/build/news.o differ diff --git a/build/notifications.o b/build/notifications.o index a66a775..5cc4a87 100644 Binary files a/build/notifications.o and b/build/notifications.o differ diff --git a/build/panel.o b/build/panel.o index f34c901..5c86553 100644 Binary files a/build/panel.o and b/build/panel.o differ diff --git a/build/systray.o b/build/systray.o index 823d325..7ee9841 100644 Binary files a/build/systray.o and b/build/systray.o differ diff --git a/build/util.o b/build/util.o index 24b4a8c..040b5ea 100644 Binary files a/build/util.o and b/build/util.o differ diff --git a/build/workspace.o b/build/workspace.o index 262d8e3..90de342 100644 Binary files a/build/workspace.o and b/build/workspace.o differ diff --git a/include/dwn.h b/include/dwn.h index 366ad30..717c4a2 100644 --- a/include/dwn.h +++ b/include/dwn.h @@ -125,6 +125,7 @@ struct Client { char title[256]; char class[64]; SnapConstraint snap; + bool floating_before_maximize; unsigned long taskbar_color; ColorAnimation title_anim; TextGlowAnimation text_glow; @@ -219,6 +220,17 @@ typedef struct { int last_mouse_x; int last_mouse_y; bool mouse_tracking_initialized; + + Cursor cursor_default; + Cursor cursor_resize_top_left; + Cursor cursor_resize_top_right; + Cursor cursor_resize_bottom_left; + Cursor cursor_resize_bottom_right; + Cursor cursor_resize_left; + Cursor cursor_resize_right; + Cursor cursor_resize_top; + Cursor cursor_resize_bottom; + Cursor cursor_move; } DWNState; #define PHASE_OFFSET_PANEL_BG 0.0f diff --git a/include/keys.h b/include/keys.h index 9c9fb7b..b293bfe 100644 --- a/include/keys.h +++ b/include/keys.h @@ -100,6 +100,7 @@ void key_resize_window_up(void); void key_resize_window_down(void); void key_set_mark(void); void key_goto_mark(void); +void key_expose_all(void); void tutorial_start(void); void tutorial_stop(void); diff --git a/src/client.c b/src/client.c index b70e662..e57e12c 100644 --- a/src/client.c +++ b/src/client.c @@ -53,6 +53,7 @@ Client *client_create(Window window) client->snap.horizontal = SNAP_H_NONE; client->snap.vertical = SNAP_V_NONE; client->snap.timestamp = 0; + client->floating_before_maximize = false; XWindowAttributes wa; int orig_width = 640, orig_height = 480; @@ -234,6 +235,16 @@ Client *client_manage(Window window) XGrabButton(dwn->display, Button1, AnyModifier, window, False, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); + unsigned int lock_combos[] = { 0, Mod2Mask, LockMask, Mod2Mask | LockMask }; + for (size_t gi = 0; gi < sizeof(lock_combos) / sizeof(lock_combos[0]); gi++) { + XGrabButton(dwn->display, Button3, Mod1Mask | lock_combos[gi], window, False, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, None, None); + XGrabButton(dwn->display, Button1, Mod1Mask | lock_combos[gi], window, False, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, None, None); + } + client_sync_log("client_manage: syncing X"); XSync(dwn->display, False); @@ -905,6 +916,7 @@ void client_set_maximize(Client *client, bool maximized) client->old_y = client->y; client->old_width = client->width; client->old_height = client->height; + client->floating_before_maximize = (client->flags & CLIENT_FLOATING) != 0; } client->flags |= CLIENT_MAXIMIZED; @@ -933,6 +945,10 @@ void client_set_maximize(Client *client, bool maximized) } else { client->flags &= ~CLIENT_MAXIMIZED; + if (!client->floating_before_maximize) { + client->flags &= ~CLIENT_FLOATING; + } + atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_MAXIMIZED_VERT, false); atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_MAXIMIZED_HORZ, false); api_emit_window_maximized(client->window, false); @@ -942,8 +958,33 @@ void client_set_maximize(Client *client, bool maximized) client->width = client->old_width; client->height = client->old_height; + if (client->width < 50) client->width = 50; + if (client->height < 50) client->height = 50; + + int area_x, area_y, area_w, area_h; + layout_get_usable_area(&area_x, &area_y, &area_w, &area_h); + + if (client->x + client->width > area_x + area_w) { + client->x = area_x + area_w - client->width; + } + if (client->x < area_x) { + client->x = area_x; + } + if (client->y + client->height > area_y + area_h) { + client->y = area_y + area_h - client->height; + } + if (client->y < area_y) { + client->y = area_y; + } + client_configure(client); decorations_render(client, true); + client_raise(client); + client_focus(client, true); + + if (!client->floating_before_maximize) { + workspace_arrange_current(); + } } } diff --git a/src/decorations.c b/src/decorations.c index 77b3682..6e4f53d 100644 --- a/src/decorations.c +++ b/src/decorations.c @@ -17,7 +17,7 @@ #define BUTTON_SIZE 16 #define BUTTON_PADDING 4 -#define RESIZE_EDGE 8 +#define RESIZE_EDGE 12 #define DEC_XFT_COLOR_CACHE_SIZE 8 diff --git a/src/keys.c b/src/keys.c index d622b93..086f184 100644 --- a/src/keys.c +++ b/src/keys.c @@ -18,6 +18,7 @@ #include "layout.h" #include "api.h" #include "marks.h" +#include "panel.h" #include #include @@ -610,6 +611,8 @@ void keys_setup_defaults(void) keys_bind(MOD_SUPER, XK_m, key_set_mark, "Set window mark"); keys_bind(MOD_SUPER, XK_apostrophe, key_goto_mark, "Go to marked window"); + + keys_bind(MOD_ALT, XK_space, key_expose_all, "Expose all windows"); } @@ -936,8 +939,11 @@ void key_show_shortcuts(void) "Alt+F9 Toggle minimize\n" "Alt+F10 Toggle maximize\n" "Alt+F11 Toggle fullscreen\n" + "Alt+Space Expose all windows\n" "Super+F9 Toggle floating\n" "Super+K Kill all applications\n" + "Alt+Drag Move window (any position)\n" + "Alt+Right-Drag Resize window (any position)\n" "\n" "=== Workspaces ===\n" "F1-F9 Switch to workspace\n" @@ -1327,6 +1333,36 @@ void key_resize_window_down(void) decorations_render(c, true); } +void key_expose_all(void) +{ + if (dwn == NULL) { + return; + } + + if (dwn->desktop_shown) { + dwn->desktop_shown = false; + dwn->desktop_minimized_count = 0; + } + + for (Client *c = dwn->client_list; c != NULL; c = c->next) { + if (c->workspace != (unsigned int)dwn->current_workspace) { + continue; + } + if (client_is_fullscreen(c)) { + client_set_fullscreen(c, false); + } + if (client_is_maximized(c)) { + client_set_maximize(c, false); + } + if (client_is_minimized(c)) { + client_restore(c); + } + } + + workspace_arrange_current(); + panel_render_all(); +} + void key_set_mark(void) { marks_start_set_mode(); diff --git a/src/main.c b/src/main.c index 55fbbbe..45b0c74 100644 --- a/src/main.c +++ b/src/main.c @@ -497,6 +497,8 @@ static void handle_leave_notify(XCrossingEvent *ev) } } +static Cursor cursor_for_direction(int direction); + static void handle_button_press(XButtonEvent *ev) { if (ev == NULL || dwn == NULL || dwn->display == NULL) { @@ -560,6 +562,50 @@ static void handle_button_press(XButtonEvent *ev) } if (is_client_window) { + bool alt_held = (ev->state & Mod1Mask) != 0; + + if (alt_held && ev->button == Button3) { + client_focus(c, true); + int cx = c->x + c->width / 2; + int cy = c->y + c->height / 2; + int dir = 0; + if (ev->x_root < cx) dir |= RESIZE_LEFT; else dir |= RESIZE_RIGHT; + if (ev->y_root < cy) dir |= RESIZE_TOP; else dir |= RESIZE_BOTTOM; + + dwn->drag_client = c; + dwn->drag_start_x = ev->x_root; + dwn->drag_start_y = ev->y_root; + dwn->drag_orig_x = c->x; + dwn->drag_orig_y = c->y; + dwn->drag_orig_w = c->width; + dwn->drag_orig_h = c->height; + dwn->resizing = true; + dwn->drag_direction = dir; + + XGrabPointer(dwn->display, dwn->root, True, + PointerMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, + cursor_for_direction(dir), CurrentTime); + return; + } + + if (alt_held && ev->button == Button1) { + client_focus(c, true); + dwn->drag_client = c; + dwn->drag_start_x = ev->x_root; + dwn->drag_start_y = ev->y_root; + dwn->drag_orig_x = c->x; + dwn->drag_orig_y = c->y; + dwn->resizing = false; + dwn->drag_direction = 0; + + XGrabPointer(dwn->display, dwn->root, True, + PointerMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, + dwn->cursor_move, CurrentTime); + return; + } + XAllowEvents(dwn->display, ReplayPointer, ev->time); client_focus(c, true); return; @@ -588,7 +634,8 @@ static void handle_button_press(XButtonEvent *ev) XGrabPointer(dwn->display, c->frame, True, PointerMotionMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + GrabModeAsync, GrabModeAsync, None, + dwn->cursor_move, CurrentTime); } return; } @@ -610,7 +657,8 @@ static void handle_button_press(XButtonEvent *ev) XGrabPointer(dwn->display, c->frame, True, PointerMotionMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + GrabModeAsync, GrabModeAsync, None, + cursor_for_direction(direction), CurrentTime); } } } @@ -645,6 +693,24 @@ static void handle_button_release(XButtonEvent *ev) api_emit_mouse_button_released(ev->button, ev->x_root, ev->y_root); } +static Cursor cursor_for_direction(int direction) +{ + bool left = (direction & RESIZE_LEFT) != 0; + bool right = (direction & RESIZE_RIGHT) != 0; + bool top = (direction & RESIZE_TOP) != 0; + bool bottom = (direction & RESIZE_BOTTOM) != 0; + + if (top && left) return dwn->cursor_resize_top_left; + if (top && right) return dwn->cursor_resize_top_right; + if (bottom && left) return dwn->cursor_resize_bottom_left; + if (bottom && right) return dwn->cursor_resize_bottom_right; + if (left) return dwn->cursor_resize_left; + if (right) return dwn->cursor_resize_right; + if (top) return dwn->cursor_resize_top; + if (bottom) return dwn->cursor_resize_bottom; + return dwn->cursor_default; +} + static void handle_motion_notify(XMotionEvent *ev) { if (ev == NULL || dwn == NULL || dwn->display == NULL) { @@ -661,7 +727,7 @@ static void handle_motion_notify(XMotionEvent *ev) slider_handle_motion(fade_speed_control.slider, ev->x, ev->y); return; } - + if (fade_intensity_control.slider != NULL && fade_intensity_control.slider->visible && ev->window == fade_intensity_control.slider->window) { LOG_DEBUG("Fade intensity slider motion: x=%d, y=%d", ev->x, ev->y); slider_handle_motion(fade_intensity_control.slider, ev->x, ev->y); @@ -669,6 +735,17 @@ static void handle_motion_notify(XMotionEvent *ev) } if (dwn->drag_client == NULL) { + Client *c = client_find_by_frame(ev->window); + if (c != NULL && c->frame != None) { + int direction = 0; + if (decorations_hit_test_resize_area(c, ev->x, ev->y, &direction)) { + XDefineCursor(dwn->display, c->frame, cursor_for_direction(direction)); + } else if (decorations_hit_test_title_bar(c, ev->x, ev->y)) { + XDefineCursor(dwn->display, c->frame, dwn->cursor_move); + } else { + XDefineCursor(dwn->display, c->frame, dwn->cursor_default); + } + } return; } @@ -986,8 +1063,18 @@ int dwn_init(void) StructureNotifyMask | PropertyChangeMask | ButtonPressMask | PointerMotionMask); - Cursor cursor = XCreateFontCursor(dwn->display, XC_left_ptr); - XDefineCursor(dwn->display, dwn->root, cursor); + dwn->cursor_default = XCreateFontCursor(dwn->display, XC_left_ptr); + dwn->cursor_resize_top_left = XCreateFontCursor(dwn->display, XC_top_left_corner); + dwn->cursor_resize_top_right = XCreateFontCursor(dwn->display, XC_top_right_corner); + dwn->cursor_resize_bottom_left = XCreateFontCursor(dwn->display, XC_bottom_left_corner); + dwn->cursor_resize_bottom_right = XCreateFontCursor(dwn->display, XC_bottom_right_corner); + dwn->cursor_resize_left = XCreateFontCursor(dwn->display, XC_left_side); + dwn->cursor_resize_right = XCreateFontCursor(dwn->display, XC_right_side); + dwn->cursor_resize_top = XCreateFontCursor(dwn->display, XC_top_side); + dwn->cursor_resize_bottom = XCreateFontCursor(dwn->display, XC_bottom_side); + dwn->cursor_move = XCreateFontCursor(dwn->display, XC_fleur); + + XDefineCursor(dwn->display, dwn->root, dwn->cursor_default); scan_existing_windows(); @@ -1055,6 +1142,17 @@ void dwn_cleanup(void) } if (dwn->display != NULL) { + if (dwn->cursor_default) XFreeCursor(dwn->display, dwn->cursor_default); + if (dwn->cursor_resize_top_left) XFreeCursor(dwn->display, dwn->cursor_resize_top_left); + if (dwn->cursor_resize_top_right) XFreeCursor(dwn->display, dwn->cursor_resize_top_right); + if (dwn->cursor_resize_bottom_left) XFreeCursor(dwn->display, dwn->cursor_resize_bottom_left); + if (dwn->cursor_resize_bottom_right) XFreeCursor(dwn->display, dwn->cursor_resize_bottom_right); + if (dwn->cursor_resize_left) XFreeCursor(dwn->display, dwn->cursor_resize_left); + if (dwn->cursor_resize_right) XFreeCursor(dwn->display, dwn->cursor_resize_right); + if (dwn->cursor_resize_top) XFreeCursor(dwn->display, dwn->cursor_resize_top); + if (dwn->cursor_resize_bottom) XFreeCursor(dwn->display, dwn->cursor_resize_bottom); + if (dwn->cursor_move) XFreeCursor(dwn->display, dwn->cursor_move); + XCloseDisplay(dwn->display); } diff --git a/src/panel.c b/src/panel.c index da7a20b..3d49088 100644 --- a/src/panel.c +++ b/src/panel.c @@ -405,24 +405,27 @@ void panel_render(Panel *panel) x += width + WIDGET_SPACING; panel_render_layout_indicator(panel, x, &width); - x += width + WIDGET_SPACING; - - panel_render_taskbar(panel, x, &width); + int taskbar_start = x + width + WIDGET_SPACING; int systray_actual_width = systray_get_width(); int systray_x = panel->width - systray_actual_width - PANEL_PADDING; - - /* Render fade controls before systray */ + int fade_width = fade_controls_get_width(); int fade_x = systray_x - fade_width - WIDGET_SPACING; - fade_controls_render(panel, fade_x); - - systray_render(panel, systray_x, &width); + + int ai_x = 0; + if (dwn->ai_enabled) { + int ai_width = panel_text_width("[AI]", 4); + ai_x = fade_x - ai_width - WIDGET_SPACING; + } + + panel_render_taskbar(panel, taskbar_start, &width); if (dwn->ai_enabled) { - int ai_x = systray_x - 60; panel_render_ai_status(panel, ai_x, &width); } + fade_controls_render(panel, fade_x); + systray_render(panel, systray_x, &width); } else { int date_width = 0; @@ -535,8 +538,14 @@ void panel_render_taskbar(Panel *panel, int x, int *width) int current_x = x; int systray_actual_width = systray_get_width(); - int ai_width = dwn->ai_enabled ? 60 : 0; - int right_reserve = systray_actual_width + ai_width + PANEL_PADDING * 2; + int fade_width = fade_controls_get_width(); + int ai_width = 0; + if (dwn->ai_enabled) { + ai_width = panel_text_width("[AI]", 4); + } + int right_reserve = PANEL_PADDING + systray_actual_width + WIDGET_SPACING + + fade_width + WIDGET_SPACING + + (ai_width > 0 ? ai_width + WIDGET_SPACING : 0); int available_width = panel->width - x - right_reserve; if (available_width < 0) available_width = 0; int item_count = 0; @@ -790,8 +799,14 @@ Client *panel_hit_test_taskbar(Panel *panel, int x, int y) } int systray_actual_width = systray_get_width(); - int ai_width = dwn->ai_enabled ? 60 : 0; - int right_reserve = systray_actual_width + ai_width + PANEL_PADDING * 2; + int fade_width = fade_controls_get_width(); + int ai_width = 0; + if (dwn->ai_enabled) { + ai_width = panel_text_width("[AI]", 4); + } + int right_reserve = PANEL_PADDING + systray_actual_width + WIDGET_SPACING + + fade_width + WIDGET_SPACING + + (ai_width > 0 ? ai_width + WIDGET_SPACING : 0); int available_width = panel->width - taskbar_start - right_reserve; if (available_width < 0) available_width = 0; int item_count = 0; diff --git a/src/systray.c b/src/systray.c index 6c5a940..b902a94 100644 --- a/src/systray.c +++ b/src/systray.c @@ -1371,21 +1371,37 @@ void fade_controls_render(Panel *panel, int x) int fade_controls_get_width(void) { - /* Calculate approximate width for both controls */ - return 80; /* Approximate width for "S:0.5 I:100%" + spacing */ + float speed = config_get_fade_speed(); + char speed_label[16]; + snprintf(speed_label, sizeof(speed_label), "S:%.1f", speed); + + float intensity = config_get_fade_intensity(); + char intensity_label[16]; + snprintf(intensity_label, sizeof(intensity_label), "I:%d%%", (int)(intensity * 100)); + + return get_text_width(speed_label) + SYSTRAY_SPACING + get_text_width(intensity_label); } int fade_controls_hit_test(int x) { - /* Check if x position hits any fade control icon */ - /* Return 1 for speed, 2 for intensity, 0 for none */ - - if (x >= fade_speed_icon_x && x < fade_speed_icon_x + 40) { - return 1; /* Speed control */ + float speed = config_get_fade_speed(); + char speed_label[16]; + snprintf(speed_label, sizeof(speed_label), "S:%.1f", speed); + int speed_width = get_text_width(speed_label); + + if (x >= fade_speed_icon_x && x < fade_speed_icon_x + speed_width) { + return 1; } - if (x >= fade_intensity_icon_x && x < fade_intensity_icon_x + 50) { - return 2; /* Intensity control */ + + float intensity = config_get_fade_intensity(); + char intensity_label[16]; + snprintf(intensity_label, sizeof(intensity_label), "I:%d%%", (int)(intensity * 100)); + int intensity_width = get_text_width(intensity_label); + + if (x >= fade_intensity_icon_x && x < fade_intensity_icon_x + intensity_width) { + return 2; } + return 0; }