x11: fix menu rendering, balloon tooltips, clip arrows, and event handling
Menu entry painting: - Add drawFrame() calls for 3D beveled entries (WTEX_SOLID) - Add menu_text_clearance to text Y positioning - Add left-side indicator drawing (radio/check/snap pixmaps) - Add indicator X offset (MENU_INDICATOR_SPACE) - Fix disabled+selected text color - Fix texture-aware background clearing - Add cascade arrow for submenu entries Balloon tooltips: - Implement x11_balloon_show() with text and mini-preview rendering - Previously was a no-op stub Clip arrows: - Draw workspace-switching triangles on clip icon - Previously stubbed out Event handling: - Add MapRequest, ConfigureRequest, ReparentNotify translation to xev_to_wme() in WINGs/wevent_x11.c - These WM-critical events were silently dropped, preventing new X clients (e.g. xterm) from being managed
This commit is contained in:
parent
f878117013
commit
b714cb783d
|
|
@ -194,6 +194,36 @@ xev_to_wme(const XEvent *xev, WMEvent *out)
|
|||
out->u.mapping.count = xev->xmapping.count;
|
||||
break;
|
||||
|
||||
case MapRequest:
|
||||
out->type = WME_MAP_REQUEST;
|
||||
out->u.mapRequest.window = (WNativeWindow)xev->xmaprequest.window;
|
||||
out->u.mapRequest.parent = (WNativeWindow)xev->xmaprequest.parent;
|
||||
break;
|
||||
|
||||
case ConfigureRequest:
|
||||
out->type = WME_CONFIGURE_REQUEST;
|
||||
out->u.configureRequest.window = (WNativeWindow)xev->xconfigurerequest.window;
|
||||
out->u.configureRequest.parent = (WNativeWindow)xev->xconfigurerequest.parent;
|
||||
out->u.configureRequest.x = xev->xconfigurerequest.x;
|
||||
out->u.configureRequest.y = xev->xconfigurerequest.y;
|
||||
out->u.configureRequest.width = xev->xconfigurerequest.width;
|
||||
out->u.configureRequest.height = xev->xconfigurerequest.height;
|
||||
out->u.configureRequest.border_width = xev->xconfigurerequest.border_width;
|
||||
out->u.configureRequest.above = (WNativeWindow)xev->xconfigurerequest.above;
|
||||
out->u.configureRequest.detail = xev->xconfigurerequest.detail;
|
||||
out->u.configureRequest.value_mask = xev->xconfigurerequest.value_mask;
|
||||
break;
|
||||
|
||||
case ReparentNotify:
|
||||
out->type = WME_REPARENT;
|
||||
out->u.reparent.window = (WNativeWindow)xev->xreparent.window;
|
||||
out->u.reparent.event = (WNativeWindow)xev->xreparent.event;
|
||||
out->u.reparent.parent = (WNativeWindow)xev->xreparent.parent;
|
||||
out->u.reparent.x = xev->xreparent.x;
|
||||
out->u.reparent.y = xev->xreparent.y;
|
||||
out->u.reparent.override_redirect = xev->xreparent.override_redirect;
|
||||
break;
|
||||
|
||||
default:
|
||||
out->type = WME_NONE;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3527,13 +3527,140 @@ static unsigned long x11_window_get_event_mask(WNativeWindow window)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int balloon_countLines(const char *text)
|
||||
{
|
||||
int count = 1;
|
||||
while (*text) { if (*text == '\n') count++; text++; }
|
||||
return count;
|
||||
}
|
||||
|
||||
static int balloon_getMaxStringWidth(WMFont *font, const char *text)
|
||||
{
|
||||
int w = 0, wt, pos = 0;
|
||||
const char *p = text, *pb = p;
|
||||
while (*p) {
|
||||
if (*p == '\n') {
|
||||
wt = WMWidthOfString(font, pb, pos);
|
||||
if (wt > w) w = wt;
|
||||
pos = 0; pb = p + 1;
|
||||
} else { pos++; }
|
||||
p++;
|
||||
}
|
||||
if (pos > 0) { wt = WMWidthOfString(font, pb, pos); if (wt > w) w = wt; }
|
||||
return w;
|
||||
}
|
||||
|
||||
static void balloon_drawMultiLineString(WMScreen *wmscr, Pixmap pixmap, WMColor *color,
|
||||
WMFont *font, int x, int y, const char *text, int len)
|
||||
{
|
||||
const char *p = text, *pb = p;
|
||||
int l = 0, pos = 0, height = WMFontHeight(font);
|
||||
while (*p && p - text < len) {
|
||||
if (*p == '\n') {
|
||||
WMDrawString(wmscr, pixmap, color, font, x, y + l * height, pb, pos);
|
||||
l++; pos = 0; pb = p + 1;
|
||||
} else { pos++; }
|
||||
p++;
|
||||
}
|
||||
if (pos > 0)
|
||||
WMDrawString(wmscr, pixmap, color, font, x, y + l * height, pb, pos);
|
||||
}
|
||||
|
||||
static void x11_balloon_show(WScreen *scr, void *bal_ptr)
|
||||
{
|
||||
/* Balloon rendering is complex X11 code (shape extension, pixmap masks).
|
||||
* For the architecture transition, we just position and map the window
|
||||
* with the text. Full rendering will be restored when balloon.c's
|
||||
* rendering functions are moved here. */
|
||||
(void)scr; (void)bal_ptr;
|
||||
WBalloon *bal = (WBalloon *)bal_ptr;
|
||||
Window foow;
|
||||
unsigned foo, obj_w;
|
||||
int x, y, width, height, bx, by;
|
||||
Pixmap pixmap;
|
||||
WMFont *font = scr->info_text_font;
|
||||
|
||||
if (!bal->text || !bal->objectWindow)
|
||||
return;
|
||||
|
||||
if (!XGetGeometry(dpy, (Window)bal->objectWindow, &foow, &x, &y, &obj_w, &foo, &foo, &foo)) {
|
||||
bal->prevType = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (wPreferences.miniwin_preview_balloon && bal->mini_preview != WM_NATIVE_NONE) {
|
||||
/* Mini-preview balloon */
|
||||
int titleHeight = 0;
|
||||
char *shortenTitle = NULL;
|
||||
width = wPreferences.minipreview_size;
|
||||
height = wPreferences.minipreview_size;
|
||||
|
||||
if (wPreferences.miniwin_title_balloon) {
|
||||
shortenTitle = ShrinkString(font, bal->text, width - MINIPREVIEW_BORDER * 2);
|
||||
titleHeight = balloon_countLines(shortenTitle) * WMFontHeight(font) + 4;
|
||||
height += titleHeight;
|
||||
}
|
||||
|
||||
bx = x;
|
||||
if (bx < 0) bx = 0;
|
||||
else if (bx + width > scr->scr_width - 1) bx = scr->scr_width - width - 1;
|
||||
|
||||
if (y - height - 2 < 0) { by = y + wPreferences.icon_size; if (by < 0) by = 0; }
|
||||
else { by = y - height - 2; }
|
||||
|
||||
if (scr->window_title_texture[0])
|
||||
XSetForeground(dpy, scr->draw_gc, scr->window_title_texture[0]->any.color.pixel);
|
||||
else
|
||||
XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
|
||||
|
||||
pixmap = XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
|
||||
XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
|
||||
|
||||
if (shortenTitle) {
|
||||
balloon_drawMultiLineString(scr->wmscreen, pixmap, scr->window_title_color[0], font,
|
||||
MINIPREVIEW_BORDER, MINIPREVIEW_BORDER, shortenTitle, strlen(shortenTitle));
|
||||
wfree(shortenTitle);
|
||||
}
|
||||
|
||||
XCopyArea(dpy, (Pixmap)bal->mini_preview, pixmap, scr->draw_gc,
|
||||
0, 0, wPreferences.minipreview_size - 1 - MINIPREVIEW_BORDER * 2,
|
||||
wPreferences.minipreview_size - 1 - MINIPREVIEW_BORDER * 2,
|
||||
MINIPREVIEW_BORDER, MINIPREVIEW_BORDER + titleHeight);
|
||||
|
||||
XResizeWindow(dpy, (Window)bal->window, width, height);
|
||||
XMoveWindow(dpy, (Window)bal->window, bx, by);
|
||||
XSetWindowBackgroundPixmap(dpy, (Window)bal->window, pixmap);
|
||||
XClearWindow(dpy, (Window)bal->window);
|
||||
XMapRaised(dpy, (Window)bal->window);
|
||||
} else {
|
||||
/* Text-only balloon */
|
||||
width = balloon_getMaxStringWidth(font, bal->text) + 8;
|
||||
height = balloon_countLines(bal->text) * WMFontHeight(font) + 4;
|
||||
|
||||
bx = x;
|
||||
if (bx < 0) bx = 0;
|
||||
else if (bx + width > scr->scr_width - 1) bx = scr->scr_width - width;
|
||||
|
||||
if (y - height - 2 < 0) { by = y + bal->h; if (by < 0) by = 0; }
|
||||
else { by = y - height - 2; }
|
||||
|
||||
if (scr->window_title_texture[0])
|
||||
XSetForeground(dpy, scr->draw_gc, scr->window_title_texture[0]->any.color.pixel);
|
||||
else
|
||||
XSetForeground(dpy, scr->draw_gc, scr->light_pixel);
|
||||
|
||||
pixmap = XCreatePixmap(dpy, scr->root_win, width, height, scr->w_depth);
|
||||
XFillRectangle(dpy, pixmap, scr->draw_gc, 0, 0, width, height);
|
||||
|
||||
balloon_drawMultiLineString(scr->wmscreen, pixmap, scr->window_title_color[0], font,
|
||||
4, 2, bal->text, strlen(bal->text));
|
||||
|
||||
XResizeWindow(dpy, (Window)bal->window, width, height);
|
||||
XMoveWindow(dpy, (Window)bal->window, bx, by);
|
||||
XSetWindowBackgroundPixmap(dpy, (Window)bal->window, pixmap);
|
||||
XClearWindow(dpy, (Window)bal->window);
|
||||
XMapRaised(dpy, (Window)bal->window);
|
||||
}
|
||||
|
||||
if (bal->contents)
|
||||
XFreePixmap(dpy, (Pixmap)bal->contents);
|
||||
bal->contents = (WMNativePixmap)pixmap;
|
||||
bal->mapped = 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4251,9 +4378,28 @@ static void x11_clip_icon_paint(WAppIcon *aicon)
|
|||
GC gc = scr->draw_gc;
|
||||
XPoint p[4];
|
||||
int pt = CLIP_BUTTON_SIZE * ICON_SIZE / 64;
|
||||
int as = pt - 15;
|
||||
XSetForeground(dpy, gc, WMColorPixel(color));
|
||||
/* arrows omitted for brevity — clip buttons are cosmetic */
|
||||
(void)p; (void)pt; (void)gc;
|
||||
|
||||
/* top-right arrow (next workspace) */
|
||||
p[0].x = p[3].x = ICON_SIZE - 5 - as;
|
||||
p[0].y = p[3].y = 5;
|
||||
p[1].x = ICON_SIZE - 6;
|
||||
p[1].y = 5;
|
||||
p[2].x = ICON_SIZE - 6;
|
||||
p[2].y = 4 + as;
|
||||
XFillPolygon(dpy, win, gc, p, 3, Convex, CoordModeOrigin);
|
||||
XDrawLines(dpy, win, gc, p, 4, CoordModeOrigin);
|
||||
|
||||
/* bottom-left arrow (prev workspace) */
|
||||
p[0].x = p[3].x = 5;
|
||||
p[0].y = p[3].y = ICON_SIZE - 5 - as;
|
||||
p[1].x = 5;
|
||||
p[1].y = ICON_SIZE - 6;
|
||||
p[2].x = 4 + as;
|
||||
p[2].y = ICON_SIZE - 6;
|
||||
XFillPolygon(dpy, win, gc, p, 3, Convex, CoordModeOrigin);
|
||||
XDrawLines(dpy, win, gc, p, 4, CoordModeOrigin);
|
||||
}
|
||||
}
|
||||
static void x11_drawer_icon_paint(WAppIcon *dicon)
|
||||
|
|
@ -4310,33 +4456,118 @@ static void x11_do_kaboom(WScreen *scr, WNativeWindow win, int x, int y)
|
|||
* For now, just free the pixmap without animation in Wayland transition. */
|
||||
#endif
|
||||
}
|
||||
static void x11_menu_draw_frame(WScreen *scr, WNativeWindow win, int y, int w, int h, int type);
|
||||
static void x11_menu_paint_entry(WMenu *menu, int index, int selected)
|
||||
{
|
||||
/* TODO: inline the full menu entry rendering from the old wMenuPaintEntry.
|
||||
* For now, just draw the text without decorations. */
|
||||
WScreen *scr = menu->frame->screen_ptr;
|
||||
WMenuEntry *entry = menu->entries[index];
|
||||
Window win = menu->menu->window;
|
||||
int y = menu->entry_height * index;
|
||||
int h = menu->entry_height;
|
||||
int w = menu->menu->width;
|
||||
int x, tw, type;
|
||||
WMColor *color;
|
||||
|
||||
if (!menu->flags.realized)
|
||||
return;
|
||||
|
||||
/* Determine frame type for flat menu style */
|
||||
if (wPreferences.menu_style == MS_FLAT && menu->entry_no > 1) {
|
||||
if (index == 0)
|
||||
type = 1; /* F_TOP */
|
||||
else if (index == menu->entry_no - 1)
|
||||
type = 2; /* F_BOTTOM */
|
||||
else
|
||||
type = 3; /* F_NONE */
|
||||
} else {
|
||||
type = 0; /* F_NORMAL */
|
||||
}
|
||||
|
||||
/* Paint background */
|
||||
if (selected) {
|
||||
XFillRectangle(dpy, win, WMColorGC(scr->select_color), 1, y + 1, w - 2, h - 3);
|
||||
color = scr->select_text_color;
|
||||
if (scr->menu_item_texture->any.type == WTEX_SOLID)
|
||||
x11_menu_draw_frame(scr, (WNativeWindow)win, y, w, h, type);
|
||||
} else {
|
||||
if (scr->menu_item_texture->any.type == WTEX_SOLID) {
|
||||
XClearArea(dpy, win, 0, y + 1, w - 1, h - 3, False);
|
||||
x11_menu_draw_frame(scr, (WNativeWindow)win, y, w, h, type);
|
||||
} else {
|
||||
XClearArea(dpy, win, 0, y, w, h, False);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine text color */
|
||||
if (selected) {
|
||||
color = entry->flags.enabled ? scr->select_text_color : scr->dtext_color;
|
||||
} else {
|
||||
XClearArea(dpy, win, 0, y, w, h, False);
|
||||
color = entry->flags.enabled ? scr->mtext_color : scr->dtext_color;
|
||||
}
|
||||
|
||||
WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font,
|
||||
8, y + 3, entry->text, strlen(entry->text));
|
||||
/* Draw text */
|
||||
x = 5;
|
||||
if (entry->flags.indicator)
|
||||
x += MENU_INDICATOR_SPACE + 2;
|
||||
|
||||
WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font,
|
||||
x, y + 3 + wPreferences.menu_text_clearance,
|
||||
entry->text, strlen(entry->text));
|
||||
|
||||
/* Draw cascade arrow */
|
||||
if (entry->cascade >= 0) {
|
||||
GC dim = scr->menu_item_auxtexture->dim_gc;
|
||||
GC light = scr->menu_item_auxtexture->light_gc;
|
||||
GC dark = scr->menu_item_auxtexture->dark_gc;
|
||||
XDrawLine(dpy, win, dim, w - 11, y + 6, w - 6, y + h / 2 - 1);
|
||||
XDrawLine(dpy, win, light, w - 11, y + h - 8, w - 6, y + h / 2 - 1);
|
||||
XDrawLine(dpy, win, dark, w - 12, y + 6, w - 12, y + h - 8);
|
||||
}
|
||||
|
||||
/* Draw left indicator */
|
||||
if (entry->flags.indicator && entry->flags.indicator_on) {
|
||||
WPixmap *indicator = NULL;
|
||||
switch (entry->flags.indicator_type) {
|
||||
case MI_DIAMOND: indicator = scr->menu_radio_indicator; break;
|
||||
case MI_CHECK: indicator = scr->menu_check_indicator; break;
|
||||
case MI_MINIWINDOW: indicator = scr->menu_mini_indicator; break;
|
||||
case MI_HIDDEN: indicator = scr->menu_hide_indicator; break;
|
||||
case MI_SHADED: indicator = scr->menu_shade_indicator; break;
|
||||
case MI_SNAP_V: indicator = scr->menu_snap_vertical_indicator; break;
|
||||
case MI_SNAP_H: indicator = scr->menu_snap_horizontal_indicator; break;
|
||||
case MI_CENTRAL: indicator = scr->menu_central_indicator; break;
|
||||
case MI_SNAP_RH: indicator = scr->menu_snap_rh_indicator; break;
|
||||
case MI_SNAP_LH: indicator = scr->menu_snap_lh_indicator; break;
|
||||
case MI_SNAP_TH: indicator = scr->menu_snap_th_indicator; break;
|
||||
case MI_SNAP_BH: indicator = scr->menu_snap_bh_indicator; break;
|
||||
case MI_SNAP_TL: indicator = scr->menu_snap_tl_indicator; break;
|
||||
case MI_SNAP_TR: indicator = scr->menu_snap_tr_indicator; break;
|
||||
case MI_SNAP_BL: indicator = scr->menu_snap_bl_indicator; break;
|
||||
case MI_SNAP_BR: indicator = scr->menu_snap_br_indicator; break;
|
||||
case MI_SNAP_TILED: indicator = scr->menu_snap_tiled_indicator; break;
|
||||
default: break;
|
||||
}
|
||||
if (indicator) {
|
||||
int iw = indicator->width;
|
||||
int ih = indicator->height;
|
||||
XSetClipMask(dpy, scr->copy_gc, indicator->mask);
|
||||
XSetClipOrigin(dpy, scr->copy_gc, 5, y + (h - ih) / 2);
|
||||
if (selected)
|
||||
XSetForeground(dpy, scr->copy_gc,
|
||||
WMColorPixel(entry->flags.enabled ? scr->select_text_color : scr->dtext_color));
|
||||
else
|
||||
XSetForeground(dpy, scr->copy_gc,
|
||||
WMColorPixel(entry->flags.enabled ? scr->mtext_color : scr->dtext_color));
|
||||
XFillRectangle(dpy, win, scr->copy_gc, 5, y + (h - ih) / 2, iw, ih);
|
||||
XSetClipMask(dpy, scr->copy_gc, None);
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw right text */
|
||||
if (entry->rtext && entry->cascade < 0) {
|
||||
int tw = WMWidthOfString(scr->menu_entry_font, entry->rtext, strlen(entry->rtext));
|
||||
tw = WMWidthOfString(scr->menu_entry_font, entry->rtext, strlen(entry->rtext));
|
||||
WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font,
|
||||
w - 8 - tw, y + 3, entry->rtext, strlen(entry->rtext));
|
||||
w - 6 - tw, y + 3 + wPreferences.menu_text_clearance,
|
||||
entry->rtext, strlen(entry->rtext));
|
||||
}
|
||||
}
|
||||
static void x11_menu_draw_frame(WScreen *scr, WNativeWindow win, int y, int w, int h, int type)
|
||||
|
|
|
|||
Loading…
Reference in New Issue