diff --git a/WAYLAND_ROADMAP.md b/WAYLAND_ROADMAP.md index e6d7853..3189050 100644 --- a/WAYLAND_ROADMAP.md +++ b/WAYLAND_ROADMAP.md @@ -82,7 +82,7 @@ Priority: HIGH — needed for daily-driver use. |---|------|-----------|------------| | A1 | Stacking (raise/lower via scene graph) | stacking.c, wlr_scene_node_raise/lower | Low | | A2 | Window maximize (full/half/vertical/horizontal) | actions.c, usable area calc | Low-Medium | -| A3 | Window shade (titlebar collapse) | actions.c, frame buffer resize | Low | +| A3 | ~~Window shade (titlebar collapse)~~ | actions.c, frame buffer resize | ✅ Done | | A4 | Workspace switching (create/destroy/switch) | workspace.c, scene tree reparenting | Medium | | A5 | Alt-Tab window cycling + switch panel | cycling.c, switchpanel.c, vtable switchpanel_enter | Medium | | A6 | Window iconification (minimize) + miniwindow | icon.c, appicon.c, vtable icon_paint/icon_update_pixmap | Medium | @@ -214,7 +214,7 @@ For running without a host compositor: NOW (daily-driver basics): A1. Stacking (raise/lower) A2. Maximize - A3. Shade + A3. ~~Shade~~ ✅ A4. Workspace switching A5. Alt-Tab cycling A6. Icon/minimize diff --git a/src/actions.c b/src/actions.c index e818e14..5d87770 100644 --- a/src/actions.c +++ b/src/actions.c @@ -521,6 +521,9 @@ void wMaximizeWindow(WWindow *wwin, int directions, int head) wWindowCropSize(wwin, usableArea.x2 - usableArea.x1, usableArea.y2 - usableArea.y1, &new_width, &new_height); + if (wm_backend->client_set_maximized) + wm_backend->client_set_maximized(wwin->client_win, 1); + wWindowConfigure(wwin, new_x, new_y, new_width, new_height); wWindowSynthConfigureNotify(wwin); @@ -959,6 +962,8 @@ void wUnmaximizeWindow(WWindow *wwin) wwin->flags.maximized = 0; wwin->flags.old_maximized = 0; + if (wm_backend->client_set_maximized) + wm_backend->client_set_maximized(wwin->client_win, 0); wWindowConfigure(wwin, x, y, w, h); wWindowSynthConfigureNotify(wwin); diff --git a/src/backend/backend.h b/src/backend/backend.h index 3a53d65..96f7063 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -1464,6 +1464,11 @@ typedef struct WMBackend { */ void (*client_set_state)(WWindow *wwin, int state, WNativeWindow icon_win); + /* Notify the client of maximized/fullscreen state changes. + * X11: no-op (handled via _NET_WM_STATE property). + * Wayland: sends xdg_toplevel set_maximized/set_fullscreen. */ + void (*client_set_maximized)(WNativeWindow win, int maximized); + /* * Send a WM_PROTOCOLS message to a client (e.g. WM_DELETE_WINDOW). * X11: XSendEvent with ClientMessage. diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 0de076d..8fda86e 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -1575,6 +1575,9 @@ handle_xdg_toplevel_initial_commit(struct wl_listener *listener, void *data) wl_list_remove(&view->commit_listener.link); wl_list_init(&view->commit_listener.link); + /* Send initial configure so the client enters the configure/ack cycle. + * Size 0,0 means "choose your own size" per the XDG spec. */ + wlr_xdg_toplevel_set_activated(view->xdg_surface->toplevel, true); wlr_xdg_toplevel_set_size(view->xdg_surface->toplevel, 0, 0); wlr_xdg_surface_schedule_configure(view->xdg_surface); } @@ -1862,6 +1865,23 @@ handle_output_frame(struct wl_listener *listener, void *data) return; if (!wl_state.scene_output) return; + static int frame_count = 0; + if (++frame_count % 120 == 0) { + struct wl_toplevel_view *v; + wl_list_for_each(v, &wl_state.toplevel_list, link) { + if (v->xdg_surface && v->xdg_surface->surface && v->xdg_surface->toplevel) { + struct wlr_surface *s = v->xdg_surface->surface; + struct wlr_xdg_toplevel *tl = v->xdg_surface->toplevel; + wwarning("frame %d: 0x%lx surf=%dx%d sched=%dx%d current=%dx%d idle=%p title=%s", + frame_count, (unsigned long)v->id, + s->current.width, s->current.height, + tl->scheduled.width, tl->scheduled.height, + tl->current.width, tl->current.height, + (void*)v->xdg_surface->configure_idle, + tl->title ? tl->title : "(null)"); + } + } + } if (wl_state.allocator) { /* Full scene commit — renders client surfaces and fires * wl_surface.frame callbacks so clients can submit next buffer. */ @@ -3927,6 +3947,8 @@ wl_frame_destroy(WScreen *scr, WNativeWindow win) static void wl_frame_configure(WNativeWindow win, int x, int y, int width, int height) { + wwarning("wl_frame_configure: win=0x%lx pos=%d,%d size=%dx%d", + (unsigned long)win, x, y, width, height); struct wl_frame_buf *fb = frame_buf_find(win); if (!fb) return; @@ -3960,6 +3982,29 @@ wl_frame_configure(WNativeWindow win, int x, int y, int width, int height) fb->base.width = width; fb->base.height = height; } + + /* Reposition client surface and child frame_bufs to follow the frame. */ + struct wl_toplevel_view *cv; + wl_list_for_each(cv, &wl_state.toplevel_list, link) { + if (cv->frame_id == win) { + cv->x = fb->x + cv->frame_rel_x; + cv->y = fb->y + cv->frame_rel_y; + wwarning("frame_configure: repositioned client 0x%lx to %d,%d (rel %d,%d)", + (unsigned long)cv->id, cv->x, cv->y, cv->frame_rel_x, cv->frame_rel_y); + if (cv->scene_node) + wlr_scene_node_set_position(&cv->scene_node->node, cv->x, cv->y); + break; + } + } + struct wl_frame_buf *child; + wl_list_for_each(child, &wl_state.frame_list, link) { + if (child->parent_id == win) { + child->x = fb->x + child->rel_x; + child->y = fb->y + child->rel_y; + if (child->scene_buf) + wlr_scene_node_set_position(&child->scene_buf->node, child->x, child->y); + } + } } static void wl_frame_paint_button(WCoreWindow *button, WTexture *texture, @@ -4178,6 +4223,9 @@ wl_frame_paint(struct WFrameWindow *fwin) } } + if (wl_state.output) + wlr_output_schedule_frame(wl_state.output); + return 1; } @@ -5905,6 +5953,9 @@ static void wl_window_resize(WNativeWindow win, int w, int h) struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v) { if (w <= 0 || h <= 0) return; + wwarning("wl_window_resize: id=0x%lx %dx%d xw=%p xdg=%p", + (unsigned long)win, w, h, (void*)v->xw_surface, + (void*)v->xdg_surface); if (v->xw_surface) { wlr_xwayland_surface_configure(v->xw_surface, (int16_t)v->x, (int16_t)v->y, (uint16_t)w, (uint16_t)h); @@ -5912,7 +5963,16 @@ static void wl_window_resize(WNativeWindow win, int w, int h) } if (!v->xdg_surface || !v->xdg_surface->toplevel) return; wlr_xdg_toplevel_set_size(v->xdg_surface->toplevel, (uint32_t)w, (uint32_t)h); - wlr_xdg_surface_schedule_configure(v->xdg_surface); + wlr_xdg_toplevel_set_activated(v->xdg_surface->toplevel, true); + /* Dispatch idle so configure is written, then flush to client */ + struct wl_event_loop *loop = wl_display_get_event_loop(wl_state.display); + wl_event_loop_dispatch(loop, 0); + wl_display_flush_clients(wl_state.display); + /* Send frame_done so client can render at new size */ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_surface_send_frame_done(v->xdg_surface->surface, &now); + wl_display_flush_clients(wl_state.display); return; } struct wl_frame_buf *fb = frame_buf_find(win); @@ -6929,6 +6989,14 @@ static void wl_noop_client_restore(WWindow *wwin) { (void)wwin; } static void wl_noop_client_set_state(WWindow *wwin, int state, WNativeWindow icon_win) { (void)wwin; (void)state; (void)icon_win; } +static void wl_client_set_maximized(WNativeWindow win, int maximized) +{ + struct wl_toplevel_view *v = wl_find_view_by_id(win); + if (!v) return; + if (v->xdg_surface && v->xdg_surface->toplevel) + wlr_xdg_toplevel_set_maximized(v->xdg_surface->toplevel, maximized); +} + static void wl_client_send_protocol(WWindow *wwin, unsigned long protocol, unsigned long timestamp) { @@ -7686,6 +7754,7 @@ const WMBackend wl_backend = { .prop_cleanup = wl_noop_prop_cleanup, .client_restore = wl_noop_client_restore, .client_set_state = wl_noop_client_set_state, + .client_set_maximized = wl_client_set_maximized, .client_send_protocol = wl_client_send_protocol, .client_kill = wl_client_kill_impl, .client_get_colormap_windows = wl_noop_client_get_colormap_windows, diff --git a/src/backend/x11/x11_backend.c b/src/backend/x11/x11_backend.c index 6b33e10..aabe1f5 100644 --- a/src/backend/x11/x11_backend.c +++ b/src/backend/x11/x11_backend.c @@ -4361,6 +4361,7 @@ const WMBackend x11_backend = { .prop_cleanup = x11_prop_cleanup, .client_restore = x11_client_restore, .client_set_state = x11_client_set_state, + .client_set_maximized = NULL, /* X11: handled via _NET_WM_STATE property */ .client_send_protocol = x11_client_send_protocol, .client_kill = x11_client_kill, .client_get_colormap_windows = x11_client_get_colormap_windows,