diff --git a/config.def.h b/config.def.h index 5f26c3e..53021cf 100644 --- a/config.def.h +++ b/config.def.h @@ -51,6 +51,9 @@ static const int repeat_delay = 600; { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + /* commands */ static const char *termcmd[] = { "alacritty", NULL }; diff --git a/dwl.c b/dwl.c index 3260d87..730e46a 100644 --- a/dwl.c +++ b/dwl.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,8 @@ #endif /* macros */ +#define BARF(fmt, ...) do { fprintf(stderr, fmt "\n", ##__VA_ARGS__); exit(EXIT_FAILURE); } while (0) +#define EBARF(fmt, ...) BARF(fmt ": %s", ##__VA_ARGS__, strerror(errno)) #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) @@ -55,9 +58,9 @@ /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ +#ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ -#ifdef XWAYLAND enum { XDGShell, X11Managed, X11Unmanaged }; /* client types */ #endif @@ -177,6 +180,7 @@ static void arrange(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); static void chvt(const Arg *arg); +static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); @@ -224,6 +228,7 @@ static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); static void setup(void); +static void sigchld(int unused); static void spawn(const Arg *arg); static void tag(const Arg *arg); static void tagmon(const Arg *arg); @@ -286,6 +291,7 @@ static Atom getatom(xcb_connection_t *xc, const char *name); static void renderindependents(struct wlr_output *output, struct timespec *now); static void updatewindowtype(Client *c); static void xwaylandready(struct wl_listener *listener, void *data); +static Client *xytoindependent(double x, double y); static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; static struct wl_listener xwayland_ready = {.notify = xwaylandready}; static struct wlr_xwayland *xwayland; @@ -327,8 +333,8 @@ applyrules(Client *c) /* rule matching */ c->isfloating = 0; #ifdef XWAYLAND - updatewindowtype(c); if (c->type != XDGShell) { + updatewindowtype(c); appid = c->surface.xwayland->class; title = c->surface.xwayland->title; } else @@ -427,10 +433,21 @@ buttonpress(struct wl_listener *listener, void *data) void chvt(const Arg *arg) { - struct wlr_session *s = wlr_backend_get_session(backend); - if (!s) - return; - wlr_session_change_vt(s, arg->ui); + wlr_session_change_vt(wlr_backend_get_session(backend), arg->ui); +} + +void +cleanup(void) +{ +#ifdef XWAYLAND + wlr_xwayland_destroy(xwayland); +#endif + wl_display_destroy_clients(dpy); + wl_display_destroy(dpy); + + wlr_xcursor_manager_destroy(cursor_mgr); + wlr_cursor_destroy(cursor); + wlr_output_layout_destroy(output_layout); } void @@ -971,7 +988,16 @@ motionnotify(uint32_t time) return; } +#ifdef XWAYLAND + /* Find an independent under the pointer and send the event along. */ + if ((c = xytoindependent(cursor->x, cursor->y))) { + surface = wlr_surface_surface_at(c->surface.xwayland->surface, + cursor->x - c->surface.xwayland->x - c->bw, + cursor->y - c->surface.xwayland->y - c->bw, &sx, &sy); + /* Otherwise, find the client under the pointer and send the event along. */ + } else +#endif if ((c = xytoclient(cursor->x, cursor->y))) { #ifdef XWAYLAND if (c->type != XDGShell) @@ -1060,6 +1086,12 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, /* Otherwise, let the client know that the mouse cursor has entered one * of its surfaces, and make keyboard focus follow if desired. */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + +#if XWAYLAND + if (c->type == X11Unmanaged) + return; +#endif + if (sloppyfocus) focusclient(selclient(), c, 0); } @@ -1266,20 +1298,13 @@ run(char *startup_cmd) /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); - if (!socket) { - perror("startup: display_add_socket_auto"); - wlr_backend_destroy(backend); - exit(EXIT_FAILURE); - } + if (!socket) + BARF("startup: display_add_socket_auto"); /* Start the backend. This will enumerate outputs and inputs, become the DRM * master, etc */ - if (!wlr_backend_start(backend)) { - perror("startup: backend_start"); - wlr_backend_destroy(backend); - wl_display_destroy(dpy); - exit(EXIT_FAILURE); - } + if (!wlr_backend_start(backend)) + BARF("startup: backend_start"); /* Now that outputs are initialized, choose initial selmon based on * cursor position, and set default cursor image */ @@ -1297,24 +1322,17 @@ run(char *startup_cmd) setenv("WAYLAND_DISPLAY", socket, 1); if (startup_cmd) { startup_pid = fork(); - if (startup_pid < 0) { - perror("startup: fork"); - wl_display_destroy(dpy); - exit(EXIT_FAILURE); - } + if (startup_pid < 0) + EBARF("startup: fork"); if (startup_pid == 0) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); - perror("startup: execl"); - wl_display_destroy(dpy); - exit(EXIT_FAILURE); + EBARF("startup: execl"); } } /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event * loop configuration to listen to libinput events, DRM events, generate * frame events at the refresh rate, and so on. */ - wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", - socket); wl_display_run(dpy); if (startup_cmd) { @@ -1445,6 +1463,13 @@ setsel(struct wl_listener *listener, void *data) void setup(void) { + /* The Wayland display is managed by libwayland. It handles accepting + * clients from the Unix socket, manging Wayland globals, and so on. */ + dpy = wl_display_create(); + + /* clean up child processes immediately */ + sigchld(0); + /* 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 @@ -1453,7 +1478,8 @@ setup(void) * backend uses the renderer, for example, to fall back to software cursors * if the backend does not support hardware cursors (some older GPUs * don't). */ - backend = wlr_backend_autocreate(dpy, NULL); + if (!(backend = wlr_backend_autocreate(dpy, NULL))) + BARF("couldn't create backend"); /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. * The renderer is responsible for defining the various pixel formats it @@ -1471,6 +1497,7 @@ setup(void) wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); wlr_data_device_manager_create(dpy); + wlr_gamma_control_manager_v1_create(dpy); wlr_primary_selection_v1_device_manager_create(dpy); wlr_viewporter_create(dpy); @@ -1566,15 +1593,22 @@ setup(void) #endif } +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + EBARF("can't install SIGCHLD handler"); + while (0 < waitpid(-1, NULL, WNOHANG)) + ; +} + void spawn(const Arg *arg) { if (fork() == 0) { setsid(); execvp(((char **)arg->v)[0], (char **)arg->v); - fprintf(stderr, "dwl: execvp %s", ((char **)arg->v)[0]); - perror(" failed"); - exit(EXIT_FAILURE); + EBARF("dwl: execvp %s failed", ((char **)arg->v)[0]); } } @@ -1827,14 +1861,12 @@ void updatewindowtype(Client *c) { size_t i; - - if (c->type != XDGShell) - for (i = 0; i < c->surface.xwayland->window_type_len; i++) - if (c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeDialog] || - c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || - c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || - c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) - c->isfloating = 1; + for (i = 0; i < c->surface.xwayland->window_type_len; i++) + if (c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeDialog] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) + c->isfloating = 1; } void @@ -1859,65 +1891,53 @@ xwaylandready(struct wl_listener *listener, void *data) xcb_disconnect(xc); } + +Client * +xytoindependent(double x, double y) +{ + /* Find the topmost visible independent at point (x, y). + * For independents, the most recently created can be used as the "top". + * We rely on the X11 convention of unmapping unmanaged when the "owning" + * client loses focus, which ensures that unmanaged are only visible on + * the current tag. */ + Client *c; + struct wlr_box geom; + wl_list_for_each_reverse(c, &independents, link) { + geom.x = c->surface.xwayland->x; + geom.y = c->surface.xwayland->y; + geom.width = c->surface.xwayland->width; + geom.height = c->surface.xwayland->height; + if (wlr_box_contains_point(&geom, x, y)) + return c; + } + return NULL; +} #endif int main(int argc, char *argv[]) { char *startup_cmd = NULL; - enum wlr_log_importance loglevel = WLR_ERROR; - int c; - while ((c = getopt(argc, argv, "qvds:h")) != -1) { - switch (c) { - case 'q': - loglevel = WLR_SILENT; - break; - case 'v': - loglevel = WLR_INFO; - break; - case 'd': - loglevel = WLR_DEBUG; - break; - case 's': + + while ((c = getopt(argc, argv, "s:h")) != -1) { + if (c == 's') startup_cmd = optarg; - break; - default: + else goto usage; - } } if (optind < argc) goto usage; - wlr_log_init(loglevel, NULL); // Wayland requires XDG_RUNTIME_DIR for creating its communications // socket - if (!getenv("XDG_RUNTIME_DIR")) { - fprintf(stderr, "XDG_RUNTIME_DIR must be set\n"); - exit(EXIT_FAILURE); - } - - /* The Wayland display is managed by libwayland. It handles accepting - * clients from the Unix socket, manging Wayland globals, and so on. */ - dpy = wl_display_create(); - + if (!getenv("XDG_RUNTIME_DIR")) + BARF("XDG_RUNTIME_DIR must be set"); setup(); run(startup_cmd); - - /* Once wl_display_run returns, we shut down the server. */ -#ifdef XWAYLAND - wlr_xwayland_destroy(xwayland); -#endif - wl_display_destroy_clients(dpy); - wl_display_destroy(dpy); - - wlr_xcursor_manager_destroy(cursor_mgr); - wlr_cursor_destroy(cursor); - wlr_output_layout_destroy(output_layout); - + cleanup(); return EXIT_SUCCESS; usage: - printf("Usage: %s [-qvd] [-s startup command]\n", argv[0]); - return EXIT_FAILURE; + BARF("Usage: %s [-s startup command]", argv[0]); }