diff options
| author | philw <dscr@duck.com> | 2024-08-11 22:18:24 +0200 |
|---|---|---|
| committer | philw <dscr@duck.com> | 2024-08-11 22:18:24 +0200 |
| commit | 88e4e232df671f2f268708f09613d3412756cf8c (patch) | |
| tree | 13595fb4305a6a3ef7a651e50230562114fe23c3 /drw.c | |
| parent | a9e2d33b5fbe507406137358196f585b7a54059b (diff) | |
| download | dwm-88e4e232df671f2f268708f09613d3412756cf8c.tar.gz dwm-88e4e232df671f2f268708f09613d3412756cf8c.zip | |
Update xf86 keybindings
Diffstat (limited to '')
| -rw-r--r-- | drw.c | 769 |
1 files changed, 361 insertions, 408 deletions
| @@ -1,472 +1,425 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | 1 | /* See LICENSE file for copyright and license details. */ |
| 2 | #include <X11/Xft/Xft.h> | ||
| 3 | #include <X11/Xlib.h> | ||
| 2 | #include <stdio.h> | 4 | #include <stdio.h> |
| 3 | #include <stdlib.h> | 5 | #include <stdlib.h> |
| 4 | #include <string.h> | 6 | #include <string.h> |
| 5 | #include <X11/Xlib.h> | ||
| 6 | #include <X11/Xft/Xft.h> | ||
| 7 | 7 | ||
| 8 | #include "drw.h" | 8 | #include "drw.h" |
| 9 | #include "util.h" | 9 | #include "util.h" |
| 10 | 10 | ||
| 11 | #define UTF_INVALID 0xFFFD | 11 | #define UTF_INVALID 0xFFFD |
| 12 | #define UTF_SIZ 4 | 12 | #define UTF_SIZ 4 |
| 13 | 13 | ||
| 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = { 0x80, 0, 0xC0, 0xE0, 0xF0 }; | 14 | static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; |
| 15 | static const unsigned char utfmask[UTF_SIZ + 1] = | 15 | static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, |
| 16 | { 0xC0, 0x80, 0xE0, 0xF0, 0xF8 }; | 16 | 0xF8}; |
| 17 | static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000 }; | 17 | static const long utfmin[UTF_SIZ + 1] = {0, 0, 0x80, 0x800, 0x10000}; |
| 18 | static const long utfmax[UTF_SIZ + 1] = | 18 | static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, |
| 19 | { 0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF }; | 19 | 0x10FFFF}; |
| 20 | 20 | ||
| 21 | static long utf8decodebyte(const char c, size_t *i) | 21 | static long utf8decodebyte(const char c, size_t *i) { |
| 22 | { | 22 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) |
| 23 | for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) | 23 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) |
| 24 | if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) | 24 | return (unsigned char)c & ~utfmask[*i]; |
| 25 | return (unsigned char)c & ~utfmask[*i]; | 25 | return 0; |
| 26 | return 0; | ||
| 27 | } | 26 | } |
| 28 | 27 | ||
| 29 | static size_t utf8validate(long *u, size_t i) | 28 | static size_t utf8validate(long *u, size_t i) { |
| 30 | { | 29 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) |
| 31 | if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) | 30 | *u = UTF_INVALID; |
| 32 | *u = UTF_INVALID; | 31 | for (i = 1; *u > utfmax[i]; ++i) |
| 33 | for (i = 1; *u > utfmax[i]; ++i) ; | 32 | ; |
| 34 | return i; | 33 | return i; |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | static size_t utf8decode(const char *c, long *u, size_t clen) | 36 | static size_t utf8decode(const char *c, long *u, size_t clen) { |
| 38 | { | 37 | size_t i, j, len, type; |
| 39 | size_t i, j, len, type; | 38 | long udecoded; |
| 40 | long udecoded; | 39 | |
| 41 | 40 | *u = UTF_INVALID; | |
| 42 | *u = UTF_INVALID; | 41 | if (!clen) |
| 43 | if (!clen) | 42 | return 0; |
| 44 | return 0; | 43 | udecoded = utf8decodebyte(c[0], &len); |
| 45 | udecoded = utf8decodebyte(c[0], &len); | 44 | if (!BETWEEN(len, 1, UTF_SIZ)) |
| 46 | if (!BETWEEN(len, 1, UTF_SIZ)) | 45 | return 1; |
| 47 | return 1; | 46 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { |
| 48 | for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { | 47 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); |
| 49 | udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); | 48 | if (type) |
| 50 | if (type) | 49 | return j; |
| 51 | return j; | 50 | } |
| 52 | } | 51 | if (j < len) |
| 53 | if (j < len) | 52 | return 0; |
| 54 | return 0; | 53 | *u = udecoded; |
| 55 | *u = udecoded; | 54 | utf8validate(u, len); |
| 56 | utf8validate(u, len); | 55 | |
| 57 | 56 | return len; | |
| 58 | return len; | ||
| 59 | } | 57 | } |
| 60 | 58 | ||
| 61 | Drw *drw_create(Display *dpy, int screen, Window root, unsigned int w, | 59 | Drw *drw_create(Display *dpy, int screen, Window root, unsigned int w, |
| 62 | unsigned int h) | 60 | unsigned int h) { |
| 63 | { | 61 | Drw *drw = ecalloc(1, sizeof(Drw)); |
| 64 | Drw *drw = ecalloc(1, sizeof(Drw)); | 62 | |
| 65 | 63 | drw->dpy = dpy; | |
| 66 | drw->dpy = dpy; | 64 | drw->screen = screen; |
| 67 | drw->screen = screen; | 65 | drw->root = root; |
| 68 | drw->root = root; | 66 | drw->w = w; |
| 69 | drw->w = w; | 67 | drw->h = h; |
| 70 | drw->h = h; | 68 | drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); |
| 71 | drw->drawable = | 69 | drw->gc = XCreateGC(dpy, root, 0, NULL); |
| 72 | XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); | 70 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); |
| 73 | drw->gc = XCreateGC(dpy, root, 0, NULL); | 71 | |
| 74 | XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); | 72 | return drw; |
| 75 | |||
| 76 | return drw; | ||
| 77 | } | 73 | } |
| 78 | 74 | ||
| 79 | void drw_resize(Drw *drw, unsigned int w, unsigned int h) | 75 | void drw_resize(Drw *drw, unsigned int w, unsigned int h) { |
| 80 | { | 76 | if (!drw) |
| 81 | if (!drw) | 77 | return; |
| 82 | return; | 78 | |
| 83 | 79 | drw->w = w; | |
| 84 | drw->w = w; | 80 | drw->h = h; |
| 85 | drw->h = h; | 81 | if (drw->drawable) |
| 86 | if (drw->drawable) | 82 | XFreePixmap(drw->dpy, drw->drawable); |
| 87 | XFreePixmap(drw->dpy, drw->drawable); | 83 | drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, |
| 88 | drw->drawable = | 84 | DefaultDepth(drw->dpy, drw->screen)); |
| 89 | XCreatePixmap(drw->dpy, drw->root, w, h, | ||
| 90 | DefaultDepth(drw->dpy, drw->screen)); | ||
| 91 | } | 85 | } |
| 92 | 86 | ||
| 93 | void drw_free(Drw *drw) | 87 | void drw_free(Drw *drw) { |
| 94 | { | 88 | XFreePixmap(drw->dpy, drw->drawable); |
| 95 | XFreePixmap(drw->dpy, drw->drawable); | 89 | XFreeGC(drw->dpy, drw->gc); |
| 96 | XFreeGC(drw->dpy, drw->gc); | 90 | drw_fontset_free(drw->fonts); |
| 97 | drw_fontset_free(drw->fonts); | 91 | free(drw); |
| 98 | free(drw); | ||
| 99 | } | 92 | } |
| 100 | 93 | ||
| 101 | /* This function is an implementation detail. Library users should use | 94 | /* This function is an implementation detail. Library users should use |
| 102 | * drw_fontset_create instead. | 95 | * drw_fontset_create instead. |
| 103 | */ | 96 | */ |
| 104 | static Fnt *xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) | 97 | static Fnt *xfont_create(Drw *drw, const char *fontname, |
| 105 | { | 98 | FcPattern *fontpattern) { |
| 106 | Fnt *font; | 99 | Fnt *font; |
| 107 | XftFont *xfont = NULL; | 100 | XftFont *xfont = NULL; |
| 108 | FcPattern *pattern = NULL; | 101 | FcPattern *pattern = NULL; |
| 109 | 102 | ||
| 110 | if (fontname) { | 103 | if (fontname) { |
| 111 | /* Using the pattern found at font->xfont->pattern does not yield the | 104 | /* Using the pattern found at font->xfont->pattern does not yield the |
| 112 | * same substitution results as using the pattern returned by | 105 | * same substitution results as using the pattern returned by |
| 113 | * FcNameParse; using the latter results in the desired fallback | 106 | * FcNameParse; using the latter results in the desired fallback |
| 114 | * behaviour whereas the former just results in missing-character | 107 | * behaviour whereas the former just results in missing-character |
| 115 | * rectangles being drawn, at least with some fonts. */ | 108 | * rectangles being drawn, at least with some fonts. */ |
| 116 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { | 109 | if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { |
| 117 | fprintf(stderr, | 110 | fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); |
| 118 | "error, cannot load font from name: '%s'\n", | 111 | return NULL; |
| 119 | fontname); | 112 | } |
| 120 | return NULL; | 113 | if (!(pattern = FcNameParse((FcChar8 *)fontname))) { |
| 121 | } | 114 | fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", |
| 122 | if (!(pattern = FcNameParse((FcChar8 *) fontname))) { | 115 | fontname); |
| 123 | fprintf(stderr, | 116 | XftFontClose(drw->dpy, xfont); |
| 124 | "error, cannot parse font name to pattern: '%s'\n", | 117 | return NULL; |
| 125 | fontname); | 118 | } |
| 126 | XftFontClose(drw->dpy, xfont); | 119 | } else if (fontpattern) { |
| 127 | return NULL; | 120 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { |
| 128 | } | 121 | fprintf(stderr, "error, cannot load font from pattern.\n"); |
| 129 | } else if (fontpattern) { | 122 | return NULL; |
| 130 | if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { | 123 | } |
| 131 | fprintf(stderr, | 124 | } else { |
| 132 | "error, cannot load font from pattern.\n"); | 125 | die("no font specified."); |
| 133 | return NULL; | 126 | } |
| 134 | } | 127 | |
| 135 | } else { | 128 | font = ecalloc(1, sizeof(Fnt)); |
| 136 | die("no font specified."); | 129 | font->xfont = xfont; |
| 137 | } | 130 | font->pattern = pattern; |
| 138 | 131 | font->h = xfont->ascent + xfont->descent; | |
| 139 | font = ecalloc(1, sizeof(Fnt)); | 132 | font->dpy = drw->dpy; |
| 140 | font->xfont = xfont; | 133 | |
| 141 | font->pattern = pattern; | 134 | return font; |
| 142 | font->h = xfont->ascent + xfont->descent; | ||
| 143 | font->dpy = drw->dpy; | ||
| 144 | |||
| 145 | return font; | ||
| 146 | } | 135 | } |
| 147 | 136 | ||
| 148 | static void xfont_free(Fnt *font) | 137 | static void xfont_free(Fnt *font) { |
| 149 | { | 138 | if (!font) |
| 150 | if (!font) | 139 | return; |
| 151 | return; | 140 | if (font->pattern) |
| 152 | if (font->pattern) | 141 | FcPatternDestroy(font->pattern); |
| 153 | FcPatternDestroy(font->pattern); | 142 | XftFontClose(font->dpy, font->xfont); |
| 154 | XftFontClose(font->dpy, font->xfont); | 143 | free(font); |
| 155 | free(font); | ||
| 156 | } | 144 | } |
| 157 | 145 | ||
| 158 | Fnt *drw_fontset_create(Drw *drw, const char *fonts[], size_t fontcount) | 146 | Fnt *drw_fontset_create(Drw *drw, const char *fonts[], size_t fontcount) { |
| 159 | { | 147 | Fnt *cur, *ret = NULL; |
| 160 | Fnt *cur, *ret = NULL; | 148 | size_t i; |
| 161 | size_t i; | 149 | |
| 162 | 150 | if (!drw || !fonts) | |
| 163 | if (!drw || !fonts) | 151 | return NULL; |
| 164 | return NULL; | 152 | |
| 165 | 153 | for (i = 1; i <= fontcount; i++) { | |
| 166 | for (i = 1; i <= fontcount; i++) { | 154 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { |
| 167 | if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { | 155 | cur->next = ret; |
| 168 | cur->next = ret; | 156 | ret = cur; |
| 169 | ret = cur; | 157 | } |
| 170 | } | 158 | } |
| 171 | } | 159 | return (drw->fonts = ret); |
| 172 | return (drw->fonts = ret); | ||
| 173 | } | 160 | } |
| 174 | 161 | ||
| 175 | void drw_fontset_free(Fnt *font) | 162 | void drw_fontset_free(Fnt *font) { |
| 176 | { | 163 | if (font) { |
| 177 | if (font) { | 164 | drw_fontset_free(font->next); |
| 178 | drw_fontset_free(font->next); | 165 | xfont_free(font); |
| 179 | xfont_free(font); | 166 | } |
| 180 | } | ||
| 181 | } | 167 | } |
| 182 | 168 | ||
| 183 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname) | 169 | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname) { |
| 184 | { | 170 | if (!drw || !dest || !clrname) |
| 185 | if (!drw || !dest || !clrname) | 171 | return; |
| 186 | return; | ||
| 187 | 172 | ||
| 188 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), | 173 | if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), |
| 189 | DefaultColormap(drw->dpy, drw->screen), | 174 | DefaultColormap(drw->dpy, drw->screen), clrname, dest)) |
| 190 | clrname, dest)) | 175 | die("error, cannot allocate color '%s'", clrname); |
| 191 | die("error, cannot allocate color '%s'", clrname); | ||
| 192 | } | 176 | } |
| 193 | 177 | ||
| 194 | /* Wrapper to create color schemes. The caller has to call free(3) on the | 178 | /* Wrapper to create color schemes. The caller has to call free(3) on the |
| 195 | * returned color scheme when done using it. */ | 179 | * returned color scheme when done using it. */ |
| 196 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) | 180 | Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) { |
| 197 | { | 181 | size_t i; |
| 198 | size_t i; | 182 | Clr *ret; |
| 199 | Clr *ret; | 183 | |
| 200 | 184 | /* need at least two colors for a scheme */ | |
| 201 | /* need at least two colors for a scheme */ | 185 | if (!drw || !clrnames || clrcount < 2 || |
| 202 | if (!drw || !clrnames || clrcount < 2 | 186 | !(ret = ecalloc(clrcount, sizeof(XftColor)))) |
| 203 | || !(ret = ecalloc(clrcount, sizeof(XftColor)))) | 187 | return NULL; |
| 204 | return NULL; | 188 | |
| 205 | 189 | for (i = 0; i < clrcount; i++) | |
| 206 | for (i = 0; i < clrcount; i++) | 190 | drw_clr_create(drw, &ret[i], clrnames[i]); |
| 207 | drw_clr_create(drw, &ret[i], clrnames[i]); | 191 | return ret; |
| 208 | return ret; | ||
| 209 | } | 192 | } |
| 210 | 193 | ||
| 211 | void drw_setfontset(Drw *drw, Fnt *set) | 194 | void drw_setfontset(Drw *drw, Fnt *set) { |
| 212 | { | 195 | if (drw) |
| 213 | if (drw) | 196 | drw->fonts = set; |
| 214 | drw->fonts = set; | ||
| 215 | } | 197 | } |
| 216 | 198 | ||
| 217 | void drw_setscheme(Drw *drw, Clr *scm) | 199 | void drw_setscheme(Drw *drw, Clr *scm) { |
| 218 | { | 200 | if (drw) |
| 219 | if (drw) | 201 | drw->scheme = scm; |
| 220 | drw->scheme = scm; | ||
| 221 | } | 202 | } |
| 222 | 203 | ||
| 223 | void | 204 | void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, |
| 224 | drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, | 205 | int filled, int invert) { |
| 225 | int invert) | 206 | if (!drw || !drw->scheme) |
| 226 | { | 207 | return; |
| 227 | if (!drw || !drw->scheme) | 208 | XSetForeground(drw->dpy, drw->gc, |
| 228 | return; | 209 | invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); |
| 229 | XSetForeground(drw->dpy, drw->gc, | 210 | if (filled) |
| 230 | invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg]. | 211 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); |
| 231 | pixel); | 212 | else |
| 232 | if (filled) | 213 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); |
| 233 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||
| 234 | else | ||
| 235 | XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, | ||
| 236 | h - 1); | ||
| 237 | } | 214 | } |
| 238 | 215 | ||
| 239 | int | 216 | int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, |
| 240 | drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, | 217 | unsigned int lpad, const char *text, int invert) { |
| 241 | unsigned int lpad, const char *text, int invert) | 218 | int i, ty, ellipsis_x = 0; |
| 242 | { | 219 | unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; |
| 243 | int i, ty, ellipsis_x = 0; | 220 | XftDraw *d = NULL; |
| 244 | unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; | 221 | Fnt *usedfont, *curfont, *nextfont; |
| 245 | XftDraw *d = NULL; | 222 | int utf8strlen, utf8charlen, render = x || y || w || h; |
| 246 | Fnt *usedfont, *curfont, *nextfont; | 223 | long utf8codepoint = 0; |
| 247 | int utf8strlen, utf8charlen, render = x || y || w || h; | 224 | const char *utf8str; |
| 248 | long utf8codepoint = 0; | 225 | FcCharSet *fccharset; |
| 249 | const char *utf8str; | 226 | FcPattern *fcpattern; |
| 250 | FcCharSet *fccharset; | 227 | FcPattern *match; |
| 251 | FcPattern *fcpattern; | 228 | XftResult result; |
| 252 | FcPattern *match; | 229 | int charexists = 0, overflow = 0; |
| 253 | XftResult result; | 230 | /* keep track of a couple codepoints for which we have no match. */ |
| 254 | int charexists = 0, overflow = 0; | 231 | enum { nomatches_len = 64 }; |
| 255 | /* keep track of a couple codepoints for which we have no match. */ | 232 | static struct { |
| 256 | enum { nomatches_len = 64 }; | 233 | long codepoint[nomatches_len]; |
| 257 | static struct { | 234 | unsigned int idx; |
| 258 | long codepoint[nomatches_len]; | 235 | } nomatches; |
| 259 | unsigned int idx; | 236 | static unsigned int ellipsis_width = 0; |
| 260 | } nomatches; | 237 | |
| 261 | static unsigned int ellipsis_width = 0; | 238 | if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) |
| 262 | 239 | return 0; | |
| 263 | if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) | 240 | |
| 264 | return 0; | 241 | if (!render) { |
| 265 | 242 | w = invert ? invert : ~invert; | |
| 266 | if (!render) { | 243 | } else { |
| 267 | w = invert ? invert : ~invert; | 244 | XSetForeground(drw->dpy, drw->gc, |
| 268 | } else { | 245 | drw->scheme[invert ? ColFg : ColBg].pixel); |
| 269 | XSetForeground(drw->dpy, drw->gc, | 246 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); |
| 270 | drw->scheme[invert ? ColFg : ColBg].pixel); | 247 | d = XftDrawCreate(drw->dpy, drw->drawable, |
| 271 | XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | 248 | DefaultVisual(drw->dpy, drw->screen), |
| 272 | d = XftDrawCreate(drw->dpy, drw->drawable, | 249 | DefaultColormap(drw->dpy, drw->screen)); |
| 273 | DefaultVisual(drw->dpy, drw->screen), | 250 | x += lpad; |
| 274 | DefaultColormap(drw->dpy, drw->screen)); | 251 | w -= lpad; |
| 275 | x += lpad; | 252 | } |
| 276 | w -= lpad; | 253 | |
| 277 | } | 254 | usedfont = drw->fonts; |
| 278 | 255 | if (!ellipsis_width && render) | |
| 279 | usedfont = drw->fonts; | 256 | ellipsis_width = drw_fontset_getwidth(drw, "..."); |
| 280 | if (!ellipsis_width && render) | 257 | while (1) { |
| 281 | ellipsis_width = drw_fontset_getwidth(drw, "..."); | 258 | ew = ellipsis_len = utf8strlen = 0; |
| 282 | while (1) { | 259 | utf8str = text; |
| 283 | ew = ellipsis_len = utf8strlen = 0; | 260 | nextfont = NULL; |
| 284 | utf8str = text; | 261 | while (*text) { |
| 285 | nextfont = NULL; | 262 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); |
| 286 | while (*text) { | 263 | for (curfont = drw->fonts; curfont; curfont = curfont->next) { |
| 287 | utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); | 264 | charexists = charexists || |
| 288 | for (curfont = drw->fonts; curfont; | 265 | XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); |
| 289 | curfont = curfont->next) { | 266 | if (charexists) { |
| 290 | charexists = charexists | 267 | drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); |
| 291 | || XftCharExists(drw->dpy, curfont->xfont, | 268 | if (ew + ellipsis_width <= w) { |
| 292 | utf8codepoint); | 269 | /* keep track where the ellipsis still fits */ |
| 293 | if (charexists) { | 270 | ellipsis_x = x + ew; |
| 294 | drw_font_getexts(curfont, text, | 271 | ellipsis_w = w - ew; |
| 295 | utf8charlen, &tmpw, | 272 | ellipsis_len = utf8strlen; |
| 296 | NULL); | 273 | } |
| 297 | if (ew + ellipsis_width <= w) { | 274 | |
| 298 | /* keep track where the ellipsis still fits */ | 275 | if (ew + tmpw > w) { |
| 299 | ellipsis_x = x + ew; | 276 | overflow = 1; |
| 300 | ellipsis_w = w - ew; | 277 | /* called from drw_fontset_getwidth_clamp(): |
| 301 | ellipsis_len = utf8strlen; | 278 | * it wants the width AFTER the overflow |
| 302 | } | 279 | */ |
| 303 | 280 | if (!render) | |
| 304 | if (ew + tmpw > w) { | 281 | x += tmpw; |
| 305 | overflow = 1; | 282 | else |
| 306 | /* called from drw_fontset_getwidth_clamp(): | 283 | utf8strlen = ellipsis_len; |
| 307 | * it wants the width AFTER the overflow | 284 | } else if (curfont == usedfont) { |
| 308 | */ | 285 | utf8strlen += utf8charlen; |
| 309 | if (!render) | 286 | text += utf8charlen; |
| 310 | x += tmpw; | 287 | ew += tmpw; |
| 311 | else | 288 | } else { |
| 312 | utf8strlen = | 289 | nextfont = curfont; |
| 313 | ellipsis_len; | 290 | } |
| 314 | } else if (curfont == usedfont) { | 291 | break; |
| 315 | utf8strlen += utf8charlen; | 292 | } |
| 316 | text += utf8charlen; | 293 | } |
| 317 | ew += tmpw; | 294 | |
| 318 | } else { | 295 | if (overflow || !charexists || nextfont) |
| 319 | nextfont = curfont; | 296 | break; |
| 320 | } | 297 | else |
| 321 | break; | 298 | charexists = 0; |
| 322 | } | 299 | } |
| 323 | } | 300 | |
| 324 | 301 | if (utf8strlen) { | |
| 325 | if (overflow || !charexists || nextfont) | 302 | if (render) { |
| 326 | break; | 303 | ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; |
| 327 | else | 304 | XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], |
| 328 | charexists = 0; | 305 | usedfont->xfont, x, ty, (XftChar8 *)utf8str, |
| 329 | } | 306 | utf8strlen); |
| 330 | 307 | } | |
| 331 | if (utf8strlen) { | 308 | x += ew; |
| 332 | if (render) { | 309 | w -= ew; |
| 333 | ty = y + (h - usedfont->h) / 2 + | 310 | } |
| 334 | usedfont->xfont->ascent; | 311 | if (render && overflow) |
| 335 | XftDrawStringUtf8(d, | 312 | drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); |
| 336 | &drw-> | 313 | |
| 337 | scheme[invert ? ColBg : | 314 | if (!*text || overflow) { |
| 338 | ColFg], | 315 | break; |
| 339 | usedfont->xfont, x, ty, | 316 | } else if (nextfont) { |
| 340 | (XftChar8 *) utf8str, | 317 | charexists = 0; |
| 341 | utf8strlen); | 318 | usedfont = nextfont; |
| 342 | } | 319 | } else { |
| 343 | x += ew; | 320 | /* Regardless of whether or not a fallback font is found, the |
| 344 | w -= ew; | 321 | * character must be drawn. */ |
| 345 | } | 322 | charexists = 1; |
| 346 | if (render && overflow) | 323 | |
| 347 | drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", | 324 | for (i = 0; i < nomatches_len; ++i) { |
| 348 | invert); | 325 | /* avoid calling XftFontMatch if we know we won't find a match */ |
| 349 | 326 | if (utf8codepoint == nomatches.codepoint[i]) | |
| 350 | if (!*text || overflow) { | 327 | goto no_match; |
| 351 | break; | 328 | } |
| 352 | } else if (nextfont) { | 329 | |
| 353 | charexists = 0; | 330 | fccharset = FcCharSetCreate(); |
| 354 | usedfont = nextfont; | 331 | FcCharSetAddChar(fccharset, utf8codepoint); |
| 355 | } else { | 332 | |
| 356 | /* Regardless of whether or not a fallback font is found, the | 333 | if (!drw->fonts->pattern) { |
| 357 | * character must be drawn. */ | 334 | /* Refer to the comment in xfont_create for more information. */ |
| 358 | charexists = 1; | 335 | die("the first font in the cache must be loaded from a font string."); |
| 359 | 336 | } | |
| 360 | for (i = 0; i < nomatches_len; ++i) { | 337 | |
| 361 | /* avoid calling XftFontMatch if we know we won't find a match */ | 338 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); |
| 362 | if (utf8codepoint == nomatches.codepoint[i]) | 339 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); |
| 363 | goto no_match; | 340 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); |
| 364 | } | 341 | |
| 365 | 342 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); | |
| 366 | fccharset = FcCharSetCreate(); | 343 | FcDefaultSubstitute(fcpattern); |
| 367 | FcCharSetAddChar(fccharset, utf8codepoint); | 344 | match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); |
| 368 | 345 | ||
| 369 | if (!drw->fonts->pattern) { | 346 | FcCharSetDestroy(fccharset); |
| 370 | /* Refer to the comment in xfont_create for more information. */ | 347 | FcPatternDestroy(fcpattern); |
| 371 | die("the first font in the cache must be loaded from a font string."); | 348 | |
| 372 | } | 349 | if (match) { |
| 373 | 350 | usedfont = xfont_create(drw, NULL, match); | |
| 374 | fcpattern = FcPatternDuplicate(drw->fonts->pattern); | 351 | if (usedfont && |
| 375 | FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); | 352 | XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { |
| 376 | FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); | 353 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) |
| 377 | 354 | ; /* NOP */ | |
| 378 | FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); | 355 | curfont->next = usedfont; |
| 379 | FcDefaultSubstitute(fcpattern); | 356 | } else { |
| 380 | match = | 357 | xfont_free(usedfont); |
| 381 | XftFontMatch(drw->dpy, drw->screen, fcpattern, | 358 | nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; |
| 382 | &result); | 359 | no_match: |
| 383 | 360 | usedfont = drw->fonts; | |
| 384 | FcCharSetDestroy(fccharset); | 361 | } |
| 385 | FcPatternDestroy(fcpattern); | 362 | } |
| 386 | 363 | } | |
| 387 | if (match) { | 364 | } |
| 388 | usedfont = xfont_create(drw, NULL, match); | 365 | if (d) |
| 389 | if (usedfont | 366 | XftDrawDestroy(d); |
| 390 | && XftCharExists(drw->dpy, usedfont->xfont, | 367 | |
| 391 | utf8codepoint)) { | 368 | return x + (render ? w : 0); |
| 392 | for (curfont = drw->fonts; curfont->next; curfont = curfont->next) ; /* NOP */ | ||
| 393 | curfont->next = usedfont; | ||
| 394 | } else { | ||
| 395 | xfont_free(usedfont); | ||
| 396 | nomatches.codepoint[++nomatches.idx % | ||
| 397 | nomatches_len] = | ||
| 398 | utf8codepoint; | ||
| 399 | no_match: | ||
| 400 | usedfont = drw->fonts; | ||
| 401 | } | ||
| 402 | } | ||
| 403 | } | ||
| 404 | } | ||
| 405 | if (d) | ||
| 406 | XftDrawDestroy(d); | ||
| 407 | |||
| 408 | return x + (render ? w : 0); | ||
| 409 | } | 369 | } |
| 410 | 370 | ||
| 411 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) | 371 | void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, |
| 412 | { | 372 | unsigned int h) { |
| 413 | if (!drw) | 373 | if (!drw) |
| 414 | return; | 374 | return; |
| 415 | 375 | ||
| 416 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); | 376 | XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); |
| 417 | XSync(drw->dpy, False); | 377 | XSync(drw->dpy, False); |
| 418 | } | 378 | } |
| 419 | 379 | ||
| 420 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text) | 380 | unsigned int drw_fontset_getwidth(Drw *drw, const char *text) { |
| 421 | { | 381 | if (!drw || !drw->fonts || !text) |
| 422 | if (!drw || !drw->fonts || !text) | 382 | return 0; |
| 423 | return 0; | 383 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); |
| 424 | return drw_text(drw, 0, 0, 0, 0, 0, text, 0); | ||
| 425 | } | 384 | } |
| 426 | 385 | ||
| 427 | unsigned int | 386 | unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, |
| 428 | drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) | 387 | unsigned int n) { |
| 429 | { | 388 | unsigned int tmp = 0; |
| 430 | unsigned int tmp = 0; | 389 | if (drw && drw->fonts && text && n) |
| 431 | if (drw && drw->fonts && text && n) | 390 | tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); |
| 432 | tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); | 391 | return MIN(n, tmp); |
| 433 | return MIN(n, tmp); | ||
| 434 | } | 392 | } |
| 435 | 393 | ||
| 436 | void | 394 | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, |
| 437 | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, | 395 | unsigned int *w, unsigned int *h) { |
| 438 | unsigned int *h) | 396 | XGlyphInfo ext; |
| 439 | { | 397 | |
| 440 | XGlyphInfo ext; | 398 | if (!font || !text) |
| 441 | 399 | return; | |
| 442 | if (!font || !text) | 400 | |
| 443 | return; | 401 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); |
| 444 | 402 | if (w) | |
| 445 | XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *) text, len, | 403 | *w = ext.xOff; |
| 446 | &ext); | 404 | if (h) |
| 447 | if (w) | 405 | *h = font->h; |
| 448 | *w = ext.xOff; | ||
| 449 | if (h) | ||
| 450 | *h = font->h; | ||
| 451 | } | 406 | } |
| 452 | 407 | ||
| 453 | Cur *drw_cur_create(Drw *drw, int shape) | 408 | Cur *drw_cur_create(Drw *drw, int shape) { |
| 454 | { | 409 | Cur *cur; |
| 455 | Cur *cur; | ||
| 456 | 410 | ||
| 457 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) | 411 | if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) |
| 458 | return NULL; | 412 | return NULL; |
| 459 | 413 | ||
| 460 | cur->cursor = XCreateFontCursor(drw->dpy, shape); | 414 | cur->cursor = XCreateFontCursor(drw->dpy, shape); |
| 461 | 415 | ||
| 462 | return cur; | 416 | return cur; |
| 463 | } | 417 | } |
| 464 | 418 | ||
| 465 | void drw_cur_free(Drw *drw, Cur *cursor) | 419 | void drw_cur_free(Drw *drw, Cur *cursor) { |
| 466 | { | 420 | if (!cursor) |
| 467 | if (!cursor) | 421 | return; |
| 468 | return; | ||
| 469 | 422 | ||
| 470 | XFreeCursor(drw->dpy, cursor->cursor); | 423 | XFreeCursor(drw->dpy, cursor->cursor); |
| 471 | free(cursor); | 424 | free(cursor); |
| 472 | } | 425 | } |
