WP = `pkg-config --variable=pkgdatadir wayland-protocols`
WS = `pkg-config --variable=wayland_scanner wayland-scanner`
+OBJS = main.o xdg.o view.o xwl.o server.o output.o dbg.o deco.o
+
all: labwc
-labwc: xdg-shell-protocol.o main.o xdg.o view.o xwl.o server.o output.o dbg.o
+labwc: xdg-shell-protocol.o $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
xdg-shell-protocol.h:
labwc is a wayland compositor based on wlroots
-[https://imgur.com/rb2hJh3](https://imgur.com/rb2hJh3)
-
## Dependencies
- wlroots
Alt+Escape Exit labwc
Alt+F2 Cycle between windows
Alt+F3 Launch dmenu
-Alt+F6 Move window
Alt+F12 Print all views (helpful if run from X11)
```
[sway](https://github.com/swaywm/sway) - 37k LOC
-[rootston](https://github.com/swaywm/wlroots/tree/master/rootston) - 7k LOC
+[rootston]() - 7k LOC
[openbox](https://github.com/danakj/openbox) - 53k LOC
--- /dev/null
+#include "labwc.h"
+
+struct wlr_box deco_max_extents(struct view *view)
+{
+ struct wlr_box box = {
+ .x = view->x - XWL_WINDOW_BORDER,
+ .y = view->y - XWL_TITLEBAR_HEIGHT - XWL_WINDOW_BORDER,
+ .width = view->surface->current.width + 2 * XWL_WINDOW_BORDER,
+ .height = view->surface->current.height + XWL_TITLEBAR_HEIGHT +
+ 2 * XWL_WINDOW_BORDER,
+ };
+ return box;
+}
+
+struct wlr_box deco_box(struct view *view, enum deco_part deco_part)
+{
+ struct wlr_box box = { .x = 0, .y = 0, .width = 0, .height = 0 };
+ if (!view)
+ return;
+ switch (deco_part) {
+ case LAB_DECO_PART_TOP:
+ box.x = view->x - XWL_WINDOW_BORDER;
+ box.y = view->y - XWL_TITLEBAR_HEIGHT - XWL_WINDOW_BORDER;
+ box.width = view->surface->current.width + 2 * XWL_WINDOW_BORDER;
+ box.height = XWL_TITLEBAR_HEIGHT + XWL_WINDOW_BORDER;
+ break;
+ }
+ return box;
+}
+
+enum deco_part deco_at(struct view *view, double lx, double ly)
+{
+ struct wlr_box box;
+ box = deco_box(view, LAB_DECO_PART_TOP);
+ if (wlr_box_contains_point(&box, lx, ly))
+ return LAB_DECO_PART_TOP;
+ return LAB_DECO_NONE;
+}
+
#define XWL_TITLEBAR_HEIGHT (10)
#define XWL_WINDOW_BORDER (3)
-/* For brevity's sake, struct members are annotated where they are used. */
enum cursor_mode {
TINYWL_CURSOR_PASSTHROUGH,
TINYWL_CURSOR_MOVE,
enum view_type { LAB_XDG_SHELL_VIEW, LAB_XWAYLAND_VIEW };
+enum deco_part {
+ LAB_DECO_NONE,
+ LAB_DECO_PART_TOP
+};
+
struct view {
enum view_type type;
struct wl_list link;
void xwl_surface_configure(struct wl_listener *listener, void *data);
void xwl_surface_new(struct wl_listener *listener, void *data);
+bool view_want_deco(struct view *view);
void view_focus_last_toplevel(struct server *server);
void focus_view(struct view *view, struct wlr_surface *surface);
void view_focus_next_toplevel(struct server *server);
bool is_toplevel(struct view *view);
struct view *desktop_view_at(struct server *server, double lx, double ly,
struct wlr_surface **surface, double *sx,
- double *sy);
+ double *sy, int *view_area);
/* TODO: try to refactor to remove from header file */
struct view *first_toplevel(struct server *server);
void dbg_show_views(struct server *server);
+struct wlr_box deco_max_extents(struct view *view);
+struct wlr_box deco_box(struct view *view, enum deco_part deco_part);
+enum deco_part deco_at(struct view *view, double lx, double ly);
+
#endif /* LABWC_H */
#include "labwc.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 wlr_renderer *renderer;
static void render_decorations(struct wlr_output *output, struct view *view)
{
- if (!view->surface)
- return;
- if (view->type != LAB_XWAYLAND_VIEW)
- return;
- if (!is_toplevel(view))
- return;
- if (view->xwayland_surface->override_redirect)
- return;
- if (view->xwayland_surface->decorations !=
- WLR_XWAYLAND_SURFACE_DECORATIONS_ALL)
+ if (!view_want_deco(view))
return;
- struct wlr_box box = {
- .x = view->x - XWL_WINDOW_BORDER,
- .y = view->y - XWL_TITLEBAR_HEIGHT - XWL_WINDOW_BORDER,
- .width = view->surface->current.width + 2 * XWL_WINDOW_BORDER,
- .height = view->surface->current.height + XWL_TITLEBAR_HEIGHT +
- 2 * XWL_WINDOW_BORDER,
- };
-
+ struct wlr_box box = deco_max_extents(view);
float matrix[9];
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
output->transform_matrix);
-
float color[] = { 0.2, 0.2, 0.7, 0.9 };
wlr_render_quad_with_matrix(view->server->renderer, color, matrix);
+
+ box = deco_box(view, LAB_DECO_PART_TOP);
+ float color2[] = { 0.7, 0.2, 0.2, 0.9 };
+ wlr_render_rect(view->server->renderer, &box, color2, output->transform_matrix);
}
static void render_surface(struct wlr_surface *surface, int sx, int sy,
double sx, sy;
struct wlr_seat *seat = server->seat;
struct wlr_surface *surface = NULL;
+ int view_area;
struct view *view = desktop_view_at(server, server->cursor->x,
server->cursor->y, &surface, &sx,
- &sy);
+ &sy, &view_area);
if (!view) {
/* If there's no view under the cursor, set the cursor image to
* a default. This is what makes the cursor image appear when
wlr_xcursor_manager_set_cursor_image(
server->cursor_mgr, "left_ptr", server->cursor);
}
+ switch (view_area) {
+ case LAB_DECO_PART_TOP:
+ wlr_xcursor_manager_set_cursor_image(
+ server->cursor_mgr, "left_ptr", server->cursor);
+ break;
+ }
if (surface) {
bool focus_changed = seat->pointer_state.focused_surface !=
surface;
event->button, event->state);
double sx, sy;
struct wlr_surface *surface;
+ int view_area;
struct view *view = desktop_view_at(server, server->cursor->x,
server->cursor->y, &surface, &sx,
- &sy);
+ &sy, &view_area);
if (event->state == WLR_BUTTON_RELEASED) {
/* If you released any buttons, we exit interactive move/resize
* mode. */
} else {
/* Focus that client if the button was _pressed_ */
focus_view(view, surface);
+ switch (view_area) {
+ case LAB_DECO_PART_TOP:
+ begin_interactive(view, TINYWL_CURSOR_MOVE, 0);
+ break;
+ }
}
}
#include "labwc.h"
+bool view_want_deco(struct view *view)
+{
+ if (!view->surface)
+ return false;
+ if (view->type != LAB_XWAYLAND_VIEW)
+ return false;
+ if (!is_toplevel(view))
+ return false;
+ if (view->xwayland_surface->override_redirect)
+ return false;
+ if (view->xwayland_surface->decorations !=
+ WLR_XWAYLAND_SURFACE_DECORATIONS_ALL)
+ return false;
+ return true;
+}
+
static struct view *last_toplevel(struct server *server)
{
struct view *view;
struct server *server = view->server;
struct wlr_surface *focused_surface =
server->seat->pointer_state.focused_surface;
- if (view->surface != focused_surface) {
- /* Deny move/resize requests from unfocused clients. */
- return;
- }
server->grabbed_view = view;
server->cursor_mode = mode;
break;
}
- if (_surface != NULL) {
+ if (_surface) {
*sx = _sx;
*sy = _sy;
*surface = _surface;
struct view *desktop_view_at(struct server *server, double lx, double ly,
struct wlr_surface **surface, double *sx,
- double *sy)
+ double *sy, int *view_area)
{
- /* This iterates over all of our surfaces and attempts to find one under
- * the cursor. This relies on server->views being ordered from
- * top-to-bottom. */
+ /*
+ * This iterates over all of our surfaces and attempts to find one under
+ * the cursor. It relies on server->views being ordered from
+ * top-to-bottom.
+ */
+ struct wlr_box border_box = {
+ .x = 0, .y = 0,
+ .width = 0, .height = 0,
+ };
struct view *view;
wl_list_for_each (view, &server->views, link) {
- if (view_at(view, lx, ly, surface, sx, sy)) {
+ if (view_at(view, lx, ly, surface, sx, sy))
+ return view;
+ if (!view_want_deco(view))
+ continue;
+ if (deco_at(view, lx, ly) == LAB_DECO_PART_TOP) {
+ *view_area = LAB_DECO_PART_TOP;
return view;
}
}
return NULL;
}
+