X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=st.c;h=37c404877f78f4f7d3db8e614636d0de7905157d;hb=e4bf56ae1a9e2612ec9a6faf2aaecd6eadcccaa7;hp=f7f9de8769e593365dd4ac2b458a729bb5b0db9d;hpb=9d82bdd94708fa2daabc0b53f8b71bca2a3c6f0a;p=st.git diff --git a/st.c b/st.c index f7f9de8..37c4048 100644 --- a/st.c +++ b/st.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -104,8 +105,11 @@ typedef struct { /* Purely graphic info */ typedef struct { Display* dis; + Colormap cmap; Window win; Pixmap buf; + XIM xim; + XIC xic; int scr; int w; /* window width */ int h; /* window height */ @@ -113,7 +117,8 @@ typedef struct { int bufh; /* pixmap height */ int ch; /* char height */ int cw; /* char width */ - int hasfocus; + int focus; + int vis; /* is visible */ } XWindow; typedef struct { @@ -186,6 +191,8 @@ static void xloadcols(void); static void xseturgency(int); static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); static char* kmap(KeySym); static void kpress(XEvent *); static void resize(XEvent *); @@ -193,17 +200,22 @@ static void focus(XEvent *); static void brelease(XEvent *); static void bpress(XEvent *); static void bmotion(XEvent *); - +static void selection_notify(XEvent *); +static void selection_request(XEvent *); static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, - [Expose] = expose, [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, [FocusIn] = focus, [FocusOut] = focus, [MotionNotify] = bmotion, [ButtonPress] = bpress, [ButtonRelease] = brelease, + [SelectionNotify] = selection_notify, + [SelectionRequest] = selection_request, }; /* Globals */ @@ -235,8 +247,9 @@ static inline int selected(int x, int y) { } static void getbuttoninfo(XEvent *e, int *b, int *x, int *y) { - if(b) *b = e->xbutton.state, - *b=*b==4096?5:*b==2048?4:*b==1024?3:*b==512?2:*b==256?1:-1; + if(b) + *b = e->xbutton.button; + *x = e->xbutton.x/xw.cw; *y = e->xbutton.y/xw.ch; sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex; @@ -269,15 +282,129 @@ static char *getseltext() { return str; } -/* TODO: use X11 clipboard */ +static void selection_notify(XEvent *e) { + unsigned long nitems; + unsigned long length; + int format, res; + unsigned char *data; + Atom type; + + res = XGetWindowProperty(xw.dis, xw.win, XA_PRIMARY, 0, 0, False, + AnyPropertyType, &type, &format, &nitems, &length, &data); + switch(res) { + case BadAtom: + case BadValue: + case BadWindow: + fprintf(stderr, "Invalid paste, XGetWindowProperty0"); + return; + } + + res = XGetWindowProperty(xw.dis, xw.win, XA_PRIMARY, 0, length, False, + AnyPropertyType, &type, &format, &nitems, &length, &data); + switch(res) { + case BadAtom: + case BadValue: + case BadWindow: + fprintf(stderr, "Invalid paste, XGetWindowProperty0"); + return; + } + + if(data) { + ttywrite((const char *) data, nitems * format / 8); + XFree(data); + } +} + +static void selpaste() { + XConvertSelection(xw.dis, XA_PRIMARY, XA_STRING, XA_PRIMARY, xw.win, CurrentTime); +} + +static void selection_request(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + int res; + Atom xa_targets; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dis, "TARGETS", 0); + if(xsre->target == xa_targets) { + /* respond with the supported type */ + Atom string = XA_STRING; + res = XChangeProperty(xsre->display, xsre->requestor, xsre->property, XA_ATOM, 32, + PropModeReplace, (unsigned char *) &string, 1); + switch(res) { + case BadAlloc: + case BadAtom: + case BadMatch: + case BadValue: + case BadWindow: + fprintf(stderr, "Error in selection_request, TARGETS"); + break; + default: + xev.property = xsre->property; + } + } else if(xsre->target == XA_STRING) { + res = XChangeProperty(xsre->display, xsre->requestor, xsre->property, + xsre->target, 8, PropModeReplace, (unsigned char *) sel.clip, + strlen(sel.clip)); + switch(res) { + case BadAlloc: + case BadAtom: + case BadMatch: + case BadValue: + case BadWindow: + fprintf(stderr, "Error in selection_request, XA_STRING"); + break; + default: + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + res = XSendEvent(xsre->display, xsre->requestor, True, 0, (XEvent *) &xev); + switch(res) { + case 0: + case BadValue: + case BadWindow: + fprintf(stderr, "Error in selection_requested, XSendEvent"); + } +} + static void selcopy(char *str) { + /* register the selection for both the clipboard and the primary */ + Atom clipboard; + int res; + free(sel.clip); sel.clip = str; -} -static void selpaste() { - if(sel.clip) - ttywrite(sel.clip, strlen(sel.clip)); + res = XSetSelectionOwner(xw.dis, XA_PRIMARY, xw.win, CurrentTime); + switch(res) { + case BadAtom: + case BadWindow: + fprintf(stderr, "Invalid copy, XSetSelectionOwner"); + return; + } + + clipboard = XInternAtom(xw.dis, "CLIPBOARD", 0); + res = XSetSelectionOwner(xw.dis, clipboard, xw.win, CurrentTime); + switch(res) { + case BadAtom: + case BadWindow: + fprintf(stderr, "Invalid copy, XSetSelectionOwner"); + return; + } + + XFlush(xw.dis); } /* TODO: doubleclick to select word */ @@ -677,7 +804,8 @@ tsetattr(int *attr, int l) { else if(BETWEEN(attr[i], 100, 107)) term.c.attr.fg = attr[i] - 100 + 8; else - fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]); + fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]), csidump(); + break; } } @@ -795,6 +923,8 @@ csihandle(void) { case 1: term.mode &= ~MODE_APPKEYPAD; break; + case 5: /* TODO: DECSCNM -- Remove reverse video */ + break; case 7: term.mode &= ~MODE_WRAP; break; @@ -850,11 +980,16 @@ csihandle(void) { case 1: term.mode |= MODE_APPKEYPAD; break; + case 5: /* DECSCNM -- Reverve video */ + /* TODO: set REVERSE on the whole screen (f) */ + break; case 7: term.mode |= MODE_WRAP; break; case 12: /* att610 -- Start blinking cursor (IGNORED) */ - break; + /* fallthrough for xterm cvvis = CSI [ ? 12 ; 25 h */ + if(escseq.narg > 1 && escseq.arg[1] != 25) + break; case 25: term.c.state &= ~CURSOR_HIDE; break; @@ -1029,7 +1164,7 @@ tputc(char c) { tnewline(); break; case '\a': - if(!xw.hasfocus) + if(!xw.focus) xseturgency(1); break; case '\033': @@ -1060,12 +1195,25 @@ tresize(int col, int row) { int i; int minrow = MIN(row, term.row); int mincol = MIN(col, term.col); + int slide = term.c.y - row + 1; if(col < 1 || row < 1) return; - /* free uneeded rows */ - for(i = row; i < term.row; i++) { + /* free unneeded rows */ + i = 0; + if(slide > 0) { + /* slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines */ + for(/* i = 0 */; i < slide; i++) { + free(term.line[i]); + free(term.alt[i]); + } + memmove(term.line, term.line + slide, row * sizeof(Line)); + memmove(term.alt, term.alt + slide, row * sizeof(Line)); + } + for(i += row; i < term.row; i++) { free(term.line[i]); free(term.alt[i]); } @@ -1100,11 +1248,10 @@ void xloadcols(void) { int i, r, g, b; XColor color; - Colormap cmap = DefaultColormap(xw.dis, xw.scr); unsigned long white = WhitePixel(xw.dis, xw.scr); for(i = 0; i < 16; i++) { - if (!XAllocNamedColor(xw.dis, cmap, colorname[i], &color, &color)) { + if (!XAllocNamedColor(xw.dis, xw.cmap, colorname[i], &color, &color)) { dc.col[i] = white; fprintf(stderr, "Could not allocate color '%s'\n", colorname[i]); } else @@ -1118,7 +1265,7 @@ xloadcols(void) { color.red = r == 0 ? 0 : 0x3737 + 0x2828 * r; color.green = g == 0 ? 0 : 0x3737 + 0x2828 * g; color.blue = b == 0 ? 0 : 0x3737 + 0x2828 * b; - if (!XAllocColor(xw.dis, cmap, &color)) { + if (!XAllocColor(xw.dis, xw.cmap, &color)) { dc.col[i] = white; fprintf(stderr, "Could not allocate color %d\n", i); } else @@ -1128,7 +1275,7 @@ xloadcols(void) { for(r = 0; r < 24; r++, i++) { color.red = color.green = color.blue = 0x0808 + 0x0a0a * r; - if (!XAllocColor(xw.dis, cmap, &color)) { + if (!XAllocColor(xw.dis, xw.cmap, &color)) { dc.col[i] = white; fprintf(stderr, "Could not allocate color %d\n", i); } else @@ -1163,6 +1310,8 @@ xhints(void) void xinit(void) { + XSetWindowAttributes attrs; + if(!(xw.dis = XOpenDisplay(NULL))) die("Can't open display\n"); xw.scr = XDefaultScreen(xw.dis); @@ -1176,25 +1325,39 @@ xinit(void) { xw.ch = dc.font->ascent + dc.font->descent; /* colors */ + xw.cmap = XDefaultColormap(xw.dis, xw.scr); xloadcols(); - /* windows */ - xw.bufh = term.row * xw.ch; - xw.bufw = term.col * xw.cw; + /* window - default size */ + xw.bufh = 24 * xw.ch; + xw.bufw = 80 * xw.cw; xw.h = xw.bufh + 2*BORDER; xw.w = xw.bufw + 2*BORDER; - xw.win = XCreateSimpleWindow(xw.dis, XRootWindow(xw.dis, xw.scr), 0, 0, - xw.w, xw.h, 0, - dc.col[DefaultBG], - dc.col[DefaultBG]); + + attrs.background_pixel = dc.col[DefaultBG]; + attrs.border_pixel = dc.col[DefaultBG]; + attrs.bit_gravity = NorthWestGravity; + attrs.event_mask = FocusChangeMask | KeyPressMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | PointerMotionMask | ButtonPressMask | ButtonReleaseMask; + attrs.colormap = xw.cmap; + + xw.win = XCreateWindow(xw.dis, XRootWindow(xw.dis, xw.scr), 0, 0, + xw.w, xw.h, 0, XDefaultDepth(xw.dis, xw.scr), InputOutput, + XDefaultVisual(xw.dis, xw.scr), + CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask + | CWColormap, + &attrs); xw.buf = XCreatePixmap(xw.dis, xw.win, xw.bufw, xw.bufh, XDefaultDepth(xw.dis, xw.scr)); + + + /* input methods */ + xw.xim = XOpenIM(xw.dis, NULL, NULL, NULL); + xw.xic = XCreateIC(xw.xim, XNInputStyle, XIMPreeditNothing + | XIMStatusNothing, XNClientWindow, xw.win, + XNFocusWindow, xw.win, NULL); /* gc */ dc.gc = XCreateGC(xw.dis, xw.win, 0, NULL); - - /* event mask */ - XSelectInput(xw.dis, xw.win, ExposureMask | KeyPressMask - | StructureNotifyMask | FocusChangeMask | PointerMotionMask - | ButtonPressMask | ButtonReleaseMask); XMapWindow(xw.dis, xw.win); xhints(); @@ -1217,8 +1380,13 @@ xdraws(char *s, Glyph base, int x, int y, int len) { XSetForeground(xw.dis, dc.gc, xfg); if(base.mode & ATTR_GFX) - for(i = 0; i < len; i++) - s[i] = gfx[(int)s[i]]; + for(i = 0; i < len; i++) { + char c = gfx[(unsigned int)s[i] % 256]; + if(c) + s[i] = c; + else if(s[i] > 0x5f) + s[i] -= 0x5f; + } XSetFont(xw.dis, dc.gc, base.mode & ATTR_BOLD ? dc.bfont->fid : dc.font->fid); XDrawImageString(xw.dis, xw.buf, dc.gc, winx, winy, s, len); @@ -1246,7 +1414,7 @@ xdrawcursor(void) { xclear(oldx, oldy, oldx, oldy); /* draw the new one */ - if(!(term.c.state & CURSOR_HIDE) && xw.hasfocus) { + if(!(term.c.state & CURSOR_HIDE) && xw.focus) { xdraws(&g.c, g, term.c.x, term.c.y, 1); oldx = term.c.x, oldy = term.c.y; } @@ -1286,8 +1454,10 @@ draw(int redraw_all) { Glyph base, new; char buf[DRAW_BUF_SIZ]; - XSetForeground(xw.dis, dc.gc, dc.col[DefaultBG]); - XFillRectangle(xw.dis, xw.buf, dc.gc, 0, 0, xw.bufw, xw.bufh); + if(!xw.vis) + return; + + xclear(0, 0, term.col-1, term.row-1); for(y = 0; y < term.row; y++) { base = term.line[y][0]; i = ox = 0; @@ -1323,6 +1493,19 @@ expose(XEvent *ev) { draw(SCREEN_REDRAW); } +void +visibility(XEvent *ev) { + XVisibilityEvent *e = &ev->xvisibility; + /* XXX if this goes from 0 to 1, need a full redraw for next Expose, + * not just a buf copy */ + xw.vis = e->state != VisibilityFullyObscured; +} + +void +unmap(XEvent *ev) { + xw.vis = 0; +} + void xseturgency(int add) { XWMHints *h = XGetWMHints(xw.dis, xw.win); @@ -1333,7 +1516,7 @@ xseturgency(int add) { void focus(XEvent *ev) { - if((xw.hasfocus = ev->type == FocusIn)) + if((xw.focus = ev->type == FocusIn)) xseturgency(0); draw(SCREEN_UPDATE); } @@ -1356,10 +1539,11 @@ kpress(XEvent *ev) { int len; int meta; int shift; + Status status; meta = e->state & Mod1Mask; shift = e->state & ShiftMask; - len = XLookupString(e, buf, sizeof(buf), &ksym, NULL); + len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status); if((customkey = kmap(ksym))) ttywrite(customkey, strlen(customkey)); @@ -1379,7 +1563,7 @@ kpress(XEvent *ev) { break; case XK_Insert: if(shift) - selpaste(), draw(1); + selpaste(); break; default: fprintf(stderr, "errkey: %d\n", (int)ksym); @@ -1406,7 +1590,6 @@ resize(XEvent *e) { xw.bufw = MAX(1, xw.bufw); XFreePixmap(xw.dis, xw.buf); xw.buf = XCreatePixmap(xw.dis, xw.win, xw.bufw, xw.bufh, XDefaultDepth(xw.dis, xw.scr)); - draw(SCREEN_REDRAW); } void @@ -1430,6 +1613,8 @@ run(void) { } while(XPending(xw.dis)) { XNextEvent(xw.dis, &ev); + if (XFilterEvent(&ev, xw.win)) + continue; if(handler[ev.type]) (handler[ev.type])(&ev); }