From 4f77d6a935814cd70e7185d58d4529d6d17fdc59 Mon Sep 17 00:00:00 2001 From: sbnwl Date: Sat, 8 Aug 2026 21:39:55 +0000 Subject: [PATCH] Apply Openbox 3.6.1 modifications --- obrender/theme.c | 458 +++++++++++++++++++++- obrender/theme.h | 75 ++++ openbox/config.c | 18 +- openbox/frame.c | 868 +++++++++++++++++++++++++++++++++++++++--- openbox/framerender.c | 173 ++++++++- 5 files changed, 1516 insertions(+), 76 deletions(-) diff --git a/obrender/theme.c b/obrender/theme.c index 2a4f6e105..8998f83f9 100644 --- a/obrender/theme.c +++ b/obrender/theme.c @@ -31,6 +31,397 @@ #include #include +/*! Titlebar button width-to-height aspect ratio. Not yet theme-configurable; + promote to an rc key (e.g. via READ_INT with fixed-point scaling) if a + real-world theme ends up looking distorted with a fixed 1.5. */ +#define WISTOH 1.5 + +/*! Discrete native-resolution glyph tiers, ordered ascending. At theme + load time, once button_height is known, we pick a tier sized to + leave a visible margin around the glyph (see pick_glyph_tier below) + and upsize the default glyphs to that tier's data, in place, before + RrPixmapMaskDraw ever runs. + + mask.c draws a glyph at its own native pixel size, centered in the + button, with no scaling of any kind -- so the tier we pick *is* + the rendered size. A single fixed source size can't serve both a + small button and a large one well: too small and it's lost in a + sea of margin on a big button, too large and it either doesn't fit + (pick_glyph_tier already excludes any tier bigger than + button_height, since mask.c doesn't clip -- an oversized native + glyph would just draw past the button's edge) or leaves no margin + at all. A small discrete set of sizes, picked per-button, covers + that range without needing mask.c to do any scaling. Themes + supplying their own .xbm are unaffected either way -- read_button_ + styles() only calls RrPixmapMaskNew() at whatever size the theme's + own file declares, and pick_glyph_tier's result is only ever + applied to masks still at the untouched 6x6 default (see + upsize_mask_inplace's width/height guard below). */ +/*! Replaces a mask's contents in place (same RrPixmapMask*, new pixel + data), rather than freeing the struct and allocating a new one. + This matters because read_button_styles() (much earlier in + RrThemeNew) already copied the *pointer value* of e.g. + btn->unpressed_mask directly into every button appearance's + texture[0].data.mask.mask. Swapping btn->unpressed_mask to point at + a freshly allocated struct leaves all of those appearances holding a + dangling pointer + to the just-freed old struct, which segfaults the moment any button + is painted (found via gdb: SIGSEGV in XSetClipMask, called from + RrPixmapMaskDraw on a freed RrPixmapMask). Mutating the existing + struct's contents keeps every existing pointer to it valid. */ +static void upsize_mask_inplace(const RrInstance *inst, RrPixmapMask *m, + const guchar *bigdata, gint bigw, gint bigh) +{ + if (!m || m->width != 6 || m->height != 6) return; + XFreePixmap(RrDisplay(inst), m->mask); + g_free(m->data); + m->width = bigw; + m->height = bigh; + m->data = g_memdup(bigdata, (bigw + 7) / 8 * bigh); + m->mask = XCreateBitmapFromData(RrDisplay(inst), RrRootWindow(inst), + (const gchar*)bigdata, bigw, bigh); +} + +static void upsize_button_masks(const RrInstance *inst, RrButton *btn, + const guchar *bigdata, + const guchar *bigdata_toggled, + gint bigw, gint bigh) +{ + /* Only touches masks that are still exactly the small 6x6 built-in + fallback (i.e. the theme never provided its own .xbm for that + specific button state) -- anything the theme customized, at any + resolution, is left completely alone. */ + upsize_mask_inplace(inst, btn->unpressed_mask, bigdata, bigw, bigh); + upsize_mask_inplace(inst, btn->pressed_mask, bigdata, bigw, bigh); + upsize_mask_inplace(inst, btn->disabled_mask, bigdata, bigw, bigh); + upsize_mask_inplace(inst, btn->hover_mask, bigdata, bigw, bigh); + if (bigdata_toggled) { + upsize_mask_inplace(inst, btn->unpressed_toggled_mask, + bigdata_toggled, bigw, bigh); + upsize_mask_inplace(inst, btn->pressed_toggled_mask, + bigdata_toggled, bigw, bigh); + upsize_mask_inplace(inst, btn->hover_toggled_mask, + bigdata_toggled, bigw, bigh); + } +} + +/*! One glyph set (all 5 buttons, 7 shapes -- max has a toggled/restore + variant, desk has a toggled/all-desktops-active variant, close/ + shade/iconify don't) at a single native resolution. Generated by + supersampled rendering + threshold-to-1-bit (4x oversample, drawn + with anti-aliasing-friendly primitives, downsampled with Lanczos, + then thresholded at 50% coverage) rather than drawn pixel-by-pixel + by hand -- this is what actually gives each tier clean, properly + placed edges instead of a naive blown-up copy of the 6x6 originals. */ +typedef struct { + gint size; + const guchar *max_normal, *max_toggled, *close; + const guchar *desk_normal, *desk_toggled; + const guchar *shade, *iconify; +} ButtonGlyphTier; + + /* ---- 8x8 tier ---- */ + + static const guchar max_normal8[] = { + 0x00, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x00 + }; + + static const guchar max_toggled8[] = { + 0xf0, 0x90, 0x90, 0xe0, 0x07, 0x0f, 0x0f, 0x0f + }; + + static const guchar close8[] = { + 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00 + }; + + static const guchar desk_normal8[] = { + 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00 + }; + + static const guchar desk_toggled8[] = { + 0x00, 0x00, 0x40, 0x20, 0x12, 0x0c, 0x00, 0x00 + }; + + static const guchar shade8[] = { + 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar iconify8[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00 + }; + + + /* ---- 16x16 tier ---- */ + + static const guchar max_normal16[] = { + 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, + 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, + 0x06, 0x60, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00 + }; + + static const guchar max_toggled16[] = { + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0xe0, 0x3f, 0x60, 0x30, 0xfc, 0x37, + 0xfc, 0x37, 0x6c, 0x36, 0x6c, 0x36, 0xec, 0x3f, 0xec, 0x3f, 0x0c, 0x06, + 0xfc, 0x07, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 + }; + + /* Small circular caps are added at each of the X's 4 endpoints, + radius roughly half the stroke width, so the strokes end in + a curve rather than a flat diagonal-cut edge. Visible at + 16/24/32; at 8px the stroke is only ~1-2px wide, too thin for + the added radius to register, so close8 above has no cap. */ + static const guchar close16[] = { + 0x00, 0x00, 0x0c, 0x30, 0x1e, 0x78, 0x3e, 0x3c, 0x7c, 0x1e, 0xf8, 0x0f, + 0xf0, 0x07, 0xe0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0x78, 0x1f, 0x3c, 0x3e, + 0x1e, 0x7c, 0x0e, 0x78, 0x04, 0x30, 0x00, 0x00 + }; + + static const guchar desk_normal16[] = { + 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x3c, 0x3c, 0x7c, 0x3e, 0x3c, 0x3c, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x3c, 0x3c, 0x7c, 0x3e, + 0x3c, 0x3c, 0x38, 0x1c, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar desk_toggled16[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x38, 0x00, 0x3c, + 0x00, 0x1e, 0x00, 0x0e, 0x18, 0x0f, 0xbc, 0x07, 0xfc, 0x03, 0xf8, 0x01, + 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar shade16[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar iconify16[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00 + }; + + + /* ---- 24x24 tier ---- */ + + static const guchar max_normal24[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x3f, 0xfc, 0xff, 0x3f, + 0xfc, 0xff, 0x3f, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, + 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, + 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, + 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0xfc, 0xff, 0x3f, + 0xfc, 0xff, 0x3f, 0xfc, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar max_toggled24[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, + 0x00, 0xff, 0x1f, 0x00, 0xff, 0x1f, 0x00, 0x07, 0x1c, 0x00, 0x07, 0x1c, + 0xf8, 0xff, 0x1c, 0xf8, 0xff, 0x1c, 0xf8, 0xff, 0x1c, 0x38, 0xe7, 0x1c, + 0x38, 0xe7, 0x1c, 0x38, 0xff, 0x1f, 0x38, 0xff, 0x1f, 0x38, 0xff, 0x1f, + 0x38, 0xe0, 0x00, 0x38, 0xe0, 0x00, 0xf8, 0xff, 0x00, 0xf8, 0xff, 0x00, + 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /* small circular caps at each endpoint, same as close16 above */ + static const guchar close24[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x3c, 0x00, 0x3c, + 0x7c, 0x00, 0x3e, 0xf8, 0x00, 0x1f, 0xf0, 0x81, 0x0f, 0xe0, 0xc3, 0x07, + 0xc0, 0xe7, 0x03, 0x80, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 0x7e, 0x00, + 0x00, 0x7e, 0x00, 0x00, 0xff, 0x00, 0x80, 0xff, 0x01, 0xc0, 0xe7, 0x03, + 0xe0, 0xc3, 0x07, 0xf0, 0x81, 0x0f, 0xf8, 0x00, 0x1f, 0x7c, 0x00, 0x3e, + 0x3c, 0x00, 0x3c, 0x1c, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar desk_normal24[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x07, + 0xf0, 0x81, 0x0f, 0xf8, 0xc3, 0x1f, 0xf8, 0xc3, 0x1f, 0xf8, 0xc3, 0x1f, + 0xf0, 0x81, 0x0f, 0xe0, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x07, 0xf0, 0x81, 0x0f, + 0xf8, 0xc3, 0x1f, 0xf8, 0xc3, 0x1f, 0xf8, 0xc3, 0x1f, 0xf0, 0x81, 0x0f, + 0xe0, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar desk_toggled24[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x1f, + 0x00, 0x80, 0x0f, 0x00, 0x80, 0x0f, 0x00, 0xc0, 0x07, 0x00, 0xe0, 0x03, + 0x20, 0xf0, 0x01, 0xf0, 0xf0, 0x01, 0xf8, 0xf9, 0x00, 0xf0, 0x7f, 0x00, + 0xe0, 0x3f, 0x00, 0xc0, 0x3f, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar shade24[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x1f, + 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar iconify24[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, + 0xf8, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + + /* ---- 32x32 tier ---- */ + + static const guchar max_normal32[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f, + 0xf8, 0xff, 0xff, 0x1f, 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, + 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, + 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, + 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, + 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, + 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x1e, + 0x78, 0x00, 0x00, 0x1e, 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f, + 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar max_toggled32[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0x0f, + 0x00, 0xfc, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0x0f, 0x00, 0x3c, 0x00, 0x0f, + 0x00, 0x3c, 0x00, 0x0f, 0xf0, 0xff, 0x1f, 0x0f, 0xf0, 0xff, 0x3f, 0x0f, + 0xf0, 0xff, 0x3f, 0x0f, 0xf0, 0xff, 0x3f, 0x0f, 0xf0, 0x7c, 0x3e, 0x0f, + 0xf0, 0x3c, 0x3c, 0x0f, 0xf0, 0x3c, 0x3c, 0x0f, 0xf0, 0x7c, 0x3e, 0x0f, + 0xf0, 0xfc, 0xff, 0x0f, 0xf0, 0xfc, 0xff, 0x0f, 0xf0, 0xfc, 0xff, 0x0f, + 0xf0, 0xf8, 0xff, 0x0f, 0xf0, 0x00, 0x3c, 0x00, 0xf0, 0x00, 0x3c, 0x00, + 0xf0, 0xff, 0x3f, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0xf0, 0xff, 0x3f, 0x00, + 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /* small circular caps at each endpoint, same as close16 above */ + static const guchar close32[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x1e, 0xf8, 0x00, 0x00, 0x1f, 0xf8, 0x01, 0x80, 0x1f, + 0xf8, 0x03, 0xc0, 0x1f, 0xf0, 0x07, 0xe0, 0x0f, 0xe0, 0x0f, 0xf0, 0x07, + 0xc0, 0x1f, 0xf8, 0x03, 0x80, 0x3f, 0xfc, 0x01, 0x00, 0x7f, 0xfe, 0x00, + 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf8, 0x1f, 0x00, + 0x00, 0xf0, 0x0f, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0xf8, 0x1f, 0x00, + 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x7f, 0xfe, 0x00, + 0x80, 0x3f, 0xfc, 0x01, 0xc0, 0x1f, 0xf8, 0x03, 0xe0, 0x0f, 0xf0, 0x07, + 0xf0, 0x07, 0xe0, 0x0f, 0xf8, 0x03, 0xc0, 0x1f, 0xfc, 0x01, 0x80, 0x1f, + 0xf8, 0x00, 0x00, 0x1f, 0x78, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar desk_normal32[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0xe0, 0x01, 0xe0, 0x0f, 0xf0, 0x07, + 0xe0, 0x1f, 0xf8, 0x07, 0xf0, 0x1f, 0xf8, 0x0f, 0xf0, 0x1f, 0xf8, 0x0f, + 0xf0, 0x1f, 0xf8, 0x0f, 0xf0, 0x1f, 0xf8, 0x0f, 0xe0, 0x0f, 0xf0, 0x07, + 0xc0, 0x07, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0xe0, 0x03, 0xe0, 0x0f, 0xf0, 0x07, + 0xf0, 0x1f, 0xf8, 0x0f, 0xf0, 0x1f, 0xf8, 0x0f, 0xf0, 0x1f, 0xf8, 0x0f, + 0xf0, 0x1f, 0xf8, 0x0f, 0xe0, 0x1f, 0xf8, 0x07, 0xe0, 0x0f, 0xf0, 0x07, + 0x80, 0x07, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar desk_toggled32[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x80, 0x1f, + 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xe0, 0x07, + 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xfc, 0x01, + 0x00, 0x00, 0xfe, 0x00, 0x80, 0x00, 0x7e, 0x00, 0xc0, 0x01, 0x3f, 0x00, + 0xe0, 0x83, 0x1f, 0x00, 0xf0, 0xc7, 0x1f, 0x00, 0xe0, 0xcf, 0x0f, 0x00, + 0xc0, 0xff, 0x07, 0x00, 0x80, 0xff, 0x03, 0x00, 0x00, 0xff, 0x03, 0x00, + 0x00, 0xfe, 0x01, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar shade32[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, + 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, + 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const guchar iconify32[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, + 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x0f, + 0xf0, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +static const ButtonGlyphTier button_glyph_tiers[] = { + { 8, max_normal8, max_toggled8, close8, + desk_normal8, desk_toggled8, shade8, iconify8 }, + { 16, max_normal16, max_toggled16, close16, + desk_normal16, desk_toggled16, shade16, iconify16 }, + { 24, max_normal24, max_toggled24, close24, + desk_normal24, desk_toggled24, shade24, iconify24 }, + { 32, max_normal32, max_toggled32, close32, + desk_normal32, desk_toggled32, shade32, iconify32 }, +}; +#define N_BUTTON_GLYPH_TIERS \ + ((gint)(sizeof(button_glyph_tiers) / sizeof(button_glyph_tiers[0]))) + +/*! Returns the tier that fills the button best while keeping a margin + around the glyph, or NULL to mean "leave the classic 6x6 default + alone" (only when button_height is below even our smallest tier). + + mask.c draws a glyph at its native pixel size, centered in the + button -- no scaling. So a tier's "fill" is just its own size, and + MAX_FILL_RATIO caps how much of the button that's allowed to + occupy, so a common theme doesn't end up with a glyph that fills + the button edge-to-edge with no visible margin. + + If no tier fits under that cap (a very large button relative to + our largest tier), we still pick the closest-fitting tier we have + rather than falling back to the 6x6 default -- a bigger native + source with more margin than intended still looks better than the + tiny default centered in a lot of empty space. */ +#define MAX_FILL_RATIO_NUM 7 /* rendered glyph may fill at most 70% of */ +#define MAX_FILL_RATIO_DEN 10 /* the button; the remaining 30% is margin */ +static const ButtonGlyphTier *pick_glyph_tier(gint button_height) +{ + const gint max_fill = (button_height * MAX_FILL_RATIO_NUM) / + MAX_FILL_RATIO_DEN; + const ButtonGlyphTier *best_under_cap = NULL; + const ButtonGlyphTier *best_over_cap = NULL; + gint i; + + for (i = 0; i < N_BUTTON_GLYPH_TIERS; ++i) { + gint size = button_glyph_tiers[i].size; + if (size > button_height) + break; /* sorted ascending; nothing further fits */ + if (size <= max_fill) + best_under_cap = &button_glyph_tiers[i]; /* largest under cap */ + else if (!best_over_cap) + best_over_cap = &button_glyph_tiers[i]; /* smallest overshoot */ + } + + /* prefer any tier that fits under the margin cap; only reach for + the least-bad over-cap tier if literally nothing fit under it */ + return best_under_cap ? best_under_cap : best_over_cap; +} + struct fallbacks { RrAppearance *focused_disabled; RrAppearance *unfocused_disabled; @@ -271,6 +662,10 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name, READ_INT("menu.separator.padding.height", theme->menu_sep_paddingy, 0, 100, 3); READ_INT("window.client.padding.width", theme->cbwidthx, 0, 100, theme->paddingx); READ_INT("window.client.padding.height", theme->cbwidthy, 0, 100, theme->cbwidthx); + /* room above the titlebar buttons reserved for the top-resize hover + zone. kept small and theme-configurable; see frame.c's topresize + and set_theme_statics() for how it drives button_height. */ + READ_INT("window.title.kgrip", theme->kgrip, 0, 2, 2); /* load colors */ READ_COLOR_("window.active.border.color", @@ -982,9 +1377,58 @@ RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name, theme->menu_title_height = theme->menu_title_label_height + theme->paddingy * 2; } - theme->button_size = theme->label_height - 2; + /* button_height is derived from the titlebar height, leaving kgrip + pixels of margin above and below the button (the margin above + doubles as the top-resize hover strip, see frame.c's topresize). + The MAX(1, ...) floor guards against a 0/negative XResizeWindow + call on themes with a very small paddingy combined with a large + kgrip. */ + theme->button_height = MAX(1, theme->title_height - 2 * theme->kgrip); + theme->button_width = (gint)(theme->button_height * WISTOH + 0.5); + /* ABI COMPATIBILITY: button_size no longer drives any of our own + geometry (frame.c/framerender.c use button_height/button_width + exclusively now), but the field itself is kept alive at its + original offset in the struct -- see the long comment on + button_size in theme.h for why that matters. We still assign it + a real value here, rather than leaving it at 0/uninitialized, + so that external programs still compiled against the stock + RrTheme layout (obconf's theme-preview button being the known + case) get a sane, if approximate, single square dimension + instead of garbage. button_height is the closer analogue of the + two new dimensions to what button_size used to mean. */ + theme->button_size = theme->button_height; theme->grip_width = 25; + /* Now that button_height is known, upgrade the default (non-theme- + provided) button glyphs to the largest native-resolution tier + that still fits -- see pick_glyph_tier and the tier table + above. pick_glyph_tier returns NULL if button_height is below + even the smallest tier, in which case we leave the classic 6x6 + defaults (already loaded by read_button_styles() above) alone. */ + { + const ButtonGlyphTier *gt = pick_glyph_tier(theme->button_height); + if (gt) { + upsize_button_masks(inst, theme->btn_max, + gt->max_normal, gt->max_toggled, + gt->size, gt->size); + upsize_button_masks(inst, theme->btn_close, + gt->close, NULL, gt->size, gt->size); + upsize_button_masks(inst, theme->btn_desk, + gt->desk_normal, gt->desk_toggled, + gt->size, gt->size); + /* shade's toggled state reuses the same shape as its + unpressed state (see the original 6x6 read_button_styles + call a bit above, which passes normal_mask for both + parameters) -- pass gt->shade for both here too, or the + toggled/shaded-window icon would be left stuck at 6x6 + while the unpressed one upgrades. */ + upsize_button_masks(inst, theme->btn_shade, + gt->shade, gt->shade, gt->size, gt->size); + upsize_button_masks(inst, theme->btn_iconify, + gt->iconify, NULL, gt->size, gt->size); + } + } + RrAppearanceFree(fbs.focused_disabled); RrAppearanceFree(fbs.unfocused_disabled); RrAppearanceFree(fbs.focused_hover); @@ -1494,8 +1938,10 @@ static void read_button_styles(XrmDatabase db, const RrInstance *inst, READ_BUTTON_MASK_COPY(disabled, btn->unpressed_mask); READ_BUTTON_MASK_COPY(hover, btn->unpressed_mask); if (toggled_mask) { - READ_BUTTON_MASK_COPY(pressed_toggled, btn->unpressed_toggled_mask); - READ_BUTTON_MASK_COPY(hover_toggled, btn->unpressed_toggled_mask); + g_snprintf(name, 128, "%s_toggled_pressed.xbm", btnname); + READ_MASK_COPY(name, btn->pressed_toggled_mask, btn->unpressed_toggled_mask); + g_snprintf(name, 128, "%s_toggled_hover.xbm", btnname); + READ_MASK_COPY(name, btn->hover_toggled_mask, btn->unpressed_toggled_mask); } #define READ_BUTTON_APPEARANCE(typedots, type, fallback) \ @@ -1532,8 +1978,8 @@ static void read_button_styles(XrmDatabase db, const RrInstance *inst, READ_BUTTON_APPEARANCE("disabled", disabled, 0); READ_BUTTON_APPEARANCE("hover", hover, 0); if (toggled_mask) { - READ_BUTTON_APPEARANCE("unpressed.toggled", unpressed_toggled, 1); - READ_BUTTON_APPEARANCE("pressed.toggled", pressed_toggled, 0); - READ_BUTTON_APPEARANCE("hover.toggled", hover_toggled, 0); + READ_BUTTON_APPEARANCE("toggled.unpressed", unpressed_toggled, 1); + READ_BUTTON_APPEARANCE("toggled.pressed", pressed_toggled, 0); + READ_BUTTON_APPEARANCE("toggled.hover", hover_toggled, 0); } } diff --git a/obrender/theme.h b/obrender/theme.h index 8797f0b69..79a11de83 100644 --- a/obrender/theme.h +++ b/obrender/theme.h @@ -58,6 +58,49 @@ struct _RrTheme { gint menu_font_height; gint label_height; gint title_height; + /*! ABI COMPATIBILITY FIELD -- do not move, rename, or remove. + ========================================================= + This field must stay at exactly this position in the struct. + RrTheme is a *public* struct (declared in this installed header, + obrender/theme.h) that external programs compile against + directly -- they don't just call our functions, they read + theme->some_field at whatever byte offset their own compiled + copy of this header says that field lives at. The shared + library (libobrender.so.32) and any such external program + (obconf is the known case, but there may be others: panels, + theme switchers, previewers, anything linking obrender) do NOT + get recompiled together -- they are separate packages, updated + independently by the package manager. + + So: inserting a field *before* this point, or deleting this + field outright, silently shifts the compiled-in offsets that + every field *after* it is read at in every external binary + that hasn't been rebuilt against the new header. Those programs + don't get a compile error -- they just start reading the wrong + bytes as soon as the new library is installed, misinterpreting + e.g. an RrColor* pointer as some unrelated gint, and typically + segfault almost immediately (this is exactly what happened to + obconf during development of the button/titlebar geometry + rework: replacing this field in place with two new ones, + button_height and button_width, shifted every RrColor pointer + and RrAppearance pointer field declared below it, and obconf + crashed the instant it dereferenced one of those now-misaligned + pointers). + + The fix, and the rule going forward: never insert or resize a + field in the middle of this struct. New fields belonging to + this stage of the work (kgrip, button_height, button_width) + are instead appended after the last original field (see the + end of this struct, past `name`) -- appending only grows the + struct and cannot change any earlier field's offset, so old + binaries keep reading everything before the append point + correctly. button_size itself is kept alive (rather than + deleted) and populated in theme.c as a mirror of button_height, + so that legacy code which still reads a single square + button_size -- like obconf's theme-preview button -- gets a + sane value instead of whatever garbage now sits at that offset. + It is not used anywhere in this codebase's own layout logic + any more; use button_height/button_width for that. */ gint button_size; gint grip_width; gint menu_title_label_height; @@ -174,6 +217,38 @@ struct _RrTheme { RrAppearance *osd_focused_button; gchar *name; + + /* ===================================================================== + APPEND-ONLY ZONE -- new fields from here on, nothing above this line. + ===================================================================== + `name` above is the last field of the original, upstream RrTheme + struct (see the ABI comment on button_size, further up, for the + full explanation of why this matters). Every field added below + was deliberately placed *after* the original struct's end rather + than inserted among the existing fields, specifically so that + external programs already compiled against the stock layout -- + obconf being the concrete example that broke -- go on reading + every pre-existing field (all the RrColor and RrAppearance pointers + etc. above) at the exact offsets they were compiled for, even + though this library's struct is now physically larger than + theirs. Those programs simply don't know these new fields exist, + which is fine: they never try to read past `name`. + + Any future geometry work should keep following this same rule: + append new fields here, don't insert them earlier in the struct, + and don't repurpose/resize an existing field's type. If a field + genuinely needs to be removed, keep it in place (like + button_size) and just stop relying on it internally, rather than + deleting it outright. */ + + /*! Room at the top of the titlebar, above the buttons, that acts as + a hover zone for the top-resize cursor. Read directly from the + theme file (clamped), like paddingx/paddingy. */ + gint kgrip; + /*! Real (non-square) button dimensions. button_width is derived + from button_height via the WISTOH constant in theme.c. */ + gint button_height; + gint button_width; }; /*! The font values are all optional. If a NULL is used for any of them, then diff --git a/openbox/config.c b/openbox/config.c index dad5d1bf9..3bcdfbbd9 100644 --- a/openbox/config.c +++ b/openbox/config.c @@ -48,6 +48,8 @@ StrutPartial config_margins; gchar *config_theme; gboolean config_theme_keepborder; guint config_theme_window_list_icon_size; +gboolean config_theme_roundcorners; +gboolean config_theme_invhandles; gchar *config_title_layout; @@ -716,9 +718,13 @@ static void parse_theme(xmlNodePtr node, gpointer d) config_theme_window_list_icon_size = obt_xml_node_int(n); if (config_theme_window_list_icon_size < 16) config_theme_window_list_icon_size = 16; - else if (config_theme_window_list_icon_size > 96) - config_theme_window_list_icon_size = 96; + else if (config_theme_window_list_icon_size > 256) + config_theme_window_list_icon_size = 256; } + if ((n = obt_xml_find_node(node, "roundCorners"))) + config_theme_roundcorners = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "invisibleHandles"))) + config_theme_invhandles = obt_xml_node_bool(n); for (n = obt_xml_find_node(node, "font"); n; @@ -754,7 +760,11 @@ static void parse_theme(xmlNodePtr node, gpointer d) } if ((fnode = obt_xml_find_node(n->children, "size"))) { int s = obt_xml_node_int(fnode); - if (s > 0) size = s; + /* enforce a minimum readable font size; smaller sizes are + silently clamped up rather than honored, since label_height + (and therefore title_height and the button/icon sizes + derived from it) is driven directly by the font metrics */ + if (s > 0) size = MAX(s, 8); } if ((fnode = obt_xml_find_node(n->children, "weight"))) { gchar *w = obt_xml_node_string(fnode); @@ -1098,6 +1108,8 @@ void config_startup(ObtXmlInst *i) config_title_layout = g_strdup("NLIMC"); config_theme_keepborder = TRUE; config_theme_window_list_icon_size = 36; + config_theme_roundcorners = FALSE; + config_theme_invhandles = FALSE; config_font_activewindow = NULL; config_font_inactivewindow = NULL; diff --git a/openbox/frame.c b/openbox/frame.c index 89669726a..9d817c664 100644 --- a/openbox/frame.c +++ b/openbox/frame.c @@ -45,6 +45,8 @@ #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b) +#define INV_WIDTH 10 + static void flash_done(gpointer data); static gboolean flash_timeout(gpointer data); @@ -54,6 +56,8 @@ static void free_theme_statics(ObFrame *self); static gboolean frame_animate_iconify(gpointer self); static void frame_adjust_cursors(ObFrame *self); +static gboolean moving = FALSE; + static Window createWindow(Window parent, Visual *visual, gulong mask, XSetWindowAttributes *attrib) { @@ -64,6 +68,14 @@ static Window createWindow(Window parent, Visual *visual, } +static Window createInputWindow (Window parent, gulong mask, XSetWindowAttributes *attrib) +{ + return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0, + CopyFromParent, InputOnly, + CopyFromParent, + mask, attrib); +} + static Visual *check_32bit_client(ObClient *c) { XWindowAttributes wattrib; @@ -125,6 +137,7 @@ ObFrame *frame_new(ObClient *client) mask |= CWEventMask; attrib.event_mask = ELEMENT_EVENTMASK; + self->innerleft = createWindow(self->window, NULL, mask, &attrib); self->innertop = createWindow(self->window, NULL, mask, &attrib); self->innerright = createWindow(self->window, NULL, mask, &attrib); @@ -176,6 +189,52 @@ ObFrame *frame_new(ObClient *client) self->rgriptop = createWindow(self->window, NULL, mask, &attrib); self->rgripbottom = createWindow(self->window, NULL, mask, &attrib); + self->outerleft = createWindow(self->window, NULL, mask, &attrib); + self->outerright = createWindow(self->window, NULL, mask, &attrib); + self->outertop = createWindow(self->window, NULL, mask, &attrib); + self->outerbottom = createWindow(self->window, NULL, mask, &attrib); + self->outerlefttop = createWindow(self->window, NULL, mask, &attrib); + self->outerrighttop = createWindow(self->window, NULL, mask, &attrib); + self->outerleftbottom = createWindow(self->window, NULL, mask, &attrib); + self->outerrightbottom = createWindow(self->window, NULL, mask, &attrib); + self->outertopleft = createWindow(self->window, NULL, mask, &attrib); + self->outertopright = createWindow(self->window, NULL, mask, &attrib); + self->outerbottomleft = createWindow(self->window, NULL, mask, &attrib); + self->outerbottomright = createWindow(self->window, NULL, mask, &attrib); + + self->edgeleft = createWindow(self->window, NULL, mask, &attrib); + self->edgeright = createWindow(self->window, NULL, mask, &attrib); + self->edgetop = createWindow(self->window, NULL, mask, &attrib); + self->edgebottom = createWindow(self->window, NULL, mask, &attrib); + self->edgelefttop = createWindow(self->window, NULL, mask, &attrib); + self->edgerighttop = createWindow(self->window, NULL, mask, &attrib); + self->edgeleftbottom = createWindow(self->window, NULL, mask, &attrib); + self->edgerightbottom = createWindow(self->window, NULL, mask, &attrib); + self->edgetopleft = createWindow(self->window, NULL, mask, &attrib); + self->edgetopright = createWindow(self->window, NULL, mask, &attrib); + self->edgebottomleft = createWindow(self->window, NULL, mask, &attrib); + self->edgebottomright = createWindow(self->window, NULL, mask, &attrib); + self->ce_tl_t = createWindow(self->window, NULL, mask, &attrib); + self->ce_tl_l = createWindow(self->window, NULL, mask, &attrib); + self->ce_tr_t = createWindow(self->window, NULL, mask, &attrib); + self->ce_tr_r = createWindow(self->window, NULL, mask, &attrib); + self->ce_bl_b = createWindow(self->window, NULL, mask, &attrib); + self->ce_bl_l = createWindow(self->window, NULL, mask, &attrib); + self->ce_br_b = createWindow(self->window, NULL, mask, &attrib); + self->ce_br_r = createWindow(self->window, NULL, mask, &attrib); + + if (config_theme_invhandles) + { + self->invleft = createInputWindow (obt_root (ob_screen), mask, &attrib); + self->invright = createInputWindow (obt_root (ob_screen), mask, &attrib); + self->invtop = createInputWindow (obt_root (ob_screen), mask, &attrib); + self->invbottom = createInputWindow (obt_root (ob_screen), mask, &attrib); + self->invtl = createInputWindow (obt_root (ob_screen), mask, &attrib); + self->invtr = createInputWindow (obt_root (ob_screen), mask, &attrib); + self->invbl = createInputWindow (obt_root (ob_screen), mask, &attrib); + self->invbr = createInputWindow (obt_root (ob_screen), mask, &attrib); + } + self->focused = FALSE; /* the other stuff is shown based on decor settings */ @@ -199,23 +258,30 @@ ObFrame *frame_new(ObClient *client) static void set_theme_statics(ObFrame *self) { + const gint bw = ob_rr_theme->button_width; + const gint bh = ob_rr_theme->button_height; /* set colors/appearance/sizes for stuff that doesn't change */ XResizeWindow(obt_display, self->max, - ob_rr_theme->button_size, ob_rr_theme->button_size); + bw, bh); XResizeWindow(obt_display, self->iconify, - ob_rr_theme->button_size, ob_rr_theme->button_size); + bw, bh); + /* the window icon is square, sized to match the button height */ XResizeWindow(obt_display, self->icon, - ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2); + ob_rr_theme->button_height, + ob_rr_theme->button_height); XResizeWindow(obt_display, self->close, - ob_rr_theme->button_size, ob_rr_theme->button_size); + bw, bh); XResizeWindow(obt_display, self->desk, - ob_rr_theme->button_size, ob_rr_theme->button_size); + bw, bh); XResizeWindow(obt_display, self->shade, - ob_rr_theme->button_size, ob_rr_theme->button_size); + bw, bh); + /* the corner resize-grip strips share the same height as the top + hover strip (topresize, see frame_adjust_area) so there's no seam + in the resize-cursor zone across the top edge */ XResizeWindow(obt_display, self->tltresize, - ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + ob_rr_theme->grip_width, ob_rr_theme->kgrip); XResizeWindow(obt_display, self->trtresize, - ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + ob_rr_theme->grip_width, ob_rr_theme->kgrip); XResizeWindow(obt_display, self->tllresize, ob_rr_theme->paddingx + 1, ob_rr_theme->title_height); XResizeWindow(obt_display, self->trrresize, @@ -231,12 +297,145 @@ void frame_free(ObFrame *self) free_theme_statics(self); XDestroyWindow(obt_display, self->window); + if (config_theme_invhandles) + { + XDestroyWindow(obt_display, self->invleft); + XDestroyWindow(obt_display, self->invright); + XDestroyWindow(obt_display, self->invtop); + XDestroyWindow(obt_display, self->invbottom); + XDestroyWindow(obt_display, self->invtr); + XDestroyWindow(obt_display, self->invtl); + XDestroyWindow(obt_display, self->invbl); + XDestroyWindow(obt_display, self->invbr); + } if (self->colormap) XFreeColormap(obt_display, self->colormap); g_slice_free(ObFrame, self); } +void frame_remove_handles (ObClient *client) +{ + ObFrame *self = client->frame; + moving = TRUE; + if (config_theme_invhandles) + { + window_remove(self->invleft); + window_remove(self->invright); + window_remove(self->invtop); + window_remove(self->invbottom); + window_remove(self->invtl); + window_remove(self->invtr); + window_remove(self->invbl); + window_remove(self->invbr); + XDestroyWindow(obt_display, self->invleft); + XDestroyWindow(obt_display, self->invright); + XDestroyWindow(obt_display, self->invtop); + XDestroyWindow(obt_display, self->invbottom); + XDestroyWindow(obt_display, self->invtr); + XDestroyWindow(obt_display, self->invtl); + XDestroyWindow(obt_display, self->invbl); + XDestroyWindow(obt_display, self->invbr); + } +} + +void frame_restore_handles (ObClient *client) +{ + ObFrame *self = client->frame; + XSetWindowAttributes attrib; + gulong mask = 0; + Visual *visual = check_32bit_client (client); + gboolean topbot = !self->client->max_vert; + gboolean sh = self->client->shaded; + XSetWindowAttributes a; + gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) && + !(self->client->max_horz && self->client->max_vert); + + moving = FALSE; + if (config_theme_invhandles) + { + if (visual) + { + /* client has a 32-bit visual */ + mask = CWColormap | CWBackPixel | CWBorderPixel; + attrib.background_pixel = BlackPixel(obt_display, ob_screen); + attrib.border_pixel = BlackPixel(obt_display, ob_screen); + attrib.colormap = RrColormap(ob_rr_inst); + } + + mask |= CWEventMask; + attrib.event_mask = ELEMENT_EVENTMASK; + + self->invleft = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x - INV_WIDTH, self->area.y, INV_WIDTH, self->area.height, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + self->invright = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x + self->area.width, self->area.y, INV_WIDTH, self->area.height, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + self->invtop = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x, self->area.y - INV_WIDTH, self->area.width, INV_WIDTH, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + self->invbottom = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x, self->area.y + self->area.height, self->area.width, INV_WIDTH, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + self->invtl = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x - INV_WIDTH, self->area.y - INV_WIDTH, INV_WIDTH, INV_WIDTH, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + self->invtr = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x + self->area.width, self->area.y - INV_WIDTH, INV_WIDTH, INV_WIDTH, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + self->invbl = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x - INV_WIDTH, self->area.y + self->area.height, INV_WIDTH, INV_WIDTH, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + self->invbr = XCreateWindow (obt_display, obt_root(ob_screen), + self->area.x + self->area.width, self->area.y + self->area.height, INV_WIDTH, INV_WIDTH, 0, + CopyFromParent, InputOnly, CopyFromParent, mask, &attrib); + + window_add(&self->invleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invtop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invtl, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invtr, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invbl, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invbr, CLIENT_AS_WINDOW(self->client)); + + XMapWindow (obt_display, self->invleft); + XMapWindow (obt_display, self->invright); + XMapWindow (obt_display, self->invtop); + XMapWindow (obt_display, self->invbottom); + XMapWindow (obt_display, self->invtl); + XMapWindow (obt_display, self->invtr); + XMapWindow (obt_display, self->invbl); + XMapWindow (obt_display, self->invbr); + + a.cursor = ob_cursor (r && topbot && !sh ? OB_CURSOR_NORTH : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invtop, CWCursor, &a); + a.cursor = ob_cursor (r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invbottom, CWCursor, &a); + a.cursor = ob_cursor (r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invtl, CWCursor, &a); + a.cursor = ob_cursor (r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invtr, CWCursor, &a); + a.cursor = ob_cursor (r ? OB_CURSOR_WEST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invleft, CWCursor, &a); + a.cursor = ob_cursor (r ? OB_CURSOR_EAST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invright, CWCursor, &a); + a.cursor = ob_cursor (r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invbl, CWCursor, &a); + a.cursor = ob_cursor (r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->invbr, CWCursor, &a); + } +} + + void frame_show(ObFrame *self) { if (!self->visible) { @@ -376,22 +575,23 @@ void frame_adjust_area(ObFrame *self, gboolean moved, self->width = MAX(self->width, (ob_rr_theme->grip_width + self->bwidth) * 2 + 1); + if (self->decorations & OB_FRAME_DECOR_TITLEBAR && config_theme_roundcorners) + self->outer = 2; + else + self->outer = 0; + + if (self->max_horz || self->max_vert) self->outer = 0; + STRUT_SET(self->size, - self->cbwidth_l + (!self->max_horz ? self->bwidth : 0), + self->cbwidth_l + (!self->max_horz ? self->bwidth : 0) + self->outer, self->cbwidth_t + - (!self->max_horz || !self->max_vert ? self->bwidth : 0), + (!self->max_horz || !self->max_vert ? self->bwidth : 0) + self->outer, self->cbwidth_r + (!self->max_horz ? self->bwidth : 0), self->cbwidth_b + (!self->max_horz || !self->max_vert ? self->bwidth : 0)); if (self->decorations & OB_FRAME_DECOR_TITLEBAR) self->size.top += ob_rr_theme->title_height + self->bwidth; - else if (self->max_horz && self->max_vert) { - /* A maximized and undecorated window needs a border on the - top of the window to let the user still undecorate/unmaximize the - window via the client menu. */ - self->size.top += self->bwidth; - } if (self->decorations & OB_FRAME_DECOR_HANDLE && ob_rr_theme->handle_height > 0) @@ -499,32 +699,32 @@ void frame_adjust_area(ObFrame *self, gboolean moved, titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0); XMoveResizeWindow(obt_display, self->titletop, - ob_rr_theme->grip_width + self->bwidth, 0, + ob_rr_theme->grip_width + self->bwidth + self->outer, self->outer, /* width + bwidth*2 - bwidth*2 - grips*2 */ self->width - ob_rr_theme->grip_width * 2, self->bwidth); XMoveResizeWindow(obt_display, self->titletopleft, - 0, 0, + self->outer, self->outer, ob_rr_theme->grip_width + self->bwidth, self->bwidth); XMoveResizeWindow(obt_display, self->titletopright, self->client->area.width + self->size.left + self->size.right - ob_rr_theme->grip_width - self->bwidth, - 0, + self->outer, ob_rr_theme->grip_width + self->bwidth, self->bwidth); if (titlesides > 0) { XMoveResizeWindow(obt_display, self->titleleft, - 0, self->bwidth, + self->outer, self->bwidth + self->outer, self->bwidth, titlesides); XMoveResizeWindow(obt_display, self->titleright, self->client->area.width + self->size.left + self->size.right - self->bwidth, - self->bwidth, + self->bwidth + self->outer, self->bwidth, titlesides); @@ -541,8 +741,8 @@ void frame_adjust_area(ObFrame *self, gboolean moved, if (self->decorations & OB_FRAME_DECOR_TITLEBAR) { XMoveResizeWindow(obt_display, self->titlebottom, - (self->max_horz ? 0 : self->bwidth), - ob_rr_theme->title_height + self->bwidth, + (self->max_horz ? 0 : self->bwidth) + self->outer, + ob_rr_theme->title_height + self->bwidth + self->outer, self->width, self->bwidth); @@ -561,18 +761,22 @@ void frame_adjust_area(ObFrame *self, gboolean moved, if (self->decorations & OB_FRAME_DECOR_TITLEBAR) { XMoveResizeWindow(obt_display, self->title, - (self->max_horz ? 0 : self->bwidth), - self->bwidth, + (self->max_horz ? 0 : self->bwidth) + self->outer, + self->bwidth + self->outer, self->width, ob_rr_theme->title_height); XMapWindow(obt_display, self->title); if (self->decorations & OB_FRAME_DECOR_GRIPS) { + /* topresize is the hover strip that turns the cursor + into the top-resize cursor; its height is kgrip, + the same margin reserved above the buttons by + button_height's derivation from title_height */ XMoveResizeWindow(obt_display, self->topresize, ob_rr_theme->grip_width, 0, self->width - ob_rr_theme->grip_width *2, - ob_rr_theme->paddingy + 1); + ob_rr_theme->kgrip); XMoveWindow(obt_display, self->tltresize, 0, 0); XMoveWindow(obt_display, self->tllresize, 0, 0); @@ -607,7 +811,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, if (self->bwidth && self->size.bottom) { XMoveResizeWindow(obt_display, self->handlebottom, ob_rr_theme->grip_width + - self->bwidth + sidebwidth, + self->bwidth + sidebwidth + self->outer, self->size.top + self->client->area.height + self->size.bottom - self->bwidth, self->width - (ob_rr_theme->grip_width + @@ -617,7 +821,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, if (sidebwidth) { XMoveResizeWindow(obt_display, self->lgripleft, - 0, + self->outer, self->size.top + self->client->area.height + self->size.bottom - @@ -651,7 +855,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, } XMoveResizeWindow(obt_display, self->lgripbottom, - sidebwidth, + sidebwidth + self->outer, self->size.top + self->client->area.height + self->size.bottom - self->bwidth, ob_rr_theme->grip_width + self->bwidth, @@ -674,7 +878,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, { XMoveResizeWindow(obt_display, self->handletop, ob_rr_theme->grip_width + - self->bwidth + sidebwidth, + self->bwidth + sidebwidth + self->outer, FRAME_HANDLE_Y(self), self->width - (ob_rr_theme->grip_width + sidebwidth) * 2, @@ -696,7 +900,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, ob_rr_theme->handle_height); XMoveResizeWindow(obt_display, self->lgriptop, - sidebwidth, + sidebwidth + self->outer, FRAME_HANDLE_Y(self), ob_rr_theme->grip_width + self->bwidth, @@ -748,7 +952,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, ob_rr_theme->handle_height > 0) { XMoveResizeWindow(obt_display, self->handle, - sidebwidth, + sidebwidth + self->outer, FRAME_HANDLE_Y(self) + self->bwidth, self->width, ob_rr_theme->handle_height); XMapWindow(obt_display, self->handle); @@ -782,7 +986,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, self->size.bottom) > ob_rr_theme->grip_width * 2) { XMoveResizeWindow(obt_display, self->left, - 0, + self->outer, self->bwidth + ob_rr_theme->grip_width, self->bwidth, self->client->area.height + @@ -799,7 +1003,7 @@ void frame_adjust_area(ObFrame *self, gboolean moved, { XMoveResizeWindow(obt_display, self->right, self->client->area.width + self->cbwidth_l + - self->cbwidth_r + self->bwidth, + self->cbwidth_r + self->bwidth + self->outer, self->bwidth + ob_rr_theme->grip_width, self->bwidth, self->client->area.height + @@ -810,6 +1014,274 @@ void frame_adjust_area(ObFrame *self, gboolean moved, } else XUnmapWindow(obt_display, self->right); + if (self->decorations & OB_FRAME_DECOR_TITLEBAR && self->outer) + { + int ww = self->width + self->outer * 2; + int th = ob_rr_theme->title_height + self->bwidth * 2; + int eh = self->client->area.height + self->cbwidth_t + self->cbwidth_b + self->bwidth; + int wh = th + eh + self->outer * 2; + + XMoveResizeWindow (obt_display, self->edgetop, + th, + 0, + ww - (th * 2), + 1); + XMapWindow (obt_display, self->edgetop); + + XMoveResizeWindow (obt_display, self->edgetopleft, + 0, + 0, + th, + 1); + XMapWindow (obt_display, self->edgetopleft); + + XMoveResizeWindow (obt_display, self->edgetopright, + ww - th, + 0, + th, + 1); + XMapWindow (obt_display, self->edgetopright); + + XMoveResizeWindow (obt_display, self->outertop, + th, + 1, + ww - (th * 2), + self->outer - 1); + XMapWindow (obt_display, self->outertop); + + XMoveResizeWindow (obt_display, self->outertopleft, + 0, + 1, + th, + self->outer - 1); + XMapWindow (obt_display, self->outertopleft); + + XMoveResizeWindow (obt_display, self->outertopright, + ww - th, + 1, + th, + self->outer - 1); + XMapWindow (obt_display, self->outertopright); + + XMoveResizeWindow (obt_display, self->edgelefttop, + 0, + self->outer, + 1, + th); + XMapWindow (obt_display, self->edgelefttop); + + XMoveResizeWindow (obt_display, self->outerlefttop, + 1, + self->outer, + self->outer - 1, + th); + XMapWindow (obt_display, self->outerlefttop); + + XMoveResizeWindow (obt_display, self->edgerighttop, + ww - 1, + self->outer, + 1, + th); + XMapWindow (obt_display, self->edgerighttop); + + XMoveResizeWindow (obt_display, self->outerrighttop, + ww - self->outer, + self->outer, + self->outer - 1, + th); + XMapWindow (obt_display, self->outerrighttop); + + XMoveResizeWindow (obt_display, self->edgeleft, + 0, + th + self->outer, + 1, + eh - th); + XMapWindow (obt_display, self->edgeleft); + + XMoveResizeWindow (obt_display, self->edgeleftbottom, + 0, + eh + self->outer, + 1, + th); + XMapWindow (obt_display, self->edgeleftbottom); + + XMoveResizeWindow (obt_display, self->outerleft, + 1, + th + self->outer, + self->outer - 1, + eh - th); + XMapWindow (obt_display, self->outerleft); + + XMoveResizeWindow (obt_display, self->outerleftbottom, + 1, + eh + self->outer, + self->outer - 1, + th); + XMapWindow (obt_display, self->outerleftbottom); + + XMoveResizeWindow (obt_display, self->edgeright, + ww - 1, + th + self->outer, + 1, + eh - th); + XMapWindow (obt_display, self->edgeright); + + XMoveResizeWindow (obt_display, self->edgerightbottom, + ww - 1, + eh + self->outer, + 1, + th); + XMapWindow (obt_display, self->edgerightbottom); + + XMoveResizeWindow (obt_display, self->outerright, + ww - self->outer, + th + self->outer, + self->outer - 1, + eh - th); + XMapWindow (obt_display, self->outerright); + + XMoveResizeWindow (obt_display, self->outerrightbottom, + ww - self->outer, + eh + self->outer, + self->outer - 1, + th); + XMapWindow (obt_display, self->outerrightbottom); + + XMoveResizeWindow (obt_display, self->edgebottom, + th, + wh - 1, + ww - (th * 2), + 1); + XMapWindow (obt_display, self->edgebottom); + + XMoveResizeWindow (obt_display, self->edgebottomleft, + 0, + wh - 1, + th, + 1); + XMapWindow (obt_display, self->edgebottomleft); + + XMoveResizeWindow (obt_display, self->edgebottomright, + ww - th, + wh - 1, + th, + 1); + XMapWindow (obt_display, self->edgebottomright); + + XMoveResizeWindow (obt_display, self->outerbottom, + th, + wh - self->outer, + ww - (th * 2), + self->outer - 1); + XMapWindow (obt_display, self->outerbottom); + + XMoveResizeWindow (obt_display, self->outerbottomleft, + 0, + wh - self->outer, + th, + self->outer - 1); + XMapWindow (obt_display, self->outerbottomleft); + + XMoveResizeWindow (obt_display, self->outerbottomright, + ww - th, + wh - self->outer, + th, + self->outer - 1); + XMapWindow (obt_display, self->outerbottomright); + + XMoveResizeWindow (obt_display, self->ce_tl_t, + 2, + 1, + 2, + 1); + XMapWindow (obt_display, self->ce_tl_t); + + XMoveResizeWindow (obt_display, self->ce_tl_l, + 1, + 2, + 1, + 2); + XMapWindow (obt_display, self->ce_tl_l); + + XMoveResizeWindow (obt_display, self->ce_tr_t, + ww - 4, + 1, + 2, + 1); + XMapWindow (obt_display, self->ce_tr_t); + + XMoveResizeWindow (obt_display, self->ce_tr_r, + ww - 2, + 2, + 1, + 2); + XMapWindow (obt_display, self->ce_tr_r); + + XMoveResizeWindow (obt_display, self->ce_bl_b, + 2, + wh - 2, + 2, + 1); + XMapWindow (obt_display, self->ce_bl_b); + + XMoveResizeWindow (obt_display, self->ce_bl_l, + 1, + wh - 4, + 1, + 2); + XMapWindow (obt_display, self->ce_bl_l); + + XMoveResizeWindow (obt_display, self->ce_br_b, + ww - 4, + wh - 2, + 2, + 1); + XMapWindow (obt_display, self->ce_br_b); + + XMoveResizeWindow (obt_display, self->ce_br_r, + ww - 2, + wh - 4, + 1, + 2); + XMapWindow (obt_display, self->ce_br_r); + + } + else + { + XUnmapWindow (obt_display, self->outertop); + XUnmapWindow (obt_display, self->outerlefttop); + XUnmapWindow (obt_display, self->outerrighttop); + XUnmapWindow (obt_display, self->outerleftbottom); + XUnmapWindow (obt_display, self->outerrightbottom); + XUnmapWindow (obt_display, self->outertopleft); + XUnmapWindow (obt_display, self->outertopright); + XUnmapWindow (obt_display, self->outerbottomleft); + XUnmapWindow (obt_display, self->outerbottomright); + XUnmapWindow (obt_display, self->outerleft); + XUnmapWindow (obt_display, self->outerright); + XUnmapWindow (obt_display, self->outerbottom); + XUnmapWindow (obt_display, self->edgetop); + XUnmapWindow (obt_display, self->edgelefttop); + XUnmapWindow (obt_display, self->edgerighttop); + XUnmapWindow (obt_display, self->edgeleftbottom); + XUnmapWindow (obt_display, self->edgerightbottom); + XUnmapWindow (obt_display, self->edgetopleft); + XUnmapWindow (obt_display, self->edgetopright); + XUnmapWindow (obt_display, self->edgebottomleft); + XUnmapWindow (obt_display, self->edgebottomright); + XUnmapWindow (obt_display, self->edgeleft); + XUnmapWindow (obt_display, self->edgeright); + XUnmapWindow (obt_display, self->edgebottom); + XUnmapWindow (obt_display, self->ce_tl_t); + XUnmapWindow (obt_display, self->ce_tl_l); + XUnmapWindow (obt_display, self->ce_tr_t); + XUnmapWindow (obt_display, self->ce_tr_r); + XUnmapWindow (obt_display, self->ce_bl_b); + XUnmapWindow (obt_display, self->ce_bl_l); + XUnmapWindow (obt_display, self->ce_br_b); + XUnmapWindow (obt_display, self->ce_br_r); + } + XMoveResizeWindow(obt_display, self->backback, self->size.left, self->size.top, self->client->area.width, @@ -834,6 +1306,66 @@ void frame_adjust_area(ObFrame *self, gboolean moved, frame_client_gravity(self, &self->area.x, &self->area.y); } + /* the invisible handles - can only set after applying gravity */ + if (config_theme_invhandles && !moving) + { + XMoveResizeWindow(obt_display, self->invleft, + self->area.x - INV_WIDTH, + self->area.y, + INV_WIDTH, + self->area.height); + XMapWindow (obt_display, self->invleft); + + XMoveResizeWindow(obt_display, self->invright, + self->area.x + self->area.width, + self->area.y, + INV_WIDTH, + self->area.height); + XMapWindow (obt_display, self->invright); + + XMoveResizeWindow(obt_display, self->invtop, + self->area.x, + self->area.y - INV_WIDTH, + self->area.width, + INV_WIDTH); + XMapWindow (obt_display, self->invtop); + + XMoveResizeWindow(obt_display, self->invbottom, + self->area.x, + self->area.y + self->area.height, + self->area.width, + INV_WIDTH); + XMapWindow (obt_display, self->invbottom); + + XMoveResizeWindow(obt_display, self->invtl, + self->area.x - INV_WIDTH, + self->area.y - INV_WIDTH, + INV_WIDTH, + INV_WIDTH); + XMapWindow (obt_display, self->invtl); + + XMoveResizeWindow(obt_display, self->invtr, + self->area.x + self->area.width, + self->area.y - INV_WIDTH, + INV_WIDTH, + INV_WIDTH); + XMapWindow (obt_display, self->invtr); + + XMoveResizeWindow(obt_display, self->invbl, + self->area.x - INV_WIDTH, + self->area.y + self->area.height, + INV_WIDTH, + INV_WIDTH); + XMapWindow (obt_display, self->invbl); + + XMoveResizeWindow(obt_display, self->invbr, + self->area.x + self->area.width, + self->area.y + self->area.height, + INV_WIDTH, + INV_WIDTH); + XMapWindow (obt_display, self->invbr); + } + if (!fake) { if (!frame_iconify_animating(self)) /* move and resize the top level frame. @@ -845,8 +1377,8 @@ void frame_adjust_area(ObFrame *self, gboolean moved, XMoveResizeWindow(obt_display, self->window, self->area.x, self->area.y, - self->area.width, - self->area.height); + self->area.width + self->outer, + self->area.height + self->outer); /* when the client has StaticGravity, it likes to move around. also this correctly positions the client when it maps. @@ -906,11 +1438,17 @@ static void frame_adjust_cursors(ObFrame *self) OB_CURSOR_NORTH : OB_CURSOR_NONE); XChangeWindowAttributes(obt_display, self->topresize, CWCursor, &a); XChangeWindowAttributes(obt_display, self->titletop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outertop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgetop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invtop, CWCursor, &a); a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE); XChangeWindowAttributes(obt_display, self->handle, CWCursor, &a); XChangeWindowAttributes(obt_display, self->handletop, CWCursor, &a); XChangeWindowAttributes(obt_display, self->handlebottom, CWCursor, &a); XChangeWindowAttributes(obt_display, self->innerbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outerbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgebottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invbottom, CWCursor, &a); /* these ones change when shaded */ a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) : @@ -919,18 +1457,38 @@ static void frame_adjust_cursors(ObFrame *self) XChangeWindowAttributes(obt_display, self->tltresize, CWCursor, &a); XChangeWindowAttributes(obt_display, self->tllresize, CWCursor, &a); XChangeWindowAttributes(obt_display, self->titletopleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgelefttop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgetopleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outerlefttop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outertopleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invtl, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_tl_t, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_tl_l, CWCursor, &a); a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) : OB_CURSOR_NONE); XChangeWindowAttributes(obt_display, self->titleright, CWCursor, &a); XChangeWindowAttributes(obt_display, self->trtresize, CWCursor, &a); XChangeWindowAttributes(obt_display, self->trrresize, CWCursor, &a); XChangeWindowAttributes(obt_display, self->titletopright, CWCursor,&a); + XChangeWindowAttributes(obt_display, self->edgerighttop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgetopright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outerrighttop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outertopright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invtr, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_tr_t, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_tr_r, CWCursor, &a); /* these ones are pretty static */ a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->outerleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgeleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invleft, CWCursor, &a); XChangeWindowAttributes(obt_display, self->left, CWCursor, &a); XChangeWindowAttributes(obt_display, self->innerleft, CWCursor, &a); a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->outerright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgeright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invright, CWCursor, &a); XChangeWindowAttributes(obt_display, self->right, CWCursor, &a); XChangeWindowAttributes(obt_display, self->innerright, CWCursor, &a); a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE); @@ -941,6 +1499,13 @@ static void frame_adjust_cursors(ObFrame *self) XChangeWindowAttributes(obt_display, self->lgripbottom, CWCursor, &a); XChangeWindowAttributes(obt_display, self->innerbll, CWCursor, &a); XChangeWindowAttributes(obt_display, self->innerblb, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_bl_b, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_bl_l, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgeleftbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgebottomleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outerleftbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outerbottomleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invbl, CWCursor, &a); a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE); XChangeWindowAttributes(obt_display, self->rgrip, CWCursor, &a); XChangeWindowAttributes(obt_display, self->handleright, CWCursor, &a); @@ -949,6 +1514,13 @@ static void frame_adjust_cursors(ObFrame *self) XChangeWindowAttributes(obt_display, self->rgripbottom, CWCursor, &a); XChangeWindowAttributes(obt_display, self->innerbrr, CWCursor, &a); XChangeWindowAttributes(obt_display, self->innerbrb, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_br_b, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->ce_br_r, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgerightbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->edgebottomright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outerrightbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->outerbottomright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->invbr, CWCursor, &a); } } @@ -958,6 +1530,8 @@ void frame_adjust_client_area(ObFrame *self) XMoveResizeWindow(obt_display, self->backfront, 0, 0, self->client->area.width, self->client->area.height); + self->need_render = TRUE; + framerender_frame(self); } void frame_adjust_state(ObFrame *self) @@ -974,6 +1548,31 @@ void frame_adjust_focus(ObFrame *self, gboolean hilite) self->focused = hilite; self->need_render = TRUE; framerender_frame(self); + if (config_theme_invhandles) + { + if (self->focused) + { + XMapWindow (obt_display, self->invleft); + XMapWindow (obt_display, self->invright); + XMapWindow (obt_display, self->invtop); + XMapWindow (obt_display, self->invbottom); + XMapWindow (obt_display, self->invtl); + XMapWindow (obt_display, self->invtr); + XMapWindow (obt_display, self->invbl); + XMapWindow (obt_display, self->invbr); + } + else + { + XUnmapWindow (obt_display, self->invleft); + XUnmapWindow (obt_display, self->invright); + XUnmapWindow (obt_display, self->invtop); + XUnmapWindow (obt_display, self->invbottom); + XUnmapWindow (obt_display, self->invtl); + XUnmapWindow (obt_display, self->invtr); + XUnmapWindow (obt_display, self->invbl); + XUnmapWindow (obt_display, self->invbr); + } + } XFlush(obt_display); } @@ -1058,6 +1657,49 @@ void frame_grab_client(ObFrame *self) window_add(&self->rgripright, CLIENT_AS_WINDOW(self->client)); window_add(&self->rgriptop, CLIENT_AS_WINDOW(self->client)); window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outertop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerlefttop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerrighttop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerleftbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerrightbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outertopleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outertopright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerbottomleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->outerbottomright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgeleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgeright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgetop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgebottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgelefttop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgerighttop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgeleftbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgerightbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgetopleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgetopright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgebottomleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->edgebottomright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_tl_t, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_tl_l, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_tr_t, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_tr_r, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_bl_b, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_bl_l, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_br_b, CLIENT_AS_WINDOW(self->client)); + window_add(&self->ce_br_r, CLIENT_AS_WINDOW(self->client)); + if (config_theme_invhandles) + { + window_add(&self->invleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invtop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invtl, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invtr, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invbl, CLIENT_AS_WINDOW(self->client)); + window_add(&self->invbr, CLIENT_AS_WINDOW(self->client)); + } } static gboolean find_reparent(XEvent *e, gpointer data) @@ -1130,6 +1772,49 @@ void frame_release_client(ObFrame *self) window_remove(self->rgripright); window_remove(self->rgriptop); window_remove(self->rgripbottom); + window_remove(self->outerleft); + window_remove(self->outerright); + window_remove(self->outertop); + window_remove(self->outerbottom); + window_remove(self->outerlefttop); + window_remove(self->outerrighttop); + window_remove(self->outerleftbottom); + window_remove(self->outerrightbottom); + window_remove(self->outertopleft); + window_remove(self->outertopright); + window_remove(self->outerbottomleft); + window_remove(self->outerbottomright); + window_remove(self->edgeleft); + window_remove(self->edgeright); + window_remove(self->edgetop); + window_remove(self->edgebottom); + window_remove(self->edgelefttop); + window_remove(self->edgerighttop); + window_remove(self->edgeleftbottom); + window_remove(self->edgerightbottom); + window_remove(self->edgetopleft); + window_remove(self->edgetopright); + window_remove(self->edgebottomleft); + window_remove(self->edgebottomright); + window_remove(self->ce_tl_t); + window_remove(self->ce_tl_l); + window_remove(self->ce_tr_t); + window_remove(self->ce_tr_r); + window_remove(self->ce_bl_b); + window_remove(self->ce_bl_l); + window_remove(self->ce_br_b); + window_remove(self->ce_br_r); + if (config_theme_invhandles) + { + window_remove(self->invleft); + window_remove(self->invright); + window_remove(self->invtop); + window_remove(self->invbottom); + window_remove(self->invtl); + window_remove(self->invtr); + window_remove(self->invbl); + window_remove(self->invbr); + } if (self->flash_timer) g_source_remove(self->flash_timer); } @@ -1182,12 +1867,19 @@ static void layout_title(ObFrame *self) gchar *lc; gint i; - const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1; + const gint bwidth = ob_rr_theme->button_width + ob_rr_theme->paddingx + 1; + /* the icon is square (button_height x button_height), which is + narrower than a button (button_width x button_height) whenever + WISTOH > 1, so it needs its own layout spacing rather than reusing + bwidth */ + const gint iconwidth = ob_rr_theme->button_height + ob_rr_theme->paddingx + 1; /* position of the leftmost button */ const gint left = ob_rr_theme->paddingx + 1; /* position of the rightmost button */ const gint right = self->width; + int leftoff, rightoff; + /* turn them all off */ self->icon_on = self->desk_on = self->shade_on = self->iconify_on = self->max_on = self->close_on = self->label_on = FALSE; @@ -1220,12 +1912,13 @@ static void layout_title(ObFrame *self) if (i > 0) { self->label_on = TRUE; self->label_x = x; + leftoff = x - left; } + else rightoff = right - x; break; /* break the for loop, do other side of label */ } else if (*lc == 'N') { if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON; - /* icon is bigger than buttons */ - place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x); + place_button(self, lc, iconwidth, left, i, &x, &self->icon_on, &self->icon_x); } else if (*lc == 'D') { if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS; place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x); @@ -1247,55 +1940,81 @@ static void layout_title(ObFrame *self) } } - /* position and map the elements */ + /* centre text... */ + if (ob_rr_theme->a_focused_label->texture[0].data.text.justify == RR_JUSTIFY_CENTER) + { + if (leftoff > rightoff) + { + self->label_width -= (leftoff - rightoff); + } + else if (rightoff > leftoff) + { + self->label_x += (rightoff - leftoff); + self->label_width -= (rightoff - leftoff); + } + } + + /* position and map the elements, each vertically centered in the + titlebar according to its own height -- label_height, button_height + (shared by all the titlebar buttons and the icon), may all differ + from one another, so a single fixed offset can't center them all */ + { + const gint label_y = + (ob_rr_theme->title_height - ob_rr_theme->label_height) / 2; + const gint button_y = + (ob_rr_theme->title_height - ob_rr_theme->button_height) / 2; + /* the icon is sized to button_height, so it shares button_y */ + const gint icon_y = button_y; + if (self->icon_on) { XMapWindow(obt_display, self->icon); XMoveWindow(obt_display, self->icon, self->icon_x, - ob_rr_theme->paddingy); + icon_y); } else XUnmapWindow(obt_display, self->icon); if (self->desk_on) { XMapWindow(obt_display, self->desk); XMoveWindow(obt_display, self->desk, self->desk_x, - ob_rr_theme->paddingy + 1); + button_y); } else XUnmapWindow(obt_display, self->desk); if (self->shade_on) { XMapWindow(obt_display, self->shade); XMoveWindow(obt_display, self->shade, self->shade_x, - ob_rr_theme->paddingy + 1); + button_y); } else XUnmapWindow(obt_display, self->shade); if (self->iconify_on) { XMapWindow(obt_display, self->iconify); XMoveWindow(obt_display, self->iconify, self->iconify_x, - ob_rr_theme->paddingy + 1); + button_y); } else XUnmapWindow(obt_display, self->iconify); if (self->max_on) { XMapWindow(obt_display, self->max); XMoveWindow(obt_display, self->max, self->max_x, - ob_rr_theme->paddingy + 1); + button_y); } else XUnmapWindow(obt_display, self->max); if (self->close_on) { XMapWindow(obt_display, self->close); XMoveWindow(obt_display, self->close, self->close_x, - ob_rr_theme->paddingy + 1); + button_y); } else XUnmapWindow(obt_display, self->close); if (self->label_on && self->label_width > 0) { XMapWindow(obt_display, self->label); XMoveWindow(obt_display, self->label, self->label_x, - ob_rr_theme->paddingy); + label_y); } else XUnmapWindow(obt_display, self->label); + } } gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx) @@ -1447,19 +2166,22 @@ ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y) fx += self->area.width - self->bwidth; /* figure out if we're over the area that should be considered a - button */ + button. use button_height for the vertical reach and + button_width (the wider of a button's two dimensions) for the + horizontal reach, since this is an approximate corner hit-zone + rather than a per-element pixel-exact test */ if (fy < self->bwidth + ob_rr_theme->paddingy + 1 + - ob_rr_theme->button_size) + ob_rr_theme->button_height) { if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 + - ob_rr_theme->button_size)) + ob_rr_theme->button_width)) { if (self->leftmost != OB_FRAME_CONTEXT_NONE) return self->leftmost; } else if (fx >= (self->area.width - (self->bwidth + ob_rr_theme->paddingx + 1 + - ob_rr_theme->button_size))) + ob_rr_theme->button_width))) { if (self->rightmost != OB_FRAME_CONTEXT_NONE) return self->rightmost; @@ -1484,16 +2206,30 @@ ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y) if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM; if (win == self->handletop) return OB_FRAME_CONTEXT_BOTTOM; if (win == self->handlebottom) return OB_FRAME_CONTEXT_BOTTOM; + if (win == self->outerbottom) return OB_FRAME_CONTEXT_BOTTOM; + if (win == self->edgebottom) return OB_FRAME_CONTEXT_BOTTOM; if (win == self->handleleft) return OB_FRAME_CONTEXT_BLCORNER; if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER; if (win == self->lgripleft) return OB_FRAME_CONTEXT_BLCORNER; if (win == self->lgriptop) return OB_FRAME_CONTEXT_BLCORNER; if (win == self->lgripbottom) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->ce_bl_b) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->ce_bl_l) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->outerleftbottom) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->edgeleftbottom) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->outerbottomleft) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->edgebottomleft) return OB_FRAME_CONTEXT_BLCORNER; if (win == self->handleright) return OB_FRAME_CONTEXT_BRCORNER; if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER; if (win == self->rgripright) return OB_FRAME_CONTEXT_BRCORNER; if (win == self->rgriptop) return OB_FRAME_CONTEXT_BRCORNER; if (win == self->rgripbottom) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->ce_br_b) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->ce_br_r) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->outerrightbottom) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->edgerightbottom) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->outerbottomright) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->edgebottomright) return OB_FRAME_CONTEXT_BRCORNER; if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR; if (win == self->titlebottom) return OB_FRAME_CONTEXT_TITLEBAR; if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER; @@ -1502,26 +2238,56 @@ ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y) if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER; if (win == self->titletop) return OB_FRAME_CONTEXT_TOP; if (win == self->topresize) return OB_FRAME_CONTEXT_TOP; + if (win == self->outertop) return OB_FRAME_CONTEXT_TOP; + if (win == self->edgetop) return OB_FRAME_CONTEXT_TOP; if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER; if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->ce_tl_t) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->ce_tl_l) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->outerlefttop) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->edgelefttop) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->outertopleft) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->edgetopleft) return OB_FRAME_CONTEXT_TLCORNER; if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER; if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->ce_tr_t) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->ce_tr_r) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->outerrighttop) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->edgerighttop) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->outertopright) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->edgetopright) return OB_FRAME_CONTEXT_TRCORNER; if (win == self->left) return OB_FRAME_CONTEXT_LEFT; + if (win == self->outerleft) return OB_FRAME_CONTEXT_LEFT; + if (win == self->edgeleft) return OB_FRAME_CONTEXT_LEFT; if (win == self->right) return OB_FRAME_CONTEXT_RIGHT; + if (win == self->outerright) return OB_FRAME_CONTEXT_RIGHT; + if (win == self->edgeright) return OB_FRAME_CONTEXT_RIGHT; if (win == self->innertop) return OB_FRAME_CONTEXT_TITLEBAR; if (win == self->innerleft) return OB_FRAME_CONTEXT_LEFT; if (win == self->innerbottom) return OB_FRAME_CONTEXT_BOTTOM; if (win == self->innerright) return OB_FRAME_CONTEXT_RIGHT; if (win == self->innerbll) return OB_FRAME_CONTEXT_BLCORNER; if (win == self->innerblb) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->ce_bl_b) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->ce_bl_l) return OB_FRAME_CONTEXT_BLCORNER; if (win == self->innerbrr) return OB_FRAME_CONTEXT_BRCORNER; if (win == self->innerbrb) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->ce_br_b) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->ce_br_r) return OB_FRAME_CONTEXT_BRCORNER; if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE; if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY; if (win == self->close) return OB_FRAME_CONTEXT_CLOSE; if (win == self->icon) return OB_FRAME_CONTEXT_ICON; if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS; if (win == self->shade) return OB_FRAME_CONTEXT_SHADE; + if (win == self->invleft) return OB_FRAME_CONTEXT_LEFT; + if (win == self->invright) return OB_FRAME_CONTEXT_RIGHT; + if (win == self->invtop) return OB_FRAME_CONTEXT_TOP; + if (win == self->invbottom) return OB_FRAME_CONTEXT_BOTTOM; + if (win == self->invtl) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->invtr) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->invbl) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->invbr) return OB_FRAME_CONTEXT_BRCORNER; return OB_FRAME_CONTEXT_NONE; } diff --git a/openbox/framerender.c b/openbox/framerender.c index 094d5962d..965c92af5 100644 --- a/openbox/framerender.c +++ b/openbox/framerender.c @@ -21,6 +21,7 @@ #include "openbox.h" #include "screen.h" #include "client.h" +#include "config.h" #include "framerender.h" #include "obrender/theme.h" @@ -43,6 +44,37 @@ void framerender_frame(ObFrame *self) self->need_render = FALSE; { + if ((self->decorations & OB_FRAME_DECOR_TITLEBAR) && !self->max_horz && !self->max_vert && config_theme_roundcorners) + { + XGCValues xgcv; + XWindowAttributes wd_att; + XGetWindowAttributes (obt_display, self->window, &wd_att); + Pixmap mask = XCreatePixmap (obt_display, self->window, wd_att.width, wd_att.height, 1); + GC shape_gc = XCreateGC (obt_display, mask, 0, &xgcv); + XSetForeground (obt_display, shape_gc, 1); + XFillRectangle (obt_display, mask, shape_gc, 0, 0, wd_att.width, wd_att.height); + XSetForeground (obt_display, shape_gc, 0); + + XFillRectangle (obt_display, mask, shape_gc, 0, 0, 2, 2); + XFillRectangle (obt_display, mask, shape_gc, 2, 0, 2, 1); + XFillRectangle (obt_display, mask, shape_gc, 0, 2, 1, 2); + + XFillRectangle (obt_display, mask, shape_gc, 0, wd_att.height - 2, 2, 2); + XFillRectangle (obt_display, mask, shape_gc, 2, wd_att.height - 1, 2, 1); + XFillRectangle (obt_display, mask, shape_gc, 0, wd_att.height - 4, 1, 2); + + XFillRectangle (obt_display, mask, shape_gc, wd_att.width - 2, 0, 2, 2); + XFillRectangle (obt_display, mask, shape_gc, wd_att.width - 4, 0, 2, 1); + XFillRectangle (obt_display, mask, shape_gc, wd_att.width - 1, 2, 1, 2); + + XFillRectangle (obt_display, mask, shape_gc, wd_att.width - 2, wd_att.height - 2, 2, 2); + XFillRectangle (obt_display, mask, shape_gc, wd_att.width - 4, wd_att.height - 1, 2, 1); + XFillRectangle (obt_display, mask, shape_gc, wd_att.width - 1, wd_att.height - 4, 1, 2); + + XShapeCombineMask (obt_display, self->window, ShapeBounding, 0, 0, mask, ShapeSet); + XFreePixmap (obt_display, mask); + } + gulong px; px = (self->focused ? @@ -123,6 +155,88 @@ void framerender_frame(ObFrame *self) XSetWindowBackground(obt_display, self->titlebottom, px); XClearWindow(obt_display, self->titlebottom); + + px = (self->focused ? + RrColorPixel (ob_rr_theme->a_focused_title->surface.primary) : + RrColorPixel (ob_rr_theme->a_unfocused_title->surface.primary)); + + XSetWindowBackground(obt_display, self->outertop, px); + XClearWindow(obt_display, self->outertop); + XSetWindowBackground(obt_display, self->outerlefttop, px); + XClearWindow(obt_display, self->outerlefttop); + XSetWindowBackground(obt_display, self->outerrighttop, px); + XClearWindow(obt_display, self->outerrighttop); + XSetWindowBackground(obt_display, self->outertopleft, px); + XClearWindow(obt_display, self->outertopleft); + XSetWindowBackground(obt_display, self->outertopright, px); + XClearWindow(obt_display, self->outertopright); + + px = (self->focused ? + RrColorPixel(ob_rr_theme->cb_focused_color) : + RrColorPixel(ob_rr_theme->cb_unfocused_color)); + + XSetWindowBackground(obt_display, self->outerleft, px); + XClearWindow(obt_display, self->outerleft); + XSetWindowBackground(obt_display, self->outerright, px); + XClearWindow(obt_display, self->outerright); + XSetWindowBackground(obt_display, self->outerbottom, px); + XClearWindow(obt_display, self->outerbottom); + XSetWindowBackground(obt_display, self->outerleftbottom, px); + XClearWindow(obt_display, self->outerleftbottom); + XSetWindowBackground(obt_display, self->outerrightbottom, px); + XClearWindow(obt_display, self->outerrightbottom); + XSetWindowBackground(obt_display, self->outerbottomleft, px); + XClearWindow(obt_display, self->outerbottomleft); + XSetWindowBackground(obt_display, self->outerbottomright, px); + XClearWindow(obt_display, self->outerbottomright); + + px = RrColorPixel (ob_rr_theme->frame_focused_border_color); + + XSetWindowBackground(obt_display, self->edgeleft, px); + XClearWindow(obt_display, self->edgeleft); + XSetWindowBackground(obt_display, self->edgeright, px); + XClearWindow(obt_display, self->edgeright); + XSetWindowBackground(obt_display, self->edgebottom, px); + XClearWindow(obt_display, self->edgebottom); + XSetWindowBackground(obt_display, self->edgebottomleft, px); + XClearWindow(obt_display, self->edgebottomleft); + XSetWindowBackground(obt_display, self->edgebottomright, px); + XClearWindow(obt_display, self->edgebottomright); + XSetWindowBackground(obt_display, self->edgeleftbottom, px); + XClearWindow(obt_display, self->edgeleftbottom); + XSetWindowBackground(obt_display, self->edgerightbottom, px); + XClearWindow(obt_display, self->edgerightbottom); + XSetWindowBackground(obt_display, self->ce_bl_b, px); + XClearWindow(obt_display, self->ce_bl_b); + XSetWindowBackground(obt_display, self->ce_bl_l, px); + XClearWindow(obt_display, self->ce_bl_l); + XSetWindowBackground(obt_display, self->ce_br_b, px); + XClearWindow(obt_display, self->ce_br_b); + XSetWindowBackground(obt_display, self->ce_br_r, px); + XClearWindow(obt_display, self->ce_br_r); + + px = (self->focused ? + RrColorPixel (ob_rr_theme->a_focused_title->surface.primary) : + RrColorPixel(ob_rr_theme->frame_focused_border_color)); + + XSetWindowBackground(obt_display, self->edgetop, px); + XClearWindow(obt_display, self->edgetop); + XSetWindowBackground(obt_display, self->edgelefttop, px); + XClearWindow(obt_display, self->edgelefttop); + XSetWindowBackground(obt_display, self->edgerighttop, px); + XClearWindow(obt_display, self->edgerighttop); + XSetWindowBackground(obt_display, self->edgetopleft, px); + XClearWindow(obt_display, self->edgetopleft); + XSetWindowBackground(obt_display, self->edgetopright, px); + XClearWindow(obt_display, self->edgetopright); + XSetWindowBackground(obt_display, self->ce_tl_t, px); + XClearWindow(obt_display, self->ce_tl_t); + XSetWindowBackground(obt_display, self->ce_tl_l, px); + XClearWindow(obt_display, self->ce_tl_l); + XSetWindowBackground(obt_display, self->ce_tr_t, px); + XClearWindow(obt_display, self->ce_tr_t); + XSetWindowBackground(obt_display, self->ce_tr_r, px); + XClearWindow(obt_display, self->ce_tr_r); } if (self->decorations & OB_FRAME_DECOR_TITLEBAR) { @@ -244,6 +358,24 @@ void framerender_frame(ObFrame *self) } clear = ob_rr_theme->a_clear; + /* mirrors the vertical-centering math in frame.c's layout_title(); + these values must stay in sync with the actual XMoveWindow + calls there, since this block only feeds parent-relative + (RR_SURFACE_PARENTREL) appearances the coordinates they need + to sample the correct region of the parent's background -- + it doesn't move any real window itself. A mismatch here + doesn't crash anything, but produces exactly the kind of + misplaced/blank rectangle artifact seen with parent-relative + (transparent-background) button themes: the real button + window sits at the new centered position, while the parent- + relative fill is sampled from the old, stale offset. */ + { + const gint label_y = + (ob_rr_theme->title_height - ob_rr_theme->label_height) / 2; + const gint button_y = + (ob_rr_theme->title_height - ob_rr_theme->button_height) / 2; + const gint icon_y = button_y; /* icon is sized to button_height */ + RrPaint(t, self->title, self->width, ob_rr_theme->title_height); clear->surface.parent = t; @@ -251,15 +383,20 @@ void framerender_frame(ObFrame *self) clear->surface.parentx = ob_rr_theme->grip_width; + /* topresize's height is kgrip now (see frame.c's + frame_adjust_area), not paddingy + 1 -- keep this RrPaint + size argument in sync with that window's real XResizeWindow + size, or the clear/parent-relative fill will be built for + the wrong dimensions */ RrPaint(clear, self->topresize, self->width - ob_rr_theme->grip_width * 2, - ob_rr_theme->paddingy + 1); + ob_rr_theme->kgrip); clear->surface.parentx = 0; if (ob_rr_theme->grip_width > 0) RrPaint(clear, self->tltresize, - ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + ob_rr_theme->grip_width, ob_rr_theme->kgrip); if (ob_rr_theme->title_height > 0) RrPaint(clear, self->tllresize, ob_rr_theme->paddingx + 1, ob_rr_theme->title_height); @@ -268,7 +405,7 @@ void framerender_frame(ObFrame *self) if (ob_rr_theme->grip_width > 0) RrPaint(clear, self->trtresize, - ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + ob_rr_theme->grip_width, ob_rr_theme->kgrip); clear->surface.parentx = self->width - (ob_rr_theme->paddingx + 1); @@ -279,31 +416,32 @@ void framerender_frame(ObFrame *self) /* set parents for any parent relative guys */ l->surface.parent = t; l->surface.parentx = self->label_x; - l->surface.parenty = ob_rr_theme->paddingy; + l->surface.parenty = label_y; m->surface.parent = t; m->surface.parentx = self->max_x; - m->surface.parenty = ob_rr_theme->paddingy + 1; + m->surface.parenty = button_y; n->surface.parent = t; n->surface.parentx = self->icon_x; - n->surface.parenty = ob_rr_theme->paddingy; + n->surface.parenty = icon_y; i->surface.parent = t; i->surface.parentx = self->iconify_x; - i->surface.parenty = ob_rr_theme->paddingy + 1; + i->surface.parenty = button_y; d->surface.parent = t; d->surface.parentx = self->desk_x; - d->surface.parenty = ob_rr_theme->paddingy + 1; + d->surface.parenty = button_y; s->surface.parent = t; s->surface.parentx = self->shade_x; - s->surface.parenty = ob_rr_theme->paddingy + 1; + s->surface.parenty = button_y; c->surface.parent = t; c->surface.parentx = self->close_x; - c->surface.parenty = ob_rr_theme->paddingy + 1; + c->surface.parenty = button_y; + } framerender_label(self, l); framerender_max(self, m); @@ -374,39 +512,42 @@ static void framerender_icon(ObFrame *self, RrAppearance *a) a->texture[0].type = RR_TEXTURE_NONE; } + /* the icon is square, sized to match the button height */ RrPaint(a, self->icon, - ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2); + ob_rr_theme->button_height, ob_rr_theme->button_height); } static void framerender_max(ObFrame *self, RrAppearance *a) { if (!self->max_on) return; - RrPaint(a, self->max, ob_rr_theme->button_size, ob_rr_theme->button_size); + RrPaint(a, self->max, + ob_rr_theme->button_width, ob_rr_theme->button_height); } static void framerender_iconify(ObFrame *self, RrAppearance *a) { if (!self->iconify_on) return; RrPaint(a, self->iconify, - ob_rr_theme->button_size, ob_rr_theme->button_size); + ob_rr_theme->button_width, ob_rr_theme->button_height); } static void framerender_desk(ObFrame *self, RrAppearance *a) { if (!self->desk_on) return; - RrPaint(a, self->desk, ob_rr_theme->button_size, ob_rr_theme->button_size); + RrPaint(a, self->desk, + ob_rr_theme->button_width, ob_rr_theme->button_height); } static void framerender_shade(ObFrame *self, RrAppearance *a) { if (!self->shade_on) return; RrPaint(a, self->shade, - ob_rr_theme->button_size, ob_rr_theme->button_size); + ob_rr_theme->button_width, ob_rr_theme->button_height); } static void framerender_close(ObFrame *self, RrAppearance *a) { if (!self->close_on) return; RrPaint(a, self->close, - ob_rr_theme->button_size, ob_rr_theme->button_size); + ob_rr_theme->button_width, ob_rr_theme->button_height); }