diff --git a/dwl.c b/dwl.c index 2bd0790..54bc335 100644 --- a/dwl.c +++ b/dwl.c @@ -35,13 +35,21 @@ /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ -struct dwl_output { - struct wl_list link; - struct wlr_output *wlr_output; - struct wl_listener frame; -}; +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; -struct dwl_view { +typedef struct { + unsigned int mod; + unsigned int button; + void (*func)(const Arg *); + const Arg arg; +} Button; + +typedef struct { struct wl_list link; struct wlr_xdg_surface *xdg_surface; struct wl_listener map; @@ -51,22 +59,7 @@ struct dwl_view { struct wl_listener request_resize; bool mapped; int x, y; -}; - -struct dwl_keyboard { - struct wl_list link; - struct wlr_input_device *device; - - struct wl_listener modifiers; - struct wl_listener key; -}; - -typedef union { - int i; - unsigned int ui; - float f; - const void *v; -} Arg; +} Client; typedef struct { uint32_t mod; @@ -76,22 +69,37 @@ typedef struct { } Key; typedef struct { - unsigned int mod; - unsigned int button; - void (*func)(const Arg *); - const Arg arg; -} Button; + struct wl_list link; + struct wlr_input_device *device; + + struct wl_listener modifiers; + struct wl_listener key; +} Keyboard; + +typedef struct { + struct wl_list link; + struct wlr_output *wlr_output; + struct wl_listener frame; +} Monitor; + +/* Used to move all of the data necessary to render a surface from the top-level + * frame handler to the per-surface render function. */ +struct render_data { + struct wlr_output *output; + Client *client; + struct timespec *when; +}; /* function declarations */ static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); static void createnotify(struct wl_listener *listener, void *data); -static void createoutput(struct wl_listener *listener, void *data); +static void createmon(struct wl_listener *listener, void *data); static void createpointer(struct wlr_input_device *device); static void cursorframe(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); -static void focus(struct dwl_view *view, struct wlr_surface *surface); +static void focus(Client *c, struct wlr_surface *surface); static void focusnext(const Arg *arg); static void handlemove(uint32_t time); static void handleresize(uint32_t time); @@ -104,17 +112,17 @@ static void motionabsolute(struct wl_listener *listener, void *data); static void motionnotify(uint32_t time); static void motionrelative(struct wl_listener *listener, void *data); static void movemouse(const Arg *arg); -static void moveresize(struct dwl_view *view, unsigned int mode); +static void moveresize(Client *c, unsigned int mode); static void quit(const Arg *arg); static void render(struct wlr_surface *surface, int sx, int sy, void *data); -static void renderoutput(struct wl_listener *listener, void *data); +static void rendermon(struct wl_listener *listener, void *data); static void resizemouse(const Arg *arg); static void setcursor(struct wl_listener *listener, void *data); static void spawn(const Arg *arg); static void unmapnotify(struct wl_listener *listener, void *data); -static bool xytosurface(struct dwl_view *view, double lx, double ly, +static bool xytosurface(Client *c, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); -static struct dwl_view * xytoview(double lx, double ly, +static Client * xytoclient(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); /* variables */ @@ -124,7 +132,7 @@ static struct wlr_renderer *renderer; static struct wlr_xdg_shell *xdg_shell; static struct wl_listener new_xdg_surface; -static struct wl_list views; +static struct wl_list clients; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -139,24 +147,16 @@ static struct wl_listener new_input; static struct wl_listener request_cursor; static struct wl_list keyboards; static unsigned int cursor_mode; -static struct dwl_view *grabbed_view; +static Client *grabbed_client; static double grab_x, grab_y; static int grab_width, grab_height; static struct wlr_output_layout *output_layout; -static struct wl_list outputs; +static struct wl_list mons; static struct wl_listener new_output; #include "config.h" -/* Used to move all of the data necessary to render a surface from the top-level - * frame handler to the per-surface render function. */ -struct render_data { - struct wlr_output *output; - struct dwl_view *view; - struct timespec *when; -}; - void axisnotify(struct wl_listener *listener, void *data) { @@ -180,14 +180,14 @@ buttonpress(struct wl_listener *listener, void *data) event->time_msec, event->button, event->state); double sx, sy; struct wlr_surface *surface; - struct dwl_view *view = xytoview(cursor->x, cursor->y, + Client *c = xytoclient(cursor->x, cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ cursor_mode = CurNormal; } else { /* Focus that client if the button was _pressed_ */ - focus(view, surface); + focus(c, surface); struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); uint32_t mods = wlr_keyboard_get_modifiers(keyboard); @@ -204,7 +204,7 @@ buttonpress(struct wl_listener *listener, void *data) void createkeyboard(struct wlr_input_device *device) { - struct dwl_keyboard *keyboard = calloc(1, sizeof(*keyboard)); + Keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->device = device; /* We need to prepare an XKB keymap and assign it to the keyboard. This @@ -240,24 +240,24 @@ createnotify(struct wl_listener *listener, void *data) return; } - /* Allocate a dwl_view for this surface */ - struct dwl_view *view = calloc(1, sizeof(*view)); - view->xdg_surface = xdg_surface; + /* Allocate a Client for this surface */ + Client *c = calloc(1, sizeof(*c)); + c->xdg_surface = xdg_surface; /* Listen to the various events it can emit */ - view->map.notify = maprequest; - wl_signal_add(&xdg_surface->events.map, &view->map); - view->unmap.notify = unmapnotify; - wl_signal_add(&xdg_surface->events.unmap, &view->unmap); - view->destroy.notify = destroynotify; - wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + c->map.notify = maprequest; + wl_signal_add(&xdg_surface->events.map, &c->map); + c->unmap.notify = unmapnotify; + wl_signal_add(&xdg_surface->events.unmap, &c->unmap); + c->destroy.notify = destroynotify; + wl_signal_add(&xdg_surface->events.destroy, &c->destroy); - /* Add it to the list of views. */ - wl_list_insert(&views, &view->link); + /* Add it to the list of clients. */ + wl_list_insert(&clients, &c->link); } void -createoutput(struct wl_listener *listener, void *data) +createmon(struct wl_listener *listener, void *data) { /* This event is rasied by the backend when a new output (aka a display or * monitor) becomes available. */ @@ -278,12 +278,12 @@ createoutput(struct wl_listener *listener, void *data) } /* Allocates and configures our state for this output */ - struct dwl_output *output = calloc(1, sizeof(*output)); - output->wlr_output = wlr_output; + Monitor *m = calloc(1, sizeof(*m)); + m->wlr_output = wlr_output; /* Sets up a listener for the frame notify event. */ - output->frame.notify = renderoutput; - wl_signal_add(&wlr_output->events.frame, &output->frame); - wl_list_insert(&outputs, &output->link); + m->frame.notify = rendermon; + wl_signal_add(&wlr_output->events.frame, &m->frame); + wl_list_insert(&mons, &m->link); /* Adds this to the output layout. The add_auto function arranges outputs * from left-to-right in the order they appear. A more sophisticated @@ -322,16 +322,16 @@ void destroynotify(struct wl_listener *listener, void *data) { /* Called when the surface is destroyed and should never be shown again. */ - struct dwl_view *view = wl_container_of(listener, view, destroy); - wl_list_remove(&view->link); - free(view); + Client *c = wl_container_of(listener, c, destroy); + wl_list_remove(&c->link); + free(c); } void -focus(struct dwl_view *view, struct wlr_surface *surface) +focus(Client *c, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ - if (view == NULL) { + if (c == NULL) { return; } struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; @@ -350,43 +350,41 @@ focus(struct dwl_view *view, struct wlr_surface *surface) wlr_xdg_toplevel_set_activated(previous, false); } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); - /* Move the view to the front */ - wl_list_remove(&view->link); - wl_list_insert(&views, &view->link); + /* Move the client to the front */ + wl_list_remove(&c->link); + wl_list_insert(&clients, &c->link); /* Activate the new surface */ - wlr_xdg_toplevel_set_activated(view->xdg_surface, true); + wlr_xdg_toplevel_set_activated(c->xdg_surface, true); /* * Tell the seat to have the keyboard enter this surface. wlroots will keep * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ - wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, + wlr_seat_keyboard_notify_enter(seat, c->xdg_surface->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } void focusnext(const Arg *arg) { - /* Cycle to the next view */ - if (wl_list_length(&views) < 2) { + /* Cycle to the next client */ + if (wl_list_length(&clients) < 2) { return; } - struct dwl_view *current_view = wl_container_of( - views.next, current_view, link); - struct dwl_view *next_view = wl_container_of( - current_view->link.next, next_view, link); - focus(next_view, next_view->xdg_surface->surface); - /* Move the previous view to the end of the list */ - wl_list_remove(¤t_view->link); - wl_list_insert(views.prev, ¤t_view->link); + Client *c = wl_container_of(clients.next, c, link); + Client *n = wl_container_of(c->link.next, n, link); + focus(n, n->xdg_surface->surface); + /* Move the previous client to the end of the list */ + wl_list_remove(&c->link); + wl_list_insert(clients.prev, &c->link); } void handlemove(uint32_t time) { - /* Move the grabbed view to the new position. */ - grabbed_view->x = cursor->x - grab_x; - grabbed_view->y = cursor->y - grab_y; + /* Move the grabbed client to the new position. */ + grabbed_client->x = cursor->x - grab_x; + grabbed_client->y = cursor->y - grab_y; } void @@ -399,7 +397,7 @@ handleresize(uint32_t time) */ double dx = cursor->x - grab_x; double dy = cursor->y - grab_y; - wlr_xdg_toplevel_set_size(grabbed_view->xdg_surface, + wlr_xdg_toplevel_set_size(grabbed_client->xdg_surface, grab_width + dx, grab_height + dy); } @@ -453,7 +451,7 @@ void keypress(struct wl_listener *listener, void *data) { /* This event is raised when a key is pressed or released. */ - struct dwl_keyboard *keyboard = + Keyboard *keyboard = wl_container_of(listener, keyboard, key); struct wlr_event_keyboard_key *event = data; @@ -486,8 +484,7 @@ keypressmod(struct wl_listener *listener, void *data) { /* This event is raised when a modifier key, such as shift or alt, is * pressed. We simply communicate this to the client. */ - struct dwl_keyboard *keyboard = - wl_container_of(listener, keyboard, modifiers); + Keyboard *keyboard = wl_container_of(listener, keyboard, modifiers); /* * A seat can only have one keyboard, but this is a limitation of the * Wayland protocol - not wlroots. We assign all connected keyboards to the @@ -504,9 +501,9 @@ void maprequest(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ - struct dwl_view *view = wl_container_of(listener, view, map); - view->mapped = true; - focus(view, view->xdg_surface->surface); + Client *c = wl_container_of(listener, c, map); + c->mapped = true; + focus(c, c->xdg_surface->surface); } void @@ -535,15 +532,15 @@ motionnotify(uint32_t time) return; } - /* Otherwise, find the view under the pointer and send the event along. */ + /* Otherwise, find the client under the pointer and send the event along. */ double sx, sy; struct wlr_surface *surface = NULL; - struct dwl_view *view = xytoview(cursor->x, cursor->y, + Client *c = xytoclient(cursor->x, cursor->y, &surface, &sx, &sy); - if (!view) { - /* If there's no view under the cursor, set the cursor image to a + if (!c) { + /* If there's no client under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it - * around the screen, not over any views. */ + * around the screen, not over any clients. */ wlr_xcursor_manager_set_cursor_image( cursor_mgr, "left_ptr", cursor); } @@ -591,33 +588,33 @@ movemouse(const Arg *arg) { double sx, sy; struct wlr_surface *surface; - struct dwl_view *view = xytoview(cursor->x, cursor->y, + Client *c = xytoclient(cursor->x, cursor->y, &surface, &sx, &sy); - if (!view) { + if (!c) { return; } - moveresize(view, CurMove); + moveresize(c, CurMove); } void -moveresize(struct dwl_view *view, unsigned int mode) +moveresize(Client *c, unsigned int mode) { /* This function sets up an interactive move or resize operation, where the * compositor stops propagating pointer events to clients and instead * consumes them itself, to move or resize windows. */ struct wlr_surface *focused_surface = seat->pointer_state.focused_surface; - if (view->xdg_surface->surface != focused_surface) { + if (c->xdg_surface->surface != focused_surface) { /* Deny move/resize requests from unfocused clients. */ return; } - grabbed_view = view; + grabbed_client = c; cursor_mode = mode; struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + wlr_xdg_surface_get_geometry(c->xdg_surface, &geo_box); if (mode == CurMove) { - grab_x = cursor->x - view->x; - grab_y = cursor->y - view->y; + grab_x = cursor->x - c->x; + grab_y = cursor->y - c->y; } else { grab_x = cursor->x + geo_box.x; grab_y = cursor->y + geo_box.y; @@ -637,7 +634,7 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) { /* This function is called for every surface that needs to be rendered. */ struct render_data *rdata = data; - struct dwl_view *view = rdata->view; + Client *c = rdata->client; struct wlr_output *output = rdata->output; /* We first obtain a wlr_texture, which is a GPU resource. wlroots @@ -650,14 +647,14 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) return; } - /* The view has a position in layout coordinates. If you have two displays, - * one next to the other, both 1080p, a view on the rightmost display might + /* The client has a position in layout coordinates. If you have two displays, + * one next to the other, both 1080p, a client on the rightmost display might * have layout coordinates of 2000,100. We need to translate that to * output-local coordinates, or (2000 - 1920). */ double ox = 0, oy = 0; wlr_output_layout_output_coords( output_layout, output, &ox, &oy); - ox += view->x + sx, oy += view->y + sy; + ox += c->x + sx, oy += c->y + sy; /* We also have to apply the scale factor for HiDPI outputs. This is only * part of the puzzle, dwl does not fully support HiDPI. */ @@ -670,10 +667,10 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) /* * Those familiar with OpenGL are also familiar with the role of matricies - * in graphics programming. We need to prepare a matrix to render the view - * with. wlr_matrix_project_box is a helper which takes a box with a desired - * x, y coordinates, width and height, and an output geometry, then - * prepares an orthographic projection and multiplies the necessary + * in graphics programming. We need to prepare a matrix to render the + * client with. wlr_matrix_project_box is a helper which takes a box with + * a desired x, y coordinates, width and height, and an output geometry, + * then prepares an orthographic projection and multiplies the necessary * transforms to produce a model-view-projection matrix. * * Naturally you can do this any way you like, for example to make a 3D @@ -695,23 +692,22 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) } void -renderoutput(struct wl_listener *listener, void *data) +rendermon(struct wl_listener *listener, void *data) { /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ - struct dwl_output *output = - wl_container_of(listener, output, frame); + Monitor *m = wl_container_of(listener, m, frame); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); /* wlr_output_attach_render makes the OpenGL context current. */ - if (!wlr_output_attach_render(output->wlr_output, NULL)) { + if (!wlr_output_attach_render(m->wlr_output, NULL)) { return; } /* The "effective" resolution can change if you rotate your outputs. */ int width, height; - wlr_output_effective_resolution(output->wlr_output, &width, &height); + wlr_output_effective_resolution(m->wlr_output, &width, &height); /* Begin the renderer (calls glViewport and some other GL sanity checks) */ wlr_renderer_begin(renderer, width, height); @@ -719,21 +715,21 @@ renderoutput(struct wl_listener *listener, void *data) wlr_renderer_clear(renderer, color); /* Each subsequent window we render is rendered on top of the last. Because - * our view list is ordered front-to-back, we iterate over it backwards. */ - struct dwl_view *view; - wl_list_for_each_reverse(view, &views, link) { - if (!view->mapped) { - /* An unmapped view should not be rendered. */ + * our client list is ordered front-to-back, we iterate over it backwards. */ + Client *c; + wl_list_for_each_reverse(c, &clients, link) { + if (!c->mapped) { + /* An unmapped client should not be rendered. */ continue; } struct render_data rdata = { - .output = output->wlr_output, - .view = view, + .output = m->wlr_output, + .client = c, .when = &now, }; /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ - wlr_xdg_surface_for_each_surface(view->xdg_surface, + wlr_xdg_surface_for_each_surface(c->xdg_surface, render, &rdata); } @@ -743,12 +739,12 @@ renderoutput(struct wl_listener *listener, void *data) * reason, wlroots provides a software fallback, which we ask it to render * here. wlr_cursor handles configuring hardware vs software cursors for you, * and this function is a no-op when hardware cursors are in use. */ - wlr_output_render_software_cursors(output->wlr_output, NULL); + wlr_output_render_software_cursors(m->wlr_output, NULL); /* Conclude rendering and swap the buffers, showing the final frame * on-screen. */ wlr_renderer_end(renderer); - wlr_output_commit(output->wlr_output); + wlr_output_commit(m->wlr_output); } void @@ -756,17 +752,17 @@ resizemouse(const Arg *arg) { double sx, sy; struct wlr_surface *surface; - struct dwl_view *view = xytoview(cursor->x, cursor->y, + Client *c = xytoclient(cursor->x, cursor->y, &surface, &sx, &sy); - if (!view) { + if (!c) { return; } struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + wlr_xdg_surface_get_geometry(c->xdg_surface, &geo_box); wlr_cursor_warp_closest(cursor, NULL, - view->x + geo_box.x + geo_box.width, - view->y + geo_box.y + geo_box.height); - moveresize(view, CurResize); + c->x + geo_box.x + geo_box.width, + c->y + geo_box.y + geo_box.height); + moveresize(c, CurResize); } void @@ -804,12 +800,12 @@ void unmapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ - struct dwl_view *view = wl_container_of(listener, view, unmap); - view->mapped = false; + Client *c = wl_container_of(listener, c, unmap); + c->mapped = false; } bool -xytosurface(struct dwl_view *view, double lx, double ly, +xytosurface(Client *c, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { /* @@ -819,15 +815,15 @@ xytosurface(struct dwl_view *view, double lx, double ly, * surface pointer to that wlr_surface and the sx and sy coordinates to the * coordinates relative to that surface's top-left corner. */ - double view_sx = lx - view->x; - double view_sy = ly - view->y; + double client_sx = lx - c->x; + double client_sy = ly - c->y; - struct wlr_surface_state *state = &view->xdg_surface->surface->current; + struct wlr_surface_state *state = &c->xdg_surface->surface->current; double _sx, _sy; struct wlr_surface *_surface = NULL; _surface = wlr_xdg_surface_surface_at( - view->xdg_surface, view_sx, view_sy, &_sx, &_sy); + c->xdg_surface, client_sx, client_sy, &_sx, &_sy); if (_surface != NULL) { *sx = _sx; @@ -839,16 +835,16 @@ xytosurface(struct dwl_view *view, double lx, double ly, return false; } -struct dwl_view * -xytoview(double lx, double ly, +Client * +xytoclient(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { /* This iterates over all of our surfaces and attempts to find one under the - * cursor. This relies on views being ordered from top-to-bottom. */ - struct dwl_view *view; - wl_list_for_each(view, &views, link) { - if (xytosurface(view, lx, ly, surface, sx, sy)) { - return view; + * cursor. This relies on clients being ordered from top-to-bottom. */ + Client *c; + wl_list_for_each(c, &clients, link) { + if (xytosurface(c, lx, ly, surface, sx, sy)) { + return c; } } return NULL; @@ -909,17 +905,17 @@ main(int argc, char *argv[]) /* Configure a listener to be notified when new outputs are available on the * backend. */ - wl_list_init(&outputs); - new_output.notify = createoutput; + wl_list_init(&mons); + new_output.notify = createmon; wl_signal_add(&backend->events.new_output, &new_output); - /* Set up our list of views and the xdg-shell. The xdg-shell is a Wayland + /* Set up our list of clients and the xdg-shell. The xdg-shell is a Wayland * protocol which is used for application windows. For more detail on * shells, refer to my article: * * https://drewdevault.com/2018/07/29/Wayland-shells.html */ - wl_list_init(&views); + wl_list_init(&clients); xdg_shell = wlr_xdg_shell_create(wl_display); new_xdg_surface.notify = createnotify; wl_signal_add(&xdg_shell->events.new_surface,