X-Git-Url: https://git.danieliu.xyz/?p=st.git;a=blobdiff_plain;f=x.c;h=5af6e4ddbb5c9e57d3b8e591e7e7c270b7ecba66;hp=d0acfee59b93daa1d8da851ad56a9666b8568bc3;hb=2cb539142b97bd2a5c1a322fd7c063c6afb67c9b;hpb=403c57ebb5b3745ff93e49b87e526c49dc59a5b9 diff --git a/x.c b/x.c index d0acfee..5af6e4d 100644 --- a/x.c +++ b/x.c @@ -1,5 +1,6 @@ /* See LICENSE for license details. */ #include +#include #include #include #include @@ -28,9 +29,11 @@ typedef struct { } Shortcut; typedef struct { - uint b; - uint mask; - char *s; + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; } MouseShortcut; typedef struct { @@ -55,6 +58,7 @@ static void selpaste(const Arg *); static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); +static void ttysend(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" @@ -90,8 +94,12 @@ typedef struct { Drawable buf; GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ Atom xembed, wmdeletewin, netwmname, netwmpid; - XIM xim; - XIC xic; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; Draw draw; Visual *vis; XSetWindowAttributes attrs; @@ -138,6 +146,9 @@ static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); static void xdrawglyph(Glyph, int, int); static void xclear(int, int, int, int); static int xgeommasktogravity(int); +static void ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); static void xinit(int, int); static void cresize(int, int); static void xresize(int, int); @@ -149,8 +160,8 @@ static void xunloadfont(Font *); static void xunloadfonts(void); static void xsetenv(void); static void xseturgency(int); -static int x2col(int); -static int y2row(int); +static int evcol(XEvent *); +static int evrow(XEvent *); static void expose(XEvent *); static void visibility(XEvent *); @@ -159,6 +170,7 @@ static void kpress(XEvent *); static void cmessage(XEvent *); static void resize(XEvent *); static void focus(XEvent *); +static int mouseaction(XEvent *, uint); static void brelease(XEvent *); static void bpress(XEvent *); static void bmotion(XEvent *); @@ -222,8 +234,9 @@ typedef struct { } Fontcache; /* Fontcache is an array now. A new font will be appended to the array. */ -static Fontcache frc[16]; +static Fontcache *frc = NULL; static int frclen = 0; +static int frccap = 0; static char *usedfont = NULL; static double usedfontsize = 0; static double defaultfontsize = 0; @@ -244,8 +257,8 @@ clipcopy(const Arg *dummy) { Atom clipboard; - if (xsel.clipboard != NULL) - free(xsel.clipboard); + free(xsel.clipboard); + xsel.clipboard = NULL; if (xsel.primary != NULL) { xsel.clipboard = xstrdup(xsel.primary); @@ -307,18 +320,24 @@ zoomreset(const Arg *arg) } } +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + int -x2col(int x) +evcol(XEvent *e) { - x -= borderpx; + int x = e->xbutton.x - borderpx; LIMIT(x, 0, win.tw - 1); return x / win.cw; } int -y2row(int y) +evrow(XEvent *e) { - y -= borderpx; + int y = e->xbutton.y - borderpx; LIMIT(y, 0, win.th - 1); return y / win.ch; } @@ -327,7 +346,7 @@ void mousesel(XEvent *e, int done) { int type, seltype = SEL_REGULAR; - uint state = e->xbutton.state & ~(Button1Mask | forceselmod); + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); for (type = 1; type < LEN(selmasks); ++type) { if (match(selmasks[type], state)) { @@ -335,7 +354,7 @@ mousesel(XEvent *e, int done) break; } } - selextend(x2col(e->xbutton.x), y2row(e->xbutton.y), seltype, done); + selextend(evcol(e), evrow(e), seltype, done); if (done) setsel(getsel(), e->xbutton.time); } @@ -343,9 +362,8 @@ mousesel(XEvent *e, int done) void mousereport(XEvent *e) { - int x = x2col(e->xbutton.x), y = y2row(e->xbutton.y), - button = e->xbutton.button, state = e->xbutton.state, - len; + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; char buf[40]; static int ox, oy; @@ -404,25 +422,37 @@ mousereport(XEvent *e) ttywrite(buf, len, 0); } +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, e->xbutton.state) || /* exact or forced */ + match(ms->mod, e->xbutton.state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + void bpress(XEvent *e) { struct timespec now; - MouseShortcut *ms; int snap; - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } - for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { - if (e->xbutton.button == ms->b - && match(ms->mask, e->xbutton.state)) { - ttywrite(ms->s, strlen(ms->s), 1); - return; - } - } + if (mouseaction(e, 0)) + return; if (e->xbutton.button == Button1) { /* @@ -440,7 +470,7 @@ bpress(XEvent *e) xsel.tclick2 = xsel.tclick1; xsel.tclick1 = now; - selstart(x2col(e->xbutton.x), y2row(e->xbutton.y), snap); + selstart(evcol(e), evrow(e), snap); } } @@ -464,18 +494,16 @@ selnotify(XEvent *e) ulong nitems, ofs, rem; int format; uchar *data, *last, *repl; - Atom type, incratom, property; + Atom type, incratom, property = None; incratom = XInternAtom(xw.dpy, "INCR", 0); ofs = 0; - if (e->type == SelectionNotify) { + if (e->type == SelectionNotify) property = e->xselection.property; - } else if(e->type == PropertyNotify) { + else if (e->type == PropertyNotify) property = e->xproperty.atom; - } else { - return; - } + if (property == None) return; @@ -620,12 +648,15 @@ selrequest(XEvent *e) void setsel(char *str, Time t) { + if (!str) + return; + free(xsel.primary); xsel.primary = str; XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) - selclear_(NULL); + selclear(); } void @@ -637,21 +668,21 @@ xsetsel(char *str) void brelease(XEvent *e) { - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } - if (e->xbutton.button == Button2) - selpaste(NULL); - else if (e->xbutton.button == Button1) + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) mousesel(e, 1); } void bmotion(XEvent *e) { - if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) { + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { mousereport(e); return; } @@ -671,6 +702,8 @@ cresize(int width, int height) col = (win.w - 2 * borderpx) / win.cw; row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); tresize(col, row); xresize(col, row); @@ -680,8 +713,8 @@ cresize(int width, int height) void xresize(int col, int row) { - win.tw = MAX(1, col * win.cw); - win.th = MAX(1, row * win.ch); + win.tw = col * win.cw; + win.th = row * win.ch; XFreePixmap(xw.dpy, xw.buf); xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, @@ -730,20 +763,20 @@ xloadcols(void) static int loaded; Color *cp; - dc.collen = MAX(LEN(colorname), 256); - dc.col = xmalloc(dc.collen * sizeof(Color)); - if (loaded) { for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); } for (i = 0; i < dc.collen; i++) if (!xloadcolor(i, NULL, &dc.col[i])) { if (colorname[i]) - die("Could not allocate color '%s'\n", colorname[i]); + die("could not allocate color '%s'\n", colorname[i]); else - die("Could not allocate color %d\n", i); + die("could not allocate color %d\n", i); } loaded = 1; } @@ -756,7 +789,6 @@ xsetcolorname(int x, const char *name) if (!BETWEEN(x, 0, dc.collen)) return 1; - if (!xloadcolor(x, name, &ncolor)) return 1; @@ -783,19 +815,21 @@ xhints(void) XClassHint class = {opt_name ? opt_name : termname, opt_class ? opt_class : termname}; XWMHints wm = {.flags = InputHint, .input = 1}; - XSizeHints *sizeh = NULL; + XSizeHints *sizeh; sizeh = XAllocSizeHints(); - sizeh->flags = PSize | PResizeInc | PBaseSize; + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; sizeh->height_inc = win.ch; sizeh->width_inc = win.cw; sizeh->base_height = 2 * borderpx; sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; if (xw.isfixed) { - sizeh->flags |= PMaxSize | PMinSize; + sizeh->flags |= PMaxSize; sizeh->min_width = sizeh->max_width = win.w; sizeh->min_height = sizeh->max_height = win.h; } @@ -868,7 +902,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "slant", 0, &haveattr) != XftResultMatch) || haveattr < wantattr) { f->badslant = 1; - fputs("st: font slant does not match\n", stderr); + fputs("font slant does not match\n", stderr); } } @@ -877,7 +911,7 @@ xloadfont(Font *f, FcPattern *pattern) if ((XftPatternGetInteger(f->match->pattern, "weight", 0, &haveattr) != XftResultMatch) || haveattr != wantattr) { f->badweight = 1; - fputs("st: font weight does not match\n", stderr); + fputs("font weight does not match\n", stderr); } } @@ -904,16 +938,14 @@ xloadfonts(char *fontstr, double fontsize) { FcPattern *pattern; double fontval; - float ceilf(float); - if (fontstr[0] == '-') { + if (fontstr[0] == '-') pattern = XftXlfdParse(fontstr, False, False); - } else { + else pattern = FcNameParse((FcChar8 *)fontstr); - } if (!pattern) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (fontsize > 1) { FcPatternDel(pattern, FC_PIXEL_SIZE); @@ -939,7 +971,7 @@ xloadfonts(char *fontstr, double fontsize) } if (xloadfont(&dc.font, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); if (usedfontsize < 0) { FcPatternGetDouble(dc.font.match->pattern, @@ -956,17 +988,17 @@ xloadfonts(char *fontstr, double fontsize) FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); if (xloadfont(&dc.ifont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_WEIGHT); FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); if (xloadfont(&dc.ibfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (xloadfont(&dc.bfont, pattern)) - die("st: can't open font %s\n", fontstr); + die("can't open font %s\n", fontstr); FcPatternDestroy(pattern); } @@ -993,6 +1025,47 @@ xunloadfonts(void) xunloadfont(&dc.ibfont); } +void +ximopen(Display *dpy) +{ + XIMCallback destroy = { .client_data = NULL, .callback = ximdestroy }; + + if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im=local"); + if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { + XSetLocaleModifiers("@im="); + if ((xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed. Could not open input device.\n"); + } + } + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &destroy, NULL) != NULL) + die("XSetIMValues failed. Could not set input method value.\n"); + xw.xic = XCreateIC(xw.ime.xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, XNFocusWindow, xw.win, NULL); + if (xw.xic == NULL) + die("XCreateIC failed. Could not obtain input method.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + ximopen(dpy); + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + void xinit(int cols, int rows) { @@ -1003,13 +1076,13 @@ xinit(int cols, int rows) XColor xmousefg, xmousebg; if (!(xw.dpy = XOpenDisplay(NULL))) - die("Can't open display\n"); + die("can't open display\n"); xw.scr = XDefaultScreen(xw.dpy); xw.vis = XDefaultVisual(xw.dpy, xw.scr); /* font */ if (!FcInit()) - die("Could not init fontconfig.\n"); + die("could not init fontconfig.\n"); usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); @@ -1030,7 +1103,7 @@ xinit(int cols, int rows) xw.attrs.background_pixel = dc.col[defaultbg].pixel; xw.attrs.border_pixel = dc.col[defaultbg].pixel; xw.attrs.bit_gravity = NorthWestGravity; - xw.attrs.event_mask = FocusChangeMask | KeyPressMask + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | ExposureMask | VisibilityChangeMask | StructureNotifyMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; xw.attrs.colormap = xw.cmap; @@ -1058,22 +1131,7 @@ xinit(int cols, int rows) xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); /* input methods */ - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im=local"); - if ((xw.xim = XOpenIM(xw.dpy, NULL, NULL, NULL)) == NULL) { - XSetLocaleModifiers("@im="); - if ((xw.xim = XOpenIM(xw.dpy, - NULL, NULL, NULL)) == NULL) { - die("XOpenIM failed. Could not open input" - " device.\n"); - } - } - } - xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing - | XIMStatusNothing, XNClientWindow, xw.win, - XNFocusWindow, xw.win, NULL); - if (xw.xic == NULL) - die("XCreateIC failed. Could not obtain input method.\n"); + ximopen(xw.dpy); /* white cursor, black outline */ cursor = XCreateFontCursor(xw.dpy, mouseshape); @@ -1104,8 +1162,8 @@ xinit(int cols, int rows) win.mode = MODE_NUMLOCK; resettitle(); - XMapWindow(xw.dpy, xw.win); xhints(); + XMapWindow(xw.dpy, xw.win); XSync(xw.dpy, False); clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); @@ -1215,13 +1273,10 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x fontpattern = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres); - /* - * Overwrite or create the new cache entry. - */ - if (frclen >= LEN(frc)) { - frclen = LEN(frc) - 1; - XftFontClose(xw.dpy, frc[frclen].font); - frc[frclen].unicodep = 0; + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); } frc[frclen].font = XftFontOpenPattern(xw.dpy, @@ -1407,12 +1462,13 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) og.mode ^= ATTR_REVERSE; xdrawglyph(og, ox, oy); + if (IS_SET(MODE_HIDE)) + return; + /* * Select the right color for the right mode. */ g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; - g.fg = defaultbg; - g.bg = defaultcs; if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; @@ -1426,17 +1482,15 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) } } else { if (selected(cx, cy)) { - drawcol = dc.col[defaultrcs]; g.fg = defaultfg; g.bg = defaultrcs; } else { - drawcol = dc.col[defaultcs]; + g.fg = defaultbg; + g.bg = defaultcs; } + drawcol = dc.col[g.bg]; } - if (IS_SET(MODE_HIDE)) - return; - /* draw the new one */ if (IS_SET(MODE_FOCUSED)) { switch (win.cursor) { @@ -1496,7 +1550,7 @@ void xsettitle(char *p) { XTextProperty prop; - DEFAULT(p, "st"); + DEFAULT(p, opt_title); Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, &prop); @@ -1552,6 +1606,18 @@ xfinishdraw(void) defaultfg : defaultbg].pixel); } +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); +} + void expose(XEvent *ev) { @@ -1626,13 +1692,13 @@ focus(XEvent *ev) return; if (ev->type == FocusIn) { - XSetICFocus(xw.xic); + XSetICFocus(xw.ime.xic); win.mode |= MODE_FOCUSED; xseturgency(0); if (IS_SET(MODE_FOCUS)) ttywrite("\033[I", 3, 0); } else { - XUnsetICFocus(xw.xic); + XUnsetICFocus(xw.ime.xic); win.mode &= ~MODE_FOCUSED; if (IS_SET(MODE_FOCUS)) ttywrite("\033[O", 3, 0); @@ -1687,7 +1753,7 @@ kpress(XEvent *ev) { XKeyEvent *e = &ev->xkey; KeySym ksym; - char buf[32], *customkey; + char buf[64], *customkey; int len; Rune c; Status status; @@ -1696,7 +1762,7 @@ kpress(XEvent *ev) if (IS_SET(MODE_KBDLOCK)) return; - len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); /* 1. shortcuts */ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { @@ -1729,7 +1795,6 @@ kpress(XEvent *ev) ttywrite(buf, len, 1); } - void cmessage(XEvent *e) { @@ -1926,19 +1991,19 @@ main(int argc, char *argv[]) opt_embed = EARGF(usage()); break; case 'v': - die("%s " VERSION " (c) 2010-2016 st engineers\n", argv0); + die("%s " VERSION "\n", argv0); break; default: usage(); } ARGEND; run: - if (argc > 0) { - /* eat all remaining arguments */ + if (argc > 0) /* eat all remaining arguments */ opt_cmd = argv; - if (!opt_title && !opt_line) - opt_title = basename(xstrdup(argv[0])); - } + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); cols = MAX(cols, 1);