Skip to content
1 change: 1 addition & 0 deletions include/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct config {
ANCHOR_RIGHT,
ANCHOR_BOTTOMLEFT,
ANCHOR_BOTTOMRIGHT,
ANCHOR_SEPARATE,
ANCHOR_NONE,
} ninb_anchor;

Expand Down
17 changes: 16 additions & 1 deletion include/server/ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ struct server_ui {
struct wl_subsurface *subsurface;
} tree;

struct {
struct wl_surface *surface;
struct xdg_toplevel *top_level;
struct xdg_surface *xdg_surface;
bool is_focused;
bool window_opened;
int32_t width;
int32_t height;
} ninbot;

struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_toplevel_decoration_v1 *xdg_decoration;
Expand All @@ -48,7 +58,7 @@ struct server_ui {
struct server_ui_config {
struct wl_buffer *background;
bool tearing;

bool xwayland_toplevel;
uint32_t ninb_opacity;
};

Expand Down Expand Up @@ -101,6 +111,8 @@ void server_ui_destroy(struct server_ui *ui);
void server_ui_hide(struct server_ui *ui);
void server_ui_set_fullscreen(struct server_ui *ui, bool fullscreen);
void server_ui_show(struct server_ui *ui);
void xwayland_toplevel_show(struct server_ui *ui);
void xwayland_toplevel_hide(struct server_ui *ui);
void server_ui_use_config(struct server_ui *ui, struct server_ui_config *config);

struct server_ui_config *server_ui_config_create(struct server_ui *ui, struct config *cfg);
Expand All @@ -117,6 +129,9 @@ void server_view_set_pos(struct server_view *view, uint32_t x, uint32_t y);
void server_view_set_size(struct server_view *view, uint32_t width, uint32_t height);
void server_view_set_visible(struct server_view *view, bool visible);

void xwayland_server_view_attach(struct server_view *view);
void xwayland_server_view_detach(struct server_view *view);

struct server_view *server_view_create(struct server_ui *ui, struct server_surface *surface,
const struct server_view_impl *impl, void *impl_data);
void server_view_destroy(struct server_view *view);
Expand Down
1 change: 1 addition & 0 deletions waywall/config/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ process_config_theme(struct config *cfg) {
[ANCHOR_RIGHT] = "right",
[ANCHOR_BOTTOMLEFT] = "bottomleft",
[ANCHOR_BOTTOMRIGHT] = "bottomright",
[ANCHOR_SEPARATE] = "separate",
};

for (size_t i = 0; i < STATIC_ARRLEN(anchor_names); i++) {
Expand Down
139 changes: 134 additions & 5 deletions waywall/server/ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ layout_floating(struct server_view *view) {
wl_fixed_from_int(-1), wl_fixed_from_int(-1));
wp_viewport_set_destination(view->viewport, -1, -1);

wl_surface_commit(view->ui->tree.surface);
if (view->ui->ninbot.surface == NULL) {
wl_surface_commit(view->ui->tree.surface);
} else {
wl_surface_commit(view->ui->ninbot.surface);
}
}

static void
Expand Down Expand Up @@ -140,6 +144,49 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.wm_capabilities = on_xdg_toplevel_wm_capabilities,
};

void ninbot_toplevel_configure_handler
(
void *data,
struct xdg_toplevel *xdg_toplevel,
int32_t width,
int32_t height,
struct wl_array *states
) {
struct server_ui *ui = data;
ui->ninbot.width = width;
ui->ninbot.height = height;
}

void xwayland_toplevel_hide(struct server_ui *ui) {
wl_surface_attach(ui->ninbot.surface, NULL, 0, 0);
wl_surface_commit(ui->ninbot.surface);
ui->ninbot.window_opened = false;
}


void ninbot_toplevel_close_handler
(
void *data,
struct xdg_toplevel *xdg_toplevel
) {
struct server_ui *ui = data;

struct server_view *view;
struct server_view *tmp_view;
wl_list_for_each_safe(view, tmp_view, &ui->views, link) {
// Can we nest native Wayland apps except Minecraft? If so this is problematic
if (strcmp(view->impl->name, "xwayland") == 0) {
wl_resource_destroy(view->surface->resource);
}
}
xwayland_toplevel_hide(ui);
}

static const struct xdg_toplevel_listener ninbot_toplevel_listener = {
.configure = ninbot_toplevel_configure_handler,
.close = ninbot_toplevel_close_handler
};

static void
on_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
struct server_ui *ui = data;
Expand All @@ -163,6 +210,19 @@ static const struct xdg_surface_listener xdg_surface_listener = {
.configure = on_xdg_surface_configure,
};

