#include <sys/wait.h>
#include <unistd.h>
#include <X11/Xlib.h>
+#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
/* 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 */
int bufh; /* pixmap height */
int ch; /* char height */
int cw; /* char width */
- int hasfocus;
+ int focus;
+ int vis; /* is visible */
} XWindow;
typedef struct {
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 *);
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 */
}
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;
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 */
void
tnewline(void) {
+ int x = term.c.x+1 < term.col ? term.c.x : 0;
int y = term.c.y;
if(term.c.y == term.bot)
tscrollup(term.top, 1);
else
y++;
- tmoveto(0, y);
+ tmoveto(x, y);
}
void
void
tclearregion(int x1, int y1, int x2, int y2) {
- int y, temp;
+ int x, y, temp;
if(x1 > x2)
temp = x1, x1 = x2, x2 = temp;
LIMIT(y2, 0, term.row-1);
for(y = y1; y <= y2; y++)
- memset(&term.line[y][x1], 0, sizeof(Glyph)*(x2-x1+1));
+ for(x = x1; x <= x2; x++)
+ term.line[y][x].state = 0;
}
void
case 1:
term.mode &= ~MODE_APPKEYPAD;
break;
+ case 5: /* TODO: DECSCNM -- Remove reverse video */
+ break;
case 7:
term.mode &= ~MODE_WRAP;
break;
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;
tnewline();
break;
case '\a':
- if(!xw.hasfocus)
+ if(!xw.focus)
xseturgency(1);
break;
case '\033':
void
tresize(int col, int row) {
- int i;
+ int i, x;
int minrow = MIN(row, term.row);
int mincol = MIN(col, term.col);
int slide = term.c.y - row + 1;
for(i = 0; i < minrow; i++) {
term.line[i] = realloc(term.line[i], col * sizeof(Glyph));
term.alt[i] = realloc(term.alt[i], col * sizeof(Glyph));
- memset(term.line[i] + mincol, 0, (col - mincol) * sizeof(Glyph));
- memset(term.alt[i] + mincol, 0, (col - mincol) * sizeof(Glyph));
+ for(x = mincol; x < col; x++) {
+ term.line[i][x].state = 0;
+ term.alt[i][x].state = 0;
+ }
}
/* allocate any new rows */
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
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
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
void
xinit(void) {
+ XSetWindowAttributes attrs;
+
if(!(xw.dis = XOpenDisplay(NULL)))
die("Can't open display\n");
xw.scr = XDefaultScreen(xw.dis);
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();
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;
}
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;
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);
void
focus(XEvent *ev) {
- if((xw.hasfocus = ev->type == FocusIn))
+ if((xw.focus = ev->type == FocusIn))
xseturgency(0);
draw(SCREEN_UPDATE);
}
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));
break;
case XK_Insert:
if(shift)
- selpaste(), draw(1);
+ selpaste();
break;
default:
fprintf(stderr, "errkey: %d\n", (int)ksym);
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
}
while(XPending(xw.dis)) {
XNextEvent(xw.dis, &ev);
+ if (XFilterEvent(&ev, xw.win))
+ continue;
if(handler[ev.type])
(handler[ev.type])(&ev);
}