-rw-r--r-- src/shell.c
1 #include <wlr/types/wlr_layer_shell_v1.h> 2 #include <wlr/types/wlr_output_damage.h> 3 #include <wlr/types/wlr_xdg_shell.h> 4 #include <wlr/util/edges.h> 5 6 #include "action.h" 7 #include "output.h" 8 #include "scratchpad.h" 9 #include "types.h" 10 11 12 void view_apply_geometry(struct view *view, struct wlr_box *new) { 13 if (new->width <= 0 || new->height <= 0) { 14 return; 15 } 16 17 struct wlr_box old = { 18 .x = view->x, 19 .y = view->y, 20 .width = view->surface->geometry.width, 21 .height = view->surface->geometry.height, 22 }; 23 24 view->x = new->x; 25 view->y = new->y, 26 wlr_xdg_toplevel_set_size(view->surface, new->width, new->height); 27 damage_box(&old, true); 28 damage_box(new, true); 29 } 30 31 32 void unmap_view(struct view *view) { 33 wl_signal_emit(&view->surface->events.unmap, view); 34 } 35 36 37 void map_view(struct view *view) { 38 wl_signal_emit(&view->surface->events.map, view); 39 } 40 41 42 void focus(void *data, struct wlr_surface *surface, bool is_layer) { 43 /* view can be NULL. In that case focus is removed from any focussed surface. */ 44 /* surface can be NULL. In that case focus goes to view->surface->surface. */ 45 struct layer_view *lview; 46 struct view *view; 47 if (data) { 48 if (is_layer) { 49 lview = (struct layer_view *)data; 50 if (!lview->surface->current.keyboard_interactive) { 51 return; 52 } 53 if (!surface) { 54 surface = lview->surface->surface; 55 } 56 } else { 57 view = (struct view *)data; 58 if (!surface) { 59 surface = view->surface->surface; 60 } 61 } 62 } 63 struct wlr_surface *prev_surface = wimp.seat->keyboard_state.focused_surface; 64 65 if (surface == prev_surface) { 66 return; 67 } 68 wlr_seat_keyboard_notify_clear_focus(wimp.seat); 69 70 if (prev_surface && wlr_surface_is_xdg_surface(prev_surface)) { 71 struct wlr_xdg_surface *prev_xdg_surface = wlr_xdg_surface_from_wlr_surface(prev_surface); 72 wlr_xdg_toplevel_set_activated(prev_xdg_surface, false); 73 damage_by_view(prev_xdg_surface->data, true); 74 } 75 76 if (!data) { 77 wimp.focussed_layer_view = NULL; 78 return; 79 } 80 81 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(wimp.seat); 82 83 if (is_layer) { 84 if (!lview->surface->mapped || !lview->surface->current.keyboard_interactive) { 85 return; 86 } 87 if (!surface) { 88 surface = lview->surface->surface; 89 } 90 wlr_seat_keyboard_notify_enter( 91 wimp.seat, surface, keyboard->keycodes, 92 keyboard->num_keycodes, &keyboard->modifiers 93 ); 94 wlr_surface_send_enter(surface, lview->surface->output); 95 wimp.focussed_layer_view = lview; 96 damage_by_lview(lview); 97 98 } else { 99 if (view->is_scratchpad) { 100 struct scratchpad *scratchpad = scratchpad_from_view(view); 101 scratchpad->is_mapped = true; 102 } else { 103 wl_list_remove(&view->link); 104 wl_list_insert(&wimp.current_desk->views, &view->link); 105 } 106 if (!surface) { 107 surface = view->surface->surface; 108 } 109 if (wlr_surface_is_xdg_surface(surface)) { 110 wlr_xdg_toplevel_set_activated(view->surface, true); 111 } 112 wlr_seat_keyboard_notify_enter( 113 wimp.seat, surface, keyboard->keycodes, 114 keyboard->num_keycodes, &keyboard->modifiers 115 ); 116 damage_by_view(view, true); 117 } 118 } 119 120 121 void focus_view(struct view *view, struct wlr_surface *surface) { 122 focus(view, surface, false); 123 } 124 125 126 void find_focus() { 127 struct scratchpad *scratchpad; 128 wl_list_for_each(scratchpad, &wimp.scratchpads, link) { 129 if (scratchpad->is_mapped) { 130 focus_view(scratchpad->view, NULL); 131 return; 132 } 133 } 134 135 if (!wl_list_empty(&wimp.current_desk->views)) { 136 struct view *view = wl_container_of(wimp.current_desk->views.next, view, link); 137 focus_view(view, NULL); 138 return; 139 }; 140 141 focus_view(NULL, NULL); 142 } 143 144 145 void pan_to_view(struct view *view) { 146 int border_width = wimp.current_desk->border_width; 147 double x = view->x - border_width; 148 double y = view->y - border_width; 149 double width = view->surface->geometry.width + border_width * 2; 150 double height = view->surface->geometry.height + border_width * 2; 151 struct wlr_box *extents = wlr_output_layout_get_box(wimp.output_layout, NULL); 152 double zoom = wimp.current_desk->zoom; 153 struct motion motion = { 154 .dx = 0, 155 .dy = 0, 156 .is_percentage = false, 157 }; 158 159 int panning = 2; 160 if (x < 0) { 161 motion.dx = x * zoom; 162 } else if (x + width > extents->width / zoom) { 163 motion.dx = (x + width) * zoom - extents->width; 164 } else { 165 panning -= 1; 166 } 167 if (y < 0) { 168 motion.dy = y * zoom; 169 } else if (y + height > extents->height / zoom) { 170 motion.dy = (y + height) * zoom - extents->height; 171 } else { 172 panning -= 1; 173 } 174 if (panning) { 175 pan_desk(&motion); 176 damage_all_outputs(); 177 } 178 } 179 180 181 void fullscreen_xdg_surface( 182 struct view *view, struct wlr_xdg_surface *xdg_surface, struct wlr_output *wlr_output 183 ) { 184 /* wlr_output can be NULL, in which case it is calculated using the surface's position */ 185 struct wlr_box *saved_geo = &wimp.current_desk->fullscreened_saved_geo; 186 187 struct wlr_xdg_surface *prev_surface = wimp.current_desk->fullscreened; 188 if (prev_surface) { 189 wlr_xdg_toplevel_set_fullscreen(prev_surface, false); 190 wlr_xdg_toplevel_set_tiled(view->surface, true); 191 wlr_xdg_toplevel_set_size(prev_surface, saved_geo->width, saved_geo->height); 192 view->x = saved_geo->x; 193 view->y = saved_geo->y; 194 } 195 196 if (prev_surface == xdg_surface) { 197 wimp.current_desk->fullscreened = NULL; 198 return; 199 } 200 201 if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) 202 return; 203 204 if (!wlr_output) { 205 double x = view->x + xdg_surface->geometry.width / 2; 206 double y = view->y + xdg_surface->geometry.height / 2; 207 double lx, ly; 208 wlr_output_layout_closest_point(wimp.output_layout, NULL, x, y, &lx, &ly); 209 wlr_output = wlr_output_layout_output_at(wimp.output_layout, lx, ly); 210 } 211 212 wimp.current_desk->fullscreened = xdg_surface; 213 saved_geo->x = view->x; 214 saved_geo->y = view->y; 215 view->x = view->y = 0; 216 saved_geo->width = xdg_surface->geometry.width; 217 saved_geo->height = xdg_surface->geometry.height; 218 wlr_xdg_toplevel_set_fullscreen(xdg_surface, true); 219 wlr_xdg_toplevel_set_tiled(view->surface, false); 220 double zoom = wimp.current_desk->zoom; 221 wlr_xdg_toplevel_set_size(xdg_surface, wlr_output->width / zoom, wlr_output->height / zoom); 222 struct output *output = wlr_output->data; 223 wlr_output_damage_add_whole(output->wlr_output_damage); 224 } 225 226 227 void unfullscreen() { 228 if (wimp.current_desk->fullscreened != NULL) { 229 toggle_fullscreen(NULL); 230 } 231 } 232 233 234 static void on_commit(struct wl_listener *listener, void *data) { 235 struct view *view = wl_container_of(listener, view, commit_listener); 236 damage_by_view(view, false); 237 } 238 239 240 static void on_map(struct wl_listener *listener, void *data) { 241 struct view *view = wl_container_of(listener, view, map_listener); 242 243 view->commit_listener.notify = on_commit; 244 wl_signal_add(&view->surface->surface->events.commit, &view->commit_listener); 245 246 if (view->is_scratchpad) { 247 struct scratchpad *scratchpad = scratchpad_from_view(view); 248 scratchpad_apply_geo(scratchpad); 249 scratchpad->is_mapped = true; 250 } 251 252 wlr_xdg_toplevel_set_tiled(view->surface, true); 253 focus_view(view, NULL); 254 } 255 256 257 static void on_unmap(struct wl_listener *listener, void *data) { 258 struct view *view = wl_container_of(listener, view, unmap_listener); 259 260 if (view->is_scratchpad) { 261 struct scratchpad *scratchpad = scratchpad_from_view(view); 262 scratchpad->is_mapped = false; 263 } else { 264 wl_list_remove(&view->link); 265 wl_list_insert(wimp.current_desk->views.prev, &view->link); 266 } 267 268 wl_list_remove(&view->commit_listener.link); 269 damage_by_view(view, true); 270 271 if (view->surface->surface == wimp.seat->keyboard_state.focused_surface) { 272 find_focus(); 273 } 274 } 275 276 277 static void on_surface_destroy(struct wl_listener *listener, void *data) { 278 struct view *view = wl_container_of(listener, view, destroy_listener); 279 if (wimp.current_desk->fullscreened == view->surface) { 280 wimp.current_desk->fullscreened = NULL; 281 } 282 283 if (view->is_scratchpad) { 284 struct scratchpad *scratchpad = scratchpad_from_view(view); 285 scratchpad->is_mapped = false; 286 scratchpad->view = NULL; 287 } else { 288 wl_list_remove(&view->link); 289 } 290 291 free(view); 292 } 293 294 295 static void process_move_resize(struct view *view, enum cursor_mode mode, uint32_t edges) { 296 struct wlr_surface *focused_surface = wimp.seat->pointer_state.focused_surface; 297 298 if (view->surface->surface != focused_surface) { 299 return; 300 } 301 wimp.grabbed_view = view; 302 wimp.cursor_mode = mode; 303 304 double zoom = wimp.current_desk->zoom; 305 wimp.grab_x = wimp.cursor->x - view->x * zoom; 306 wimp.grab_y = wimp.cursor->y - view->y * zoom; 307 308 if (mode == CURSOR_RESIZE) { 309 wlr_xdg_surface_get_geometry(view->surface, &wimp.grab_geobox); 310 wimp.grab_geobox.x = view->x; 311 wimp.grab_geobox.y = view->y; 312 wimp.resize_edges = edges; 313 } 314 } 315 316 317 static void on_request_move(struct wl_listener *listener, void *data) { 318 struct view *view = wl_container_of(listener, view, request_move_listener); 319 wlr_xdg_toplevel_set_tiled(view->surface, false); 320 process_move_resize(view, CURSOR_MOVE, 0); 321 } 322 323 324 static void on_request_resize(struct wl_listener *listener, void *data) { 325 struct wlr_xdg_toplevel_resize_event *event = data; 326 struct view *view = wl_container_of(listener, view, request_resize_listener); 327 wlr_xdg_toplevel_set_tiled(view->surface, false); 328 process_move_resize(view, CURSOR_RESIZE, event->edges); 329 } 330 331 332 static void on_request_fullscreen(struct wl_listener *listener, void *data) { 333 struct wlr_xdg_toplevel_set_fullscreen_event *event = data; 334 struct view *view = wl_container_of(listener, view, request_fullscreen_listener); 335 fullscreen_xdg_surface(view, event->surface, event->output); 336 } 337 338 339 static void on_new_xdg_surface(struct wl_listener *listener, void *data) { 340 struct wlr_xdg_surface *surface = data; 341 if (surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { 342 return; 343 } 344 345 struct view *view = calloc(1, sizeof(struct view)); 346 view->surface = surface; 347 surface->data = view; 348 349 view->map_listener.notify = on_map; 350 wl_signal_add(&surface->events.map, &view->map_listener); 351 view->unmap_listener.notify = on_unmap; 352 wl_signal_add(&surface->events.unmap, &view->unmap_listener); 353 view->destroy_listener.notify = on_surface_destroy; 354 wl_signal_add(&surface->events.destroy, &view->destroy_listener); 355 356 struct wlr_xdg_toplevel *toplevel = surface->toplevel; 357 view->request_move_listener.notify = on_request_move; 358 wl_signal_add(&toplevel->events.request_move, &view->request_move_listener); 359 view->request_resize_listener.notify = on_request_resize; 360 wl_signal_add(&toplevel->events.request_resize, &view->request_resize_listener); 361 view->request_fullscreen_listener.notify = on_request_fullscreen; 362 wl_signal_add(&toplevel->events.request_fullscreen, &view->request_fullscreen_listener); 363 364 if (wimp.scratchpad_waiting) { 365 if (catch_scratchpad(view)) { 366 return; 367 } 368 } 369 370 view->is_scratchpad = false; 371 view->x = wimp.current_desk->border_width; 372 view->y = wimp.current_desk->border_width; 373 wl_list_insert(&wimp.current_desk->views, &view->link); 374 } 375 376 377 void set_up_shell() { 378 wimp.shell = wlr_xdg_shell_create(wimp.display); 379 wimp.new_xdg_surface_listener.notify = on_new_xdg_surface; 380 wl_signal_add(&wimp.shell->events.new_surface, &wimp.new_xdg_surface_listener); 381 } 382