static void xdg_surface_configure_handler (void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
struct server_ui *ui = data;
if (ui->ninbot.width != 0 && ui->ninbot.height != 0) {
xdg_surface_set_window_geometry(xdg_surface, 0, 0, ui->ninbot.width, ui->ninbot.height);
}
xdg_surface_ack_configure(xdg_surface, serial);
wl_surface_commit(ui->ninbot.surface);
}

static const struct xdg_surface_listener ninbot_surface_listener = {
.configure = xdg_surface_configure_handler,
};

static void
on_view_surface_commit(struct wl_listener *listener, void *data) {
struct server_view *view = wl_container_of(listener, view, on_surface_commit);
Expand All @@ -188,6 +248,29 @@ on_view_surface_commit(struct wl_listener *listener, void *data) {
}
}

void xwayland_toplevel_create(struct server_ui *ui) {
ui->ninbot.surface = wl_compositor_create_surface(ui->server->backend->compositor);
check_alloc(ui->ninbot.surface);

ui->ninbot.xdg_surface = xdg_wm_base_get_xdg_surface(ui->server->backend->xdg_wm_base, ui->ninbot.surface);
check_alloc(ui->ninbot.xdg_surface);
xdg_surface_add_listener(ui->ninbot.xdg_surface, &ninbot_surface_listener, ui);

ui->ninbot.top_level = xdg_surface_get_toplevel(ui->ninbot.xdg_surface);
check_alloc(ui->ninbot.top_level);
xdg_toplevel_add_listener(ui->ninbot.top_level, &ninbot_toplevel_listener, ui);
}

void xwayland_toplevel_destroy(struct server_ui *ui) {
xdg_toplevel_destroy(ui->ninbot.top_level);
xdg_surface_destroy(ui->ninbot.xdg_surface);
wl_surface_destroy(ui->ninbot.surface);
ui->ninbot.top_level = NULL;
ui->ninbot.xdg_surface = NULL;
ui->ninbot.surface = NULL;
ui->ninbot.window_opened = false;
}

