#define _POSIX_C_SOURCE 200112L #include #include #include "view.h" #include "node.h" #include "seat.h" #include "output.h" #include "cursor.h" #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) { wlr_foreign_toplevel_handle_v1_set_activated(view->toplevel.handle, activated); } server->active_view = NULL; if (activated) { diyac_seat_focus_surface(&server->seat, view->xdg_toplevel->base->surface); server->active_view = view; } } static void raise_to_front(struct diyac_view *view) { /* Move the toplevel to the front */ wlr_scene_node_raise_to_top(&view->scene_tree->node); wl_list_remove(&view->link); wl_list_insert(&view->server->views, &view->link); /* Activate the new surface */ } void diyac_focus_view(struct diyac_view *toplevel, bool raise) { /* Note: this function only deals with keyboard focus. */ if (toplevel == NULL) { return; } if (!toplevel->mapped) { // dont focus unmapped view return; } 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) { diyac_view_set_activated(toplevel->server->active_view, false); } raise_to_front(toplevel); if (raise) { struct diyac_view **subview = NULL; struct wl_array subviews; wl_array_init(&subviews); diyac_get_children_views(toplevel, &subviews); wl_array_for_each(subview, &subviews) { raise_to_front(*subview); } wl_array_release(&subviews); } diyac_view_set_activated(toplevel, true); // 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); }*/ } 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); if (!node_descriptor || node_descriptor->type != DIYAC_NODE_VIEW) { return NULL; } return node_descriptor->data; } void diyac_focus_topmost_view(struct diyac_server *server, bool raise) { struct diyac_view *view = diyac_topmost_focusable_view(server); if (view) { if (raise) { struct diyac_view *root = diyac_get_root_view(view); if (root) { diyac_focus_view(root, true); return; } } diyac_focus_view(view, false); } else { wlr_log(WLR_INFO, "No view found"); /* * Defocus previous focused surface/view if no longer * focusable (e.g. unmapped or on a different workspace). */ diyac_seat_focus_surface(&server->seat, NULL); } } struct diyac_view *diyac_topmost_focusable_view(struct diyac_server *server) { struct wlr_surface *prev = server->seat.wlr_seat->keyboard_state.focused_surface; struct diyac_view *view; struct wl_list *node_list; struct wlr_scene_node *node; node_list = &server->view_tree->children; wl_list_for_each_reverse(node, node_list, link) { if (!node->data) { /* We found some non-view, most likely the region overlay */ continue; } view = diyac_view_from_node(node); if (view->mapped /*&& view_is_focusable_from(view, prev)*/) { return view; } } return NULL; } void diyac_arrange_all_views(struct diyac_server *server) { /* * Adjust window positions/sizes. Skip views with no size since * we can't do anything useful with them; they will presumably * be initialized with valid positions/sizes later. * * We do not simply check view->mapped/been_mapped here because * views can have maximized/fullscreen geometry applied while * still unmapped. We do want to adjust the geometry of those * views. */ struct diyac_view *view; wl_list_for_each(view, &server->views, link) { diyac_view_update_geometry(view, false); } } bool diyac_view_update_geometry(struct diyac_view *view, bool grabbed) { assert(view); // if (wlr_output_layout_intersects(view->server->output_layout, // view->output->wlr_output, &view->current)) //{ if (!view->mapped) { wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); return false; } struct wlr_box usable = diyac_output_usable_area(view->output); struct diyac_server *server = view->server; // 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) { 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) { wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, false); if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel.handle, false); } view->pending_size = view->original; view->state.maximized = false; } bool updated = false; if (view->requested.minimized) { // TODO implement minimize return false; } 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); if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_fullscreen(view->toplevel.handle, true); } diyac_view_configure(view, box); updated = true; } else if (view->requested.maximized) { wlr_xdg_toplevel_set_maximized(view->xdg_toplevel, true); if (view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_set_maximized(view->toplevel.handle, true); } diyac_view_configure(view, usable); updated = true; } else { // if (wlr_output_layout_intersects(view->server->output_layout, // view->output->wlr_output, &view->current)) //{ /**Normal state, recalculate current geometry*/ struct diyac_view *root = diyac_get_root_view(view); struct wlr_box geometry = view->pending_size; if (!root) { root = view; } if (!root->state.fullscreen) { // Only adjust position only when not in fullscreen mode if (!grabbed && geometry.x < usable.x) { geometry.x = usable.x; } if (!grabbed && geometry.y < usable.y) { geometry.y = usable.y; } if (grabbed && server->seat.cursor->x <= usable.x) { geometry.x = usable.x - server->grab_x; } if (grabbed && server->seat.cursor->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; } if (grabbed && server->seat.cursor->y >= usable.y + usable.height) { geometry.y = usable.y + usable.height - server->grab_y; } } 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) { struct diyac_view *child; wl_list_for_each_reverse(child, &view->server->views, link) { if (child == view) { continue; } if (!child->mapped /*&& !view->minimized*/) { continue; } if (diyac_get_root_view(child) != view) { continue; } struct diyac_view **item = wl_array_add(children, sizeof(*item)); *item = child; } } struct diyac_view *diyac_get_root_view(struct diyac_view *view) { struct wlr_xdg_toplevel *toplevel = view->xdg_toplevel; while (toplevel->parent) { toplevel = toplevel->parent; } return toplevel->base->data; } struct wlr_box diyac_view_get_geometry(struct diyac_view *view) { struct wlr_box box; if (view->state.fullscreen) { wlr_output_layout_get_box(view->server->output_layout, view->output->wlr_output, &box); return box; } if (view->state.maximized) { return view->output->usable_area; } return view->original; } 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) { 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) { // view->requested.minimized = activated; // TODO implement minimize wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); } void diyac_view_update_title(struct diyac_view *view) { struct wlr_xdg_toplevel *xdg_toplevel = view->xdg_toplevel; if (!xdg_toplevel) { return; } const char *title = xdg_toplevel->title; wlr_log(WLR_INFO, "diyac_view_update_title: %s", title ? title : ""); if (!view->toplevel.handle || !title) { return; } wlr_foreign_toplevel_handle_v1_set_title(view->toplevel.handle, title); } void diyac_view_update_app_id(struct diyac_view *view) { struct wlr_xdg_toplevel *xdg_toplevel = view->xdg_toplevel; if (!xdg_toplevel) { return; } const char *appid = xdg_toplevel->app_id; wlr_log(WLR_INFO, "diyac_view_update_app_id: %s", appid ? appid : ""); if (!view->toplevel.handle || !appid) { return; } wlr_foreign_toplevel_handle_v1_set_app_id(view->toplevel.handle, appid); } void diyac_view_sync_geo(struct diyac_view *view) { struct wlr_box size; int current_x, current_y, next_x, next_y; wlr_scene_node_coords(&view->scene_tree->node, ¤t_x, ¤t_y); 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; next_x = view->original.x; next_y = view->original.y; view->pending_size = view->original; } else { next_x = view->pending_size.x; next_y = view->pending_size.y; } if(current_x != next_x || current_y != next_y) { //wlr_log(WLR_INFO, "update fullscreen position [%d,%d] VS pending [%d,%d]", // current_x, current_y, next_x, next_y); wlr_scene_node_set_position(&view->scene_tree->node, next_x, next_y); } }