#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "../../menu.h" #include "../../balloon.h" #include "../../framewin.h" #include "../../texture.h" #include "../../def_pixmaps.h" #include #include "../../screen.h" #include "wl_types.h" /* Forward declarations from sibling modules. */ struct wl_toplevel_view *wl_find_view_by_xdg_surface(struct wlr_xdg_surface *surf); void wmaker_popup_apply(struct wl_toplevel_view *view); void frame_buf_update_children_abs(WNativeWindow parent_id); struct wl_frame_buf *frame_buf_find(WNativeWindow id); extern void *W_RegisterBacking(unsigned long id, void *img); extern void W_UnregisterBacking(unsigned long id); struct wl_toplevel_view *wl_find_view_by_id(WNativeWindow id); __attribute__((weak)) pixman_image_t *get_window_image_from_x11(Display *x_display, WNativeWindow win) { (void)x_display; (void)win; return NULL; } Bool wl_window_fetch_name(WNativeWindow win, char **name_out) { struct wl_toplevel_view *v = wl_find_view_by_id(win); const char *title = NULL; if (v && v->xw_surface) title = v->xw_surface->title; else if (v && v->xdg_surface && v->xdg_surface->toplevel) title = v->xdg_surface->toplevel->title; if (title) { *name_out = strdup(title); return True; } *name_out = NULL; return False; } Bool wl_window_get_icon_name(WNativeWindow win, char **name_out) { struct wl_toplevel_view *v = wl_find_view_by_id(win); const char *title = NULL; if (v && v->xw_surface) title = v->xw_surface->title; else if (v && v->xdg_surface && v->xdg_surface->toplevel) title = v->xdg_surface->toplevel->title; if (title) { *name_out = strdup(title); return True; } *name_out = NULL; return False; } void * wl_window_get_icon_image(WNativeWindow win) { /* For XWayland clients, parse _NET_WM_ICON via the Xwayland display. * For native Wayland clients there is no equivalent (the upcoming * xdg-toplevel-icon protocol is not yet wired). */ if (!wl_state.x_display) return NULL; return get_window_image_from_x11(wl_state.x_display, (WNativeWindow)win); } void wl_window_map(WNativeWindow win) { struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v && v->scene_node) { wlr_scene_node_set_enabled(&v->scene_node->node, true); /* Ensure XWayland X11 window is mapped so it composites children * (needed for dockapps whose content is in a child icon_window). */ /* For XWayland dockapps: map the X11 window and reparent the * icon_window child into it so XWayland composites the content. */ if (v->xw_surface && wl_state.x_display && v->xw_surface->hints && (v->xw_surface->hints->flags & XCB_ICCCM_WM_HINT_STATE) && v->xw_surface->hints->initial_state == 0 /* WithdrawnState */) { Window xwin = (Window)v->xw_surface->window_id; XMapWindow(wl_state.x_display, xwin); if (v->xw_surface->hints->flags & XCB_ICCCM_WM_HINT_ICON_WINDOW) { Window icon_xwin = (Window)v->xw_surface->hints->icon_window; XReparentWindow(wl_state.x_display, icon_xwin, xwin, 0, 0); XMapWindow(wl_state.x_display, icon_xwin); } XFlush(wl_state.x_display); } return; } if (v) return; /* Frame buffer: enable its scene node and children. */ struct wl_frame_buf *fb = frame_buf_find(win); if (fb && fb->scene_buf) { /* If this is a child, only enable if parent is enabled */ if (fb->parent_id) { struct wl_frame_buf *parent = frame_buf_find(fb->parent_id); if (parent && parent->scene_buf && !parent->scene_buf->node.enabled) return; /* parent not visible yet */ } wlr_scene_node_set_enabled(&fb->scene_buf->node, true); if (fb->tree) { wlr_scene_node_set_enabled(&fb->tree->node, true); } /* Also enable children (and grandchildren) */ struct wl_frame_buf *child; wl_list_for_each(child, &wl_state.frame_list, link) { if (child->parent_id == win && child->scene_buf) { wlr_scene_node_set_enabled(&child->scene_buf->node, true); struct wl_frame_buf *gc; wl_list_for_each(gc, &wl_state.frame_list, link) { if (gc->parent_id == child->id && gc->scene_buf) wlr_scene_node_set_enabled(&gc->scene_buf->node, true); } } } return; } } void wl_window_map_raised(WNativeWindow win) { struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v && v->scene_node) { wlr_scene_node_set_enabled(&v->scene_node->node, true); if (v->frame_id) { struct wl_frame_buf *fb = frame_buf_find(v->frame_id); if (fb && fb->tree) { wlr_scene_node_raise_to_top(&fb->tree->node); return; } } wlr_scene_node_raise_to_top(&v->scene_node->node); return; } struct wl_frame_buf *fb = frame_buf_find(win); if (fb && fb->scene_buf) { wlr_scene_node_set_enabled(&fb->scene_buf->node, true); if (fb->tree) { /* Top-level frame: enable tree (decoration children become visible). */ wlr_scene_node_set_enabled(&fb->tree->node, true); wlr_scene_node_raise_to_top(&fb->tree->node); } else { wlr_scene_node_raise_to_top(&fb->scene_buf->node); } return; } } void wl_window_map_subwindows(WNativeWindow parent) { (void)parent; /* Child windows are sub-regions of the frame buffer — no-op. */ } void wl_window_unmap(WNativeWindow win) { struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v && v->scene_node) { wlr_scene_node_set_enabled(&v->scene_node->node, false); return; } struct wl_frame_buf *fb = frame_buf_find(win); if (!fb) return; /* For top-level frames, disable the whole tree. This hides the frame * buffer, all child scene_bufs (titlebar, resizebar, buttons), and the * client surface reparented into fb->tree — atomically and irrevocably * until the frame is explicitly mapped again. Disabling individual * child nodes is insufficient because a subsequent wl_frame_paint can * re-enable them while the window is still iconified. */ if (fb->tree) { wlr_scene_node_set_enabled(&fb->tree->node, false); return; } /* Child frame_buf: disable its scene_buf node only. */ if (fb->scene_buf) wlr_scene_node_set_enabled(&fb->scene_buf->node, false); } void wl_window_move(WNativeWindow win, int x, int y) { struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v) { if (v->frame_id) { /* Client is a child of a frame — coords are relative to frame */ v->frame_rel_x = x; v->frame_rel_y = y; struct wl_frame_buf *fb = frame_buf_find(v->frame_id); if (fb) { v->x = fb->x + x; v->y = fb->y + y; } } else { v->x = x; v->y = y; } if (v->scene_node) { if (v->frame_id) wlr_scene_node_set_position(&v->scene_node->node, x, y); else wlr_scene_node_set_position(&v->scene_node->node, v->x, v->y); } if (v->xw_surface) wlr_xwayland_surface_configure(v->xw_surface, (int16_t)v->x, (int16_t)v->y, v->xw_surface->width, v->xw_surface->height); /* Re-apply any popups whose parent is this toplevel so they * track the parent's position. */ struct wl_toplevel_view *p; wl_list_for_each(p, &wl_state.toplevel_list, link) { if (p->popup && p->popup_parent && wl_find_view_by_xdg_surface(p->popup_parent->base) == v) { wmaker_popup_apply(p); } } return; } struct wl_frame_buf *fb = frame_buf_find(win); if (fb) { fb->rel_x = x; fb->rel_y = y; if (fb->parent_id) { struct wl_frame_buf *parent = frame_buf_find(fb->parent_id); if (parent) { fb->x = parent->x + x; fb->y = parent->y + y; } else { fb->x = x; fb->y = y; } /* Child frame_buf: reposition scene_buf at relative offset */ if (fb->scene_buf) wlr_scene_node_set_position(&fb->scene_buf->node, x, y); } else { fb->x = x; fb->y = y; /* Top-level frame: move the tree (children follow automatically) */ if (fb->tree) wlr_scene_node_set_position(&fb->tree->node, x, y); } /* Update absolute position tracking for hit-testing — recursively * for all descendants so grandchildren (e.g. WINGs panel inside * a fake_leader inside a WMaker frame) stay in sync. */ frame_buf_update_children_abs(win); return; } } void wl_window_resize(WNativeWindow win, int w, int h) { struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v) { if (w <= 0 || h <= 0) return; if (v->xw_surface) { wlr_xwayland_surface_configure(v->xw_surface, (int16_t)v->x, (int16_t)v->y, (uint16_t)w, (uint16_t)h); return; } if (!v->xdg_surface || !v->xdg_surface->toplevel) return; wlr_xdg_toplevel_set_size(v->xdg_surface->toplevel, (uint32_t)w, (uint32_t)h); wlr_xdg_surface_schedule_configure(v->xdg_surface); return; } struct wl_frame_buf *fb = frame_buf_find(win); if (fb && w > 0 && h > 0 && (w != fb->width || h != fb->height)) { if (fb->scene_buf) wlr_scene_buffer_set_buffer(fb->scene_buf, NULL); pixman_image_unref(fb->image); fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0); fb->width = w; fb->height = h; fb->base.width = w; fb->base.height = h; W_RegisterBacking(fb->id, fb->image); if (fb->scene_buf) wlr_scene_buffer_set_dest_size(fb->scene_buf, w, h); } } void wl_window_set_border(WNativeWindow win, unsigned long pixel) { (void)win; (void)pixel; /* borders are drawn into the frame buffer */ } void wl_window_set_border_width(WNativeWindow win, int width) { (void)win; (void)width; /* no-op for native frames */ } void wl_window_set_save_under(WNativeWindow win, int save_under) { (void)win; (void)save_under; } void wl_noop_window_set_background(WNativeWindow win, unsigned long pixel) { (void)win; (void)pixel; } extern void *W_GetViewBacking(unsigned long id); void wl_window_set_background_pixmap(WNativeWindow win, unsigned long pixmap) { if (pixmap == 0) return; struct wl_frame_buf *fb = frame_buf_find(win); pixman_image_t *dst_img = NULL; if (fb && fb->image) { dst_img = fb->image; fb->bg_pixmap = (pixman_image_t *)(uintptr_t)pixmap; } else { dst_img = (pixman_image_t *)W_GetViewBacking(win); } if (!dst_img) return; pixman_image_t *src = (pixman_image_t *)(uintptr_t)pixmap; int src_w = pixman_image_get_width(src); int src_h = pixman_image_get_height(src); /* Blit source pixmap into frame_buf's image */ pixman_image_composite32(PIXMAN_OP_SRC, src, /* src */ NULL, /* mask */ dst_img, /* dest */ 0, 0, /* src x,y */ 0, 0, /* mask x,y */ 0, 0, /* dest x,y */ src_w < pixman_image_get_width(dst_img) ? src_w : pixman_image_get_width(dst_img), src_h < pixman_image_get_height(dst_img) ? src_h : pixman_image_get_height(dst_img)); if (fb) wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); } void wl_pixmap_free(unsigned long pixmap) { if (pixmap == 0) return; pixman_image_t *img = (pixman_image_t *)(uintptr_t)pixmap; pixman_image_unref(img); } void wl_window_clear(WNativeWindow win) { struct wl_frame_buf *fb = frame_buf_find(win); pixman_image_t *img = NULL; if (fb && fb->image) { img = fb->image; } else { img = (pixman_image_t *)W_GetViewBacking(win); } if (!img) return; int width = pixman_image_get_width(img); int height = pixman_image_get_height(img); /* If a background pixmap is set, repaint from it (like XClearWindow) */ if (fb && fb->bg_pixmap) { pixman_image_composite32(PIXMAN_OP_SRC, fb->bg_pixmap, NULL, img, 0, 0, 0, 0, 0, 0, width, height); } else { /* No background pixmap — fill with transparent black */ int stride = pixman_image_get_stride(img); uint32_t *data = (uint32_t *)pixman_image_get_data(img); memset(data, 0, stride * height); } /* Notify the scene buffer of the update */ if (fb) wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); } int wl_window_get_rect(WNativeWindow win, int *win_x, int *win_y, int *win_w, int *win_h) { if (win == WNATIVE_NONE) { if (win_x) { *win_x = 0; } if (win_y) { *win_y = 0; } if (win_w) { *win_w = 1; } if (win_h) { *win_h = 1; } return 0; /* invalid/none */ } struct wl_toplevel_view *v = wl_find_view_by_id(win); if (v && v->xw_surface) { if (win_x) { *win_x = v->x; } if (win_y) { *win_y = v->y; } if (win_w) { *win_w = v->xw_surface->width > 0 ? v->xw_surface->width : 1; } if (win_h) { *win_h = v->xw_surface->height > 0 ? v->xw_surface->height : 1; } return 1; } if (v && v->xdg_surface && v->xdg_surface->surface) { int w = v->xdg_surface->surface->current.width; int h = v->xdg_surface->surface->current.height; if (win_x) { *win_x = v->x; } if (win_y) { *win_y = v->y; } if (win_w) { *win_w = w > 0 ? w : 1; } if (win_h) { *win_h = h > 0 ? h : 1; } return 1; } /* Frame buffer geometry. */ struct wl_frame_buf *fb = frame_buf_find(win); if (fb) { if (win_x) { *win_x = fb->x; } if (win_y) { *win_y = fb->y; } if (win_w) { *win_w = fb->width; } if (win_h) { *win_h = fb->height; } return 1; } /* Synthetic child IDs (titlebar, buttons) — return minimal geometry. */ if (win_x) { *win_x = 0; } if (win_y) { *win_y = 0; } if (win_w) { *win_w = 1; } if (win_h) { *win_h = 1; } return 0; } void wl_window_add_events(WNativeWindow win, unsigned long extra_mask) { (void)win; (void)extra_mask; } void wl_window_select_events(WNativeWindow win, unsigned long mask) { (void)win; (void)mask; } void wl_window_shape_clear(WWindow *wwin) { (void)wwin; } void wl_window_shape_set(WWindow *wwin) { (void)wwin; } void wl_menu_paint_entry(WMenu *menu, int index, int selected) { if (!menu || !menu->menu || !menu->menu->window) return; struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)menu->menu->window); if (!fb || !fb->image) return; /* Guard against painting before wMenuRealize: entry_height is 0, * fb->image is the initial 1x10 placeholder, and the bevel-line * computations below would write outside the pixman buffer. * wMenuSetEnabled() in particular triggers paints on the brother * during updateOptionsMenu before the menu is realized. */ if (menu->entry_height <= 0 || fb->width <= 1 || fb->height <= 10) return; WScreen *scr = menu->frame->screen_ptr; WMenuEntry *entry = menu->entries[index]; int y = menu->entry_height * index; int h = menu->entry_height; int w = fb->width; uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); int stride = pixman_image_get_stride(fb->image) / 4; /* Background color from appearance preferences */ uint32_t bg; if (selected) { WMColor *sc = scr->select_color; bg = 0xFF000000 | ((uint32_t)WMRedComponentOfColor(sc) >> 8 << 16) | ((uint32_t)WMGreenComponentOfColor(sc) >> 8 << 8) | ((uint32_t)WMBlueComponentOfColor(sc) >> 8); } else { WTexSolid *ts = (WTexSolid *)scr->menu_item_texture; if (ts) bg = 0xFF000000 | ((uint32_t)ts->normal.red >> 8 << 16) | ((uint32_t)ts->normal.green >> 8 << 8) | ((uint32_t)ts->normal.blue >> 8); else bg = 0xFFAEAAAE; } for (int row = y; row < y + h && row < fb->height; row++) for (int col = 0; col < w; col++) dst[row * stride + col] = bg; /* Draw text using WINGs font backend (renders via PangoFT2 → pixman) */ WMColor *color = selected ? scr->select_text_color : (entry->flags.enabled ? scr->mtext_color : scr->dtext_color); /* Ensure text is readable against background: if text color is too * close to bg (dark theme with black text), use white instead. */ if (color && !selected) { unsigned tr = WMRedComponentOfColor(color) >> 8; unsigned tg = WMGreenComponentOfColor(color) >> 8; unsigned tb = WMBlueComponentOfColor(color) >> 8; unsigned br = (bg >> 16) & 0xFF; unsigned bg_ = (bg >> 8) & 0xFF; unsigned bb = bg & 0xFF; int text_lum = (int)(tr * 299 + tg * 587 + tb * 114) / 1000; int bg_lum = (int)(br * 299 + bg_ * 587 + bb * 114) / 1000; if (abs(text_lum - bg_lum) < 64) color = (bg_lum < 128) ? scr->white : scr->black; } WMFont *font = scr->menu_entry_font; WMDrawable d = (WMDrawable)(uintptr_t)fb->image; if (entry->text && font && color) { int text_y = y + 3; WMDrawString(scr->wmscreen, d, color, font, 8, text_y, entry->text, strlen(entry->text)); } if (entry->rtext && entry->cascade < 0 && font && color) { int tw = WMWidthOfString(font, entry->rtext, strlen(entry->rtext)); int text_y = y + 3; WMDrawString(scr->wmscreen, d, color, font, w - 8 - tw, text_y, entry->rtext, strlen(entry->rtext)); } /* Draw cascade arrow for submenus (3D right-pointing triangle matching X11) */ if (entry->cascade >= 0) { WTexSolid *aux = (WTexSolid *)scr->menu_item_texture; uint32_t dim_c = 0xFF606060; uint32_t light_c = 0xFFE0E0E0; uint32_t dark_c = 0xFF303030; if (aux) { dim_c = 0xFF000000 | ((uint32_t)aux->dim.red >> 8 << 16) | ((uint32_t)aux->dim.green >> 8 << 8) | ((uint32_t)aux->dim.blue >> 8); light_c = 0xFF000000 | ((uint32_t)aux->light.red >> 8 << 16) | ((uint32_t)aux->light.green >> 8 << 8) | ((uint32_t)aux->light.blue >> 8); dark_c = 0xFF000000 | ((uint32_t)aux->dark.red >> 8 << 16) | ((uint32_t)aux->dark.green >> 8 << 8) | ((uint32_t)aux->dark.blue >> 8); } /* Top edge: dim line from (w-11, y+6) to (w-6, y+h/2-1) */ int x0 = w - 11, y0 = y + 6, x1 = w - 6, y1 = y + h / 2 - 1; for (int i = 0; i <= 5; i++) { int lx = x0 + i * (x1 - x0) / 5; int ly = y0 + i * (y1 - y0) / 5; if (lx >= 0 && lx < (int)fb->width && ly >= 0 && ly < fb->height) dst[ly * stride + lx] = dim_c; } /* Bottom edge: light line from (w-11, y+h-8) to (w-6, y+h/2-1) */ y0 = y + h - 8; for (int i = 0; i <= 5; i++) { int lx = x0 + i * (x1 - x0) / 5; int ly = y0 + i * (y1 - y0) / 5; if (lx >= 0 && lx < (int)fb->width && ly >= 0 && ly < fb->height) dst[ly * stride + lx] = light_c; } /* Left edge: dark vertical line at w-12 from y+6 to y+h-8 */ int lx = w - 12; if (lx >= 0 && lx < (int)fb->width) for (int ly = y + 6; ly <= y + h - 8 && ly < fb->height; ly++) if (ly >= 0) dst[ly * stride + lx] = dark_c; } /* Bevel lines (separators between entries) */ uint32_t light = 0xFFDDDDDD; uint32_t dark = 0xFF555555; /* Left edge - light */ for (int row = y; row < y + h && row < fb->height; row++) dst[row * stride] = light; /* Right edge - dark */ for (int row = y; row < y + h && row < fb->height; row++) if (w - 1 < fb->width) dst[row * stride + (w - 1)] = dark; /* Top line - light (skip first entry) */ if (index > 0 && y < fb->height) for (int col = 0; col < w && col < fb->width; col++) dst[y * stride + col] = light; /* Bottom line - dark (skip last entry) */ if (index < menu->entry_no - 1 && y + h - 1 < fb->height) for (int col = 0; col < w && col < fb->width; col++) dst[(y + h - 1) * stride + col] = dark; wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); } void wl_menu_draw_frame(WScreen *scr, WNativeWindow win, int y, int w, int h, int type) { struct wl_frame_buf *fb = frame_buf_find(win); if (!fb || !fb->image) return; uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); int stride = pixman_image_get_stride(fb->image) / 4; uint32_t dark = 0xFF555555; uint32_t light = 0xFFDDDDDD; /* Right edge */ for (int row = y; row < y + h && row < fb->height; row++) if (w - 1 < fb->width) dst[row * stride + (w - 1)] = dark; /* Bottom edge */ if (y + h - 1 < fb->height) for (int col = 0; col < w && col < fb->width; col++) dst[(y + h - 1) * stride + col] = dark; /* Left edge */ for (int row = y; row < y + h && row < fb->height; row++) dst[row * stride] = light; /* Top edge */ if (y < fb->height) for (int col = 0; col < w && col < fb->width; col++) dst[y * stride + col] = light; (void)scr; (void)type; wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); } void wl_balloon_init(WScreen *scr) { scr->balloon = wmalloc(sizeof(WBalloon)); memset(scr->balloon, 0, sizeof(WBalloon)); /* Create a frame_buf in the overlay layer for balloon tooltips */ if (wl_state.scene) { struct wl_frame_buf *fb = wmalloc(sizeof(*fb)); memset(fb, 0, sizeof(*fb)); int w = 1, h = 1; fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0); wlr_buffer_init(&fb->base, &frame_buf_impl, w, h); fb->id = wl_state.next_synthetic_id++; fb->width = w; fb->height = h; struct wlr_scene_tree *parent = wl_state.level_trees[WL_LAYER_OVERLAY] ? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree; fb->tree = wlr_scene_tree_create(parent); if (fb->tree) { fb_attach_tree_listener(fb); wlr_scene_node_set_position(&fb->tree->node, 0, 0); fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base); if (fb->scene_buf) { fb_attach_scene_buf_listener(fb); wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0); } wlr_scene_node_set_enabled(&fb->tree->node, false); } wl_list_insert(&wl_state.frame_list, &fb->link); W_RegisterBacking(fb->id, fb->image); scr->balloon->window = (WNativeWindow)fb->id; } } void wl_balloon_show(WScreen *scr, void *bal_ptr) { WBalloon *bal = (WBalloon *)bal_ptr; if (!bal->text || !bal->text[0]) return; struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)bal->window); if (!fb) return; WMFont *font = scr->info_text_font; int textlen = strlen(bal->text); int tw = WMWidthOfString(font, bal->text, textlen); int th = WMFontHeight(font); int pad = 4; int bw = tw + pad * 2; int bh = th + pad * 2; /* Resize pixman buffer if needed */ if (bw != fb->width || bh != fb->height) { if (fb->scene_buf) wlr_scene_buffer_set_buffer(fb->scene_buf, NULL); pixman_image_unref(fb->image); fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, bw, bh, NULL, 0); fb->width = bw; fb->height = bh; fb->base.width = bw; fb->base.height = bh; if (fb->scene_buf) wlr_scene_buffer_set_dest_size(fb->scene_buf, bw, bh); } /* Fill pale yellow background */ uint32_t *data = pixman_image_get_data(fb->image); for (int i = 0; i < bw * bh; i++) data[i] = 0xFFFFFFE0; /* 1px black border */ for (int x = 0; x < bw; x++) { data[x] = 0xFF000000; data[(bh - 1) * bw + x] = 0xFF000000; } for (int y = 0; y < bh; y++) { data[y * bw] = 0xFF000000; data[y * bw + bw - 1] = 0xFF000000; } /* Draw text */ WMDrawString(scr->wmscreen, (WMDrawable)(uintptr_t)fb->image, scr->black, font, pad, pad, bal->text, textlen); /* Position near objectWindow */ int bx = 0, by = 0; struct wl_frame_buf *obj = frame_buf_find((WNativeWindow)bal->objectWindow); if (obj) { bx = obj->x; by = obj->y + bal->h + 2; } /* Clamp to screen */ if (bx + bw > scr->scr_width) bx = scr->scr_width - bw; if (by + bh > scr->scr_height) by = by - bal->h - bh - 2; if (fb->scene_buf) { wlr_scene_node_set_position(&fb->scene_buf->node, bx, by); wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); wlr_scene_node_set_enabled(&fb->tree->node, true); } bal->mapped = 1; } void wl_context_init(void) { /* Wayland uses a hash table instead of X11 contexts */ w_global.context.client_win = 1; w_global.context.app_win = 2; w_global.context.stack = 3; } int wl_window_get_command(WNativeWindow win, char ***argv_out, int *argc_out) { /* Wayland native clients: return [app_id] as a single-arg command. * Most app_ids match the binary name (e.g. "foot", "firefox") so this * is sufficient for session restore via PATH lookup or a desktop-file * Exec= line. * * XWayland clients: prefer WM_COMMAND (read from wl_state.x_display) * and fall back to xw_surface->instance / class. */ *argv_out = NULL; *argc_out = 0; struct wl_toplevel_view *v = wl_find_view_by_id(win); if (!v) return 0; const char *cmd = NULL; if (v->xw_surface) { #if USE_XWAYLAND /* XWayland: try WM_COMMAND first via the X server. */ if (wl_state.x_display && v->xw_surface->window_id) { char **cmd_argv = NULL; int cmd_argc = 0; if (XGetCommand(wl_state.x_display, v->xw_surface->window_id, &cmd_argv, &cmd_argc) && cmd_argc > 0 && cmd_argv) { *argv_out = wmalloc(sizeof(char *) * cmd_argc); for (int i = 0; i < cmd_argc; i++) (*argv_out)[i] = wstrdup(cmd_argv[i]); *argc_out = cmd_argc; XFreeStringList(cmd_argv); return 1; } } #endif cmd = v->xw_surface->instance ? v->xw_surface->instance : v->xw_surface->class; } else if (v->xdg_surface && v->xdg_surface->toplevel) { cmd = v->xdg_surface->toplevel->app_id; } if (!cmd || !*cmd) return 0; *argv_out = wmalloc(sizeof(char *)); (*argv_out)[0] = wstrdup(cmd); *argc_out = 1; return 1; }