static bool update_keys(struct chck_iter_pool *keys, uint32_t key, enum wl_keyboard_key_state state) { assert(keys); uint32_t *k; chck_iter_pool_for_each(keys, k) { if (*k != key) continue; if (state == WL_KEYBOARD_KEY_STATE_PRESSED) return false; wlc_dlog(WLC_DBG_KEYBOARD, "remove key: %u", *k); chck_iter_pool_remove(keys, --_I); } if (state == WL_KEYBOARD_KEY_STATE_PRESSED && !chck_iter_pool_push_back(keys, &key)) return false; if (state == WL_KEYBOARD_KEY_STATE_PRESSED) wlc_dlog(WLC_DBG_KEYBOARD, "add key: %u", key); return true; }
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); }
static bool surface_attach(struct ctx *context, struct wlc_context *bound, struct wlc_surface *surface, struct wlc_buffer *buffer) { assert(context && bound && surface); struct wl_resource *wl_buffer; if (!buffer || !(wl_buffer = convert_to_wl_resource(buffer, "buffer"))) { surface_destroy(context, bound, surface); return true; } EGLint format; bool attached = false; struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get(wl_buffer); if (shm_buffer) { attached = shm_attach(surface, buffer, shm_buffer); } else if (wlc_context_query_buffer(bound, (void*)wl_buffer, EGL_TEXTURE_FORMAT, &format)) { attached = egl_attach(context, bound, surface, buffer, format); } else { /* unknown buffer */ wlc_log(WLC_LOG_WARN, "Unknown buffer"); } if (attached) wlc_dlog(WLC_DBG_RENDER, "-> Attached surface (%" PRIuWLC ") with buffer of size (%ux%u)", convert_to_wlc_resource(surface), buffer->size.w, buffer->size.h); return attached; }
static void surface_destroy(struct ctx *context, struct wlc_context *bound, struct wlc_surface *surface) { (void)context; assert(context && bound && surface); surface_flush_textures(surface); surface_flush_images(bound, surface); wlc_dlog(WLC_DBG_RENDER, "-> Destroyed surface"); }
static void set_geometry(xcb_window_t window, const struct wlc_geometry *g) { assert(g); const uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; const uint32_t values[] = { g->origin.x, g->origin.y, g->size.w, g->size.h }; wlc_dlog(WLC_DBG_XWM, "-> Configure x11 window (%u) %ux%u+%d,%d", window, g->size.w, g->size.h, g->origin.x, g->origin.y); XCB_CALL(xcb_configure_window_checked(x11.connection, window, mask, (uint32_t*)&values)); xcb_flush(x11.connection); }
static bool add_window(struct wlc_xwm *xwm, xcb_window_t window, bool override_redirect) { assert(xwm); struct wlc_x11_window win = {0}; win.id = window; win.override_redirect = override_redirect; const bool ret = chck_hash_table_set(&xwm->unpaired, window, &win); wlc_dlog(WLC_DBG_XWM, "-> Unpaired collisions (%u)", chck_hash_table_collisions(&xwm->unpaired)); return ret; }
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);
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); }
static void wl_cb_shell_surface_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, uint32_t edges) { (void)client, (void)resource, (void)serial; struct wlc_seat *seat; if (!(seat = wl_resource_get_user_data(seat_resource))) return; if (!seat->pointer.focused.view) return; wlc_dlog(WLC_DBG_REQUEST, "(%" PRIuWLC ") requested resize", seat->pointer.focused.view); const struct wlc_point o = { seat->pointer.pos.x, seat->pointer.pos.y }; WLC_INTERFACE_EMIT(view.request.resize, seat->pointer.focused.view, edges, &o); }
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)); }
static void send_release_for_keys(struct chck_iter_pool *resources, struct chck_iter_pool *keys) { assert(keys); wlc_dlog(WLC_DBG_KEYBOARD, "release keys"); uint32_t *k; uint32_t time = wlc_get_time(NULL); chck_iter_pool_for_each(keys, k) { wlc_resource *r; chck_iter_pool_for_each(resources, r) { struct wl_resource *wr; if (!(wr = wl_resource_from_wlc_resource(*r, "keyboard"))) continue; uint32_t serial = wl_display_next_serial(wlc_display()); wl_keyboard_send_key(wr, serial, time, *k, WL_KEYBOARD_KEY_STATE_RELEASED); } }
static void read_properties(struct wlc_xwm *xwm, struct wlc_x11_window *win, const xcb_atom_t *props, size_t nmemb) { assert(win); struct wlc_view *view; if (!(view = view_for_window(win))) return; xcb_get_property_cookie_t *cookies; if (!(cookies = chck_calloc_of(nmemb, sizeof(xcb_get_property_cookie_t)))) return; for (uint32_t i = 0; i < nmemb; ++i) cookies[i] = xcb_get_property(x11.connection, 0, win->id, props[i], XCB_ATOM_ANY, 0, 2048); for (uint32_t i = 0; i < nmemb; ++i) { xcb_get_property_reply_t *reply; if (!(reply = xcb_get_property_reply(x11.connection, cookies[i], NULL))) continue; if (reply->type == XCB_ATOM_STRING || reply->type == x11.atoms[UTF8_STRING]) { // Class && Name // STRING == latin1, but we naively just read it as is. For full support we should convert to utf8. if (props[i] == XCB_ATOM_WM_CLASS) { wlc_view_set_class_ptr(view, xcb_get_property_value(reply), xcb_get_property_value_length(reply)); wlc_dlog(WLC_DBG_XWM, "WM_CLASS: %s", view->data._class.data); } else if (props[i] == XCB_ATOM_WM_NAME || props[i] == x11.atoms[NET_WM_NAME]) { if (reply->type != XCB_ATOM_STRING || !win->has_utf8_title) { wlc_view_set_title_ptr(view, xcb_get_property_value(reply), xcb_get_property_value_length(reply)); win->has_utf8_title = true; } wlc_dlog(WLC_DBG_XWM, "(%d) %s %s %s", win->has_utf8_title, (reply->type == XCB_ATOM_STRING ? "STRING" : "UTF8_STRING"), (props[i] == XCB_ATOM_WM_NAME ? "WM_NAME" : "NET_WM_NAME"), view->data.title.data); } } else if (props[i] == XCB_ATOM_WM_TRANSIENT_FOR && reply->type == XCB_ATOM_WINDOW) { // Transient xcb_window_t *xid = xcb_get_property_value(reply); set_parent(xwm, win, *xid); wlc_dlog(WLC_DBG_XWM, "WM_TRANSIENT_FOR: %u", *xid); } else if (props[i] == x11.atoms[NET_WM_PID] && reply->type == XCB_ATOM_CARDINAL) { // PID wlc_dlog(WLC_DBG_XWM, "NET_WM_PID"); } else if (props[i] == x11.atoms[NET_WM_WINDOW_TYPE] && reply->type == XCB_ATOM_ATOM) { // Window type view->type &= ~WLC_BIT_UNMANAGED | ~WLC_BIT_SPLASH | ~WLC_BIT_MODAL; xcb_atom_t *atoms = xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; ++i) { if (atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_TOOLTIP] || atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_UTILITY] || atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_DND] || atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_DROPDOWN_MENU] || atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_POPUP_MENU] || atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_COMBO]) { wlc_view_set_type_ptr(view, WLC_BIT_UNMANAGED, true); } if (atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_DIALOG]) wlc_view_set_type_ptr(view, WLC_BIT_MODAL, true); if (atoms[i] == x11.atoms[NET_WM_WINDOW_TYPE_SPLASH]) wlc_view_set_type_ptr(view, WLC_BIT_SPLASH, true); } wlc_dlog(WLC_DBG_XWM, "NET_WM_WINDOW_TYPE: %u", view->type); } else if (props[i] == x11.atoms[WM_PROTOCOLS]) { xcb_atom_t *atoms = xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; ++i) { if (atoms[i] == x11.atoms[WM_DELETE_WINDOW]) win->has_delete_window = true; } wlc_dlog(WLC_DBG_XWM, "WM_PROTOCOLS: %u", view->type); } else if (props[i] == x11.atoms[WM_NORMAL_HINTS]) { wlc_dlog(WLC_DBG_XWM, "WM_NORMAL_HINTS"); } else if (props[i] == x11.atoms[NET_WM_STATE]) { handle_state(win, xcb_get_property_value(reply), reply->value_len, NET_WM_STATE_ADD); wlc_dlog(WLC_DBG_XWM, "NET_WM_STATE"); } else if (props[i] == x11.atoms[MOTIF_WM_HINTS]) { // Motif hints wlc_dlog(WLC_DBG_XWM, "MOTIF_WM_HINTS"); } free(reply); } free(cookies); }
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)); }