diff --git a/cursor.c b/cursor.c index 9b4477a..91f9fba 100644 --- a/cursor.c +++ b/cursor.c @@ -67,8 +67,8 @@ static void process_cursor_move(struct diyac_server *server, uint32_t time) // move the windows to cursor server->grab_x = toplevel->original.width * server->grab_x / toplevel->output->usable_area.width; } - toplevel->original.y = server->seat.cursor->y - server->grab_y; - toplevel->original.x = server->seat.cursor->x - server->grab_x; + toplevel->pending_size.y = server->seat.cursor->y - server->grab_y; + toplevel->pending_size.x = server->seat.cursor->x - server->grab_x; diyac_view_update_geometry(toplevel, true); /* @@ -137,12 +137,12 @@ static void process_cursor_resize(struct diyac_server *server, uint32_t time) struct wlr_box geo_box; wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); - toplevel->original.x = new_left - geo_box.x; - toplevel->original.y = new_top - geo_box.y; + toplevel->pending_size.x = new_left - geo_box.x; + toplevel->pending_size.y = new_top - geo_box.y; int new_width = new_right - new_left; int new_height = new_bottom - new_top; - toplevel->original.width = new_width; - toplevel->original.height = new_height; + toplevel->pending_size.width = new_width; + toplevel->pending_size.height = new_height; toplevel->requested.maximized = false; diyac_view_update_geometry(toplevel, false); /* diff --git a/diyac.c b/diyac.c index 8c9ebc0..7eb628f 100644 --- a/diyac.c +++ b/diyac.c @@ -38,6 +38,7 @@ int main(int argc, char *argv[]) /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ server.wl_display = wl_display_create(); + server.wl_event_loop = wl_display_get_event_loop(server.wl_display); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window diff --git a/diyac.h b/diyac.h index 17b601c..41fc75c 100644 --- a/diyac.h +++ b/diyac.h @@ -106,6 +106,7 @@ struct diyac_server struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; + struct wl_event_loop *wl_event_loop; struct wlr_scene *scene; struct wlr_scene_output_layout *scene_layout; @@ -185,15 +186,18 @@ struct diyac_view struct wlr_scene_tree *scene_tree; struct diyac_view_state state; struct diyac_view_state requested; + uint32_t configuration_serial; + struct wl_event_source * configuration_timeout; /* * Geometry of the wlr_surface contained within the view, as * currently displayed. Should be kept in sync with the * scene-graph at all times. */ struct wlr_box original; + struct wlr_box pending_size; bool mapped; struct diyac_output *output; - + struct wl_listener commit; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; diff --git a/view.c b/view.c index fd0b578..f4691ba 100644 --- a/view.c +++ b/view.c @@ -7,18 +7,48 @@ #include "output.h" #include "cursor.h" -static void diyac_view_set_activated(struct diyac_view * view, bool activated) +#define CONFIGURE_TIMEOUT_MS 150 +static int handle_configure_timeout(void *data) +{ + struct diyac_view *view = data; + assert(view->configuration_serial > 0); + assert(view->configuration_timeout); + + wlr_log(WLR_INFO, "client (%s) did not respond to configure request " + "in %d ms", + view->xdg_toplevel->app_id, CONFIGURE_TIMEOUT_MS); + + wl_event_source_remove(view->configuration_timeout); + view->configuration_serial = 0; + view->configuration_timeout = NULL; + + diyac_view_sync_geo(view); + return 0; /* ignored per wl_event_loop docs */ +} + +static void diyac_view_configure(struct diyac_view *view, struct wlr_box geo) +{ + view->pending_size = geo; + view->configuration_serial = wlr_xdg_toplevel_set_size(view->xdg_toplevel, geo.width, geo.height); + if (!view->configuration_timeout) + { + view->configuration_timeout = wl_event_loop_add_timer(view->server->wl_event_loop, handle_configure_timeout, view); + } + wl_event_source_timer_update(view->configuration_timeout, CONFIGURE_TIMEOUT_MS); +} + +static void diyac_view_set_activated(struct diyac_view *view, bool activated) { struct diyac_server *server = view->server; wlr_xdg_toplevel_set_activated(view->xdg_toplevel, activated); - if(view->toplevel.handle) + if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_activated(view->toplevel.handle, activated); } server->active_view = NULL; - if(activated) + if (activated) { - + diyac_seat_focus_surface(&server->seat, view->xdg_toplevel->base->surface); server->active_view = view; } @@ -40,18 +70,19 @@ void diyac_focus_view(struct diyac_view *toplevel, bool raise) { return; } - if(!toplevel->mapped) + if (!toplevel->mapped) { // dont focus unmapped view return; } - if(toplevel == toplevel->server->active_view) + struct wlr_surface *prev_surface = toplevel->server->seat.wlr_seat->keyboard_state.focused_surface; + if (prev_surface == toplevel->xdg_toplevel->base->surface) { // Don't re-focus an already focused surface. wlr_log(WLR_DEBUG, "Don't re-focus an already focused surface"); return; } - if(toplevel->server->active_view) + if (toplevel->server->active_view) { diyac_view_set_activated(toplevel->server->active_view, false); } @@ -69,7 +100,7 @@ void diyac_focus_view(struct diyac_view *toplevel, bool raise) wl_array_release(&subviews); } diyac_view_set_activated(toplevel, true); - //diyac_seat_focus_surface(&server->seat, toplevel->xdg_toplevel->base->surface); + // diyac_seat_focus_surface(&server->seat, toplevel->xdg_toplevel->base->surface); /*if(toplevel->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_activated(toplevel->toplevel.handle, true); @@ -80,7 +111,7 @@ struct diyac_view *diyac_view_at( struct diyac_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - struct diyac_node_descriptor *node_descriptor = diyac_node_at(server,lx,ly,surface,sx,sy); + struct diyac_node_descriptor *node_descriptor = diyac_node_at(server, lx, ly, surface, sx, sy); if (!node_descriptor || node_descriptor->type != DIYAC_NODE_VIEW) { return NULL; @@ -93,10 +124,10 @@ void diyac_focus_topmost_view(struct diyac_server *server, bool raise) struct diyac_view *view = diyac_topmost_focusable_view(server); if (view) { - if(raise) + if (raise) { - struct diyac_view * root = diyac_get_root_view(view); - if(root) + struct diyac_view *root = diyac_get_root_view(view); + if (root) { diyac_focus_view(root, true); return; @@ -131,7 +162,7 @@ struct diyac_view *diyac_topmost_focusable_view(struct diyac_server *server) continue; } view = diyac_view_from_node(node); - + if (view->mapped /*&& view_is_focusable_from(view, prev)*/) { return view; @@ -161,8 +192,6 @@ void diyac_arrange_all_views(struct diyac_server *server) bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed) { assert(view); - struct wlr_box geo_box, intersect_box; - struct wlr_box *geometry = &view->original; // if (wlr_output_layout_intersects(view->server->output_layout, // view->output->wlr_output, &view->current)) //{ @@ -172,66 +201,63 @@ bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed) return false; } struct wlr_box usable = diyac_output_usable_area(view->output); - wlr_log(WLR_DEBUG, "diyac_view_update_geometry: current: [%d,%d,%d,%d], usable: [%d,%d,%d,%d] ", - geometry->x, geometry->y, geometry->width, geometry->height, - usable.x, usable.y, usable.width, usable.height); struct diyac_server *server = view->server; - wlr_box_intersection(&intersect_box,geometry, &usable); - wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); - if ( wlr_box_equal(geometry, &geo_box) && diyac_view_state_equal(view->state,view->requested) && wlr_box_empty(&intersect_box)) - { - wlr_log(WLR_INFO, "No geometry update needed"); - return false; - } - // invalidate old state if change state - if(view->state.fullscreen && !view->requested.fullscreen) + // invalidate old state if change state + if (view->state.fullscreen && !view->requested.fullscreen) { wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, false); - if(view->toplevel.handle) + if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel.handle, false); } wlr_scene_node_set_enabled(&view->output->scenes.top->node, true); view->state.fullscreen = false; + view->pending_size = view->original; } - if(view->state.maximized && !view->requested.maximized) + if (view->state.maximized && !view->requested.maximized) { wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, false); - if(view->toplevel.handle) + if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel.handle, false); } + view->pending_size = view->original; view->state.maximized = false; } - view->state = view->requested; - - if(view->requested.minimized) + bool updated = false; + if (view->requested.minimized) { // TODO implement minimize return false; } - else if(view->requested.fullscreen) + else if (!view->state.fullscreen && view->requested.fullscreen) { + struct wlr_box box = {0}; + wlr_output_effective_resolution(view->output->wlr_output, + &box.width, &box.height); + double ox = 0, oy = 0; + wlr_output_layout_output_coords(view->server->output_layout, + view->output->wlr_output, &ox, &oy); + box.x -= ox; + box.y -= oy; + wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, true); wlr_scene_node_set_enabled(&view->output->scenes.top->node, false); - wlr_scene_node_set_position(&view->scene_tree->node, 0, 0); - wlr_xdg_toplevel_set_size(view->xdg_toplevel, view->output->wlr_output->width, view->output->wlr_output->height); - wlr_xdg_toplevel_set_fullscreen(view->xdg_toplevel, false); - if(view->toplevel.handle) + if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel.handle, true); } - return true; + diyac_view_configure(view, box); + updated = true; } - else if(view->requested.maximized) + else if (view->requested.maximized) { - wlr_scene_node_set_position(&view->scene_tree->node, usable.x, usable.y); - wlr_xdg_toplevel_set_size(view->xdg_toplevel, usable.width, usable.height); wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, true); - if(view->toplevel.handle) + if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel.handle, true); } - return true; + diyac_view_configure(view, usable); + updated = true; } else { @@ -239,46 +265,53 @@ bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed) // view->output->wlr_output, &view->current)) //{ /**Normal state, recalculate current geometry*/ - struct diyac_view * root = diyac_get_root_view(view); - if(!root) + struct diyac_view *root = diyac_get_root_view(view); + struct wlr_box geometry = view->pending_size; + if (!root) { root = view; } - if(!root->state.fullscreen) + if (!root->state.fullscreen) { + // Only adjust position only when not in fullscreen mode - if (!grabbed && geometry->x < usable.x) + if (!grabbed && geometry.x < usable.x) { - geometry->x = usable.x; + geometry.x = usable.x; } - if (!grabbed && geometry->y < usable.y) + if (!grabbed && geometry.y < usable.y) { - geometry->y = usable.y; + geometry.y = usable.y; } if (grabbed && server->seat.cursor->x <= usable.x) { - geometry->x = usable.x - server->grab_x; + geometry.x = usable.x - server->grab_x; } if (grabbed && server->seat.cursor->y <= usable.y) { - geometry->y = usable.y; + geometry.y = usable.y; } if (grabbed && server->seat.cursor->x >= usable.x + usable.width) { - geometry->x = usable.x + usable.width - server->grab_x; + geometry.x = usable.x + usable.width - server->grab_x; } if (grabbed && server->seat.cursor->y >= usable.y + usable.height) { - geometry->y = usable.y + usable.height - server->grab_y; + geometry.y = usable.y + usable.height - server->grab_y; } } - - wlr_log(WLR_DEBUG, "diyac_view_update_geometry: updating geometry: %d %d %d %d", geometry->x, geometry->y, geometry->width, geometry->height); - wlr_scene_node_set_position(&view->scene_tree->node, geometry->x, geometry->y); - wlr_xdg_toplevel_set_size(view->xdg_toplevel, geometry->width, geometry->height); - return true; + if (!wlr_box_equal(&geometry, &view->original) || !view->requested.maximized) + { + wlr_log(WLR_DEBUG, "diyac_view_update_geometry: updating geometry: %d %d %d %d", geometry.x, geometry.y, geometry.width, geometry.height); + // wlr_scene_node_set_position(&view->scene_tree->node, geometry.x, geometry.y); + // wlr_xdg_toplevel_set_size(view->xdg_toplevel, geometry.width, geometry.height); + diyac_view_configure(view, geometry); + updated = true; + } } + view->state = view->requested; + return updated; } void diyac_get_children_views(struct diyac_view *view, struct wl_array *children) @@ -316,42 +349,42 @@ struct diyac_view *diyac_get_root_view(struct diyac_view *view) struct wlr_box diyac_view_get_geometry(struct diyac_view *view) { struct wlr_box box; - if(view->state.fullscreen) + if (view->state.fullscreen) { wlr_output_layout_get_box(view->server->output_layout, view->output->wlr_output, &box); return box; } - if(view->state.maximized) + if (view->state.maximized) { return view->output->usable_area; } return view->original; } -void diyac_view_set_maximize(struct diyac_view * view, bool activated) +void diyac_view_set_maximize(struct diyac_view *view, bool activated) { view->requested.maximized = activated; diyac_reset_cursor_mode(view->server); diyac_view_update_geometry(view, false); } -void diyac_view_set_fullscreen(struct diyac_view * view, bool activated) +void diyac_view_set_fullscreen(struct diyac_view *view, bool activated) { view->requested.fullscreen = activated; /** * TODO: use client specific output for fullscreen * toplevel->xdg_toplevel->requested.fullscreen_output - */ + */ diyac_reset_cursor_mode(view->server); diyac_view_update_geometry(view, false); } -void diyac_view_set_mimimize(struct diyac_view * view, bool activated) +void diyac_view_set_mimimize(struct diyac_view *view, bool activated) { // view->requested.minimized = activated; - //TODO implement minimize + // TODO implement minimize wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); } -void diyac_view_update_title(struct diyac_view * view) +void diyac_view_update_title(struct diyac_view *view) { struct wlr_xdg_toplevel *xdg_toplevel = view->xdg_toplevel; if (!xdg_toplevel) @@ -366,7 +399,7 @@ void diyac_view_update_title(struct diyac_view * view) } wlr_foreign_toplevel_handle_v1_set_title(view->toplevel.handle, title); } -void diyac_view_update_app_id(struct diyac_view * view) +void diyac_view_update_app_id(struct diyac_view *view) { struct wlr_xdg_toplevel *xdg_toplevel = view->xdg_toplevel; if (!xdg_toplevel) @@ -380,4 +413,48 @@ void diyac_view_update_app_id(struct diyac_view * view) return; } wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel.handle, appid); -} \ No newline at end of file +} + +void diyac_view_sync_geo(struct diyac_view *view) +{ + struct wlr_box size; + wlr_xdg_surface_get_geometry(view->xdg_surface, &size); + if (!view->state.fullscreen && !view->state.maximized) + { + + if ((view->server->resize_edges & WLR_EDGE_LEFT) || ( + (view->original.x != view->pending_size.x) && + (view->original.x + view->original.width == view->pending_size.x + view->pending_size.width))) + { + view->original.x = view->pending_size.x + view->pending_size.width - size.width; + } + else + { + view->original.x = view->pending_size.x; + } + if ((view->server->resize_edges & WLR_EDGE_TOP) || ( + (view->original.y != view->pending_size.y) && + (view->original.y + view->original.height == view->pending_size.y + view->pending_size.height))) + { + view->original.y = view->pending_size.y + view->pending_size.height - size.height; + } + else + { + view->original.y = view->pending_size.y; + } + + view->original.width = size.width; + view->original.height = size.height; + wlr_scene_node_set_position(&view->scene_tree->node, view->original.x, view->original.y); + view->pending_size = view->original; + } + else + { + if(wlr_box_equal(&size, &view->pending_size)) + { + return; + } + wlr_log(WLR_ERROR, "update fullscreen position"); + wlr_scene_node_set_position(&view->scene_tree->node, view->pending_size.x, view->pending_size.y); + } +} diff --git a/view.h b/view.h index b5ae8a8..ec2de0c 100644 --- a/view.h +++ b/view.h @@ -20,4 +20,5 @@ void diyac_view_set_fullscreen(struct diyac_view * view, bool activated); void diyac_view_set_mimimize(struct diyac_view * view, bool activated); void diyac_view_update_title(struct diyac_view * view); void diyac_view_update_app_id(struct diyac_view * view); +void diyac_view_sync_geo(struct diyac_view *view); #endif \ No newline at end of file diff --git a/xdg.c b/xdg.c index d8d40a4..a548318 100644 --- a/xdg.c +++ b/xdg.c @@ -53,7 +53,19 @@ static void begin_interactive(struct diyac_view *toplevel, server->resize_edges = edges; } } - +static void xdg_toplevel_commit(struct wl_listener *listener, void *data) +{ + struct diyac_view *view = wl_container_of(listener, view, commit); + uint32_t serial = view->configuration_serial; + if (serial > 0 && serial == view->xdg_surface->current.configure_serial) + { + wl_event_source_remove(view->configuration_timeout); + view->configuration_serial = 0; + view->configuration_timeout = NULL; + // TODO move view + } + diyac_view_sync_geo(view); +} static void xdg_toplevel_map(struct wl_listener *listener, void *data) { @@ -69,21 +81,24 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE); //WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN */ - wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &toplevel->original); + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &toplevel->pending_size); wlr_scene_node_set_enabled(&toplevel->scene_tree->node, true); toplevel->mapped = true; wl_list_insert(&toplevel->server->views, &toplevel->link); + toplevel->commit.notify = xdg_toplevel_commit; + wl_signal_add(&toplevel->xdg_surface->surface->events.commit, &toplevel->commit); + diyac_view_update_app_id(toplevel); diyac_view_update_title(toplevel); - toplevel->original.x = (toplevel->output->usable_area.width - toplevel->original.width) / 2; - toplevel->original.y = (toplevel->output->usable_area.height - toplevel->original.height) / 2; - if (toplevel->original.width > toplevel->output->usable_area.width) + toplevel->pending_size.x = (toplevel->output->usable_area.width - toplevel->pending_size.width) / 2; + toplevel->pending_size.y = (toplevel->output->usable_area.height - toplevel->pending_size.height) / 2; + if (toplevel->pending_size.width > toplevel->output->usable_area.width) { - toplevel->original.width = toplevel->output->usable_area.width; + toplevel->pending_size.width = toplevel->output->usable_area.width; } - if (toplevel->original.height > toplevel->output->usable_area.height) + if (toplevel->pending_size.height > toplevel->output->usable_area.height) { - toplevel->original.height = toplevel->output->usable_area.height; + toplevel->pending_size.height = toplevel->output->usable_area.height; } diyac_view_update_geometry(toplevel, false); diyac_focus_view(toplevel, false); @@ -188,6 +203,12 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { wlr_foreign_toplevel_handle_v1_destroy(toplevel->toplevel.handle); } + if (toplevel->configuration_timeout) + { + wl_event_source_remove(toplevel->configuration_timeout); + toplevel->configuration_timeout = NULL; + } + wl_list_remove(&toplevel->commit.link); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); wl_list_remove(&toplevel->destroy.link); @@ -378,6 +399,7 @@ void diyac_new_xdg_surface(struct wl_listener *listener, void *data) toplevel->xdg_toplevel = xdg_surface->toplevel; toplevel->xdg_surface = xdg_surface; toplevel->mapped = false; + toplevel->configuration_timeout = NULL; toplevel->output = diyac_output_from_cursor(server); toplevel->scene_tree = wlr_scene_xdg_surface_create( toplevel->server->view_tree, toplevel->xdg_toplevel->base); @@ -393,7 +415,7 @@ void diyac_new_xdg_surface(struct wl_listener *listener, void *data) diyac_node_descriptor_create(&toplevel->scene_tree->node, DIYAC_NODE_VIEW, toplevel); diyac_init_foreign_toplevel(toplevel); - + /* Listen to the various events it can emit */ toplevel->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map);