void wlc_view_set_class_ptr(struct wlc_view *view, const char *class_, size_t length) { if (view && !chck_cstrneq(view->data._class.data, class_, length) && chck_string_set_cstr_with_length(&view->data._class, class_, length, true)) { WLC_INTERFACE_EMIT(view.properties_updated, convert_to_wlc_handle(view), WLC_BIT_PROPERTY_CLASS); } }
void wlc_view_set_app_id_ptr(struct wlc_view *view, const char *app_id) { if (view && !chck_cstreq(view->data.app_id.data, app_id) && chck_string_set_cstr(&view->data.app_id, app_id, true)) { WLC_INTERFACE_EMIT(view.properties_updated, convert_to_wlc_handle(view), WLC_BIT_PROPERTY_APP_ID); } }
void wlc_view_set_title_ptr(struct wlc_view *view, const char *title, size_t length) { if (view && !chck_cstrneq(view->data.title.data, title, length) && chck_string_set_cstr_with_length(&view->data.title, title, length, true)) { WLC_INTERFACE_EMIT(view.properties_updated, convert_to_wlc_handle(view), WLC_BIT_PROPERTY_TITLE); } }
bool wlc_view_request_geometry(struct wlc_view *view, const struct wlc_geometry *r) { assert(view && r); wlc_dlog(WLC_DBG_REQUEST, "(%" PRIuWLC ") requested geometry %ux%u+%d,%d", convert_to_wlc_handle(view), r->size.w, r->size.h, r->origin.x, r->origin.y); if (view->state.created && wlc_interface()->view.request.geometry) { WLC_INTERFACE_EMIT(view.request.geometry, convert_to_wlc_handle(view), r); } else { memcpy(&view->pending.geometry, r, sizeof(view->pending.geometry)); } configure_view(view, view->pending.edges, &view->pending.geometry); wlc_dlog(WLC_DBG_REQUEST, "(%" PRIuWLC ") applied geometry %ux%u+%d,%d", convert_to_wlc_handle(view), view->pending.geometry.size.w, view->pending.geometry.size.h, view->pending.geometry.origin.x, view->pending.geometry.origin.y); return wlc_geometry_equals(r, &view->pending.geometry); }
void wlc_view_set_parent_ptr(struct wlc_view *view, struct wlc_view *parent) { if (!view || view == parent) return; view->parent = convert_to_wlc_handle(parent); wlc_view_update(view); }
void wlc_view_ack_surface_attach(struct wlc_view *view, struct wlc_surface *surface) { assert(view && surface); if (view->x11.id) { surface->pending.opaque.extents = (pixman_box32_t){ 0, 0, surface->size.w, surface->size.h }; view->surface_pending.visible = (struct wlc_geometry){ wlc_origin_zero, surface->size }; } const bool resizing = (view->pending.state & WLC_BIT_RESIZING || view->commit.state & WLC_BIT_RESIZING); if (!resizing && !wlc_geometry_equals(&view->surface_pending.visible, &view->surface_commit.visible)) { struct wlc_geometry g = (struct wlc_geometry){ view->pending.geometry.origin, view->surface_pending.visible.size }; wlc_view_request_geometry(view, &g); } view->surface_commit = view->surface_pending; wlc_dlog(WLC_DBG_COMMIT, "=> surface view %" PRIuWLC, convert_to_wlc_handle(view)); } static bool should_be_transformed_by_parent(struct wlc_view *view) { return !(view->type & WLC_BIT_OVERRIDE_REDIRECT) && !(view->type & WLC_BIT_UNMANAGED); } void wlc_view_get_bounds(struct wlc_view *view, struct wlc_geometry *out_bounds, struct wlc_geometry *out_visible) { assert(view && out_bounds && out_bounds != out_visible); memcpy(out_bounds, &view->commit.geometry, sizeof(struct wlc_geometry)); struct wlc_surface *surface; if (!(surface = convert_from_wlc_resource(view->surface, "surface"))) return; if (should_be_transformed_by_parent(view)) { for (struct wlc_view *parent = convert_from_wlc_handle(view->parent, "view"); parent; parent = convert_from_wlc_handle(parent->parent, "view")) { out_bounds->origin.x += parent->commit.geometry.origin.x; out_bounds->origin.y += parent->commit.geometry.origin.y; } } if (view->xdg_surface && view->surface_commit.visible.size.w > 0 && view->surface_commit.visible.size.h > 0) { // xdg-surface client that draws drop shadows or other stuff. out_bounds->origin.x -= view->surface_commit.visible.origin.x; out_bounds->origin.y -= view->surface_commit.visible.origin.y; out_bounds->size.w += surface->size.w - view->surface_commit.visible.size.w; out_bounds->size.h += surface->size.h - view->surface_commit.visible.size.h; } // Make sure bounds is never 0x0 w/h wlc_size_max(&out_bounds->size, &(struct wlc_size){ 1, 1 }, &out_bounds->size);
void wlc_view_unmap(struct wlc_view *view) { assert(view); wlc_output_unlink_view(wlc_view_get_output_ptr(view), view); if (!view->state.created) return; WLC_INTERFACE_EMIT(view.destroyed, convert_to_wlc_handle(view)); view->state.created = false; }
bool wlc_view_request_state(struct wlc_view *view, enum wlc_view_state_bit state, bool toggle) { if (!view || !view->state.created) return false; if (!!(view->pending.state & state) == toggle) { // refresh geometry if (state == WLC_BIT_FULLSCREEN || state == WLC_BIT_MAXIMIZED) configure_view(view, view->pending.edges, &view->pending.geometry); return true; } wlc_dlog(WLC_DBG_REQUEST, "(%" PRIuWLC ") requested state %d", convert_to_wlc_handle(view), state); if (wlc_interface()->view.request.state) { WLC_INTERFACE_EMIT(view.request.state, convert_to_wlc_handle(view), state, toggle); } else { wlc_view_set_state_ptr(view, state, toggle); } wlc_dlog(WLC_DBG_REQUEST, "(%" PRIuWLC ") applied states %d", convert_to_wlc_handle(view), view->pending.state); return (!!(view->pending.state & state) == toggle); }
void wlc_view_commit_state(struct wlc_view *view, struct wlc_view_state *pending, struct wlc_view_state *out) { struct wlc_surface *surface; if (!(surface = convert_from_wlc_resource(view->surface, "surface"))) return; // FIXME: handle ping #if 0 struct wl_resource *r; if (view->shell_surface && (r = wl_resource_from_wlc_resource(view->shell_surface, "shell-surface"))) wl_shell_surface_send_ping(r, wl_display_next_serial(wlc_display())); wlc_dlog(WLC_DBG_COMMIT, "=> ping view %zu", convert_to_wlc_handle(view)); return; #endif if (!view->state.created) { // Initial size of the view view->pending.geometry.size = surface->size; view->state.created = true; if (WLC_INTERFACE_EMIT_EXCEPT(view.created, false, convert_to_wlc_handle(view))) { wlc_view_close_ptr(view); return; } } if (!memcmp(pending, out, sizeof(struct wlc_view_state))) return; if (pending->state != out->state) { const struct { uint32_t bit; uint32_t state; } map[] = { { WLC_BIT_MAXIMIZED, XDG_SURFACE_STATE_MAXIMIZED }, { WLC_BIT_FULLSCREEN, XDG_SURFACE_STATE_FULLSCREEN }, { WLC_BIT_RESIZING, XDG_SURFACE_STATE_RESIZING }, { WLC_BIT_ACTIVATED, XDG_SURFACE_STATE_ACTIVATED }, { 0, 0 }, }; chck_iter_pool_flush(&view->wl_state); for (uint32_t i = 0; map[i].state != 0; ++i) { if (pending->state & map[i].bit) chck_iter_pool_push_back(&view->wl_state, &map[i].state); } } const bool size_changed = (!wlc_size_equals(&pending->geometry.size, &out->geometry.size) || !wlc_size_equals(&pending->geometry.size, &surface->size)); wlc_dlog(WLC_DBG_COMMIT, "=> pending commit %zu (%d) pending: %ux%u commited: %ux%u surface: %ux%u", convert_to_wlc_handle(view), size_changed, pending->geometry.size.w, pending->geometry.size.h, out->geometry.size.w, out->geometry.size.h, surface->size.w, surface->size.h); if (pending->state != out->state || size_changed) { struct wl_resource *r; if (view->xdg_surface && (r = wl_resource_from_wlc_resource(view->xdg_surface, "xdg-surface"))) { const uint32_t serial = wl_display_next_serial(wlc_display()); struct wl_array states = { .size = view->wl_state.items.used, .alloc = view->wl_state.items.allocated, .data = view->wl_state.items.buffer }; xdg_surface_send_configure(r, pending->geometry.size.w, pending->geometry.size.h, &states, serial); } else if (view->shell_surface && (r = wl_resource_from_wlc_resource(view->shell_surface, "shell-surface"))) { wl_shell_surface_send_configure(r, pending->edges, pending->geometry.size.w, pending->geometry.size.h); } } if (view->x11.id) { if (!wlc_origin_equals(&pending->geometry.origin, &out->geometry.origin)) wlc_x11_window_position(&view->x11, pending->geometry.origin.x, pending->geometry.origin.y); if (size_changed) wlc_x11_window_resize(&view->x11, pending->geometry.size.w, pending->geometry.size.h); } memcpy(out, pending, sizeof(struct wlc_view_state)); wlc_dlog(WLC_DBG_COMMIT, "=> commit %zu", convert_to_wlc_handle(view)); }
void wlc_view_commit_state(struct wlc_view *view, struct wlc_view_state *pending, struct wlc_view_state *out) { assert(view && pending && out); struct wlc_surface *surface; if (!(surface = convert_from_wlc_resource(view->surface, "surface"))) return; // FIXME: handle ping #if 0 struct wl_resource *r; if (view->shell_surface && (r = wl_resource_from_wlc_resource(view->shell_surface, "shell-surface"))) wl_shell_surface_send_ping(r, wl_display_next_serial(wlc_display())); wlc_dlog(WLC_DBG_COMMIT, "=> ping view %" PRIuWLC, convert_to_wlc_handle(view)); return; #endif if (!view->state.created) { // Initial size of the view view->pending.geometry.size = surface->size; view->state.created = true; if (WLC_INTERFACE_EMIT_EXCEPT(view.created, false, convert_to_wlc_handle(view))) { wlc_view_close_ptr(view); return; } } if (!memcmp(pending, out, sizeof(struct wlc_view_state))) return; if (pending->state != out->state) { const struct { enum wlc_view_state_bit bit; uint32_t state; } map[] = { { WLC_BIT_MAXIMIZED, XDG_SURFACE_STATE_MAXIMIZED }, { WLC_BIT_FULLSCREEN, XDG_SURFACE_STATE_FULLSCREEN }, { WLC_BIT_RESIZING, XDG_SURFACE_STATE_RESIZING }, { WLC_BIT_ACTIVATED, XDG_SURFACE_STATE_ACTIVATED }, }; chck_iter_pool_flush(&view->wl_state); for (uint32_t i = 0; i < LENGTH(map); ++i) { if (pending->state & map[i].bit) chck_iter_pool_push_back(&view->wl_state, &map[i].state); } } const bool size_changed = (!wlc_size_equals(&pending->geometry.size, &out->geometry.size) || !wlc_size_equals(&pending->geometry.size, &surface->size)); wlc_dlog(WLC_DBG_COMMIT, "=> pending view commit %" PRIuWLC " (%d) pending: %ux%u commited: %ux%u surface: %ux%u", convert_to_wlc_handle(view), size_changed, pending->geometry.size.w, pending->geometry.size.h, out->geometry.size.w, out->geometry.size.h, surface->size.w, surface->size.h); if (pending->state != out->state || size_changed) configure_view(view, pending->edges, &pending->geometry); *out = *pending; wlc_dlog(WLC_DBG_COMMIT, "=> commit view %" PRIuWLC, convert_to_wlc_handle(view)); }
int main(void) { // TEST: Basic source and handle creation { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1, sizeof(struct wlc_resource))); struct wlc_resource *ptr; assert(!constructor_called); assert((ptr = wlc_handle_create(&source))); assert(constructor_called); assert(source.pool.items.count == 1); wlc_handle handle; #pragma GCC diagnostic ignored "-Wpointer-arith" assert(!convert_to_wlc_handle(NULL)); #pragma GCC diagnostic warning "-Wpointer-arith" assert((handle = convert_to_wlc_handle(ptr))); assert(!convert_from_wlc_handle(handle, "invalid")); assert(convert_from_wlc_handle(handle, "test") == ptr); const char *test = "foobar"; wlc_handle_set_user_data(handle, test); assert(wlc_handle_get_user_data(handle) == test); assert(!destructor_called); wlc_handle_release(handle); assert(destructor_called); assert(!convert_from_wlc_handle(handle, "invalid")); assert(!convert_from_wlc_handle(handle, "test")); assert(!wlc_handle_get_user_data(handle)); assert(source.pool.items.count == 0); wlc_source_release(&source); wlc_resources_terminate(); } // TEST: Handle invalidation on source release { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1, sizeof(struct wlc_resource))); struct wlc_resource *ptr; assert((ptr = wlc_handle_create(&source))); assert(source.pool.items.count == 1); wlc_handle handle; assert((handle = convert_to_wlc_handle(ptr))); assert(!(destructor_called = false)); wlc_source_release(&source); assert(destructor_called); assert(source.pool.items.count == 0); assert(!convert_from_wlc_handle(handle, "test")); wlc_resources_terminate(); } // TEST: Handle invalidation on resources termination { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1, sizeof(struct wlc_resource))); struct wlc_resource *ptr; assert((ptr = wlc_handle_create(&source))); assert(source.pool.items.count == 1); wlc_handle handle; assert((handle = convert_to_wlc_handle(ptr))); assert(!(destructor_called = false)); wlc_resources_terminate(); assert(destructor_called); assert(!convert_from_wlc_handle(handle, "test")); wlc_source_release(&source); } // TEST: Relocation of source inside container of handle, when the handle's source changes location { assert(wlc_resources_init()); struct wlc_source source; assert(wlc_source(&source, "test", constructor2, destructor2, 1, sizeof(struct contains_source))); struct contains_source *ptr; assert((ptr = wlc_handle_create(&source))); assert(source.pool.items.count == 1); void *original_source = &ptr->source; wlc_handle handle; assert((handle = convert_to_wlc_handle(ptr))); struct wlc_resource *ptr2; assert((ptr2 = wlc_handle_create(&ptr->source))); wlc_handle handle2; assert((handle2 = convert_to_wlc_handle(ptr2))); assert(convert_from_wlc_handle(handle2, "test2") == ptr2); // Play with heap until realloc does not return linear memory anymore { void *original = source.pool.items.buffer; do { void *garbage; assert((garbage = malloc(1024))); assert(wlc_handle_create(&source)); free(garbage); } while (original == source.pool.items.buffer); } // So this should be true assert(ptr = convert_from_wlc_handle(handle, "test")); assert(original_source != &ptr->source); wlc_resources_terminate(); assert(!convert_from_wlc_handle(handle2, "test2")); wlc_source_release(&source); } // TEST: Benchmark (many insertions, and removal expanding from center) { assert(wlc_resources_init()); struct container { wlc_handle self; }; struct wlc_source source; assert(wlc_source(&source, "test", constructor, destructor, 1024, sizeof(struct container))); wlc_handle first = 0; const uint32_t iters = 0xFFFFF; for (uint32_t i = 0; i < iters; ++i) { struct container *ptr = wlc_handle_create(&source); ptr->self = convert_to_wlc_handle(ptr); if (!first) first = ptr->self; assert(convert_from_wlc_handle(first, "test")); } assert(source.pool.items.count == iters); for (uint32_t i = iters / 2, d = iters / 2; i < iters; ++i, --d) { assert(((struct container*)convert_from_wlc_handle(i + 1, "test"))->self == i + 1); assert(((struct container*)convert_from_wlc_handle(d + 1, "test"))->self == d + 1); wlc_handle_release(i + 1); wlc_handle_release(d + 1); } assert(source.pool.items.count == 0); assert(!convert_from_wlc_handle(first, "test")); wlc_source_release(&source); wlc_resources_terminate(); } // TODO: Needs test for wlc_resource. // For this we need to start compositor and some clients, or dummy the wl_resource struct. // (Latter probably better) return EXIT_SUCCESS; }
WLC_API wlc_handle wlc_view_get_output(wlc_handle view) { return convert_to_wlc_handle(wlc_view_get_output_ptr(convert_from_wlc_handle(view, "view"))); }
void wlc_view_set_surface(struct wlc_view *view, struct wlc_surface *surface) { if (!view || view->surface == convert_to_wlc_resource(surface)) return; wlc_handle old = view->surface; view->surface = convert_to_wlc_resource(surface); wlc_surface_attach_to_view(convert_from_wlc_resource(old, "surface"), NULL); wlc_surface_attach_to_view(surface, view); if (surface && surface->commit.attached) { wlc_view_map(view); } else { wlc_view_unmap(view); } wlc_dlog(WLC_DBG_RENDER, "-> Linked surface (%" PRIuWLC ") to view (%" PRIuWLC ")", convert_to_wlc_resource(surface), convert_to_wlc_handle(view)); }