diff --git a/src/Makefile.am b/src/Makefile.am index e39908e..a453bc4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -178,7 +178,8 @@ wmaker_SOURCES += \ backend/wayland/wl_screen.c \ backend/wayland/wl_shm.c \ backend/wayland/wl_wings.c \ - backend/wayland/wl_xdisplay.c + backend/wayland/wl_xdisplay.c \ + backend/wayland/wl_stubs.c endif if USE_NESTED_FUNC diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 10a6e26..40ca379 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -292,7 +292,7 @@ void wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp); int wl_client_get_pid(WNativeWindow win); /* Phase 13: forward declarations — defined in pointer section below. */ static void wl_update_pointer_focus(uint32_t time_msec); -static void wl_pointer_warp_to(int x, int y); + void wl_pointer_warp_to(int x, int y); /* wmaker-popup-v1 helper — defined in protocol section below. */ void wmaker_popup_apply(struct wl_toplevel_view *view); /* Phase 14: pointer event handler forward declarations. */ @@ -407,7 +407,7 @@ handle_toplevel_title(struct wl_listener *listener, void *data) * Phase 23: set the cursor image by name from the loaded xcursor theme. * No-op if cursor or xcursor_mgr was not initialised. */ -static void +void wl_set_cursor_image(const char *name) { if (!wl_state.cursor || !wl_state.xcursor_mgr) @@ -2832,7 +2832,7 @@ wl_display_open(const char *name) return NULL; /* dpy=NULL so WM core skips Xlib calls */ } -static void + void wl_display_close(void) { if (!wl_state.started) @@ -3052,20 +3052,20 @@ wl_display_close(void) wl_state.started = 0; } -static void + void wl_display_post_open(const char *display_name, int *multi_head_out) { (void)display_name; (void)multi_head_out; } -static int + int wl_display_screen_count(void) { return wl_state.started ? 1 : 0; } -static int + int wl_display_default_screen(void) { return 0; @@ -3592,20 +3592,20 @@ wm_dock_app_remove(WNativeWindow id) /* Key / button grabs */ /* ------------------------------------------------------------------ */ -static void + void wl_key_grab(WNativeWindow win, unsigned int keycode, unsigned int modifier, int owner_events) { (void)win; (void)keycode; (void)modifier; (void)owner_events; } -static void wl_key_ungrab_all(WNativeWindow win) { (void)win; } + void wl_key_ungrab_all(WNativeWindow win) { (void)win; } -static void + void wl_button_grab(WNativeWindow win, unsigned int button, unsigned int modifier, unsigned int event_mask, int owner_events) { (void)win; (void)button; (void)modifier; (void)event_mask; (void)owner_events; } -static void wl_button_ungrab_all(WNativeWindow win) { (void)win; } + void wl_button_ungrab_all(WNativeWindow win) { (void)win; } /* ------------------------------------------------------------------ */ /* Phase 14: pointer input event handlers */ @@ -4796,7 +4796,7 @@ handle_pointer_frame(struct wl_listener *listener, void *data) /* Pointer grabs */ /* ------------------------------------------------------------------ */ -static int + int wl_pointer_grab(WNativeWindow grab_win, WNativeWindow confine_to, unsigned int event_mask, int cursor_id, int owner_events, unsigned long timestamp) @@ -4831,7 +4831,7 @@ wl_pointer_grab(WNativeWindow grab_win, WNativeWindow confine_to, return 1; /* success */ } -static void wl_pointer_ungrab(unsigned long timestamp) + void wl_pointer_ungrab(unsigned long timestamp) { (void)timestamp; wwarning("wl_pointer_ungrab: grab_type=%d", wl_state.grab_type); @@ -4992,7 +4992,7 @@ wl_update_pointer_focus(uint32_t time_msec) } } -static void + void wl_pointer_warp(int dx, int dy) { /* Phase 54: relative warp — clamp to output bounds. */ @@ -5012,7 +5012,7 @@ wl_pointer_warp(int dx, int dy) wl_update_pointer_focus(0); } -static void +void wl_pointer_warp_to(int x, int y) { /* Phase 54: absolute warp — clamp to output bounds. */ @@ -5031,7 +5031,7 @@ wl_pointer_warp_to(int x, int y) wl_update_pointer_focus(0); } -static int + int wl_pointer_query(WNativeWindow root, int *root_x, int *root_y) { /* Phase 54: return tracked virtual pointer position. */ @@ -5041,7 +5041,7 @@ wl_pointer_query(WNativeWindow root, int *root_x, int *root_y) return wl_state.started ? 1 : 0; } -static int + int wl_pointer_query_full(WNativeWindow root, WNativeWindow *child_out, int *root_x, int *root_y, @@ -5056,7 +5056,7 @@ wl_pointer_query_full(WNativeWindow root, return wl_state.started ? 1 : 0; } -static void + void wl_pointer_change_grab(unsigned int event_mask, int cursor_id, unsigned long timestamp) { @@ -5093,7 +5093,7 @@ wl_pointer_change_grab(unsigned int event_mask, int cursor_id, /* Keyboard grabs */ /* ------------------------------------------------------------------ */ -static int + int wl_keyboard_grab(WNativeWindow win) { (void)win; @@ -5101,7 +5101,7 @@ wl_keyboard_grab(WNativeWindow win) return 0; } -static void + void wl_keyboard_ungrab(void) { wl_state.keyboard_grabbed = false; @@ -5115,12 +5115,12 @@ wl_keyboard_ungrab(void) /* Screen internals */ /* ------------------------------------------------------------------ */ -static int wl_screen_replace_wm(WScreen *scr) { (void)scr; return 1; } -static int wl_screen_select_input(WScreen *scr, long mask) { (void)scr; (void)mask; return 1; } -static void wl_screen_set_root_cursor(WScreen *scr, int cursor_id) { (void)scr; (void)cursor_id; } -static void wl_screen_set_icon_sizes(WScreen *scr, int min_size, int max_size) { (void)scr; (void)min_size; (void)max_size; } -static void wl_screen_setup_noticeboard(WScreen *scr) { (void)scr; } -static void + int wl_screen_replace_wm(WScreen *scr) { (void)scr; return 1; } + int wl_screen_select_input(WScreen *scr, long mask) { (void)scr; (void)mask; return 1; } + void wl_screen_set_root_cursor(WScreen *scr, int cursor_id) { (void)scr; (void)cursor_id; } + void wl_screen_set_icon_sizes(WScreen *scr, int min_size, int max_size) { (void)scr; (void)min_size; (void)max_size; } + void wl_screen_setup_noticeboard(WScreen *scr) { (void)scr; } + void wl_screen_alloc_gcs(WScreen *scr) { XGCValues gcv; @@ -5215,7 +5215,7 @@ wl_screen_alloc_gcs(WScreen *scr) scr->mono_gc = XCreateGC(wl_state.x_display, scr->stipple_bitmap, GCGraphicsExposures, &gcv); } -static void + void wl_screen_create_internals(WScreen *scr) { /* Create a real off-screen InputOnly X window that absorbs keyboard focus @@ -5342,8 +5342,8 @@ wl_screen_create_internals(WScreen *scr) } } } -static void wl_screen_create_pixmaps(WScreen *scr) { (void)scr; } -static void + void wl_screen_create_pixmaps(WScreen *scr) { (void)scr; } + void wl_screen_load_tech_font(WScreen *scr) { if (!scr) return; @@ -5354,10 +5354,10 @@ wl_screen_load_tech_font(WScreen *scr) scr->tech_draw_font_ascent = OVL_GH - 2; /* 7 */ scr->tech_draw_font_descent = 2; } -static void wl_screen_show_mini_screenshot(WScreen *scr, RImage *img) { (void)scr; (void)img; } -static RImage *wl_screen_capture_area(WScreen *scr) { (void)scr; return NULL; } -static RImage *wl_screen_capture_window(WScreen *scr, WWindow *wwin) { (void)scr; (void)wwin; return NULL; } -static void wl_refresh_desktop(WScreen *scr) { (void)scr; } + void wl_screen_show_mini_screenshot(WScreen *scr, RImage *img) { (void)scr; (void)img; } + RImage *wl_screen_capture_area(WScreen *scr) { (void)scr; return NULL; } + RImage *wl_screen_capture_window(WScreen *scr, WWindow *wwin) { (void)scr; (void)wwin; return NULL; } + void wl_refresh_desktop(WScreen *scr) { (void)scr; } /* --- Desktop background rendering --- */ @@ -5409,7 +5409,7 @@ static int parse_color_to_rcolor(WScreen *scr, const char *name, RColor *out) return 1; } -static void wl_set_desktop_background(WScreen *scr, const char *texture_desc) + void wl_set_desktop_background(WScreen *scr, const char *texture_desc) { if (!scr || !texture_desc || !wl_state.output) return; @@ -5645,7 +5645,7 @@ static void wl_set_desktop_background(WScreen *scr, const char *texture_desc) } /* ------------------------------------------------------------------ */ -static void + void wl_monitors_query(WScreen *scr, WHeadGeometry **heads_out, int *count_out) { (void)scr; @@ -5682,1524 +5682,9 @@ wl_monitors_query(WScreen *scr, WHeadGeometry **heads_out, int *count_out) *count_out = 1; } -static void wl_monitors_select_events(WScreen *scr) { (void)scr; } + void wl_monitors_select_events(WScreen *scr) { (void)scr; } /* ------------------------------------------------------------------ */ -/* Phase 80: no-op stubs for X11-only operations */ -/* ------------------------------------------------------------------ */ - -static void wl_noop_frame_alloc_border_pixel(unsigned long colormap, - const char *color_name, unsigned long **pixel_out) -{ (void)colormap; (void)color_name; *pixel_out = NULL; } - -static void wl_noop_frame_update_texture(struct WFrameWindow *fwin) { (void)fwin; } - -void wl_frame_paint_button(WCoreWindow *button, WTexture *texture, - unsigned long color, WPixmap *image, int pushed) -{ - (void)texture; (void)image; - WFrameWindow *fwin = (WFrameWindow *)button->descriptor.parent; - if (!fwin || !fwin->core) - return; - - struct wl_frame_buf *btn_fb = frame_buf_find(button->window); - if (!btn_fb || !btn_fb->image) - return; - - int bw = btn_fb->width; - int bh = btn_fb->height; - - /* Determine which icon to use */ - RImage *icon = NULL; - if (button == fwin->right_button) { - if (!wl_state.btn_close_img) - wl_state.btn_close_img = RGetImageFromXPMData( - button->screen_ptr->rcontext, PRED_CLOSE_XPM); - icon = wl_state.btn_close_img; - } else if (button == fwin->left_button) { - if (!wl_state.btn_iconify_img) - wl_state.btn_iconify_img = RGetImageFromXPMData( - button->screen_ptr->rcontext, PRED_ICONIFY_XPM); - icon = wl_state.btn_iconify_img; - } - if (!icon) - return; - - /* Get titlebar background color */ - struct wl_frame_buf *tb_fb = fwin->titlebar ? frame_buf_find(fwin->titlebar->window) : NULL; - uint32_t bg_pixel = 0xFF333333; - if (tb_fb && tb_fb->image) { - uint32_t *tb_data = pixman_image_get_data(tb_fb->image); - int tb_stride = pixman_image_get_stride(tb_fb->image) / 4; - int sample_y = btn_fb->rel_y + bh / 2; - int sample_x = btn_fb->rel_x + bw / 2; - if (sample_y >= 0 && sample_y < tb_fb->height && - sample_x >= 0 && sample_x < tb_fb->width) - bg_pixel = tb_data[sample_y * tb_stride + sample_x]; - } - - /* Foreground = title text color; when pushed, swap fg/bg */ - uint32_t fg_pixel = 0xFF000000 | ((color >> 16 & 0xFF) << 16) | - ((color >> 8 & 0xFF) << 8) | (color & 0xFF); - uint32_t fill_bg = pushed ? fg_pixel : bg_pixel; - uint32_t fill_fg = pushed ? bg_pixel : fg_pixel; - - uint32_t *dst = pixman_image_get_data(btn_fb->image); - int dst_stride = pixman_image_get_stride(btn_fb->image) / 4; - - /* Fill background */ - for (int y = 0; y < bh; y++) - for (int x = 0; x < bw; x++) - dst[y * dst_stride + x] = fill_bg; - - /* Draw border bevel derived from bg color (raised normal, sunken pushed) */ - unsigned char bg_r = (bg_pixel >> 16) & 0xFF; - unsigned char bg_g = (bg_pixel >> 8) & 0xFF; - unsigned char bg_b = bg_pixel & 0xFF; - uint32_t hi_c = 0xFF000000 | ((uint32_t)(bg_r + (255-bg_r)/3) << 16) | - ((uint32_t)(bg_g + (255-bg_g)/3) << 8) | (bg_b + (255-bg_b)/3); - uint32_t lo_c = 0xFF000000 | ((uint32_t)(bg_r * 2/3) << 16) | - ((uint32_t)(bg_g * 2/3) << 8) | (bg_b * 2/3); - uint32_t hi = pushed ? lo_c : hi_c; - uint32_t lo = pushed ? hi_c : lo_c; - for (int x = 0; x < bw; x++) { dst[x] = hi; dst[(bh-1) * dst_stride + x] = lo; } - for (int y = 0; y < bh; y++) { dst[y * dst_stride] = hi; dst[y * dst_stride + bw - 1] = lo; } - - /* Blit icon centered, using fill_fg color for opaque pixels */ - int ix = (bw - icon->width) / 2 + (pushed ? 1 : 0); - int iy = (bh - icon->height) / 2 + (pushed ? 1 : 0); - - for (int y = 0; y < icon->height; y++) { - int dy = iy + y; - if (dy < 0 || dy >= bh) continue; - uint32_t *dst_row = dst + dy * dst_stride; - for (int x = 0; x < icon->width; x++) { - int dx = ix + x; - if (dx < 0 || dx >= bw) continue; - unsigned char a; - if (icon->format == RRGBAFormat) - a = icon->data[(y * icon->width + x) * 4 + 3]; - else - a = 255; - if (a == 0) continue; - dst_row[dx] = fill_fg; - } - } - - /* Enable and commit the button's own scene_buf */ - wlr_scene_buffer_set_buffer_with_damage(btn_fb->scene_buf, &btn_fb->base, NULL); - wlr_scene_node_set_enabled(&btn_fb->scene_buf->node, true); - wlr_scene_node_raise_to_top(&btn_fb->scene_buf->node); -} - -static void wl_noop_netwm_init(WScreen *scr) { (void)scr; } -static void wl_noop_netwm_cleanup(WScreen *scr) { (void)scr; } -static void wl_noop_netwm_update_desktop(WScreen *scr) { (void)scr; } -static int wl_noop_netwm_get_current_desktop(WScreen *scr) { (void)scr; return -1; } -static void wl_netwm_update_opacity(WWindow *wwin) -{ - if (!wwin->frame) - return; - - struct wl_frame_buf *fb = frame_buf_find(wwin->frame->core->window); - if (!fb || !fb->scene_buf) - return; - - float alpha = 1.0f; - if (wl_state.x_display && wwin->client_win) { - WMNativeAtom type; - int format; - unsigned long items, rest; - unsigned char *prop = NULL; - WMNativeAtom opacity_atom = XInternAtom(wl_state.x_display, - "_NET_WM_WINDOW_OPACITY", False); - if (XGetWindowProperty(wl_state.x_display, (WNativeWindow)wwin->client_win, - opacity_atom, 0L, 1L, False, AnyPropertyType, - &type, &format, &items, &rest, &prop) == Success - && format == 32 && items == 1 && prop) { - unsigned long val = *(unsigned long *)prop; - alpha = (float)val / (float)0xFFFFFFFF; - } - if (prop) - XFree(prop); - } - - wlr_scene_buffer_set_opacity(fb->scene_buf, alpha); - - /* Apply to all child frame_bufs (titlebar buttons, resizebar) */ - struct wl_frame_buf *child; - wl_list_for_each(child, &wl_state.frame_list, link) { - if (child->parent_id == fb->id && child->scene_buf) - wlr_scene_buffer_set_opacity(child->scene_buf, alpha); - } - - /* Apply to the client surface */ - struct wl_toplevel_view *v; - wl_list_for_each(v, &wl_state.toplevel_list, link) { - if (v->frame_id == fb->id && v->scene_node) { - struct wlr_scene_node *node; - wl_list_for_each(node, &v->scene_node->children, link) { - if (node->type == WLR_SCENE_NODE_BUFFER) - wlr_scene_buffer_set_opacity( - wlr_scene_buffer_from_node(node), alpha); - } - } - } -} -static void wl_noop_netwm_update_actions(WWindow *wwin, int del) { (void)wwin; (void)del; } -static void wl_noop_netwm_check_client_hints(WWindow *wwin, int *layer, int *workspace) -{ (void)wwin; (void)layer; (void)workspace; } -static void wl_noop_netwm_check_initial_state(WWindow *wwin) { (void)wwin; } -static int wl_noop_netwm_get_pid(WNativeWindow window) { (void)window; return 0; } -static void wl_noop_netwm_cleanup_frame_extents(WWindow *wwin) { (void)wwin; } -static void wl_noop_netwm_set_frame_extents(WWindow *wwin) { (void)wwin; } - -static void wl_prop_get_protocols(WNativeWindow window, WProtocols *prots) -{ - (void)window; - memset(prots, 0, sizeof(WProtocols)); - prots->DELETE_WINDOW = 1; /* All XDG toplevels support close */ -} - -static unsigned char *wl_noop_prop_get_check_property(WNativeWindow window, - unsigned long hint, unsigned long type, int format, int count, int *ret_count) -{ (void)window; (void)hint; (void)type; (void)format; (void)count; - if (ret_count) *ret_count = 0; return NULL; } - -static void wl_noop_prop_set_wmaker_protocols(WNativeWindow root) { (void)root; } -static void wl_noop_prop_set_icon_tile(WScreen *scr, RImage *image) { (void)scr; (void)image; } -static void wl_noop_prop_cleanup(WNativeWindow root) { (void)root; } - -static void wl_noop_client_restore(WWindow *wwin) { (void)wwin; } -static void wl_client_set_state(WWindow *wwin, int state, WNativeWindow icon_win) -{ - (void)icon_win; - struct wl_toplevel_view *v = wl_find_view_by_id(wwin->client_win); - if (!v) - return; - switch (state) { - case 3: /* WM_STATE_ICONIC */ - if (v->scene_node) - wlr_scene_node_set_enabled(&v->scene_node->node, false); - break; - case 1: /* WM_STATE_NORMAL */ - if (v->scene_node) - wlr_scene_node_set_enabled(&v->scene_node->node, true); - break; - } -} - -static void wl_client_set_maximized(WNativeWindow win, int maximized) -{ - struct wl_toplevel_view *v = wl_find_view_by_id(win); - if (!v) return; - if (v->xdg_surface && v->xdg_surface->toplevel) - wlr_xdg_toplevel_set_maximized(v->xdg_surface->toplevel, maximized); -} - -static void wl_client_send_protocol(WWindow *wwin, unsigned long protocol, - unsigned long timestamp) -{ - (void)protocol; (void)timestamp; - wm_backend->client_close(wwin->client_win); -} - -static void wl_client_kill_impl(WWindow *wwin) -{ - wm_backend->client_close(wwin->client_win); -} - -static void wl_noop_client_get_colormap_windows(WWindow *wwin) { (void)wwin; } - -static void wl_noop_eat_expose(void) { } - -static void wl_noop_move_window(WNativeWindow win, int from_x, int from_y, - int to_x, int to_y) -{ (void)win; (void)from_x; (void)from_y; (void)to_x; (void)to_y; } - -static void wl_noop_slide_windows(WNativeWindow *wins, int n, - int from_x, int from_y, int to_x, int to_y) -{ (void)wins; (void)n; (void)from_x; (void)from_y; (void)to_x; (void)to_y; } - -static char *wl_noop_get_text_selection(WScreen *scr, unsigned long selection) -{ (void)scr; (void)selection; return NULL; } - -static char *wl_noop_get_shortcut_string(WShortKey key) -{ (void)key; return NULL; } - -static char *wl_get_command_for_window(WNativeWindow win, int elements) -{ - char **argv, *command = NULL; - int argc; - if (!wm_backend->icccm_get_command(win, &argv, &argc)) - return NULL; - if (argc > 0 && argv != NULL) { - if (elements == 0) - elements = argc; - command = wtokenjoin(argv, WMIN(argc, elements)); - if (command[0] == 0) { - wfree(command); - command = NULL; - } - } - wm_backend->icccm_free_command(argv); - return command; -} - -static void wl_noop_icon_update_pixmap(WIcon *icon) { (void)icon; } - -/* - * wl_icon_paint - render a miniwindow (iconified window) icon into its - * frame_buf and commit it to the scene graph. - * - * Mirrors wl_appicon_paint: tile background + optional image + title text. - * Called at the end of wIconPaint() in icon.c so the backend finalises the - * surface after the policy layer has set up icon->file_image etc. - */ -static void -wl_icon_paint(WIcon *icon) -{ - if (!icon || !icon->core) return; - - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)icon->core->window); - if (!fb || !fb->image) return; - - WScreen *scr = icon->core->screen_ptr; - if (!scr) return; - int size = wPreferences.icon_size; - - /* --- Tile background --- */ - RImage *tile = scr->icon_tile ? RCloneImage(scr->icon_tile) : NULL; - if (!tile) { - tile = RCreateImage(size, size, 1); - if (!tile) return; - RColor grey = {0xAE, 0xAA, 0xAE, 0xFF}; - RClearImage(tile, &grey); - } - - /* --- Composite icon image (file_image from database / NET_WM_ICON) --- */ - RImage *img = icon->file_image; - if (img) { - int theight = icon->show_title ? WMFontHeight(scr->icon_title_font) : 0; - unsigned w = ((unsigned)img->width > (unsigned)size) ? (unsigned)size : (unsigned)img->width; - unsigned h = ((unsigned)img->height + (unsigned)theight > (unsigned)size) - ? (unsigned)(size - theight) : (unsigned)img->height; - int x = (size - (int)w) / 2; - int y = theight + (size - theight - (int)h) / 2; - int sx = ((int)img->width - (int)w) / 2; - int sy = ((int)img->height - (int)h) / 2; - RCombineArea(tile, img, sx, sy, w, h, x, y); - } - - /* --- Blit RImage (RGBA/RGB) -> pixman ARGB32 --- */ - uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); - int stride = pixman_image_get_stride(fb->image) / 4; - unsigned char *tsrc = tile->data; - int has_alpha = (tile->format == RRGBAFormat); - for (int row = 0; row < size && row < fb->height; row++) { - for (int col = 0; col < size && col < fb->width; col++) { - int si = (row * tile->width + col) * (has_alpha ? 4 : 3); - unsigned char r = tsrc[si], g = tsrc[si+1], b = tsrc[si+2]; - unsigned char a = has_alpha ? tsrc[si+3] : 0xFF; - dst[row * stride + col] = ((uint32_t)a << 24) | ((uint32_t)r << 16) - | ((uint32_t)g << 8) | (uint32_t)b; - } - } - RReleaseImage(tile); - - /* --- Title text --- */ - if (icon->show_title && icon->icon_name && scr->icon_title_font) { - WMDrawable d = (WMDrawable)(uintptr_t)fb->image; - int tw = WMWidthOfString(scr->icon_title_font, icon->icon_name, - strlen(icon->icon_name)); - int tx = (size - tw) / 2; - if (tx < 2) tx = 2; - WMColor *color = scr->icon_title_color; - if (color) - WMDrawString(scr->wmscreen, d, color, scr->icon_title_font, - tx, 0, icon->icon_name, strlen(icon->icon_name)); - } - - /* --- Commit to scene graph --- */ - wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); - wlr_scene_node_set_enabled(&fb->scene_buf->node, true); - if (fb->tree) - wlr_scene_node_set_enabled(&fb->tree->node, true); -} -static void wl_appicon_show(WApplication *wapp) { paint_app_icon(wapp); } -static void wl_appicon_paint(WAppIcon *aicon) -{ - WIcon *icon = aicon->icon; - if (!icon || !icon->core) - return; - - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)icon->core->window); - if (!fb || !fb->image) - return; - - WScreen *scr = icon->core->screen_ptr; - int size = wPreferences.icon_size; - - /* Compose the tile image */ - RImage *tile = NULL; - switch (icon->tile_type) { - case TILE_CLIP: tile = scr->clip_tile ? RCloneImage(scr->clip_tile) : NULL; break; - case TILE_DRAWER: tile = scr->drawer_tile ? RCloneImage(scr->drawer_tile) : NULL; break; - default: tile = scr->icon_tile ? RCloneImage(scr->icon_tile) : NULL; break; - } - if (!tile) { - /* Fallback: solid grey */ - tile = RCreateImage(size, size, 1); - if (!tile) return; - RColor grey = {0xAE, 0xAA, 0xAE, 0xFF}; - RClearImage(tile, &grey); - } - - /* Composite the app image on top */ - if (icon->file_image) { - RImage *img = icon->file_image; - int theight = icon->show_title ? WMFontHeight(scr->icon_title_font) : 0; - unsigned w = (img->width > size) ? size : img->width; - unsigned h = (img->height + theight > size) ? size - theight : img->height; - int x = (size - (int)w) / 2; - int y = theight + (size - theight - (int)h) / 2; - int sx = ((int)img->width - (int)w) / 2; - int sy = ((int)img->height - (int)h) / 2; - RCombineArea(tile, img, sx, sy, w, h, x, y); - } - - /* Blit RImage (RGBA) → pixman ARGB32 buffer */ - uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); - int stride = pixman_image_get_stride(fb->image) / 4; - unsigned char *src = tile->data; - int has_alpha = (tile->format == RRGBAFormat); - - for (int row = 0; row < size && row < fb->height; row++) { - for (int col = 0; col < size && col < fb->width; col++) { - int si = (row * tile->width + col) * (has_alpha ? 4 : 3); - unsigned char r = src[si], g = src[si+1], b = src[si+2]; - unsigned char a = has_alpha ? src[si+3] : 0xFF; - dst[row * stride + col] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | - ((uint32_t)g << 8) | (uint32_t)b; - } - } - - /* Draw title text */ - if (icon->show_title && icon->icon_name && scr->icon_title_font) { - WMDrawable d = (WMDrawable)(uintptr_t)fb->image; - int fh = WMFontHeight(scr->icon_title_font); - int tw = WMWidthOfString(scr->icon_title_font, icon->icon_name, - strlen(icon->icon_name)); - int tx = (size - tw) / 2; - if (tx < 2) tx = 2; - int ty = 0; /* top of text area */ - WMColor *color = scr->icon_title_color; - if (color) - WMDrawString(scr->wmscreen, d, color, scr->icon_title_font, - tx, ty, icon->icon_name, strlen(icon->icon_name)); - } - - RReleaseImage(tile); - - /* Commit to scene graph */ - wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); - wlr_scene_node_set_enabled(&fb->scene_buf->node, true); -} -static void wl_noop_restore_desktop(WScreen *scr) { (void)scr; } -static void wl_noop_wipe_desktop(WScreen *scr) { (void)scr; } - -static int wl_get_color_for_colormap(unsigned long colormap, - const char *color_name, WMColorRGB *color) -{ - unsigned int r, g, b; - (void)colormap; - - if (!color_name || !color) - return 0; - - /* Parse #RGB, #RRGGBB, #RRRRGGGGBBBB */ - if (color_name[0] == '#') { - int len = strlen(color_name + 1); - if (len == 6) { - if (sscanf(color_name, "#%2x%2x%2x", &r, &g, &b) == 3) { - color->red = r << 8 | r; - color->green = g << 8 | g; - color->blue = b << 8 | b; - color->pixel = (0xFF << 24) | (r << 16) | (g << 8) | b; - return 1; - } - } else if (len == 12) { - if (sscanf(color_name, "#%4hx%4hx%4hx", - &color->red, &color->green, &color->blue) == 3) { - color->pixel = (0xFF << 24) | - ((color->red >> 8) << 16) | - ((color->green >> 8) << 8) | - (color->blue >> 8); - return 1; - } - } else if (len == 3) { - if (sscanf(color_name, "#%1x%1x%1x", &r, &g, &b) == 3) { - color->red = r << 12 | r << 8 | r << 4 | r; - color->green = g << 12 | g << 8 | g << 4 | g; - color->blue = b << 12 | b << 8 | b << 4 | b; - color->pixel = (0xFF << 24) | (r*17 << 16) | (g*17 << 8) | b*17; - return 1; - } - } - } - - /* X11 rgb:RR/GG/BB or rgb:RRRR/GGGG/BBBB format */ - if (strncasecmp(color_name, "rgb:", 4) == 0) { - unsigned int rv, gv, bv; - const char *p = color_name + 4; - int n = sscanf(p, "%x/%x/%x", &rv, &gv, &bv); - if (n == 3) { - /* Determine component width from first component */ - const char *slash = strchr(p, '/'); - int width = slash ? (int)(slash - p) : 2; - if (width == 1) { - color->red = rv << 12 | rv << 8 | rv << 4 | rv; - color->green = gv << 12 | gv << 8 | gv << 4 | gv; - color->blue = bv << 12 | bv << 8 | bv << 4 | bv; - } else if (width == 2) { - color->red = rv << 8 | rv; - color->green = gv << 8 | gv; - color->blue = bv << 8 | bv; - } else { - color->red = rv; - color->green = gv; - color->blue = bv; - } - color->pixel = (0xFF << 24) | - ((color->red >> 8) << 16) | - ((color->green >> 8) << 8) | - (color->blue >> 8); - return 1; - } - } - - /* Named colors — common ones */ - struct { const char *name; unsigned short r, g, b; } named[] = { - {"black", 0x0000, 0x0000, 0x0000}, - {"white", 0xFFFF, 0xFFFF, 0xFFFF}, - {"red", 0xFFFF, 0x0000, 0x0000}, - {"green", 0x0000, 0xFFFF, 0x0000}, - {"blue", 0x0000, 0x0000, 0xFFFF}, - {"gray", 0xBEBE, 0xBEBE, 0xBEBE}, - {"grey", 0xBEBE, 0xBEBE, 0xBEBE}, - {"darkgray",0xA9A9, 0xA9A9, 0xA9A9}, - {"darkgrey",0xA9A9, 0xA9A9, 0xA9A9}, - {"lightgray",0xD3D3, 0xD3D3, 0xD3D3}, - {"lightgrey",0xD3D3, 0xD3D3, 0xD3D3}, - {NULL, 0, 0, 0} - }; - for (int i = 0; named[i].name; i++) { - if (strcasecmp(color_name, named[i].name) == 0) { - color->red = named[i].r; - color->green = named[i].g; - color->blue = named[i].b; - color->pixel = (0xFF << 24) | - ((color->red >> 8) << 16) | - ((color->green >> 8) << 8) | - (color->blue >> 8); - return 1; - } - } - - /* grayNN / greyNN (0-100) */ - if ((strncasecmp(color_name, "gray", 4) == 0 || strncasecmp(color_name, "grey", 4) == 0) - && color_name[4] >= '0' && color_name[4] <= '9') { - int pct = atoi(color_name + 4); - if (pct >= 0 && pct <= 100) { - unsigned int v = (pct * 255 + 50) / 100; - color->red = color->green = color->blue = v << 8 | v; - color->pixel = (0xFF << 24) | (v << 16) | (v << 8) | v; - return 1; - } - } - - /* Look up in X11 rgb.txt database */ - { - static const char *rgb_paths[] = { - "/usr/share/X11/rgb.txt", - "/usr/lib/X11/rgb.txt", - "/etc/X11/rgb.txt", - NULL - }; - FILE *f = NULL; - for (int i = 0; rgb_paths[i] && !f; i++) - f = fopen(rgb_paths[i], "r"); - if (f) { - char line[256]; - while (fgets(line, sizeof(line), f)) { - unsigned int rv, gv, bv; - char name[128]; - if (line[0] == '!' || line[0] == '#') - continue; - if (sscanf(line, "%u %u %u %127[^\n]", &rv, &gv, &bv, name) == 4) { - /* Trim leading whitespace/tabs from name */ - char *n = name; - while (*n == ' ' || *n == '\t') n++; - if (strcasecmp(n, color_name) == 0) { - color->red = rv << 8 | rv; - color->green = gv << 8 | gv; - color->blue = bv << 8 | bv; - color->pixel = (0xFF << 24) | (rv << 16) | (gv << 8) | bv; - fclose(f); - return 1; - } - } - } - fclose(f); - } - } - - wwarning(_("could not parse color \"%s\" (Wayland)"), color_name); - return 0; -} - -static void wl_noop_free_color(WScreen *scr, unsigned long pixel) -{ (void)scr; (void)pixel; } - -static void wl_noop_colormap_install(WScreen *scr, WWindow *wwin) -{ (void)scr; (void)wwin; } - -static void wl_noop_xdnd_init(void) { } - -static int wl_noop_motif_get_hints(WNativeWindow window, MWMHints *hints_out) -{ (void)window; (void)hints_out; return 0; } - -static void wl_noop_appicon_bounce(WAppIcon *aicon, void *data) -{ (void)aicon; (void)data; } - -static void wl_noop_manage_all_windows(WScreen *scr, int crash_recovery) -{ (void)scr; (void)crash_recovery; } - -static void wl_event_handle_signal_exit(void) -{ - wm_backend->event_loop_terminate(); -} - -static void wl_noop_icon_select(WIcon *icon) { (void)icon; } -static void wl_noop_icon_change_title(WIcon *icon, WWindow *wwin) { (void)icon; (void)wwin; } -static void wl_clip_icon_paint(WAppIcon *aicon) -{ - if (!aicon || !aicon->icon || !aicon->icon->core) - return; - - WScreen *scr = aicon->icon->core->screen_ptr; - if (!scr->workspaces || !scr->workspaces[scr->current_workspace]) - return; - - /* Paint base tile + icon image */ - wl_appicon_paint(aicon); - - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)aicon->icon->core->window); - if (!fb || !fb->image) - return; - - WWorkspace *workspace = scr->workspaces[scr->current_workspace]; - /* Use aicon->dock to read collapsed state — workspace->clip may be a - * stale freed pointer during wWorkspaceRestoreState (the old clip is - * destroyed before scr->workspaces[i]->clip is reassigned, and we - * are called from wDockCreate inside that gap). */ - int collapsed = aicon->dock ? aicon->dock->collapsed : 0; - WMColor *color; - if (wPreferences.flags.noclip || !collapsed) - color = scr->clip_title_color[CLIP_NORMAL]; - else - color = scr->clip_title_color[CLIP_COLLAPSED]; - if (!color || !scr->clip_title_font) - return; - - int size = wPreferences.icon_size; - WMDrawable d = (WMDrawable)(uintptr_t)fb->image; - - /* Draw workspace number at top center */ - char ws_number[20]; - snprintf(ws_number, sizeof ws_number, "%u", scr->current_workspace + 1); - int nlength = strlen(ws_number); - int tx = (size / 2 - WMWidthOfString(scr->clip_title_font, ws_number, nlength)) / 2; - WMDrawString(scr->wmscreen, d, color, scr->clip_title_font, tx, 2, ws_number, nlength); - - /* Draw workspace name at bottom */ - if (wPreferences.show_clip_title) { - int length = strlen(workspace->name); - int ty = size - WMFontHeight(scr->clip_title_font) - 3; - tx = CLIP_BUTTON_SIZE * size / 64; - WMDrawString(scr->wmscreen, d, color, scr->clip_title_font, tx, ty, workspace->name, length); - } - - /* Commit updated buffer */ - wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); -} -static void wl_drawer_icon_paint(WAppIcon *aicon) -{ - if (!aicon || !aicon->icon || !aicon->icon->core) - return; - - wl_appicon_paint(aicon); - - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)aicon->icon->core->window); - if (!fb || !fb->image) - return; - - WScreen *scr = aicon->icon->core->screen_ptr; - WMColor *color; - if (!aicon->dock->collapsed) - color = scr->clip_title_color[CLIP_NORMAL]; - else - color = scr->clip_title_color[CLIP_COLLAPSED]; - if (!color) - return; - - int size = wPreferences.icon_size; - uint32_t pixel = ((uint32_t)0xFF << 24) | - ((uint32_t)WMRedComponentOfColor(color) >> 8 << 16) | - ((uint32_t)WMGreenComponentOfColor(color) >> 8 << 8) | - ((uint32_t)WMBlueComponentOfColor(color) >> 8); - uint32_t *data = (uint32_t *)pixman_image_get_data(fb->image); - int stride = pixman_image_get_stride(fb->image) / 4; - - /* Draw directional arrow (triangle) */ - int cx, cy = size / 2; - if (aicon->dock->on_right_side) { - /* Arrow pointing left: tip at x=5, base at x=10 */ - for (int dy = -5; dy <= 5; dy++) { - int row = cy + dy; - if (row < 0 || row >= size) continue; - int width = 5 - abs(dy); - for (int dx = 0; dx <= width; dx++) { - cx = 10 - dx; - if (cx >= 0 && cx < size) - data[row * stride + cx] = pixel; - } - } - } else { - /* Arrow pointing right: tip at x=size-6, base at x=size-11 */ - for (int dy = -5; dy <= 5; dy++) { - int row = cy + dy; - if (row < 0 || row >= size) continue; - int width = 5 - abs(dy); - for (int dx = 0; dx <= width; dx++) { - cx = size - 11 + dx; - if (cx >= 0 && cx < size) - data[row * stride + cx] = pixel; - } - } - } - - wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); -} -static 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; - } -} -static void wl_noop_do_kaboom(WScreen *scr, WNativeWindow win, int x, int y) -{ (void)scr; (void)win; (void)x; (void)y; } -static 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); - 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); -} - -static 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); -} - -static void wl_noop_draw_bevel(WNativeWindow d, unsigned int width, unsigned int height, - WTexSolid *texture, int relief) -{ (void)d; (void)width; (void)height; (void)texture; (void)relief; } - -static void wl_noop_appmenu_send(WNativeWindow window, int what, int tag) -{ (void)window; (void)what; (void)tag; } - -static const char *wl_display_get_name(const char *name) -{ - /* For X11 clients launched under XWayland, return the XWayland display. - * For native Wayland clients, DON'T set DISPLAY (they use WAYLAND_DISPLAY). - * Fallback: return the Wayland display name for backward compatibility. */ - (void)name; - if (wl_state.xwayland && wl_state.xwayland->display_name) - return wl_state.xwayland->display_name; - return "wayland-0"; -} - -static void wl_noop_switchpanel_send_enter(WNativeWindow info_win, WNativeWindow window) -{ (void)info_win; (void)window; } - -static void wl_noop_format_x_error(void *error, char *buf, int size) -{ (void)error; if (size > 0) buf[0] = '\0'; } - -static WPixmap *wl_noop_pixmap_create_from_xbm(WScreen *scr, char *data, char *mask, - int width, int height, unsigned long fg, unsigned long bg) -{ (void)scr; (void)data; (void)mask; (void)width; (void)height; (void)fg; (void)bg; return NULL; } - -static WPixmap *wl_noop_pixmap_create(unsigned long image, unsigned long mask) -{ (void)image; (void)mask; return NULL; } - -static unsigned long wl_keysym_from_name(const char *name) -{ - return (unsigned long)xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); -} - -static void wl_frame_paint_resizebar(WFrameWindow *fwin) -{ - /* Resizebar is rendered into the parent frame buffer by wl_frame_paint. - * Don't commit a separate buffer here — it would overlay the texture. */ - (void)fwin; -} - -static void wl_frame_paint_titlebar(WFrameWindow *fwin, int state) -{ - if (!fwin->titlebar || !fwin->titlebar->window) - return; - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)fwin->titlebar->window); - if (!fb || !fb->image) - return; - - WScreen *scr = fwin->screen_ptr; - uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); - int stride = pixman_image_get_stride(fb->image) / 4; - - /* Background: use texture color or default */ - uint32_t bg; - if (fwin->title_texture[state] && - fwin->title_texture[state]->any.type == WTEX_SOLID) { - unsigned long px = fwin->title_texture[state]->solid.normal.pixel; - bg = 0xFF000000 | (px & 0xFFFFFF); - } else { - bg = (state == 0) ? 0xFF4A6984 : 0xFF808080; /* focused blue / unfocused grey */ - } - - for (int row = 0; row < fb->height; row++) - for (int col = 0; col < fb->width; col++) - dst[row * stride + col] = bg; - - /* Draw title text */ - if (fwin->title && fwin->font && fwin->title_color[state]) { - char *title = ShrinkString(*fwin->font, fwin->title, fb->width - 12); - int titlelen = strlen(title); - int tw = WMWidthOfString(*fwin->font, title, titlelen); - int x; - switch (fwin->flags.justification) { - case WTJ_LEFT: x = 6; break; - case WTJ_RIGHT: x = fb->width - tw - 6; break; - default: x = (fb->width - tw) / 2; break; - } - int fh = WMFontHeight(*fwin->font); - int y = (fb->height - fh) / 2; - - WMDrawable d = (WMDrawable)(uintptr_t)fb->image; - WMDrawString(scr->wmscreen, d, fwin->title_color[state], - *fwin->font, x, y, title, titlelen); - wfree(title); - } - - wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); -} - -static WNativeWindow wl_noop_prop_get_client_leader(WNativeWindow window) { (void)window; return WNATIVE_NONE; } -static int wl_noop_prop_get_window_state(WNativeWindow window) { (void)window; return -1; } - -static void wl_noop_setup_modifier_lock_masks(void) { } -static void wl_noop_key_grab_hacked(WNativeWindow window, int keycode, unsigned int modifiers) -{ (void)window; (void)keycode; (void)modifiers; } -static void wl_noop_button_grab_hacked(unsigned int button, unsigned int modifiers, - WNativeWindow window, int owner_events, unsigned int event_mask) -{ (void)button; (void)modifiers; (void)window; (void)owner_events; (void)event_mask; } -static 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; -} -static void wl_noop_query_tree(WNativeWindow root, WNativeWindow **children_out, - unsigned int *nchildren_out) -{ (void)root; *children_out = NULL; *nchildren_out = 0; } - -static WScreen *wl_noop_screen_for_window(WNativeWindow window) -{ (void)window; return wScreenWithNumber(0); } - -static int wl_parse_color(WScreen *scr, const char *name, WMColorRGB *color_out) -{ (void)scr; return wl_get_color_for_colormap(0, name, color_out); } -static int wl_noop_cursor_create_from_files(WScreen *scr, const char *bitmap_path, - const char *mask_path, WMNativeCursor *cursor_out) -{ (void)scr; (void)bitmap_path; (void)mask_path; (void)cursor_out; return 0; } -static void wl_noop_cursor_free(WMNativeCursor cursor) { (void)cursor; } -static void wl_noop_cursor_set(WNativeWindow window, WMNativeCursor cursor) { (void)window; (void)cursor; } -static void wl_pointer_change_cursor(int cursor_id) -{ - const char *name; - switch (cursor_id) { - case 1: /* WCUR_MOVE */ name = "fleur"; break; - case 2: /* WCUR_RESIZE */ name = "bottom_right_corner"; break; - case 7: /* WCUR_WAIT */ name = "watch"; break; - case 8: /* WCUR_ARROW */ name = "left_ptr"; break; - case 9: /* WCUR_QUESTION */name = "question_arrow"; break; - case 10:/* WCUR_TEXT */ name = "xterm"; break; - default: name = "left_ptr"; break; - } - wl_set_cursor_image(name); -} -static void wl_noop_appicon_paint_dock_dots(WAppIcon *aicon, void *wapp) -{ (void)aicon; (void)wapp; } - -static int wl_noop_pixmap_get_size(unsigned long drawable, unsigned int *w, unsigned int *h, unsigned int *depth) -{ (void)drawable; *w = 0; *h = 0; *depth = 0; return 0; } -static void wl_noop_icon_paint_title_bg(WScreen *scr, unsigned long pixmap, int height) -{ (void)scr; (void)pixmap; (void)height; } -static void wl_noop_icon_select_paint(WIcon *icon, int step) -{ (void)icon; (void)step; } -static unsigned long wl_noop_window_get_event_mask(WNativeWindow window) -{ (void)window; return 0; } -static 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) { - W_UnregisterBacking(fb->id); - 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; - W_RegisterBacking(fb->id, fb->image); - } - - /* 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)fb->id, 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; -} -static void wl_noop_window_fill_stipple(WNativeWindow win, int x, int y, int w, int h) -{ (void)win; (void)x; (void)y; (void)w; (void)h; } -static int wl_noop_event_check_enter_leave(void *dock) { (void)dock; return 1; } - -static void wl_noop_event_allow_async(void) { } -static void wl_noop_event_replay_pointer(void) { } -static void wl_noop_keyboard_mapping_refresh(WMEvent *event) { (void)event; } -static void wl_noop_event_flush_window(WNativeWindow win, int t) { (void)win; (void)t; } -static void wl_noop_event_flush_type(int t) { (void)t; } -static int wl_noop_event_check_window(WNativeWindow win, int t) { (void)win; (void)t; return 0; } -static int wl_noop_event_check_type(int t, WMEvent *e) { (void)t; (void)e; return 0; } -static void wl_noop_client_send_event(WNativeWindow win, void *d) { (void)win; (void)d; } -static void wl_client_message_forward(WMEvent *event, WNativeWindow target_win) -{ - /* Forward OffiX DND ClientMessage to the real client_win for - * XWayland clients. Native Wayland clients have no analog. */ - if (!wl_state.x_display || !event) return; - XEvent *xev = (XEvent *)event->_x11_raw; - if (!xev) return; - xev->xclient.window = (WNativeWindow)target_win; - XSendEvent(wl_state.x_display, (WNativeWindow)target_win, False, 0L, xev); -} -static void wl_client_send_key(WNativeWindow win, unsigned int keycode, - unsigned int modifier_mask, int press) -{ - /* For XWayland clients, deliver via the X server (wl_state.x_display) - * the same way the X11 backend does. Native Wayland clients are not - * yet supported (would require zwp_virtual_keyboard_v1). */ - if (!wl_state.x_display) return; - XEvent ev; - memset(&ev, 0, sizeof(ev)); - ev.xkey.type = press ? KeyPress : KeyRelease; - ev.xkey.display = wl_state.x_display; - ev.xkey.window = (WNativeWindow)win; - ev.xkey.root = DefaultRootWindow(wl_state.x_display); - ev.xkey.subwindow = WM_NATIVE_NONE; - ev.xkey.same_screen = True; - ev.xkey.time = WM_CURRENT_TIME; - ev.xkey.keycode = keycode; - ev.xkey.state = modifier_mask; - XSendEvent(wl_state.x_display, (WNativeWindow)win, press ? False : True, - press ? WM_EVMASK_KEY_PRESS : WM_EVMASK_KEY_RELEASE, &ev); -} -static int wl_noop_window_exists(WNativeWindow win) { (void)win; return 1; } -static void wl_noop_window_get_colormap(WNativeWindow win, unsigned long *c) { (void)win; (void)c; } -static WNativeWindow wl_noop_pointer_query_window(WNativeWindow root) { (void)root; return WNATIVE_NONE; } -static void wl_noop_dialog_paint_icon(unsigned long d, void *b, void *p, WScreen *s, - int x, int y, int w, int h) { (void)d;(void)b;(void)p;(void)s;(void)x;(void)y;(void)w;(void)h; } -static void wl_pointer_get_position(int *x, int *y) { - *x = (int)wl_state.pointer_x; - *y = (int)wl_state.pointer_y; -} -static const char *wl_noop_keysym_to_string(unsigned long ks) { (void)ks; return ""; } -static 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) { - /* 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) { - /* Copy to wmaker's allocator */ - *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; - } - } - 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; -} - -static int wl_noop_window_get_command(WNativeWindow win, char ***a, int *c) { (void)win; (void)a; *c=0; return 0; } -static int wl_noop_netwm_process_client_message(WMEvent *e) { (void)e; return 0; } -static int wl_noop_xdnd_process_client_message(WMEvent *e) { (void)e; return 0; } -static void wl_noop_xdnd_process_selection(WMEvent *e) { (void)e; } -static void wl_noop_xdnd_make_awareness(WNativeWindow w) { (void)w; } -static void wl_noop_handle_selection_request(WMEvent *e) { (void)e; } -static void wl_noop_handle_selection_clear(WMEvent *e) { (void)e; } -static void wl_noop_netwm_check_client_hint_change(WWindow *w, void *e) { (void)w; (void)e; } -static void wl_noop_netwm_position_splash(WWindow *w, int *x, int *y, int ww, int h) { (void)w;(void)x;(void)y;(void)ww;(void)h; } -static void wl_noop_netwm_check_initial_frame_state(WWindow *w) { (void)w; } - -static RContext *wl_create_rcontext(int screen_number, RContextAttributes *attribs) -{ - (void)screen_number; - return RCreateContextWayland(1920, 1080, attribs); -} - -/* ------------------------------------------------------------------ */ -/* vtable */ -/* ------------------------------------------------------------------ */ - -const WMBackend wl_backend = { - .is_wayland = 1, /* Path 1: distinguishes Wayland from X11 */ - .display_open = wl_display_open, - .display_close = wl_display_close, - .display_post_open = wl_display_post_open, - .display_screen_count = wl_display_screen_count, - .display_default_screen = wl_display_default_screen, - .atom_intern = wl_atom_intern, - .event_loop_run = wl_event_loop_run, - .event_loop_terminate = wl_event_loop_terminate, - - .screen_open = wl_screen_open, - .screen_init_display = wl_screen_init_display, - .screen_close = wl_screen_close, - .show_crash_dialog = wl_show_crash_dialog, - - .frame_create_toplevel = wl_frame_create_toplevel, - .frame_create_child = wl_frame_create_child, - .frame_destroy = wl_frame_destroy, - .frame_configure = wl_frame_configure, - .frame_paint = wl_frame_paint, - - .context_save = wl_context_save, - .context_find = wl_context_find, - .context_delete = wl_context_delete, - - .focus_set = wl_focus_set, - - .key_grab = wl_key_grab, - .key_ungrab_all = wl_key_ungrab_all, - .button_grab = wl_button_grab, - .button_ungrab_all = wl_button_ungrab_all, - - .pointer_grab = wl_pointer_grab, - .pointer_ungrab = wl_pointer_ungrab, - .pointer_warp = wl_pointer_warp, - .pointer_warp_to = wl_pointer_warp_to, - .pointer_query = wl_pointer_query, - .pointer_query_full = wl_pointer_query_full, - .pointer_change_grab = wl_pointer_change_grab, - - .keyboard_grab = wl_keyboard_grab, - .keyboard_ungrab = wl_keyboard_ungrab, - - .server_grab = wl_server_grab, - .server_ungrab = wl_server_ungrab, - - .stacking_raise = wl_stacking_raise, - .stacking_lower = wl_stacking_lower, - .stacking_restack = wl_stacking_restack, - .stacking_query = wl_stacking_query, - .stacking_set_level = wl_stacking_set_level, - - .event_flush = wl_event_flush, - .event_flush_expose = wl_event_flush_expose, - .event_sync = wl_event_sync, - .event_pending = wl_event_pending, - .event_drain_motion = wl_event_drain_motion, - .event_discard_enter_leave_until = wl_event_discard_enter_leave_until, - .event_handle_extensions = wl_event_handle_extensions, - .event_randr_update = wl_event_randr_update, - - .prop_get_normal_hints = wl_prop_get_normal_hints, - .prop_get_wm_hints = wl_prop_get_wm_hints, - .prop_get_wm_class = wl_prop_get_wm_class, - .window_fetch_name = wl_window_fetch_name, - .window_get_icon_name = wl_window_get_icon_name, - .screen_get_head_usable_area = wl_noop_screen_get_head_usable_area, - .window_get_icon_image = wl_window_get_icon_image, - .prop_get_wmaker_menu = wl_prop_get_wmaker_menu, - .prop_get_transient_for = wl_prop_get_transient_for, - .prop_has_wmaker_menu = wl_prop_has_wmaker_menu, - .prop_has_gtk_object_path = wl_prop_has_gtk_object_path, - .prop_set_wm_state = wl_prop_set_wm_state, - .prop_get_wm_state = wl_prop_get_wm_state, - - .window_map = wl_window_map, - .window_map_raised = wl_window_map_raised, - .window_map_subwindows = wl_window_map_subwindows, - .pixmap_free = wl_pixmap_free, - .window_unmap = wl_window_unmap, - .window_move = wl_window_move, - .window_resize = wl_window_resize, - .window_set_border = wl_window_set_border, - .window_set_border_width = wl_window_set_border_width, - .window_set_save_under = wl_window_set_save_under, - .window_set_background = wl_noop_window_set_background, - .window_set_background_pixmap = wl_window_set_background_pixmap, - .window_clear = wl_window_clear, - .window_get_rect = wl_window_get_rect, - .window_add_events = wl_window_add_events, - .window_select_events = wl_window_select_events, - .window_shape_clear = wl_window_shape_clear, - .window_shape_set = wl_window_shape_set, - - .overlay_draw_line = wl_overlay_draw_line, - .overlay_draw_lines = wl_overlay_draw_lines, - .overlay_draw_rect = wl_overlay_draw_rect, - .overlay_draw_segments = wl_overlay_draw_segments, - .overlay_draw_string = wl_overlay_draw_string, - .overlay_text_width = wl_overlay_text_width, - - .window_get_client_attrs = wl_window_get_client_attrs, - .client_get_visual_attrs = wl_client_get_visual_attrs, - .client_adopt_attributes = wl_client_adopt_attributes, - .client_reparent = wl_client_reparent, - .client_synth_configure = wl_client_synth_configure, - .client_add_to_save_set = wl_client_add_to_save_set, - .client_remove_from_save_set = wl_client_remove_from_save_set, - .client_close = wl_client_close, - .client_query_shaped = wl_client_query_shaped, - .client_get_gnustep_attrs = wl_client_get_gnustep_attrs, - .client_get_pid = wl_client_get_pid, - .client_snapshot = wl_client_snapshot, - .coords_translate = wl_coords_translate, - - .icccm_set_wm_hints = wl_icccm_set_wm_hints, - .icccm_set_class_hint = wl_icccm_set_class_hint, - .icccm_set_class_hint_if_absent = wl_icccm_set_class_hint_if_absent, - .icccm_set_command = wl_icccm_set_command, - .icccm_get_command = wl_icccm_get_command, - .icccm_free_command = wl_icccm_free_command, - - .fake_leader_create = wl_fake_leader_create, - .fake_leader_destroy = wl_fake_leader_destroy, - - .screen_replace_wm = wl_screen_replace_wm, - .screen_select_input = wl_screen_select_input, - .screen_set_root_cursor = wl_screen_set_root_cursor, - .screen_set_icon_sizes = wl_screen_set_icon_sizes, - .screen_setup_noticeboard = wl_screen_setup_noticeboard, - .screen_alloc_gcs = wl_screen_alloc_gcs, - .screen_create_internals = wl_screen_create_internals, - .screen_create_pixmaps = wl_screen_create_pixmaps, - .screen_load_tech_font = wl_screen_load_tech_font, - .screen_show_mini_screenshot = wl_screen_show_mini_screenshot, - .screen_capture_area = wl_screen_capture_area, - .screen_capture_window = wl_screen_capture_window, - .refresh_desktop = wl_refresh_desktop, - .set_desktop_background = wl_set_desktop_background, - - .monitors_query = wl_monitors_query, - .monitors_select_events = wl_monitors_select_events, - - .workspace_badge_render = wl_workspace_badge_render, - .workspace_badge_update = wl_workspace_badge_update, - .workspace_badge_get_background = wl_workspace_badge_get_background, - - .keysym_to_keycode = wl_keysym_to_keycode, - .keysym_from_keycode = wl_keysym_from_keycode, - .xkb_lock_group = wl_xkb_lock_group, - .xkb_get_group = wl_xkb_get_group, - - .extensions_init = wl_extensions_init, - .cursors_init = wl_cursors_init, - .cursor_create_named = wl_cursor_create_named, - .shape_combine_mask = wl_shape_combine_mask, - .register_error_handlers = wl_register_error_handlers, - .texture_alloc_color = wl_texture_alloc_color, - .texture_free_colors = wl_texture_free_colors, - .texture_create_gc = wl_texture_create_gc, - .texture_free_gc = wl_texture_free_gc, - .frame_alloc_border_pixel = wl_noop_frame_alloc_border_pixel, - .frame_update_texture = wl_noop_frame_update_texture, - .frame_paint_button = wl_frame_paint_button, - .netwm_init = wl_noop_netwm_init, - .netwm_cleanup = wl_noop_netwm_cleanup, - .netwm_update_desktop = wl_noop_netwm_update_desktop, - .netwm_get_current_desktop = wl_noop_netwm_get_current_desktop, - .netwm_update_opacity = wl_netwm_update_opacity, - .netwm_update_actions = wl_noop_netwm_update_actions, - .netwm_check_client_hints = wl_noop_netwm_check_client_hints, - .netwm_check_initial_state = wl_noop_netwm_check_initial_state, - .netwm_get_pid = wl_noop_netwm_get_pid, - .netwm_cleanup_frame_extents = wl_noop_netwm_cleanup_frame_extents, - .netwm_set_frame_extents = wl_noop_netwm_set_frame_extents, - .prop_get_protocols = wl_prop_get_protocols, - .prop_get_check_property = wl_noop_prop_get_check_property, - .prop_set_wmaker_protocols = wl_noop_prop_set_wmaker_protocols, - .prop_set_icon_tile = wl_noop_prop_set_icon_tile, - .prop_cleanup = wl_noop_prop_cleanup, - .client_restore = wl_noop_client_restore, - .client_set_state = wl_client_set_state, - .client_set_maximized = wl_client_set_maximized, - .client_send_protocol = wl_client_send_protocol, - .client_kill = wl_client_kill_impl, - .client_get_colormap_windows = wl_noop_client_get_colormap_windows, - .eat_expose = wl_noop_eat_expose, - .move_window = wl_noop_move_window, - .slide_windows = wl_noop_slide_windows, - .get_text_selection = wl_noop_get_text_selection, - .get_shortcut_string = wl_noop_get_shortcut_string, - .get_command_for_window = wl_get_command_for_window, - .icon_update_pixmap = wl_noop_icon_update_pixmap, - .icon_paint = wl_icon_paint, - .appicon_show = wl_appicon_show, - .appicon_paint = wl_appicon_paint, - .restore_desktop = wl_noop_restore_desktop, - .wipe_desktop = wl_noop_wipe_desktop, - .get_color_for_colormap = wl_get_color_for_colormap, - .free_color = wl_noop_free_color, - .colormap_install = wl_noop_colormap_install, - .xdnd_init = wl_noop_xdnd_init, - .motif_get_hints = wl_noop_motif_get_hints, - .appicon_bounce = wl_noop_appicon_bounce, - .manage_all_windows = wl_noop_manage_all_windows, - .event_handle_signal_exit = wl_event_handle_signal_exit, - .icon_select = wl_noop_icon_select, - .icon_change_title = wl_noop_icon_change_title, - .clip_icon_paint = wl_clip_icon_paint, - .drawer_icon_paint = wl_drawer_icon_paint, - .balloon_init = wl_balloon_init, - .do_kaboom = wl_noop_do_kaboom, - .menu_paint_entry = wl_menu_paint_entry, - .menu_draw_frame = wl_menu_draw_frame, - .draw_bevel = wl_noop_draw_bevel, - .appmenu_send = wl_noop_appmenu_send, - .display_get_name = wl_display_get_name, - .switchpanel_send_enter = wl_noop_switchpanel_send_enter, - .format_x_error = wl_noop_format_x_error, - .pixmap_create_from_xbm = wl_noop_pixmap_create_from_xbm, - .pixmap_create = wl_noop_pixmap_create, - .keysym_from_name = wl_keysym_from_name, - .frame_paint_resizebar = wl_frame_paint_resizebar, - .frame_paint_titlebar = wl_frame_paint_titlebar, - .prop_get_client_leader = wl_noop_prop_get_client_leader, - .prop_get_window_state = wl_noop_prop_get_window_state, - .setup_modifier_lock_masks = wl_noop_setup_modifier_lock_masks, - .key_grab_hacked = wl_noop_key_grab_hacked, - .button_grab_hacked = wl_noop_button_grab_hacked, - .context_init = wl_context_init, - .query_tree = wl_noop_query_tree, - .screen_for_window = wl_noop_screen_for_window, - .parse_color = wl_parse_color, - .cursor_create_from_files = wl_noop_cursor_create_from_files, - .cursor_free = wl_noop_cursor_free, - .cursor_set = wl_noop_cursor_set, - .pointer_change_cursor = wl_pointer_change_cursor, - .appicon_paint_dock_dots = wl_noop_appicon_paint_dock_dots, - .pixmap_get_size = wl_noop_pixmap_get_size, - .icon_paint_title_bg = wl_noop_icon_paint_title_bg, - .icon_select_paint = wl_noop_icon_select_paint, - .window_get_event_mask = wl_noop_window_get_event_mask, - .balloon_show = wl_balloon_show, - .window_fill_stipple = wl_noop_window_fill_stipple, - .event_check_enter_leave = wl_noop_event_check_enter_leave, - .event_allow_async = wl_noop_event_allow_async, - .event_replay_pointer = wl_noop_event_replay_pointer, - .keyboard_mapping_refresh = wl_noop_keyboard_mapping_refresh, - .event_flush_window = wl_noop_event_flush_window, - .event_flush_type = wl_noop_event_flush_type, - .event_check_window = wl_noop_event_check_window, - .event_check_type = wl_noop_event_check_type, - .client_send_event = wl_noop_client_send_event, - .client_message_forward = wl_client_message_forward, - .client_send_key = wl_client_send_key, - .window_exists = wl_noop_window_exists, - .window_get_colormap = wl_noop_window_get_colormap, - .pointer_query_window = wl_noop_pointer_query_window, - .dialog_paint_icon = wl_noop_dialog_paint_icon, - .pointer_get_position = wl_pointer_get_position, - .keysym_to_string = wl_noop_keysym_to_string, - .window_get_command = wl_window_get_command, - .netwm_process_client_message = wl_noop_netwm_process_client_message, - .xdnd_process_client_message = wl_noop_xdnd_process_client_message, - .xdnd_process_selection = wl_noop_xdnd_process_selection, - .xdnd_make_awareness = wl_noop_xdnd_make_awareness, .netwm_check_client_hint_change = wl_noop_netwm_check_client_hint_change, - .netwm_position_splash = wl_noop_netwm_position_splash, - .netwm_check_initial_frame_state = wl_noop_netwm_check_initial_frame_state, - .handle_selection_request = wl_noop_handle_selection_request, - .handle_selection_clear = wl_noop_handle_selection_clear, - .create_rcontext = wl_create_rcontext, -}; diff --git a/src/backend/wayland/wl_stubs.c b/src/backend/wayland/wl_stubs.c new file mode 100644 index 0000000..5c504e9 --- /dev/null +++ b/src/backend/wayland/wl_stubs.c @@ -0,0 +1,1559 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../screen.h" +#include "../../framewin.h" +#include "../../window.h" +#include "../../icon.h" +#include "../../defaults.h" +#include "../../dialog.h" +#include "../../menu.h" +#include "../../balloon.h" +#include "../../texture.h" +#include "../../workspace.h" +#include "../../dock.h" +#include "../../misc.h" +#include "../../def_pixmaps.h" +#include "../../stacking.h" +#include "../../startup.h" +#include "wl_types.h" +#include + +/* Forward declarations */ +struct wl_toplevel_view *wl_find_view_by_id(WNativeWindow id); +void wl_set_cursor_image(const char *name); +extern void *W_RegisterBacking(unsigned long id, void *img); +extern void W_UnregisterBacking(unsigned long id); + +/* Phase 80: no-op stubs for X11-only operations */ +/* ------------------------------------------------------------------ */ + +void wl_noop_frame_alloc_border_pixel(unsigned long colormap, + const char *color_name, unsigned long **pixel_out) +{ (void)colormap; (void)color_name; *pixel_out = NULL; } + +void wl_noop_frame_update_texture(struct WFrameWindow *fwin) { (void)fwin; } + +void wl_frame_paint_button(WCoreWindow *button, WTexture *texture, + unsigned long color, WPixmap *image, int pushed) +{ + (void)texture; (void)image; + WFrameWindow *fwin = (WFrameWindow *)button->descriptor.parent; + if (!fwin || !fwin->core) + return; + + struct wl_frame_buf *btn_fb = frame_buf_find(button->window); + if (!btn_fb || !btn_fb->image) + return; + + int bw = btn_fb->width; + int bh = btn_fb->height; + + /* Determine which icon to use */ + RImage *icon = NULL; + if (button == fwin->right_button) { + if (!wl_state.btn_close_img) + wl_state.btn_close_img = RGetImageFromXPMData( + button->screen_ptr->rcontext, PRED_CLOSE_XPM); + icon = wl_state.btn_close_img; + } else if (button == fwin->left_button) { + if (!wl_state.btn_iconify_img) + wl_state.btn_iconify_img = RGetImageFromXPMData( + button->screen_ptr->rcontext, PRED_ICONIFY_XPM); + icon = wl_state.btn_iconify_img; + } + if (!icon) + return; + + /* Get titlebar background color */ + struct wl_frame_buf *tb_fb = fwin->titlebar ? frame_buf_find(fwin->titlebar->window) : NULL; + uint32_t bg_pixel = 0xFF333333; + if (tb_fb && tb_fb->image) { + uint32_t *tb_data = pixman_image_get_data(tb_fb->image); + int tb_stride = pixman_image_get_stride(tb_fb->image) / 4; + int sample_y = btn_fb->rel_y + bh / 2; + int sample_x = btn_fb->rel_x + bw / 2; + if (sample_y >= 0 && sample_y < tb_fb->height && + sample_x >= 0 && sample_x < tb_fb->width) + bg_pixel = tb_data[sample_y * tb_stride + sample_x]; + } + + /* Foreground = title text color; when pushed, swap fg/bg */ + uint32_t fg_pixel = 0xFF000000 | ((color >> 16 & 0xFF) << 16) | + ((color >> 8 & 0xFF) << 8) | (color & 0xFF); + uint32_t fill_bg = pushed ? fg_pixel : bg_pixel; + uint32_t fill_fg = pushed ? bg_pixel : fg_pixel; + + uint32_t *dst = pixman_image_get_data(btn_fb->image); + int dst_stride = pixman_image_get_stride(btn_fb->image) / 4; + + /* Fill background */ + for (int y = 0; y < bh; y++) + for (int x = 0; x < bw; x++) + dst[y * dst_stride + x] = fill_bg; + + /* Draw border bevel derived from bg color (raised normal, sunken pushed) */ + unsigned char bg_r = (bg_pixel >> 16) & 0xFF; + unsigned char bg_g = (bg_pixel >> 8) & 0xFF; + unsigned char bg_b = bg_pixel & 0xFF; + uint32_t hi_c = 0xFF000000 | ((uint32_t)(bg_r + (255-bg_r)/3) << 16) | + ((uint32_t)(bg_g + (255-bg_g)/3) << 8) | (bg_b + (255-bg_b)/3); + uint32_t lo_c = 0xFF000000 | ((uint32_t)(bg_r * 2/3) << 16) | + ((uint32_t)(bg_g * 2/3) << 8) | (bg_b * 2/3); + uint32_t hi = pushed ? lo_c : hi_c; + uint32_t lo = pushed ? hi_c : lo_c; + for (int x = 0; x < bw; x++) { dst[x] = hi; dst[(bh-1) * dst_stride + x] = lo; } + for (int y = 0; y < bh; y++) { dst[y * dst_stride] = hi; dst[y * dst_stride + bw - 1] = lo; } + + /* Blit icon centered, using fill_fg color for opaque pixels */ + int ix = (bw - icon->width) / 2 + (pushed ? 1 : 0); + int iy = (bh - icon->height) / 2 + (pushed ? 1 : 0); + + for (int y = 0; y < icon->height; y++) { + int dy = iy + y; + if (dy < 0 || dy >= bh) continue; + uint32_t *dst_row = dst + dy * dst_stride; + for (int x = 0; x < icon->width; x++) { + int dx = ix + x; + if (dx < 0 || dx >= bw) continue; + unsigned char a; + if (icon->format == RRGBAFormat) + a = icon->data[(y * icon->width + x) * 4 + 3]; + else + a = 255; + if (a == 0) continue; + dst_row[dx] = fill_fg; + } + } + + /* Enable and commit the button's own scene_buf */ + wlr_scene_buffer_set_buffer_with_damage(btn_fb->scene_buf, &btn_fb->base, NULL); + wlr_scene_node_set_enabled(&btn_fb->scene_buf->node, true); + wlr_scene_node_raise_to_top(&btn_fb->scene_buf->node); +} + +void wl_noop_netwm_init(WScreen *scr) { (void)scr; } +void wl_noop_netwm_cleanup(WScreen *scr) { (void)scr; } +void wl_noop_netwm_update_desktop(WScreen *scr) { (void)scr; } +int wl_noop_netwm_get_current_desktop(WScreen *scr) { (void)scr; return -1; } +void wl_netwm_update_opacity(WWindow *wwin) +{ + if (!wwin->frame) + return; + + struct wl_frame_buf *fb = frame_buf_find(wwin->frame->core->window); + if (!fb || !fb->scene_buf) + return; + + float alpha = 1.0f; + if (wl_state.x_display && wwin->client_win) { + WMNativeAtom type; + int format; + unsigned long items, rest; + unsigned char *prop = NULL; + WMNativeAtom opacity_atom = XInternAtom(wl_state.x_display, + "_NET_WM_WINDOW_OPACITY", False); + if (XGetWindowProperty(wl_state.x_display, (WNativeWindow)wwin->client_win, + opacity_atom, 0L, 1L, False, AnyPropertyType, + &type, &format, &items, &rest, &prop) == Success + && format == 32 && items == 1 && prop) { + unsigned long val = *(unsigned long *)prop; + alpha = (float)val / (float)0xFFFFFFFF; + } + if (prop) + XFree(prop); + } + + wlr_scene_buffer_set_opacity(fb->scene_buf, alpha); + + /* Apply to all child frame_bufs (titlebar buttons, resizebar) */ + struct wl_frame_buf *child; + wl_list_for_each(child, &wl_state.frame_list, link) { + if (child->parent_id == fb->id && child->scene_buf) + wlr_scene_buffer_set_opacity(child->scene_buf, alpha); + } + + /* Apply to the client surface */ + struct wl_toplevel_view *v; + wl_list_for_each(v, &wl_state.toplevel_list, link) { + if (v->frame_id == fb->id && v->scene_node) { + struct wlr_scene_node *node; + wl_list_for_each(node, &v->scene_node->children, link) { + if (node->type == WLR_SCENE_NODE_BUFFER) + wlr_scene_buffer_set_opacity( + wlr_scene_buffer_from_node(node), alpha); + } + } + } +} +void wl_noop_netwm_update_actions(WWindow *wwin, int del) { (void)wwin; (void)del; } +void wl_noop_netwm_check_client_hints(WWindow *wwin, int *layer, int *workspace) +{ (void)wwin; (void)layer; (void)workspace; } +void wl_noop_netwm_check_initial_state(WWindow *wwin) { (void)wwin; } +int wl_noop_netwm_get_pid(WNativeWindow window) { (void)window; return 0; } +void wl_noop_netwm_cleanup_frame_extents(WWindow *wwin) { (void)wwin; } +void wl_noop_netwm_set_frame_extents(WWindow *wwin) { (void)wwin; } + +void wl_prop_get_protocols(WNativeWindow window, WProtocols *prots) +{ + (void)window; + memset(prots, 0, sizeof(WProtocols)); + prots->DELETE_WINDOW = 1; /* All XDG toplevels support close */ +} + +unsigned char *wl_noop_prop_get_check_property(WNativeWindow window, + unsigned long hint, unsigned long type, int format, int count, int *ret_count) +{ (void)window; (void)hint; (void)type; (void)format; (void)count; + if (ret_count) *ret_count = 0; return NULL; } + +void wl_noop_prop_set_wmaker_protocols(WNativeWindow root) { (void)root; } +void wl_noop_prop_set_icon_tile(WScreen *scr, RImage *image) { (void)scr; (void)image; } +void wl_noop_prop_cleanup(WNativeWindow root) { (void)root; } + +void wl_noop_client_restore(WWindow *wwin) { (void)wwin; } +void wl_client_set_state(WWindow *wwin, int state, WNativeWindow icon_win) +{ + (void)icon_win; + struct wl_toplevel_view *v = wl_find_view_by_id(wwin->client_win); + if (!v) + return; + switch (state) { + case 3: /* WM_STATE_ICONIC */ + if (v->scene_node) + wlr_scene_node_set_enabled(&v->scene_node->node, false); + break; + case 1: /* WM_STATE_NORMAL */ + if (v->scene_node) + wlr_scene_node_set_enabled(&v->scene_node->node, true); + break; + } +} + +void wl_client_set_maximized(WNativeWindow win, int maximized) +{ + struct wl_toplevel_view *v = wl_find_view_by_id(win); + if (!v) return; + if (v->xdg_surface && v->xdg_surface->toplevel) + wlr_xdg_toplevel_set_maximized(v->xdg_surface->toplevel, maximized); +} + +void wl_client_send_protocol(WWindow *wwin, unsigned long protocol, + unsigned long timestamp) +{ + (void)protocol; (void)timestamp; + wm_backend->client_close(wwin->client_win); +} + +void wl_client_kill_impl(WWindow *wwin) +{ + wm_backend->client_close(wwin->client_win); +} + +void wl_noop_client_get_colormap_windows(WWindow *wwin) { (void)wwin; } + +void wl_noop_eat_expose(void) { } + +void wl_noop_move_window(WNativeWindow win, int from_x, int from_y, + int to_x, int to_y) +{ (void)win; (void)from_x; (void)from_y; (void)to_x; (void)to_y; } + +void wl_noop_slide_windows(WNativeWindow *wins, int n, + int from_x, int from_y, int to_x, int to_y) +{ (void)wins; (void)n; (void)from_x; (void)from_y; (void)to_x; (void)to_y; } + +char *wl_noop_get_text_selection(WScreen *scr, unsigned long selection) +{ (void)scr; (void)selection; return NULL; } + +char *wl_noop_get_shortcut_string(WShortKey key) +{ (void)key; return NULL; } + +char *wl_get_command_for_window(WNativeWindow win, int elements) +{ + char **argv, *command = NULL; + int argc; + if (!wm_backend->icccm_get_command(win, &argv, &argc)) + return NULL; + if (argc > 0 && argv != NULL) { + if (elements == 0) + elements = argc; + command = wtokenjoin(argv, WMIN(argc, elements)); + if (command[0] == 0) { + wfree(command); + command = NULL; + } + } + wm_backend->icccm_free_command(argv); + return command; +} + +void wl_noop_icon_update_pixmap(WIcon *icon) { (void)icon; } + +/* + * wl_icon_paint - render a miniwindow (iconified window) icon into its + * frame_buf and commit it to the scene graph. + * + * Mirrors wl_appicon_paint: tile background + optional image + title text. + * Called at the end of wIconPaint() in icon.c so the backend finalises the + * surface after the policy layer has set up icon->file_image etc. + */ +void +wl_icon_paint(WIcon *icon) +{ + if (!icon || !icon->core) return; + + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)icon->core->window); + if (!fb || !fb->image) return; + + WScreen *scr = icon->core->screen_ptr; + if (!scr) return; + int size = wPreferences.icon_size; + + /* --- Tile background --- */ + RImage *tile = scr->icon_tile ? RCloneImage(scr->icon_tile) : NULL; + if (!tile) { + tile = RCreateImage(size, size, 1); + if (!tile) return; + RColor grey = {0xAE, 0xAA, 0xAE, 0xFF}; + RClearImage(tile, &grey); + } + + /* --- Composite icon image (file_image from database / NET_WM_ICON) --- */ + RImage *img = icon->file_image; + if (img) { + int theight = icon->show_title ? WMFontHeight(scr->icon_title_font) : 0; + unsigned w = ((unsigned)img->width > (unsigned)size) ? (unsigned)size : (unsigned)img->width; + unsigned h = ((unsigned)img->height + (unsigned)theight > (unsigned)size) + ? (unsigned)(size - theight) : (unsigned)img->height; + int x = (size - (int)w) / 2; + int y = theight + (size - theight - (int)h) / 2; + int sx = ((int)img->width - (int)w) / 2; + int sy = ((int)img->height - (int)h) / 2; + RCombineArea(tile, img, sx, sy, w, h, x, y); + } + + /* --- Blit RImage (RGBA/RGB) -> pixman ARGB32 --- */ + uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + unsigned char *tsrc = tile->data; + int has_alpha = (tile->format == RRGBAFormat); + for (int row = 0; row < size && row < fb->height; row++) { + for (int col = 0; col < size && col < fb->width; col++) { + int si = (row * tile->width + col) * (has_alpha ? 4 : 3); + unsigned char r = tsrc[si], g = tsrc[si+1], b = tsrc[si+2]; + unsigned char a = has_alpha ? tsrc[si+3] : 0xFF; + dst[row * stride + col] = ((uint32_t)a << 24) | ((uint32_t)r << 16) + | ((uint32_t)g << 8) | (uint32_t)b; + } + } + RReleaseImage(tile); + + /* --- Title text --- */ + if (icon->show_title && icon->icon_name && scr->icon_title_font) { + WMDrawable d = (WMDrawable)(uintptr_t)fb->image; + int tw = WMWidthOfString(scr->icon_title_font, icon->icon_name, + strlen(icon->icon_name)); + int tx = (size - tw) / 2; + if (tx < 2) tx = 2; + WMColor *color = scr->icon_title_color; + if (color) + WMDrawString(scr->wmscreen, d, color, scr->icon_title_font, + tx, 0, icon->icon_name, strlen(icon->icon_name)); + } + + /* --- Commit to scene graph --- */ + wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); + wlr_scene_node_set_enabled(&fb->scene_buf->node, true); + if (fb->tree) + wlr_scene_node_set_enabled(&fb->tree->node, true); +} +void wl_appicon_show(WApplication *wapp) { paint_app_icon(wapp); } +void wl_appicon_paint(WAppIcon *aicon) +{ + WIcon *icon = aicon->icon; + if (!icon || !icon->core) + return; + + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)icon->core->window); + if (!fb || !fb->image) + return; + + WScreen *scr = icon->core->screen_ptr; + int size = wPreferences.icon_size; + + /* Compose the tile image */ + RImage *tile = NULL; + switch (icon->tile_type) { + case TILE_CLIP: tile = scr->clip_tile ? RCloneImage(scr->clip_tile) : NULL; break; + case TILE_DRAWER: tile = scr->drawer_tile ? RCloneImage(scr->drawer_tile) : NULL; break; + default: tile = scr->icon_tile ? RCloneImage(scr->icon_tile) : NULL; break; + } + if (!tile) { + /* Fallback: solid grey */ + tile = RCreateImage(size, size, 1); + if (!tile) return; + RColor grey = {0xAE, 0xAA, 0xAE, 0xFF}; + RClearImage(tile, &grey); + } + + /* Composite the app image on top */ + if (icon->file_image) { + RImage *img = icon->file_image; + int theight = icon->show_title ? WMFontHeight(scr->icon_title_font) : 0; + unsigned w = (img->width > size) ? size : img->width; + unsigned h = (img->height + theight > size) ? size - theight : img->height; + int x = (size - (int)w) / 2; + int y = theight + (size - theight - (int)h) / 2; + int sx = ((int)img->width - (int)w) / 2; + int sy = ((int)img->height - (int)h) / 2; + RCombineArea(tile, img, sx, sy, w, h, x, y); + } + + /* Blit RImage (RGBA) → pixman ARGB32 buffer */ + uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + unsigned char *src = tile->data; + int has_alpha = (tile->format == RRGBAFormat); + + for (int row = 0; row < size && row < fb->height; row++) { + for (int col = 0; col < size && col < fb->width; col++) { + int si = (row * tile->width + col) * (has_alpha ? 4 : 3); + unsigned char r = src[si], g = src[si+1], b = src[si+2]; + unsigned char a = has_alpha ? src[si+3] : 0xFF; + dst[row * stride + col] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | + ((uint32_t)g << 8) | (uint32_t)b; + } + } + + /* Draw title text */ + if (icon->show_title && icon->icon_name && scr->icon_title_font) { + WMDrawable d = (WMDrawable)(uintptr_t)fb->image; + int fh = WMFontHeight(scr->icon_title_font); + int tw = WMWidthOfString(scr->icon_title_font, icon->icon_name, + strlen(icon->icon_name)); + int tx = (size - tw) / 2; + if (tx < 2) tx = 2; + int ty = 0; /* top of text area */ + WMColor *color = scr->icon_title_color; + if (color) + WMDrawString(scr->wmscreen, d, color, scr->icon_title_font, + tx, ty, icon->icon_name, strlen(icon->icon_name)); + } + + RReleaseImage(tile); + + /* Commit to scene graph */ + wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); + wlr_scene_node_set_enabled(&fb->scene_buf->node, true); +} +void wl_noop_restore_desktop(WScreen *scr) { (void)scr; } +void wl_noop_wipe_desktop(WScreen *scr) { (void)scr; } + +int wl_get_color_for_colormap(unsigned long colormap, + const char *color_name, WMColorRGB *color) +{ + unsigned int r, g, b; + (void)colormap; + + if (!color_name || !color) + return 0; + + /* Parse #RGB, #RRGGBB, #RRRRGGGGBBBB */ + if (color_name[0] == '#') { + int len = strlen(color_name + 1); + if (len == 6) { + if (sscanf(color_name, "#%2x%2x%2x", &r, &g, &b) == 3) { + color->red = r << 8 | r; + color->green = g << 8 | g; + color->blue = b << 8 | b; + color->pixel = (0xFF << 24) | (r << 16) | (g << 8) | b; + return 1; + } + } else if (len == 12) { + if (sscanf(color_name, "#%4hx%4hx%4hx", + &color->red, &color->green, &color->blue) == 3) { + color->pixel = (0xFF << 24) | + ((color->red >> 8) << 16) | + ((color->green >> 8) << 8) | + (color->blue >> 8); + return 1; + } + } else if (len == 3) { + if (sscanf(color_name, "#%1x%1x%1x", &r, &g, &b) == 3) { + color->red = r << 12 | r << 8 | r << 4 | r; + color->green = g << 12 | g << 8 | g << 4 | g; + color->blue = b << 12 | b << 8 | b << 4 | b; + color->pixel = (0xFF << 24) | (r*17 << 16) | (g*17 << 8) | b*17; + return 1; + } + } + } + + /* X11 rgb:RR/GG/BB or rgb:RRRR/GGGG/BBBB format */ + if (strncasecmp(color_name, "rgb:", 4) == 0) { + unsigned int rv, gv, bv; + const char *p = color_name + 4; + int n = sscanf(p, "%x/%x/%x", &rv, &gv, &bv); + if (n == 3) { + /* Determine component width from first component */ + const char *slash = strchr(p, '/'); + int width = slash ? (int)(slash - p) : 2; + if (width == 1) { + color->red = rv << 12 | rv << 8 | rv << 4 | rv; + color->green = gv << 12 | gv << 8 | gv << 4 | gv; + color->blue = bv << 12 | bv << 8 | bv << 4 | bv; + } else if (width == 2) { + color->red = rv << 8 | rv; + color->green = gv << 8 | gv; + color->blue = bv << 8 | bv; + } else { + color->red = rv; + color->green = gv; + color->blue = bv; + } + color->pixel = (0xFF << 24) | + ((color->red >> 8) << 16) | + ((color->green >> 8) << 8) | + (color->blue >> 8); + return 1; + } + } + + /* Named colors — common ones */ + struct { const char *name; unsigned short r, g, b; } named[] = { + {"black", 0x0000, 0x0000, 0x0000}, + {"white", 0xFFFF, 0xFFFF, 0xFFFF}, + {"red", 0xFFFF, 0x0000, 0x0000}, + {"green", 0x0000, 0xFFFF, 0x0000}, + {"blue", 0x0000, 0x0000, 0xFFFF}, + {"gray", 0xBEBE, 0xBEBE, 0xBEBE}, + {"grey", 0xBEBE, 0xBEBE, 0xBEBE}, + {"darkgray",0xA9A9, 0xA9A9, 0xA9A9}, + {"darkgrey",0xA9A9, 0xA9A9, 0xA9A9}, + {"lightgray",0xD3D3, 0xD3D3, 0xD3D3}, + {"lightgrey",0xD3D3, 0xD3D3, 0xD3D3}, + {NULL, 0, 0, 0} + }; + for (int i = 0; named[i].name; i++) { + if (strcasecmp(color_name, named[i].name) == 0) { + color->red = named[i].r; + color->green = named[i].g; + color->blue = named[i].b; + color->pixel = (0xFF << 24) | + ((color->red >> 8) << 16) | + ((color->green >> 8) << 8) | + (color->blue >> 8); + return 1; + } + } + + /* grayNN / greyNN (0-100) */ + if ((strncasecmp(color_name, "gray", 4) == 0 || strncasecmp(color_name, "grey", 4) == 0) + && color_name[4] >= '0' && color_name[4] <= '9') { + int pct = atoi(color_name + 4); + if (pct >= 0 && pct <= 100) { + unsigned int v = (pct * 255 + 50) / 100; + color->red = color->green = color->blue = v << 8 | v; + color->pixel = (0xFF << 24) | (v << 16) | (v << 8) | v; + return 1; + } + } + + /* Look up in X11 rgb.txt database */ + { + const char *rgb_paths[] = { + "/usr/share/X11/rgb.txt", + "/usr/lib/X11/rgb.txt", + "/etc/X11/rgb.txt", + NULL + }; + FILE *f = NULL; + for (int i = 0; rgb_paths[i] && !f; i++) + f = fopen(rgb_paths[i], "r"); + if (f) { + char line[256]; + while (fgets(line, sizeof(line), f)) { + unsigned int rv, gv, bv; + char name[128]; + if (line[0] == '!' || line[0] == '#') + continue; + if (sscanf(line, "%u %u %u %127[^\n]", &rv, &gv, &bv, name) == 4) { + /* Trim leading whitespace/tabs from name */ + char *n = name; + while (*n == ' ' || *n == '\t') n++; + if (strcasecmp(n, color_name) == 0) { + color->red = rv << 8 | rv; + color->green = gv << 8 | gv; + color->blue = bv << 8 | bv; + color->pixel = (0xFF << 24) | (rv << 16) | (gv << 8) | bv; + fclose(f); + return 1; + } + } + } + fclose(f); + } + } + + wwarning(_("could not parse color \"%s\" (Wayland)"), color_name); + return 0; +} + +void wl_noop_free_color(WScreen *scr, unsigned long pixel) +{ (void)scr; (void)pixel; } + +void wl_noop_colormap_install(WScreen *scr, WWindow *wwin) +{ (void)scr; (void)wwin; } + +void wl_noop_xdnd_init(void) { } + +int wl_noop_motif_get_hints(WNativeWindow window, MWMHints *hints_out) +{ (void)window; (void)hints_out; return 0; } + +void wl_noop_appicon_bounce(WAppIcon *aicon, void *data) +{ (void)aicon; (void)data; } + +void wl_noop_manage_all_windows(WScreen *scr, int crash_recovery) +{ (void)scr; (void)crash_recovery; } + +void wl_event_handle_signal_exit(void) +{ + wm_backend->event_loop_terminate(); +} + +void wl_noop_icon_select(WIcon *icon) { (void)icon; } +void wl_noop_icon_change_title(WIcon *icon, WWindow *wwin) { (void)icon; (void)wwin; } +void wl_clip_icon_paint(WAppIcon *aicon) +{ + if (!aicon || !aicon->icon || !aicon->icon->core) + return; + + WScreen *scr = aicon->icon->core->screen_ptr; + if (!scr->workspaces || !scr->workspaces[scr->current_workspace]) + return; + + /* Paint base tile + icon image */ + wl_appicon_paint(aicon); + + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)aicon->icon->core->window); + if (!fb || !fb->image) + return; + + WWorkspace *workspace = scr->workspaces[scr->current_workspace]; + /* Use aicon->dock to read collapsed state — workspace->clip may be a + * stale freed pointer during wWorkspaceRestoreState (the old clip is + * destroyed before scr->workspaces[i]->clip is reassigned, and we + * are called from wDockCreate inside that gap). */ + int collapsed = aicon->dock ? aicon->dock->collapsed : 0; + WMColor *color; + if (wPreferences.flags.noclip || !collapsed) + color = scr->clip_title_color[CLIP_NORMAL]; + else + color = scr->clip_title_color[CLIP_COLLAPSED]; + if (!color || !scr->clip_title_font) + return; + + int size = wPreferences.icon_size; + WMDrawable d = (WMDrawable)(uintptr_t)fb->image; + + /* Draw workspace number at top center */ + char ws_number[20]; + snprintf(ws_number, sizeof ws_number, "%u", scr->current_workspace + 1); + int nlength = strlen(ws_number); + int tx = (size / 2 - WMWidthOfString(scr->clip_title_font, ws_number, nlength)) / 2; + WMDrawString(scr->wmscreen, d, color, scr->clip_title_font, tx, 2, ws_number, nlength); + + /* Draw workspace name at bottom */ + if (wPreferences.show_clip_title) { + int length = strlen(workspace->name); + int ty = size - WMFontHeight(scr->clip_title_font) - 3; + tx = CLIP_BUTTON_SIZE * size / 64; + WMDrawString(scr->wmscreen, d, color, scr->clip_title_font, tx, ty, workspace->name, length); + } + + /* Commit updated buffer */ + wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); +} +void wl_drawer_icon_paint(WAppIcon *aicon) +{ + if (!aicon || !aicon->icon || !aicon->icon->core) + return; + + wl_appicon_paint(aicon); + + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)aicon->icon->core->window); + if (!fb || !fb->image) + return; + + WScreen *scr = aicon->icon->core->screen_ptr; + WMColor *color; + if (!aicon->dock->collapsed) + color = scr->clip_title_color[CLIP_NORMAL]; + else + color = scr->clip_title_color[CLIP_COLLAPSED]; + if (!color) + return; + + int size = wPreferences.icon_size; + uint32_t pixel = ((uint32_t)0xFF << 24) | + ((uint32_t)WMRedComponentOfColor(color) >> 8 << 16) | + ((uint32_t)WMGreenComponentOfColor(color) >> 8 << 8) | + ((uint32_t)WMBlueComponentOfColor(color) >> 8); + uint32_t *data = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + + /* Draw directional arrow (triangle) */ + int cx, cy = size / 2; + if (aicon->dock->on_right_side) { + /* Arrow pointing left: tip at x=5, base at x=10 */ + for (int dy = -5; dy <= 5; dy++) { + int row = cy + dy; + if (row < 0 || row >= size) continue; + int width = 5 - abs(dy); + for (int dx = 0; dx <= width; dx++) { + cx = 10 - dx; + if (cx >= 0 && cx < size) + data[row * stride + cx] = pixel; + } + } + } else { + /* Arrow pointing right: tip at x=size-6, base at x=size-11 */ + for (int dy = -5; dy <= 5; dy++) { + int row = cy + dy; + if (row < 0 || row >= size) continue; + int width = 5 - abs(dy); + for (int dx = 0; dx <= width; dx++) { + cx = size - 11 + dx; + if (cx >= 0 && cx < size) + data[row * stride + cx] = pixel; + } + } + } + + 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_noop_do_kaboom(WScreen *scr, WNativeWindow win, int x, int y) +{ (void)scr; (void)win; (void)x; (void)y; } +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); + 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_noop_draw_bevel(WNativeWindow d, unsigned int width, unsigned int height, + WTexSolid *texture, int relief) +{ (void)d; (void)width; (void)height; (void)texture; (void)relief; } + +void wl_noop_appmenu_send(WNativeWindow window, int what, int tag) +{ (void)window; (void)what; (void)tag; } + +const char *wl_display_get_name(const char *name) +{ + /* For X11 clients launched under XWayland, return the XWayland display. + * For native Wayland clients, DON'T set DISPLAY (they use WAYLAND_DISPLAY). + * Fallback: return the Wayland display name for backward compatibility. */ + (void)name; + if (wl_state.xwayland && wl_state.xwayland->display_name) + return wl_state.xwayland->display_name; + return "wayland-0"; +} + +void wl_noop_switchpanel_send_enter(WNativeWindow info_win, WNativeWindow window) +{ (void)info_win; (void)window; } + +void wl_noop_format_x_error(void *error, char *buf, int size) +{ (void)error; if (size > 0) buf[0] = '\0'; } + +WPixmap *wl_noop_pixmap_create_from_xbm(WScreen *scr, char *data, char *mask, + int width, int height, unsigned long fg, unsigned long bg) +{ (void)scr; (void)data; (void)mask; (void)width; (void)height; (void)fg; (void)bg; return NULL; } + +WPixmap *wl_noop_pixmap_create(unsigned long image, unsigned long mask) +{ (void)image; (void)mask; return NULL; } + +unsigned long wl_keysym_from_name(const char *name) +{ + return (unsigned long)xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); +} + +void wl_frame_paint_resizebar(WFrameWindow *fwin) +{ + /* Resizebar is rendered into the parent frame buffer by wl_frame_paint. + * Don't commit a separate buffer here — it would overlay the texture. */ + (void)fwin; +} + +void wl_frame_paint_titlebar(WFrameWindow *fwin, int state) +{ + if (!fwin->titlebar || !fwin->titlebar->window) + return; + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)fwin->titlebar->window); + if (!fb || !fb->image) + return; + + WScreen *scr = fwin->screen_ptr; + uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + + /* Background: use texture color or default */ + uint32_t bg; + if (fwin->title_texture[state] && + fwin->title_texture[state]->any.type == WTEX_SOLID) { + unsigned long px = fwin->title_texture[state]->solid.normal.pixel; + bg = 0xFF000000 | (px & 0xFFFFFF); + } else { + bg = (state == 0) ? 0xFF4A6984 : 0xFF808080; /* focused blue / unfocused grey */ + } + + for (int row = 0; row < fb->height; row++) + for (int col = 0; col < fb->width; col++) + dst[row * stride + col] = bg; + + /* Draw title text */ + if (fwin->title && fwin->font && fwin->title_color[state]) { + char *title = ShrinkString(*fwin->font, fwin->title, fb->width - 12); + int titlelen = strlen(title); + int tw = WMWidthOfString(*fwin->font, title, titlelen); + int x; + switch (fwin->flags.justification) { + case WTJ_LEFT: x = 6; break; + case WTJ_RIGHT: x = fb->width - tw - 6; break; + default: x = (fb->width - tw) / 2; break; + } + int fh = WMFontHeight(*fwin->font); + int y = (fb->height - fh) / 2; + + WMDrawable d = (WMDrawable)(uintptr_t)fb->image; + WMDrawString(scr->wmscreen, d, fwin->title_color[state], + *fwin->font, x, y, title, titlelen); + wfree(title); + } + + wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); +} + +WNativeWindow wl_noop_prop_get_client_leader(WNativeWindow window) { (void)window; return WNATIVE_NONE; } +int wl_noop_prop_get_window_state(WNativeWindow window) { (void)window; return -1; } + +void wl_noop_setup_modifier_lock_masks(void) { } +void wl_noop_key_grab_hacked(WNativeWindow window, int keycode, unsigned int modifiers) +{ (void)window; (void)keycode; (void)modifiers; } +void wl_noop_button_grab_hacked(unsigned int button, unsigned int modifiers, + WNativeWindow window, int owner_events, unsigned int event_mask) +{ (void)button; (void)modifiers; (void)window; (void)owner_events; (void)event_mask; } +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; +} +void wl_noop_query_tree(WNativeWindow root, WNativeWindow **children_out, + unsigned int *nchildren_out) +{ (void)root; *children_out = NULL; *nchildren_out = 0; } + +WScreen *wl_noop_screen_for_window(WNativeWindow window) +{ (void)window; return wScreenWithNumber(0); } + +int wl_parse_color(WScreen *scr, const char *name, WMColorRGB *color_out) +{ (void)scr; return wl_get_color_for_colormap(0, name, color_out); } +int wl_noop_cursor_create_from_files(WScreen *scr, const char *bitmap_path, + const char *mask_path, WMNativeCursor *cursor_out) +{ (void)scr; (void)bitmap_path; (void)mask_path; (void)cursor_out; return 0; } +void wl_noop_cursor_free(WMNativeCursor cursor) { (void)cursor; } +void wl_noop_cursor_set(WNativeWindow window, WMNativeCursor cursor) { (void)window; (void)cursor; } +void wl_pointer_change_cursor(int cursor_id) +{ + const char *name; + switch (cursor_id) { + case 1: /* WCUR_MOVE */ name = "fleur"; break; + case 2: /* WCUR_RESIZE */ name = "bottom_right_corner"; break; + case 7: /* WCUR_WAIT */ name = "watch"; break; + case 8: /* WCUR_ARROW */ name = "left_ptr"; break; + case 9: /* WCUR_QUESTION */name = "question_arrow"; break; + case 10:/* WCUR_TEXT */ name = "xterm"; break; + default: name = "left_ptr"; break; + } + wl_set_cursor_image(name); +} +void wl_noop_appicon_paint_dock_dots(WAppIcon *aicon, void *wapp) +{ (void)aicon; (void)wapp; } + +int wl_noop_pixmap_get_size(unsigned long drawable, unsigned int *w, unsigned int *h, unsigned int *depth) +{ (void)drawable; *w = 0; *h = 0; *depth = 0; return 0; } +void wl_noop_icon_paint_title_bg(WScreen *scr, unsigned long pixmap, int height) +{ (void)scr; (void)pixmap; (void)height; } +void wl_noop_icon_select_paint(WIcon *icon, int step) +{ (void)icon; (void)step; } +unsigned long wl_noop_window_get_event_mask(WNativeWindow window) +{ (void)window; return 0; } +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) { + W_UnregisterBacking(fb->id); + 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; + W_RegisterBacking(fb->id, fb->image); + } + + /* 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)fb->id, 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_noop_window_fill_stipple(WNativeWindow win, int x, int y, int w, int h) +{ (void)win; (void)x; (void)y; (void)w; (void)h; } +int wl_noop_event_check_enter_leave(void *dock) { (void)dock; return 1; } + +void wl_noop_event_allow_async(void) { } +void wl_noop_event_replay_pointer(void) { } +void wl_noop_keyboard_mapping_refresh(WMEvent *event) { (void)event; } +void wl_noop_event_flush_window(WNativeWindow win, int t) { (void)win; (void)t; } +void wl_noop_event_flush_type(int t) { (void)t; } +int wl_noop_event_check_window(WNativeWindow win, int t) { (void)win; (void)t; return 0; } +int wl_noop_event_check_type(int t, WMEvent *e) { (void)t; (void)e; return 0; } +void wl_noop_client_send_event(WNativeWindow win, void *d) { (void)win; (void)d; } +void wl_client_message_forward(WMEvent *event, WNativeWindow target_win) +{ + /* Forward OffiX DND ClientMessage to the real client_win for + * XWayland clients. Native Wayland clients have no analog. */ + if (!wl_state.x_display || !event) return; + XEvent *xev = (XEvent *)event->_x11_raw; + if (!xev) return; + xev->xclient.window = (WNativeWindow)target_win; + XSendEvent(wl_state.x_display, (WNativeWindow)target_win, False, 0L, xev); +} +void wl_client_send_key(WNativeWindow win, unsigned int keycode, + unsigned int modifier_mask, int press) +{ + /* For XWayland clients, deliver via the X server (wl_state.x_display) + * the same way the X11 backend does. Native Wayland clients are not + * yet supported (would require zwp_virtual_keyboard_v1). */ + if (!wl_state.x_display) return; + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xkey.type = press ? KeyPress : KeyRelease; + ev.xkey.display = wl_state.x_display; + ev.xkey.window = (WNativeWindow)win; + ev.xkey.root = DefaultRootWindow(wl_state.x_display); + ev.xkey.subwindow = WM_NATIVE_NONE; + ev.xkey.same_screen = True; + ev.xkey.time = WM_CURRENT_TIME; + ev.xkey.keycode = keycode; + ev.xkey.state = modifier_mask; + XSendEvent(wl_state.x_display, (WNativeWindow)win, press ? False : True, + press ? WM_EVMASK_KEY_PRESS : WM_EVMASK_KEY_RELEASE, &ev); +} +int wl_noop_window_exists(WNativeWindow win) { (void)win; return 1; } +void wl_noop_window_get_colormap(WNativeWindow win, unsigned long *c) { (void)win; (void)c; } +WNativeWindow wl_noop_pointer_query_window(WNativeWindow root) { (void)root; return WNATIVE_NONE; } +void wl_noop_dialog_paint_icon(unsigned long d, void *b, void *p, WScreen *s, + int x, int y, int w, int h) { (void)d;(void)b;(void)p;(void)s;(void)x;(void)y;(void)w;(void)h; } +void wl_pointer_get_position(int *x, int *y) { + *x = (int)wl_state.pointer_x; + *y = (int)wl_state.pointer_y; +} +const char *wl_noop_keysym_to_string(unsigned long ks) { (void)ks; return ""; } +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) { + /* 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) { + /* Copy to wmaker's allocator */ + *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; + } + } + 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; +} + +int wl_noop_window_get_command(WNativeWindow win, char ***a, int *c) { (void)win; (void)a; *c=0; return 0; } +int wl_noop_netwm_process_client_message(WMEvent *e) { (void)e; return 0; } +int wl_noop_xdnd_process_client_message(WMEvent *e) { (void)e; return 0; } +void wl_noop_xdnd_process_selection(WMEvent *e) { (void)e; } +void wl_noop_xdnd_make_awareness(WNativeWindow w) { (void)w; } +void wl_noop_handle_selection_request(WMEvent *e) { (void)e; } +void wl_noop_handle_selection_clear(WMEvent *e) { (void)e; } +void wl_noop_netwm_check_client_hint_change(WWindow *w, void *e) { (void)w; (void)e; } +void wl_noop_netwm_position_splash(WWindow *w, int *x, int *y, int ww, int h) { (void)w;(void)x;(void)y;(void)ww;(void)h; } +void wl_noop_netwm_check_initial_frame_state(WWindow *w) { (void)w; } + +RContext *wl_create_rcontext(int screen_number, RContextAttributes *attribs) +{ + (void)screen_number; + return RCreateContextWayland(1920, 1080, attribs); +} + +/* ------------------------------------------------------------------ */ +/* vtable */ +/* ------------------------------------------------------------------ */ + +const WMBackend wl_backend = { + .is_wayland = 1, /* Path 1: distinguishes Wayland from X11 */ + .display_open = wl_display_open, + .display_close = wl_display_close, + .display_post_open = wl_display_post_open, + .display_screen_count = wl_display_screen_count, + .display_default_screen = wl_display_default_screen, + .atom_intern = wl_atom_intern, + .event_loop_run = wl_event_loop_run, + .event_loop_terminate = wl_event_loop_terminate, + + .screen_open = wl_screen_open, + .screen_init_display = wl_screen_init_display, + .screen_close = wl_screen_close, + .show_crash_dialog = wl_show_crash_dialog, + + .frame_create_toplevel = wl_frame_create_toplevel, + .frame_create_child = wl_frame_create_child, + .frame_destroy = wl_frame_destroy, + .frame_configure = wl_frame_configure, + .frame_paint = wl_frame_paint, + + .context_save = wl_context_save, + .context_find = wl_context_find, + .context_delete = wl_context_delete, + + .focus_set = wl_focus_set, + + .key_grab = wl_key_grab, + .key_ungrab_all = wl_key_ungrab_all, + .button_grab = wl_button_grab, + .button_ungrab_all = wl_button_ungrab_all, + + .pointer_grab = wl_pointer_grab, + .pointer_ungrab = wl_pointer_ungrab, + .pointer_warp = wl_pointer_warp, + .pointer_warp_to = wl_pointer_warp_to, + .pointer_query = wl_pointer_query, + .pointer_query_full = wl_pointer_query_full, + .pointer_change_grab = wl_pointer_change_grab, + + .keyboard_grab = wl_keyboard_grab, + .keyboard_ungrab = wl_keyboard_ungrab, + + .server_grab = wl_server_grab, + .server_ungrab = wl_server_ungrab, + + .stacking_raise = wl_stacking_raise, + .stacking_lower = wl_stacking_lower, + .stacking_restack = wl_stacking_restack, + .stacking_query = wl_stacking_query, + .stacking_set_level = wl_stacking_set_level, + + .event_flush = wl_event_flush, + .event_flush_expose = wl_event_flush_expose, + .event_sync = wl_event_sync, + .event_pending = wl_event_pending, + .event_drain_motion = wl_event_drain_motion, + .event_discard_enter_leave_until = wl_event_discard_enter_leave_until, + .event_handle_extensions = wl_event_handle_extensions, + .event_randr_update = wl_event_randr_update, + + .prop_get_normal_hints = wl_prop_get_normal_hints, + .prop_get_wm_hints = wl_prop_get_wm_hints, + .prop_get_wm_class = wl_prop_get_wm_class, + .window_fetch_name = wl_window_fetch_name, + .window_get_icon_name = wl_window_get_icon_name, + .screen_get_head_usable_area = wl_noop_screen_get_head_usable_area, + .window_get_icon_image = wl_window_get_icon_image, + .prop_get_wmaker_menu = wl_prop_get_wmaker_menu, + .prop_get_transient_for = wl_prop_get_transient_for, + .prop_has_wmaker_menu = wl_prop_has_wmaker_menu, + .prop_has_gtk_object_path = wl_prop_has_gtk_object_path, + .prop_set_wm_state = wl_prop_set_wm_state, + .prop_get_wm_state = wl_prop_get_wm_state, + + .window_map = wl_window_map, + .window_map_raised = wl_window_map_raised, + .window_map_subwindows = wl_window_map_subwindows, + .pixmap_free = wl_pixmap_free, + .window_unmap = wl_window_unmap, + .window_move = wl_window_move, + .window_resize = wl_window_resize, + .window_set_border = wl_window_set_border, + .window_set_border_width = wl_window_set_border_width, + .window_set_save_under = wl_window_set_save_under, + .window_set_background = wl_noop_window_set_background, + .window_set_background_pixmap = wl_window_set_background_pixmap, + .window_clear = wl_window_clear, + .window_get_rect = wl_window_get_rect, + .window_add_events = wl_window_add_events, + .window_select_events = wl_window_select_events, + .window_shape_clear = wl_window_shape_clear, + .window_shape_set = wl_window_shape_set, + + .overlay_draw_line = wl_overlay_draw_line, + .overlay_draw_lines = wl_overlay_draw_lines, + .overlay_draw_rect = wl_overlay_draw_rect, + .overlay_draw_segments = wl_overlay_draw_segments, + .overlay_draw_string = wl_overlay_draw_string, + .overlay_text_width = wl_overlay_text_width, + + .window_get_client_attrs = wl_window_get_client_attrs, + .client_get_visual_attrs = wl_client_get_visual_attrs, + .client_adopt_attributes = wl_client_adopt_attributes, + .client_reparent = wl_client_reparent, + .client_synth_configure = wl_client_synth_configure, + .client_add_to_save_set = wl_client_add_to_save_set, + .client_remove_from_save_set = wl_client_remove_from_save_set, + .client_close = wl_client_close, + .client_query_shaped = wl_client_query_shaped, + .client_get_gnustep_attrs = wl_client_get_gnustep_attrs, + .client_get_pid = wl_client_get_pid, + .client_snapshot = wl_client_snapshot, + .coords_translate = wl_coords_translate, + + .icccm_set_wm_hints = wl_icccm_set_wm_hints, + .icccm_set_class_hint = wl_icccm_set_class_hint, + .icccm_set_class_hint_if_absent = wl_icccm_set_class_hint_if_absent, + .icccm_set_command = wl_icccm_set_command, + .icccm_get_command = wl_icccm_get_command, + .icccm_free_command = wl_icccm_free_command, + + .fake_leader_create = wl_fake_leader_create, + .fake_leader_destroy = wl_fake_leader_destroy, + + .screen_replace_wm = wl_screen_replace_wm, + .screen_select_input = wl_screen_select_input, + .screen_set_root_cursor = wl_screen_set_root_cursor, + .screen_set_icon_sizes = wl_screen_set_icon_sizes, + .screen_setup_noticeboard = wl_screen_setup_noticeboard, + .screen_alloc_gcs = wl_screen_alloc_gcs, + .screen_create_internals = wl_screen_create_internals, + .screen_create_pixmaps = wl_screen_create_pixmaps, + .screen_load_tech_font = wl_screen_load_tech_font, + .screen_show_mini_screenshot = wl_screen_show_mini_screenshot, + .screen_capture_area = wl_screen_capture_area, + .screen_capture_window = wl_screen_capture_window, + .refresh_desktop = wl_refresh_desktop, + .set_desktop_background = wl_set_desktop_background, + + .monitors_query = wl_monitors_query, + .monitors_select_events = wl_monitors_select_events, + + .workspace_badge_render = wl_workspace_badge_render, + .workspace_badge_update = wl_workspace_badge_update, + .workspace_badge_get_background = wl_workspace_badge_get_background, + + .keysym_to_keycode = wl_keysym_to_keycode, + .keysym_from_keycode = wl_keysym_from_keycode, + .xkb_lock_group = wl_xkb_lock_group, + .xkb_get_group = wl_xkb_get_group, + + .extensions_init = wl_extensions_init, + .cursors_init = wl_cursors_init, + .cursor_create_named = wl_cursor_create_named, + .shape_combine_mask = wl_shape_combine_mask, + .register_error_handlers = wl_register_error_handlers, + .texture_alloc_color = wl_texture_alloc_color, + .texture_free_colors = wl_texture_free_colors, + .texture_create_gc = wl_texture_create_gc, + .texture_free_gc = wl_texture_free_gc, + .frame_alloc_border_pixel = wl_noop_frame_alloc_border_pixel, + .frame_update_texture = wl_noop_frame_update_texture, + .frame_paint_button = wl_frame_paint_button, + .netwm_init = wl_noop_netwm_init, + .netwm_cleanup = wl_noop_netwm_cleanup, + .netwm_update_desktop = wl_noop_netwm_update_desktop, + .netwm_get_current_desktop = wl_noop_netwm_get_current_desktop, + .netwm_update_opacity = wl_netwm_update_opacity, + .netwm_update_actions = wl_noop_netwm_update_actions, + .netwm_check_client_hints = wl_noop_netwm_check_client_hints, + .netwm_check_initial_state = wl_noop_netwm_check_initial_state, + .netwm_get_pid = wl_noop_netwm_get_pid, + .netwm_cleanup_frame_extents = wl_noop_netwm_cleanup_frame_extents, + .netwm_set_frame_extents = wl_noop_netwm_set_frame_extents, + .prop_get_protocols = wl_prop_get_protocols, + .prop_get_check_property = wl_noop_prop_get_check_property, + .prop_set_wmaker_protocols = wl_noop_prop_set_wmaker_protocols, + .prop_set_icon_tile = wl_noop_prop_set_icon_tile, + .prop_cleanup = wl_noop_prop_cleanup, + .client_restore = wl_noop_client_restore, + .client_set_state = wl_client_set_state, + .client_set_maximized = wl_client_set_maximized, + .client_send_protocol = wl_client_send_protocol, + .client_kill = wl_client_kill_impl, + .client_get_colormap_windows = wl_noop_client_get_colormap_windows, + .eat_expose = wl_noop_eat_expose, + .move_window = wl_noop_move_window, + .slide_windows = wl_noop_slide_windows, + .get_text_selection = wl_noop_get_text_selection, + .get_shortcut_string = wl_noop_get_shortcut_string, + .get_command_for_window = wl_get_command_for_window, + .icon_update_pixmap = wl_noop_icon_update_pixmap, + .icon_paint = wl_icon_paint, + .appicon_show = wl_appicon_show, + .appicon_paint = wl_appicon_paint, + .restore_desktop = wl_noop_restore_desktop, + .wipe_desktop = wl_noop_wipe_desktop, + .get_color_for_colormap = wl_get_color_for_colormap, + .free_color = wl_noop_free_color, + .colormap_install = wl_noop_colormap_install, + .xdnd_init = wl_noop_xdnd_init, + .motif_get_hints = wl_noop_motif_get_hints, + .appicon_bounce = wl_noop_appicon_bounce, + .manage_all_windows = wl_noop_manage_all_windows, + .event_handle_signal_exit = wl_event_handle_signal_exit, + .icon_select = wl_noop_icon_select, + .icon_change_title = wl_noop_icon_change_title, + .clip_icon_paint = wl_clip_icon_paint, + .drawer_icon_paint = wl_drawer_icon_paint, + .balloon_init = wl_balloon_init, + .do_kaboom = wl_noop_do_kaboom, + .menu_paint_entry = wl_menu_paint_entry, + .menu_draw_frame = wl_menu_draw_frame, + .draw_bevel = wl_noop_draw_bevel, + .appmenu_send = wl_noop_appmenu_send, + .display_get_name = wl_display_get_name, + .switchpanel_send_enter = wl_noop_switchpanel_send_enter, + .format_x_error = wl_noop_format_x_error, + .pixmap_create_from_xbm = wl_noop_pixmap_create_from_xbm, + .pixmap_create = wl_noop_pixmap_create, + .keysym_from_name = wl_keysym_from_name, + .frame_paint_resizebar = wl_frame_paint_resizebar, + .frame_paint_titlebar = wl_frame_paint_titlebar, + .prop_get_client_leader = wl_noop_prop_get_client_leader, + .prop_get_window_state = wl_noop_prop_get_window_state, + .setup_modifier_lock_masks = wl_noop_setup_modifier_lock_masks, + .key_grab_hacked = wl_noop_key_grab_hacked, + .button_grab_hacked = wl_noop_button_grab_hacked, + .context_init = wl_context_init, + .query_tree = wl_noop_query_tree, + .screen_for_window = wl_noop_screen_for_window, + .parse_color = wl_parse_color, + .cursor_create_from_files = wl_noop_cursor_create_from_files, + .cursor_free = wl_noop_cursor_free, + .cursor_set = wl_noop_cursor_set, + .pointer_change_cursor = wl_pointer_change_cursor, + .appicon_paint_dock_dots = wl_noop_appicon_paint_dock_dots, + .pixmap_get_size = wl_noop_pixmap_get_size, + .icon_paint_title_bg = wl_noop_icon_paint_title_bg, + .icon_select_paint = wl_noop_icon_select_paint, + .window_get_event_mask = wl_noop_window_get_event_mask, + .balloon_show = wl_balloon_show, + .window_fill_stipple = wl_noop_window_fill_stipple, + .event_check_enter_leave = wl_noop_event_check_enter_leave, + .event_allow_async = wl_noop_event_allow_async, + .event_replay_pointer = wl_noop_event_replay_pointer, + .keyboard_mapping_refresh = wl_noop_keyboard_mapping_refresh, + .event_flush_window = wl_noop_event_flush_window, + .event_flush_type = wl_noop_event_flush_type, + .event_check_window = wl_noop_event_check_window, + .event_check_type = wl_noop_event_check_type, + .client_send_event = wl_noop_client_send_event, + .client_message_forward = wl_client_message_forward, + .client_send_key = wl_client_send_key, + .window_exists = wl_noop_window_exists, + .window_get_colormap = wl_noop_window_get_colormap, + .pointer_query_window = wl_noop_pointer_query_window, + .dialog_paint_icon = wl_noop_dialog_paint_icon, + .pointer_get_position = wl_pointer_get_position, + .keysym_to_string = wl_noop_keysym_to_string, + .window_get_command = wl_window_get_command, + .netwm_process_client_message = wl_noop_netwm_process_client_message, + .xdnd_process_client_message = wl_noop_xdnd_process_client_message, + .xdnd_process_selection = wl_noop_xdnd_process_selection, + .xdnd_make_awareness = wl_noop_xdnd_make_awareness, .netwm_check_client_hint_change = wl_noop_netwm_check_client_hint_change, + .netwm_position_splash = wl_noop_netwm_position_splash, + .netwm_check_initial_frame_state = wl_noop_netwm_check_initial_frame_state, + .handle_selection_request = wl_noop_handle_selection_request, + .handle_selection_clear = wl_noop_handle_selection_clear, + .create_rcontext = wl_create_rcontext, +}; diff --git a/src/backend/wayland/wl_types.h b/src/backend/wayland/wl_types.h index baa3390..d25ec32 100644 --- a/src/backend/wayland/wl_types.h +++ b/src/backend/wayland/wl_types.h @@ -555,3 +555,48 @@ void wm_shm_surface_destroy(struct wm_shm_surface *s); WMScreen *wl_get_wmscreen(void); void wl_wings_dispatch(int timeout_ms, void *ud); Display *wl_get_x_display(void); + +/* Declarations for functions still in wl_backend.c referenced by vtable */ +void wl_button_grab(WNativeWindow win, unsigned int button, unsigned int modifier, unsigned int event_mask, int owner_events); +void wl_button_ungrab_all(WNativeWindow win); +void wl_display_close(void); +int wl_display_default_screen(void); +void wl_display_post_open(const char *display_name, int *multi_head_out); +int wl_display_screen_count(void); +void wl_key_grab(WNativeWindow win, unsigned int keycode, unsigned int modifier, int owner_events); +void wl_key_ungrab_all(WNativeWindow win); +int wl_keyboard_grab(WNativeWindow win); +void wl_keyboard_ungrab(void); +void wl_monitors_query(WScreen *scr, WHeadGeometry **heads_out, int *count_out); +void wl_monitors_select_events(WScreen *scr); +void wl_pointer_change_grab(unsigned int event_mask, int cursor_id, unsigned long timestamp); +int wl_pointer_query(WNativeWindow root, int *root_x, int *root_y); +int wl_pointer_query_full(WNativeWindow root, WNativeWindow *child_out, int *root_x, int *root_y, unsigned int *buttons_out); +void wl_pointer_ungrab(unsigned long timestamp); +void wl_pointer_warp(int dx, int dy); +void wl_refresh_desktop(WScreen *scr); +void wl_screen_alloc_gcs(WScreen *scr); +RImage *wl_screen_capture_area(WScreen *scr); +RImage *wl_screen_capture_window(WScreen *scr, WWindow *wwin); +void wl_screen_create_internals(WScreen *scr); +void wl_screen_create_pixmaps(WScreen *scr); +void wl_screen_load_tech_font(WScreen *scr); +int wl_screen_replace_wm(WScreen *scr); +int wl_screen_select_input(WScreen *scr, long mask); +void wl_screen_set_icon_sizes(WScreen *scr, int min_size, int max_size); +void wl_screen_set_root_cursor(WScreen *scr, int cursor_id); +void wl_screen_setup_noticeboard(WScreen *scr); +void wl_screen_show_mini_screenshot(WScreen *scr, RImage *img); +void wl_set_desktop_background(WScreen *scr, const char *texture_desc); +void *wl_display_open(const char *name); +const char *wl_display_get_name(const char *name); +RContext *wl_create_rcontext(int screen_number, RContextAttributes *attribs); +unsigned long wl_keysym_from_name(const char *name); +const char *wl_noop_keysym_to_string(unsigned long ks); +WPixmap *wl_noop_pixmap_create(unsigned long image, unsigned long mask); +WPixmap *wl_noop_pixmap_create_from_xbm(WScreen *scr, char *data, char *mask, int width, int height, unsigned long fg, unsigned long bg); +unsigned char *wl_noop_prop_get_check_property(WNativeWindow window, unsigned long hint, unsigned long type, int format, int count, int *ret_count); +WScreen *wl_noop_screen_for_window(WNativeWindow window); +unsigned long wl_noop_window_get_event_mask(WNativeWindow window); +void wl_pointer_warp_to(int x, int y); +int wl_pointer_grab(WNativeWindow grab_win, WNativeWindow confine_to, unsigned int event_mask, int cursor_id, int owner_events, unsigned long timestamp);