struct server_ui *
server_ui_create(struct server *server, struct config *cfg) {
struct server_ui *ui = zalloc(1, sizeof(*ui));
Expand Down Expand Up @@ -289,6 +372,7 @@ server_ui_destroy(struct server_ui *ui) {
zxdg_toplevel_decoration_v1_destroy(ui->xdg_decoration);
}

xwayland_toplevel_destroy(ui);
xdg_toplevel_destroy(ui->xdg_toplevel);
xdg_surface_destroy(ui->xdg_surface);
wl_subsurface_destroy(ui->tree.subsurface);
Expand All @@ -310,6 +394,7 @@ server_ui_hide(struct server_ui *ui) {

ui->mapped = false;
wl_signal_emit_mutable(&ui->server->events.map_status, &ui->mapped);
xwayland_toplevel_hide(ui);
}

void
Expand Down Expand Up @@ -349,6 +434,22 @@ server_ui_show(struct server_ui *ui) {
wl_signal_emit_mutable(&ui->server->events.map_status, &ui->mapped);
}

void xwayland_toplevel_show(struct server_ui *ui) {
struct wl_display *display = ui->server->backend->display;

wl_surface_attach(ui->ninbot.surface, NULL, 0, 0);
wl_surface_commit(ui->ninbot.surface);
wl_display_roundtrip(display);

wl_surface_attach(ui->ninbot.surface, ui->config->background, 0, 0);
wl_surface_commit(ui->ninbot.surface);
wl_display_roundtrip(display);

xdg_toplevel_set_title(ui->ninbot.top_level, "NinjabrainBot Wrapper");
xdg_toplevel_set_app_id(ui->ninbot.top_level, "NinjabrainBot Wrapper");
ui->ninbot.window_opened = true;
}

void
server_ui_use_config(struct server_ui *ui, struct server_ui_config *config) {
if (ui->config) {
Expand All @@ -367,6 +468,12 @@ server_ui_use_config(struct server_ui *ui, struct server_ui_config *config) {
wl_surface_damage_buffer(ui->root.surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(ui->root.surface);
}

if (config->xwayland_toplevel && ui->ninbot.surface == NULL) {
xwayland_toplevel_create(ui);
}else if (ui->ninbot.surface != NULL && !config->xwayland_toplevel) {
xwayland_toplevel_destroy(ui);
}
}

struct server_ui_config *
Expand All @@ -386,6 +493,12 @@ server_ui_config_create(struct server_ui *ui, struct config *cfg) {
return NULL;
}

if (cfg->theme.ninb_anchor == ANCHOR_SEPARATE) {
config->xwayland_toplevel = true;
} else {
config->xwayland_toplevel = false;
}

config->tearing = cfg->experimental.tearing;

config->ninb_opacity = cfg->theme.ninb_opacity * UINT32_MAX;
Expand Down Expand Up @@ -452,10 +565,11 @@ server_view_commit(struct server_view *view) {
if (visibility_changed && view->current.visible) {
ww_assert(!view->subsurface);

view->subsurface =
wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor,
view->surface->remote, view->ui->tree.surface);
check_alloc(view->subsurface);
if (view->current.centered || view->ui->ninbot.surface == NULL) {
xwayland_server_view_detach(view);
} else {
xwayland_server_view_attach(view);
}

wl_subsurface_set_desync(view->subsurface);
} else if (visibility_changed && !view->current.visible) {
Expand All @@ -476,6 +590,21 @@ server_view_commit(struct server_view *view) {
view_state_reset(&view->pending);
}

void
xwayland_server_view_attach(struct server_view *view) {
view->subsurface =
wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->ninbot.surface);
check_alloc(view->subsurface);
wl_surface_commit(view->ui->ninbot.surface);
}

void
xwayland_server_view_detach(struct server_view *view) {
view->subsurface =
wl_subcompositor_get_subsurface(view->ui->server->backend->subcompositor, view->surface->remote, view->ui->tree.surface);
check_alloc(view->subsurface);
}

void
server_view_refresh(struct server_view *view) {
if (view->subsurface && view->current.centered) {
Expand Down
7 changes: 6 additions & 1 deletion waywall/server/wl_seat.c
Original file line number Diff line number Diff line change
Expand Up @@ -744,9 +744,14 @@ on_pointer_enter(void *data, struct wl_pointer *wl, uint32_t serial, struct wl_s
wl_fixed_t surface_x, wl_fixed_t surface_y) {
struct server_seat *seat = data;
seat->last_serial = serial;
seat->server->ui->ninbot.is_focused = false;

if (surface == seat->server->ui->ninbot.surface) {
seat->server->ui->ninbot.is_focused = true;
return;
}

if (surface != seat->server->ui->root.surface) {
ww_log(LOG_WARN, "received wl_pointer.enter for unknown surface");
return;
}

Expand Down
39 changes: 38 additions & 1 deletion waywall/wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ floating_update_anchored(struct wrap *wrap) {
x = 0;
y = wrap->height - win_height;
break;
case ANCHOR_SEPARATE:
x = 0;
y = 0;
server_view_set_visible(fview->view, true);
break;
case ANCHOR_BOTTOMRIGHT:
x = wrap->width - win_width;
y = wrap->height - win_height;
Expand Down Expand Up @@ -217,6 +222,12 @@ floating_view_create(struct wrap *wrap, struct server_view *view) {

server_view_set_centered(view, false);
floating_update_anchored(wrap);
if (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE) {
if (!wrap->server->ui->ninbot.window_opened) {
xwayland_toplevel_show(wrap->server->ui);
}
floating_set_visible(wrap, true);
}
}
}

Expand Down Expand Up @@ -251,6 +262,14 @@ floating_view_destroy(struct wrap *wrap, struct server_view *view) {
floating_find_anchored(wrap);
floating_update_anchored(wrap);
}

if (wl_list_length(&wrap->floating.views) == 0 && view->ui->ninbot.window_opened) {
xwayland_toplevel_hide(view->ui);
if (view->ui->ninbot.is_focused) {
server_set_input_focus(wrap->server, wrap->view);
view->ui->ninbot.is_focused = false;
}
}
return;
}

Expand Down Expand Up @@ -448,7 +467,7 @@ on_button(void *data, uint32_t button, bool pressed) {
// Check to see if the input focus should be changed to a new window. If the user did not click
// on any floating window, then input focus should be given back to the Minecraft instance.
struct floating_view *fview = floating_view_at(wrap, wrap->input.x, wrap->input.y);
if (!fview) {
if (!fview || (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE && !wrap->server->ui->ninbot.is_focused)) {
ww_assert(wrap->view);
server_set_input_focus(wrap->server, wrap->view);
return false;
Expand Down Expand Up @@ -632,6 +651,24 @@ wrap_set_config(struct wrap *wrap, struct config *cfg) {
config_vm_set_wrap(cfg->vm, wrap);

wrap->cfg = cfg;

if (wrap->cfg->theme.ninb_anchor == ANCHOR_SEPARATE) {
struct floating_view *fview;
wl_list_for_each(fview, &wrap->floating.views, link) {
wl_subsurface_destroy(fview->view->subsurface);
xwayland_server_view_attach(fview->view);
}
if (wl_list_length(&wrap->floating.views) > 0) {
xwayland_toplevel_show(wrap->server->ui);
}
} else {
struct floating_view *fview;
wl_list_for_each(fview, &wrap->floating.views, link) {
wl_subsurface_destroy(fview->view->subsurface);
xwayland_server_view_detach(fview->view);
}
}

if (wrap->cfg->theme.ninb_anchor == ANCHOR_NONE) {
// If anchoring has been disabled, ensure there is no anchored view.
if (wrap->floating.anchored) {
Expand Down