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