gimp but for windows: a wayland compositor
git clone https://github.com/m-col/wimp
Files | Refs | Readme

-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