1 ////////////////////////////////////////////////////////////////////////////////
3 static void k8t_tmWantRedraw (K8Term
*term
, int forceFast
) {
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
) {
22 term
->sel
.tclick1
= term
->sel
.tclick2
= mclock_ticks();
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) {
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
);
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 };
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);
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 */
89 //sprintf(buf, "\x1b[M%c%c%c", 0, 32+x+1, 32+y+1);
90 p
= buf
+sprintf(buf
, "\x1b[M");
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;
97 } else if (e
->xbutton
.type
== ButtonRelease
|| button
== AnyButton
) {
98 if (term
->mousemode
!= 1006) {
99 button
= 3; // 'release' flag
103 if (button
>= 3) button
+= 64-3;
107 if (button
>= 3) button
+= 64-3;
108 if (e
->xbutton
.type
== ButtonPress
) {
109 term
->mouseob
= button
;
114 ss
= (state
&ShiftMask
? 4 : 0)+(state
&Mod4Mask
? 8 : 0)+(state
&ControlMask
? 16 : 0);
115 switch (term
->mousemode
) {
118 p
+= sprintf(p
, "%d;", button
+ss
);
120 case 1015: /* urxvt */
122 p
+= sprintf(p
, "%d;", 32+button
+ss
);
129 switch (term
->mousemode
) {
130 case 1005: /* utf-8 */
131 p
+= k8t_UTF8Encode(p
, x
+1);
132 p
+= k8t_UTF8Encode(p
, y
+1);
135 p
+= sprintf(p
, "%d;%d%c", x
+1, y
+1, lastCh
);
137 case 1015: /* urxvt */
138 p
+= sprintf(p
, "%d;%dM", x
+1, y
+1);
141 p
+= sprintf(p
, "%c%c", 32+x
+1, 32+y
+1);
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
);
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
);
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
);
178 //fprintf(stderr, "[%s]\n", str);
182 static void k8t_selClear (K8Term
*term
) {
183 if (lastSelStr
!= NULL
) free(lastSelStr
);
186 if (term
->sel
.clip
!= NULL
) free(term
->sel
.clip
);
187 term
->sel
.clip
= NULL
;
189 if (term
->sel
.bx
!= 0) {
191 k8t_tmFullDirty(term
);
198 static void xevtcbselclear (XEvent
*e
) {
199 k8t_selClear(curterm
);
203 static K8TLine
k8t_selGet (K8Term
*term
, int y
) {
206 if (y
>= term
->row
) return NULL
;
208 if (y
< -(term
->maxhistory
)) return NULL
;
209 l
= term
->line
[term
->row
+(-y
)-1];
217 static void k8t_selCopy (K8Term
*term
) {
219 int x
, y
, bufsize
, is_selected
= 0;
221 if (term
== NULL
|| term
->sel
.bx
== -1) {
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
);
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]);
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
);
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';
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
;
274 XSelectionEvent
*se
= (XSelectionEvent
*)e
;
280 if (curterm
== NULL
|| curterm
->cmdline
.cmdMode
== K8T_CMDMODE_MESSAGE
) return;
281 #ifdef PASTE_SELECTION_DEBUG
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
);
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
);
317 if (se
->target
== XA_UTF8
) {
319 } else if (se
->target
== XA_STRING
) {
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");
328 for (targ
= (Atom
*)data
; nitems
> 0; --nitems
, ++targ
) {
329 #ifdef PASTE_SELECTION_DEBUG
330 fprintf(stderr
, " TGT: [%s]\n", XGetAtomName(se
->display
, *targ
));
332 if (*targ
== XA_UTF8
) rqtype
= XA_UTF8
;
333 else if (*targ
== XA_STRING
&& rqtype
== None
) rqtype
= XA_STRING
;
337 if (rqtype
!= None
) XConvertSelection(xw
.dpy
, se
->selection
, rqtype
, XA_VT_SELECTION
, xw
.win
, CurrentTime
);
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");
352 //fprintf(stderr, "nitems=%d; format=%d; rem=%d\n", (int)nitems, format, (int)rem);
353 blen
= nitems
*format
/8;
356 int newsz
= blen
*4+64;
358 if (ucbufsize
< newsz
) {
359 char *n
= realloc(ucbuf
, newsz
);
361 if (n
== NULL
) { XFree(data
); break; }
366 blen
= loc2utf(ucbuf
, (const char *)data
, blen
);
372 if (curterm
->cmdline
.cmdMode
!= K8T_CMDMODE_NONE
) {
373 tcmdput(curterm
, &curterm
->cmdline
, str
, blen
);
375 if (nitems
*format
/8 > 0 && !wasbrk
&& K8T_ISSET(curterm
, K8T_MODE_BRACPASTE
)) {
377 k8t_ttyWriteStrNoEnc(curterm
, "\x1b[200~");
379 k8t_ttyWrite(curterm
, str
, blen
);
382 /* number of 32-bit chunks returned */
383 ofs
+= nitems
*format
/32;
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
;
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
;
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);
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
;
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");