diff options
-rw-r--r-- | LICENSE | 2 | ||||
-rw-r--r-- | README.md | 85 | ||||
-rw-r--r-- | examples/support/config.c | 5 | ||||
-rw-r--r-- | examples/support/ini.c | 2 | ||||
-rw-r--r-- | include/wlr/types/wlr_output.h | 2 | ||||
-rw-r--r-- | include/wlr/types/wlr_xdg_shell.h | 10 | ||||
-rw-r--r-- | include/wlr/types/wlr_xdg_shell_v6.h | 10 | ||||
-rw-r--r-- | include/wlr/xwayland.h | 13 | ||||
-rw-r--r-- | protocol/meson.build | 9 | ||||
-rw-r--r-- | rootston/config.c | 5 | ||||
-rw-r--r-- | rootston/ini.c | 2 | ||||
-rw-r--r-- | rootston/xwayland.c | 48 | ||||
-rw-r--r-- | xwayland/xwm.c | 82 |
13 files changed, 184 insertions, 91 deletions
@@ -1,4 +1,4 @@ -Copyright (c) 2017 Drew DeVault +Copyright (c) 2017, 2018 Drew DeVault Copyright (c) 2014 Jari Vetoniemi Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -1,49 +1,100 @@ # wlroots -Pluggable, composable modules for building a -[Wayland](http://wayland.freedesktop.org/) compositor. +Pluggable, composable, unopinionated modules for building a +[Wayland](http://wayland.freedesktop.org/) compositor; or about 40,000 lines of +code you were going to write anyway. -This is a WIP: [status](https://github.com/swaywm/wlroots/issues/9) +- wlroots provides backends that abstract the underlying display and input + hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends, + plus any custom backends you choose to write, which can all be created or + destroyed at runtime and used in concert with each other. +- wlroots provides unopinionated, mostly standalone implementations of many + Wayland interfaces, both from wayland.xml and various protocol extensions. + We also promote the standardization of portable extensions across + many compositors. +- wlroots provides several powerful, standalone, and optional tools that + implement components common to many compositors, such as the arrangement of + outputs in physical space. +- wlroots provides an Xwayland abstraction that allows you to have excellent + Xwayland support without worrying about writing your own X11 window manager + on top of writing your compositor. +- wlroots provides a renderer abstraction that simple compositors can use to + avoid writing GL code directly, but which steps out of the way when your + needs demand custom rendering code. -## Contributing +wlroots implements a huge variety of Wayland compositor features and implements +them *right*, so you can focus on the features that make your compositor +unique. By using wlroots, you get high performance, excellent hardware +compatability, broad support for many wayland interfaces, and comfortable +development tools - or any subset of these features you like, because all of +them work independently of one another and freely compose with anything you want +to implement yourself. + +**Status**: prior to 1.0 the API is not stable, but we've done most of the work +and various projects are using wlroots to build Wayland compositors with. -See [CONTRIBUTING.md](https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md) +wlroots is developed under the direction of the +[sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries +[are available](https://github.com/swaywm) for using it with your favorite +programming language. ## Building Install dependencies: +* meson * wayland * wayland-protocols * EGL * GLESv2 -* DRM +* libdrm * GBM * libinput +* xkbcommon * udev * pixman * systemd (optional, for logind support) * elogind (optional, for logind support on systems without systemd) * libcap (optional, for capability support) -* asciidoc (optional, for man pages) + +If you choose to enable X11 support: + +* xkb +* xkb-composite +* xkb-xfixes +* xkb-image +* xkb-render +* x11-xcb +* xcb-errors (optional, for improved error reporting) +* x11-icccm (optional, for improved Xwayland introspection) +* xkb-xcb (optional, for improved keyboard handling on the X11 backend) Run these commands: meson build ninja -C build -(On FreeBSD, you need to pass an extra flag to prevent a linking error: `meson build -D b_lundef=false`) +On FreeBSD, you need to pass an extra flag to prevent a linking error: +`meson build -D b_lundef=false`. -## Running the Reference Compositor +Install like so: -wlroots comes with a reference compositor called rootston that demonstrates the -features of the library. + sudo ninja -C build install -After building, run rootston from a terminal or VT with: +## Running the test compositor - ./build/rootston/rootston +wlroots comes with a test compositor called rootston, which demonstrates the +features of the library and is used as a testbed for the development of the +library. It may also be useful as a reference for understanding how to use +various wlroots features. + +If you followed the build instructions above the rootston executable can be +found at `./build/rootston/rootston`. To use it, refer to the example config at +[./rootston/rootston.ini.example](https://github.com/swaywm/wlroots/blob/master/rootston/rootston.ini.example) +and place a config file of your own at `rootston.ini` in the working directory +(or in an arbitrary location via `rootston -C`). Other options are available, +refer to `rootston -h`. + +## Contributing -Now you can run windows in the compositor from the command line or by -configuring bindings in your -[`rootston.ini`](https://github.com/swaywm/wlroots/blob/master/rootston/rootston.ini.example) -file. +See [CONTRIBUTING.md](https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md). diff --git a/examples/support/config.c b/examples/support/config.c index f0efa594..319be31a 100644 --- a/examples/support/config.c +++ b/examples/support/config.c @@ -202,7 +202,10 @@ struct example_config *parse_args(int argc, char *argv[]) { char cwd[MAXPATHLEN]; if (getcwd(cwd, sizeof(cwd)) != NULL) { char buf[MAXPATHLEN]; - snprintf(buf, MAXPATHLEN, "%s/%s", cwd, "wlr-example.ini"); + if (snprintf(buf, MAXPATHLEN, "%s/%s", cwd, "wlr-example.ini") >= MAXPATHLEN) { + wlr_log(L_ERROR, "config path too long"); + exit(1); + } config->config_path = strdup(buf); } else { wlr_log(L_ERROR, "could not get cwd"); diff --git a/examples/support/ini.c b/examples/support/ini.c index 6be9c44a..6bc1eae6 100644 --- a/examples/support/ini.c +++ b/examples/support/ini.c @@ -64,7 +64,7 @@ static char* find_chars_or_comment(const char* s, const char* chars) /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ static char* strncpy0(char* dest, const char* src, size_t size) { - strncpy(dest, src, size); + strncpy(dest, src, size-1); dest[size - 1] = '\0'; return dest; } diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index cc03452d..cef3fc5d 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -55,7 +55,7 @@ struct wlr_output { struct wl_global *wl_global; struct wl_list wl_resources; - char name[16]; + char name[24]; char make[48]; char model[16]; char serial[16]; diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index faf398a7..5046339a 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -102,6 +102,16 @@ struct wlr_xdg_surface_configure { struct wlr_xdg_toplevel_state *toplevel_state; }; +/** + * An xdg-surface is a user interface element requiring management by the + * compositor. An xdg-surface alone isn't useful, a role should be assigned to + * it in order to map it. + * + * When a surface has a role and is ready to be displayed, the `map` event is + * emitted. When a surface should no longer be displayed, the `unmap` event is + * emitted. The `unmap` event is guaranted to be emitted before the `destroy` + * event if the view is destroyed when mapped. + */ struct wlr_xdg_surface { struct wlr_xdg_client *client; struct wl_resource *resource; diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 337d96ab..a315c4d9 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -90,6 +90,16 @@ struct wlr_xdg_toplevel_v6_state { uint32_t min_width, min_height; }; +/** + * An xdg-surface is a user interface element requiring management by the + * compositor. An xdg-surface alone isn't useful, a role should be assigned to + * it in order to map it. + * + * When a surface has a role and is ready to be displayed, the `map` event is + * emitted. When a surface should no longer be displayed, the `unmap` event is + * emitted. The `unmap` event is guaranted to be emitted before the `destroy` + * event if the view is destroyed when mapped. + */ struct wlr_xdg_toplevel_v6 { struct wl_resource *resource; struct wlr_xdg_surface_v6 *base; diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 1fad54d1..11aedc37 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -75,6 +75,15 @@ struct wlr_xwayland_surface_size_hints { uint32_t win_gravity; }; +/** + * An Xwayland user interface component. It has an absolute position in + * layout-local coordinates. + * + * When a surface is ready to be displayed, the `map` event is emitted. When a + * surface should no longer be displayed, the `unmap` event is emitted. The + * `unmap` event is guaranted to be emitted before the `destroy` event if the + * view is destroyed when mapped. + */ struct wlr_xwayland_surface { xcb_window_t window_id; struct wlr_xwm *xwm; @@ -89,7 +98,6 @@ struct wlr_xwayland_surface { uint16_t saved_width, saved_height; bool override_redirect; bool mapped; - bool added; char *title; char *class; @@ -116,8 +124,7 @@ struct wlr_xwayland_surface { // _NET_WM_STATE bool fullscreen; - bool maximized_vert; - bool maximized_horz; + bool maximized_vert, maximized_horz; bool has_alpha; diff --git a/protocol/meson.build b/protocol/meson.build index bdbde96a..4730baf7 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -8,10 +8,17 @@ wayland_scanner_server = generator( arguments: ['server-header', '@INPUT@', '@OUTPUT@'], ) +# should check wayland_scanner's version, but it is hard to get +if wayland_server.version().version_compare('>=1.14.91') + code_type = 'private-code' +else + code_type = 'code' +endif + wayland_scanner_code = generator( wayland_scanner, output: '@BASENAME@-protocol.c', - arguments: ['code', '@INPUT@', '@OUTPUT@'], + arguments: [code_type, '@INPUT@', '@OUTPUT@'], ) wayland_scanner_client = generator( diff --git a/rootston/config.c b/rootston/config.c index 0883f6d4..67bf83e9 100644 --- a/rootston/config.c +++ b/rootston/config.c @@ -418,7 +418,10 @@ struct roots_config *roots_config_create_from_args(int argc, char *argv[]) { char cwd[MAXPATHLEN]; if (getcwd(cwd, sizeof(cwd)) != NULL) { char buf[MAXPATHLEN]; - snprintf(buf, MAXPATHLEN, "%s/%s", cwd, "rootston.ini"); + if (snprintf(buf, MAXPATHLEN, "%s/%s", cwd, "rootston.ini") >= MAXPATHLEN) { + wlr_log(L_ERROR, "config path too long"); + exit(1); + } config->config_path = strdup(buf); } else { wlr_log(L_ERROR, "could not get cwd"); diff --git a/rootston/ini.c b/rootston/ini.c index 56cc9ea6..f515dd38 100644 --- a/rootston/ini.c +++ b/rootston/ini.c @@ -64,7 +64,7 @@ static char* find_chars_or_comment(const char* s, const char* chars) /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ static char* strncpy0(char* dest, const char* src, size_t size) { - strncpy(dest, src, size); + strncpy(dest, src, size-1); dest[size - 1] = '\0'; return dest; } diff --git a/rootston/xwayland.c b/rootston/xwayland.c index 27a27b65..b7dbab54 100644 --- a/rootston/xwayland.c +++ b/rootston/xwayland.c @@ -228,19 +228,31 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { static void handle_map(struct wl_listener *listener, void *data) { struct roots_xwayland_surface *roots_surface = wl_container_of(listener, roots_surface, map); - struct wlr_xwayland_surface *xsurface = data; + struct wlr_xwayland_surface *surface = data; struct roots_view *view = roots_surface->view; - view->x = xsurface->x; - view->y = xsurface->y; - view->width = xsurface->surface->current->width; - view->height = xsurface->surface->current->height; - - view_map(view, xsurface->surface); + view->x = surface->x; + view->y = surface->y; + view->width = surface->surface->current->width; + view->height = surface->surface->current->height; roots_surface->surface_commit.notify = handle_surface_commit; - wl_signal_add(&xsurface->surface->events.commit, + wl_signal_add(&surface->surface->events.commit, &roots_surface->surface_commit); + + view_map(view, surface->surface); + + if (!surface->override_redirect) { + if (surface->decorations == WLR_XWAYLAND_SURFACE_DECORATIONS_ALL) { + view->decorated = true; + view->border_width = 4; + view->titlebar_height = 12; + } + + view_setup(view); + } else { + view_initial_focus(view); + } } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -289,10 +301,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { wl_signal_add(&surface->events.request_fullscreen, &roots_surface->request_fullscreen); - roots_surface->surface_commit.notify = handle_surface_commit; - wl_signal_add(&surface->surface->events.commit, - &roots_surface->surface_commit); - struct roots_view *view = view_create(desktop); if (view == NULL) { free(roots_surface); @@ -301,8 +309,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { view->type = ROOTS_XWAYLAND_VIEW; view->x = (double)surface->x; view->y = (double)surface->y; - view->width = surface->surface->current->width; - view->height = surface->surface->current->height; view->xwayland_surface = surface; view->roots_xwayland_surface = roots_surface; @@ -315,18 +321,4 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { view->close = close; view->destroy = destroy; roots_surface->view = view; - - view_map(view, surface->surface); - - if (!surface->override_redirect) { - if (surface->decorations == WLR_XWAYLAND_SURFACE_DECORATIONS_ALL) { - view->decorated = true; - view->border_width = 4; - view->titlebar_height = 12; - } - - view_setup(view); - } else { - view_initial_focus(view); - } } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index bad40117..77dc0797 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -109,7 +109,7 @@ static int xwayland_surface_handle_ping_timeout(void *data) { return 1; } -static struct wlr_xwayland_surface *wlr_xwayland_surface_create( +static struct wlr_xwayland_surface *xwayland_surface_create( struct wlr_xwm *xwm, xcb_window_t window_id, int16_t x, int16_t y, uint16_t width, uint16_t height, bool override_redirect) { struct wlr_xwayland_surface *surface = @@ -171,6 +171,8 @@ static struct wlr_xwayland_surface *wlr_xwayland_surface_create( return NULL; } + wlr_signal_emit_safe(&xwm->xwayland->events.new_surface, surface); + return surface; } @@ -273,8 +275,12 @@ static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { i, property); } +static void xsurface_unmap(struct wlr_xwayland_surface *surface); + static void wlr_xwayland_surface_destroy( struct wlr_xwayland_surface *xsurface) { + xsurface_unmap(xsurface); + wlr_signal_emit_safe(&xsurface->events.destroy, xsurface); if (xsurface == xsurface->xwm->focus_surface) { @@ -293,6 +299,8 @@ static void wlr_xwayland_surface_destroy( wlr_surface_set_role_committed(xsurface->surface, NULL, NULL); } + wl_event_source_remove(xsurface->ping_timer); + free(xsurface->title); free(xsurface->class); free(xsurface->instance); @@ -613,27 +621,22 @@ static void read_surface_property(struct wlr_xwm *xwm, static void handle_surface_commit(struct wlr_surface *wlr_surface, void *role_data) { - struct wlr_xwayland_surface *xsurface = role_data; + struct wlr_xwayland_surface *surface = role_data; - if (!xsurface->added && - wlr_surface_has_buffer(xsurface->surface) && - xsurface->mapped) { - wlr_signal_emit_safe(&xsurface->xwm->xwayland->events.new_surface, xsurface); - xsurface->added = true; + if (!surface->mapped && wlr_surface_has_buffer(surface->surface)) { + wlr_signal_emit_safe(&surface->events.map, surface); + surface->mapped = true; } } static void handle_surface_destroy(struct wl_listener *listener, void *data) { - struct wlr_xwayland_surface *xsurface = - wl_container_of(listener, xsurface, surface_destroy); - - xsurface->surface = NULL; - // TODO destroy xwayland surface? + struct wlr_xwayland_surface *surface = + wl_container_of(listener, surface, surface_destroy); + xsurface_unmap(surface); } static void xwm_map_shell_surface(struct wlr_xwm *xwm, - struct wlr_xwayland_surface *xsurface, - struct wlr_surface *surface) { + struct wlr_xwayland_surface *xsurface, struct wlr_surface *surface) { xsurface->surface = surface; // read all surface properties @@ -660,15 +663,40 @@ static void xwm_map_shell_surface(struct wlr_xwm *xwm, xsurface->surface_destroy.notify = handle_surface_destroy; wl_signal_add(&surface->events.destroy, &xsurface->surface_destroy); +} - xsurface->mapped = true; - wlr_signal_emit_safe(&xsurface->events.map, xsurface); +static void xsurface_unmap(struct wlr_xwayland_surface *surface) { + if (surface->mapped) { + surface->mapped = false; + wlr_signal_emit_safe(&surface->events.unmap, surface); + } + + if (surface->surface_id) { + // Make sure we're not on the unpaired surface list or we + // could be assigned a surface during surface creation that + // was mapped before this unmap request. + wl_list_remove(&surface->unpaired_link); + surface->surface_id = 0; + } + + if (surface->surface) { + wlr_surface_set_role_committed(surface->surface, NULL, NULL); + wl_list_remove(&surface->surface_destroy.link); + surface->surface = NULL; + } } static void xwm_handle_create_notify(struct wlr_xwm *xwm, xcb_create_notify_event_t *ev) { wlr_log(L_DEBUG, "XCB_CREATE_NOTIFY (%u)", ev->window); - wlr_xwayland_surface_create(xwm, ev->window, ev->x, ev->y, + + if (ev->window == xwm->window || + ev->window == xwm->selection_window || + ev->window == xwm->dnd_window) { + return; + } + + xwayland_surface_create(xwm, ev->window, ev->x, ev->y, ev->width, ev->height, ev->override_redirect); } @@ -778,25 +806,7 @@ static void xwm_handle_unmap_notify(struct wlr_xwm *xwm, return; } - if (xsurface->mapped) { - xsurface->mapped = false; - wlr_signal_emit_safe(&xsurface->events.unmap, xsurface); - } - - if (xsurface->surface_id) { - // Make sure we're not on the unpaired surface list or we - // could be assigned a surface during surface creation that - // was mapped before this unmap request. - wl_list_remove(&xsurface->unpaired_link); - xsurface->surface_id = 0; - } - - if (xsurface->surface) { - wlr_surface_set_role_committed(xsurface->surface, NULL, NULL); - wl_list_remove(&xsurface->surface_destroy.link); - } - xsurface->surface = NULL; - + xsurface_unmap(xsurface); xsurface_set_wm_state(xsurface, ICCCM_WITHDRAWN_STATE); } |