alot of renaming...
[k8sterm.git] / src / selection.c
blobb52672fb9ddcfbf429e28ca8a62678d429b65501
1 ////////////////////////////////////////////////////////////////////////////////
2 // selection
3 static void k8t_tmWantRedraw (K8Term *term, int forceFast) {
4 if (term != NULL) {
5 term->wantRedraw = 1;
6 if (forceFast) term->lastDrawTime = 0;
7 else if (!term->fastredraw) term->lastDrawTime = mclock_ticks(); //FIXME: avoid excess redraw?
12 static void k8t_tmDirtyMark (K8Term *term, int lineno, int flag) {
13 if (term != NULL && lineno >= 0 && lineno < term->row) {
14 term->dirty[lineno] |= flag;
15 k8t_tmWantRedraw(term, 0);
20 static void k8t_selInit (K8Term *term) {
21 if (term != NULL) {
22 term->sel.tclick1 = term->sel.tclick2 = mclock_ticks();
23 term->sel.mode = 0;
24 term->sel.bx = -1;
25 term->sel.clip = NULL;
26 term->sel.xtarget = XA_UTF8;
27 //if (term->sel.xtarget == None) term->sel.xtarget = XA_STRING;
32 static void k8t_selHide (K8Term *term) {
33 if (term != NULL && term->sel.bx != -1) {
34 term->sel.mode = 0;
35 term->sel.bx = -1;
36 k8t_tmFullDirty(term);
41 static int k8t_isSelected (K8Term *term, int x, int y) {
42 if (term == NULL || term->sel.bx == -1) return 0;
44 if (y >= term->row) y = 0-(y-term->row+1);
45 if (term->sel.ey == y && term->sel.by == y) {
46 int bx = K8T_MIN(term->sel.bx, term->sel.ex);
47 int ex = K8T_MAX(term->sel.bx, term->sel.ex);
49 return K8T_BETWEEN(x, bx, ex);
52 return
53 ((term->sel.b.y < y && y < term->sel.e.y) || (y == term->sel.e.y && x <= term->sel.e.x)) ||
54 (y == term->sel.b.y && x >= term->sel.b.x && (x <= term->sel.e.x || term->sel.b.y != term->sel.e.y));
58 static void k8t_getButtonInfo (K8Term *term, XEvent *e, int *b, int *x, int *y) {
59 if (b != NULL) *b = e->xbutton.button;
60 if (x != NULL) *x = K8T_X2COL(e->xbutton.x);
61 if (y != NULL) *y = K8T_Y2ROW(term, e->xbutton.y);
62 term->sel.b.x = (term->sel.by < term->sel.ey ? term->sel.bx : term->sel.ex);
63 term->sel.b.y = K8T_MIN(term->sel.by, term->sel.ey);
64 term->sel.e.x = (term->sel.by < term->sel.ey ? term->sel.ex : term->sel.bx);
65 term->sel.e.y = K8T_MAX(term->sel.by, term->sel.ey);
69 static void k8t_mouseReport (K8Term *term, XEvent *e) {
70 int x = K8T_X2COL(e->xbutton.x);
71 int y = K8T_Y2ROW(term, e->xbutton.y);
72 int button = e->xbutton.button;
73 int state = e->xbutton.state;
74 //char buf[] = { '\033', '[', 'M', 0, 32+x+1, 32+y+1 };
75 char buf[32], *p;
76 char lastCh = 'M';
77 int ss;
79 if (term == NULL) return;
80 if (x < 0 || x >= term->col || y < 0 || y >= term->row) return;
82 //fprintf(stderr, "mr(%d): x=%d; y=%d\n", term->mousemode, x, y);
83 #if 0
84 case 1000: /* X11 xterm mouse reporting */
85 case 1005: /* utf-8 mouse encoding */
86 case 1006: /* sgr mouse encoding */
87 case 1015: /* urxvt mouse encoding */
88 #endif
89 //sprintf(buf, "\x1b[M%c%c%c", 0, 32+x+1, 32+y+1);
90 p = buf+sprintf(buf, "\x1b[M");
91 /* from urxvt */
92 if (e->xbutton.type == MotionNotify) {
93 if (!K8T_ISSET(term, K8T_MODE_MOUSEMOTION) || (x == term->mouseox && y == term->mouseoy)) return;
94 button = term->mouseob+32;
95 term->mouseox = x;
96 term->mouseoy = y;
97 } else if (e->xbutton.type == ButtonRelease || button == AnyButton) {
98 if (term->mousemode != 1006) {
99 button = 3; // 'release' flag
100 } else {
101 lastCh = 'm';
102 button -= Button1;
103 if (button >= 3) button += 64-3;
105 } else {
106 button -= Button1;
107 if (button >= 3) button += 64-3;
108 if (e->xbutton.type == ButtonPress) {
109 term->mouseob = button;
110 term->mouseox = x;
111 term->mouseoy = y;
114 ss = (state&ShiftMask ? 4 : 0)+(state&Mod4Mask ? 8 : 0)+(state&ControlMask ? 16 : 0);
115 switch (term->mousemode) {
116 case 1006: /* sgr */
117 buf[2] = '<';
118 p += sprintf(p, "%d;", button+ss);
119 break;
120 case 1015: /* urxvt */
121 --p; // remove 'M'
122 p += sprintf(p, "%d;", 32+button+ss);
123 break;
124 default:
125 *p++ = 32+button+ss;
126 break;
128 // coords
129 switch (term->mousemode) {
130 case 1005: /* utf-8 */
131 p += k8t_UTF8Encode(p, x+1);
132 p += k8t_UTF8Encode(p, y+1);
133 break;
134 case 1006: /* sgr */
135 p += sprintf(p, "%d;%d%c", x+1, y+1, lastCh);
136 break;
137 case 1015: /* urxvt */
138 p += sprintf(p, "%d;%dM", x+1, y+1);
139 break;
140 default:
141 p += sprintf(p, "%c%c", 32+x+1, 32+y+1);
142 break;
144 *p = 0;
147 fprintf(stderr, "(%d)<", term->mousemode);
148 for (const unsigned char *s = (const unsigned char *)buf; *s; ++s) {
149 if (s[0] < 32) fprintf(stderr, "{%d}", s[0]); else fputc(s[0], stderr);
151 fputs(">\n", stderr);
154 k8t_ttyWriteStrNoEnc(term, buf);
158 static void xfixsel (void) {
159 if (lastSelStr != NULL) {
160 XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, CurrentTime);
161 XSetSelectionOwner(xw.dpy, XA_CLIPBOARD, xw.win, CurrentTime);
162 } else {
163 if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) == xw.win) XSetSelectionOwner(xw.dpy, XA_PRIMARY, None, CurrentTime);
164 if (XGetSelectionOwner(xw.dpy, XA_CLIPBOARD) == xw.win) XSetSelectionOwner(xw.dpy, XA_CLIPBOARD, None, CurrentTime);
166 XFlush(xw.dpy);
170 static void k8t_selXSet (K8Term *term, char *str) {
171 /* register the selection for both the clipboard and the primary */
172 if (term == NULL) return;
173 if (term->sel.clip != NULL) free(term->sel.clip);
174 term->sel.clip = str;
175 if (lastSelStr != NULL) free(lastSelStr);
176 lastSelStr = (str != NULL ? strdup(str) : NULL);
177 xfixsel();
178 //fprintf(stderr, "[%s]\n", str);
182 static void k8t_selClear (K8Term *term) {
183 if (lastSelStr != NULL) free(lastSelStr);
184 lastSelStr = NULL;
185 if (term != NULL) {
186 if (term->sel.clip != NULL) free(term->sel.clip);
187 term->sel.clip = NULL;
188 term->sel.mode = 0;
189 if (term->sel.bx != 0) {
190 term->sel.bx = -1;
191 k8t_tmFullDirty(term);
192 k8t_Draw(term, 1);
198 static void xevtcbselclear (XEvent *e) {
199 k8t_selClear(curterm);
203 static K8TLine k8t_selGet (K8Term *term, int y) {
204 K8TLine l;
206 if (y >= term->row) return NULL;
207 if (y < 0) {
208 if (y < -(term->maxhistory)) return NULL;
209 l = term->line[term->row+(-y)-1];
210 } else {
211 l = term->line[y];
213 return l;
217 static void k8t_selCopy (K8Term *term) {
218 char *str, *ptr;
219 int x, y, bufsize, is_selected = 0;
221 if (term == NULL || term->sel.bx == -1) {
222 str = NULL;
223 } else {
224 int sy = K8T_MIN(term->sel.e.y, term->sel.b.y);
225 int ey = K8T_MAX(term->sel.e.y, term->sel.b.y);
228 fprintf(stderr, "bx=%d; ex=%d; by=%d; ey=%d\n", term->sel.bx, term->sel.by, term->sel.ex, term->sel.ey);
229 fprintf(stderr, " b.x=%d; e.x=%d; b.y=%d; e.y=%d\n", term->sel.b.x, term->sel.b.y, term->sel.e.x, term->sel.e.y);
230 fprintf(stderr, " sy=%d; ey=%d\n", sy, ey);
232 if (ey >= term->row) { k8t_selClear(term); return; }
233 bufsize = (term->col+1)*(ey-sy+1)*UTF_SIZ;
234 ptr = str = malloc(bufsize);
235 /* append every set and selected glyph to the selection */
236 for (y = sy; y <= ey; ++y) {
237 K8TLine l = k8t_selGet(term, y);
238 char *pstart = ptr;
240 if (l == NULL) continue;
241 for (x = 0; x < term->col; ++x) {
242 if ((is_selected = k8t_isSelected(term, x, y)) != 0) {
243 int size = k8t_UTF8Size(l[x].c);
245 //if (size == 1) fprintf(stderr, "x=%d; y=%d; size=%d; c=%d\n", x, y, size, l[x].c[0]);
246 if (size == 1) {
247 unsigned char c = (unsigned char)l[x].c[0];
249 *ptr = (c < 32 || c >= 127) ? ' ' : c;
250 } else if (size > 0) {
251 memcpy(ptr, l[x].c, size);
253 ptr += size;
256 //fprintf(stderr, "y=%d; linebytes=%d\n", y, (int)(ptr-pstart));
257 // trim trailing spaces
258 while (ptr > pstart && ptr[-1] == ' ') --ptr;
259 // \n at the end of every unwrapped selected line except for the last one
260 if (is_selected && y < ey && k8t_isSelected(term, term->col-1, y) && !(l[term->col-1].state&K8T_GLYPH_WRAP)) *ptr++ = '\n';
262 *ptr = 0;
264 k8t_selXSet(term, str);
265 if (!str || !str[0]) k8t_selClear(term);
269 static void xevtcbselnotify (XEvent *e) {
270 unsigned long nitems, ofs, rem;
271 int format;
272 uint8_t *data;
273 Atom type;
274 XSelectionEvent *se = (XSelectionEvent *)e;
275 int isutf8;
276 int wasbrk = 0;
277 char *ucbuf = NULL;
278 int ucbufsize = 0;
280 if (curterm == NULL || curterm->cmdline.cmdMode == K8T_CMDMODE_MESSAGE) return;
281 #ifdef PASTE_SELECTION_DEBUG
283 char *name;
285 fprintf(stderr, "selnotify!\n");
287 name = se->selection != None ? XGetAtomName(se->display, se->selection) : NULL;
288 fprintf(stderr, " selection: [%s]\n", name);
289 if (name != NULL) XFree(name);
291 name = se->target != None ? XGetAtomName(se->display, se->target) : NULL;
292 fprintf(stderr, " target: [%s]\n", name);
293 if (name != NULL) XFree(name);
295 name = se->property != None ? XGetAtomName(se->display, se->property) : NULL;
296 fprintf(stderr, " property: [%s]\n", name);
297 if (name != NULL) XFree(name);
299 #endif
301 if (se->property != XA_VT_SELECTION) return;
303 #ifdef PASTE_SELECTION_DEBUG
305 fprintf(stderr, "selection:\n");
306 fprintf(stderr, " primary: %d\n", se->selection == XA_PRIMARY);
307 fprintf(stderr, " secondary: %d\n", se->selection == XA_SECONDARY);
308 fprintf(stderr, " clipboard: %d\n", se->selection == XA_CLIPBOARD);
309 fprintf(stderr, " vtsel: %d\n", se->selection == XA_VT_SELECTION);
310 fprintf(stderr, "target:\n");
311 fprintf(stderr, " primary: %d\n", se->target == XA_PRIMARY);
312 fprintf(stderr, " secondary: %d\n", se->target == XA_SECONDARY);
313 fprintf(stderr, " clipboard: %d\n", se->target == XA_CLIPBOARD);
314 fprintf(stderr, " vtsel: %d\n", se->target == XA_VT_SELECTION);
316 #endif
317 if (se->target == XA_UTF8) {
318 isutf8 = 1;
319 } else if (se->target == XA_STRING) {
320 isutf8 = 0;
321 } else if (se->target == XA_TARGETS) {
322 Atom rqtype = None, *targ;
324 if (XGetWindowProperty(xw.dpy, xw.win, se->property, 0, 65536, False, XA_ATOM, &type, &format, &nitems, &rem, &data)) {
325 //fprintf(stderr, "no targets\n");
326 rqtype = XA_STRING;
327 } else {
328 for (targ = (Atom *)data; nitems > 0; --nitems, ++targ) {
329 #ifdef PASTE_SELECTION_DEBUG
330 fprintf(stderr, " TGT: [%s]\n", XGetAtomName(se->display, *targ));
331 #endif
332 if (*targ == XA_UTF8) rqtype = XA_UTF8;
333 else if (*targ == XA_STRING && rqtype == None) rqtype = XA_STRING;
335 XFree(data);
337 if (rqtype != None) XConvertSelection(xw.dpy, se->selection, rqtype, XA_VT_SELECTION, xw.win, CurrentTime);
338 return;
339 } else {
340 return;
343 ofs = 0;
344 do {
345 int blen;
346 char *str;
348 if (XGetWindowProperty(xw.dpy, xw.win, se->property, ofs, BUFSIZ/4, False, AnyPropertyType, &type, &format, &nitems, &rem, &data)) {
349 fprintf(stderr, "Clipboard allocation failed\n");
350 break;
352 //fprintf(stderr, "nitems=%d; format=%d; rem=%d\n", (int)nitems, format, (int)rem);
353 blen = nitems*format/8;
355 if (!isutf8) {
356 int newsz = blen*4+64;
358 if (ucbufsize < newsz) {
359 char *n = realloc(ucbuf, newsz);
361 if (n == NULL) { XFree(data); break; }
362 ucbuf = n;
363 ucbufsize = newsz;
366 blen = loc2utf(ucbuf, (const char *)data, blen);
367 str = ucbuf;
368 } else {
369 str = (char *)data;
372 if (curterm->cmdline.cmdMode != K8T_CMDMODE_NONE) {
373 tcmdput(curterm, &curterm->cmdline, str, blen);
374 } else {
375 if (nitems*format/8 > 0 && !wasbrk && K8T_ISSET(curterm, K8T_MODE_BRACPASTE)) {
376 wasbrk = 1;
377 k8t_ttyWriteStrNoEnc(curterm, "\x1b[200~");
379 k8t_ttyWrite(curterm, str, blen);
381 XFree(data);
382 /* number of 32-bit chunks returned */
383 ofs += nitems*format/32;
384 } while (rem > 0);
386 if (wasbrk) k8t_ttyWriteStrNoEnc(curterm, "\x1b[201~");
387 if (ucbuf != NULL) free(ucbuf);
391 static void selpaste (K8Term *term, Atom which) {
392 if (term == NULL) return;
393 if (XGetSelectionOwner(xw.dpy, which) == None) return;
394 //XConvertSelection(xw.dpy, which, term->sel.xtarget, XA_VT_SELECTION, xw.win, CurrentTime);
395 XConvertSelection(xw.dpy, which, XA_TARGETS, XA_VT_SELECTION, xw.win, CurrentTime);
397 if (which == XA_PRIMARY) selpaste(XA_SECONDARY);
398 else if (which == XA_SECONDARY) selpaste(XA_CLIPBOARD);
403 static void xevtcbselrequest (XEvent *e) {
404 XSelectionRequestEvent *xsre;
405 XSelectionEvent xev;
407 if (lastSelStr == NULL) return;
408 xsre = (XSelectionRequestEvent *)e;
409 xev.type = SelectionNotify;
410 xev.requestor = xsre->requestor;
411 xev.selection = xsre->selection;
412 xev.target = xsre->target;
413 xev.time = xsre->time;
414 /* reject */
415 xev.property = None;
416 if (xsre->target == XA_TARGETS) {
417 /* respond with the supported type */
418 Atom tlist[3] = {XA_UTF8, XA_STRING, XA_TARGETS};
420 XChangeProperty(xsre->display, xsre->requestor, xsre->property, XA_ATOM, 32, PropModeReplace, (uint8_t *)tlist, 3);
421 xev.property = xsre->property;
422 } else if (xsre->target == XA_UTF8 && lastSelStr != NULL) {
423 XChangeProperty(xsre->display, xsre->requestor, xsre->property, XA_UTF8, 8, PropModeReplace, (uint8_t *)lastSelStr, strlen(lastSelStr));
424 xev.property = xsre->property;
425 } else if (xsre->target == XA_STRING && lastSelStr != NULL) {
426 char *s = malloc(strlen(lastSelStr)*4+8);
428 if (s != NULL) {
429 int len = utf2loc(s, lastSelStr, strlen(lastSelStr));
431 XChangeProperty(xsre->display, xsre->requestor, xsre->property, XA_STRING, 8, PropModeReplace, (uint8_t *)s, len);
432 xev.property = xsre->property;
433 free(s);
436 /* all done, send a notification to the listener */
437 if (!XSendEvent(xsre->display, xsre->requestor, True, 0, (XEvent *)&xev)) fprintf(stderr, "Error sending SelectionNotify event\n");