updated README
[k8sterm.git] / src / termswitch.c
blobe0e68af3f048f47678eb6099df62d43876c504a3
1 #define k8stx_output(buf_,buflen_) do { \
2 if (!xw.paste_term || xw.paste_term->dead) { \
3 xw.paste_term = NULL; \
4 break; \
5 } \
6 if ((buflen_) <= 0) break; \
7 if (K8T_DATA(xw.paste_term)->cmdline.cmdMode != K8T_CMDMODE_NONE) { \
8 if (K8T_DATA(xw.paste_term)->cmdline.cmdMode == K8T_CMDMODE_INPUT) { \
9 tcmdput(xw.paste_term, &K8T_DATA(xw.paste_term)->cmdline, (buf_), (buflen_)); \
10 } \
11 } else { \
12 if (!xw.past_wasbrcp && K8T_ISSET(xw.paste_term, K8T_MODE_BRACPASTE)) { \
13 xw.past_wasbrcp = 1; \
14 k8t_ttyWriteStrNoEnc(xw.paste_term, "\x1b[200~"); \
15 } \
16 k8t_ttyWrite(xw.paste_term, (buf_), (buflen_)); \
17 } \
18 } while (0)
21 #define k8stx_finish_paste() do { \
22 if (xw.paste_term && xw.past_wasbrcp && !xw.paste_term->dead) { \
23 k8t_ttyWriteStrNoEnc(xw.paste_term, "\x1b[201~"); \
24 } \
25 xw.paste_term = NULL; \
26 } while (0)
29 ////////////////////////////////////////////////////////////////////////////////
30 static const char *get_process_name (int pfd) {
31 static char path[256], res[4097], *c;
32 int fd;
33 pid_t pgrp;
34 if ((pgrp = tcgetpgrp(pfd)) == -1) return NULL;
35 snprintf(path, sizeof(path), "/proc/%d/cmdline", pgrp);
36 if ((fd = open(path, O_RDONLY|O_CLOEXEC)) < 0) return NULL;
37 res[sizeof(res)-1] = 0;
38 read(fd, res, sizeof(res)-1);
39 close(fd);
40 if ((c = strrchr(res, '/')) == NULL) c = res; else ++c;
41 return c;
45 static const char *get_process_cwd (int pfd) {
46 static char path[256], res[4097];
47 pid_t pgrp;
48 ssize_t n;
49 if ((pgrp = tcgetpgrp(pfd)) == -1) return NULL;
50 snprintf(path, sizeof(path), "/proc/%d/cwd", pgrp);
51 n = readlink(path, res, sizeof(res)-1);
52 if (n > 0) {
53 res[n] = '\0';
54 return res;
56 return NULL;
60 static void fixWindowTitle (const K8Term *t) {
61 if (t != NULL) {
62 const char *title = t->title;
64 if (title == NULL || !title[0]) {
65 title = opt_title;
66 if (title == NULL) title = "";
68 XStoreName(xw.dpy, xw.win, title);
69 XChangeProperty(xw.dpy, xw.win, XA_NETWM_NAME, XA_UTF8, 8, PropModeReplace, (const unsigned char *)title, strlen(title));
74 static void checkAndFixWindowTitle (K8Term *t, int forceupd) {
75 if (t != NULL && t->cmdfd >= 0) {
76 const char *pname, *ppath;
77 int doupd = 0;
78 if ((pname = get_process_name(t->cmdfd)) && strcmp(K8T_DATA(t)->lastpname, pname) != 0) {
79 // new process
80 int is_mc;
81 doupd = 1;
82 //fprintf(stderr, "new process: [%s]\n", pname);
83 K8T_DATA(t)->titleset = 0;
84 if (K8T_DATA(t)->lastpname) free(K8T_DATA(t)->lastpname);
85 K8T_DATA(t)->lastpname = strdup(pname);
86 is_mc = (strcmp(pname, "mc") == 0 || strcmp(pname, "mcedit") == 0 || strcmp(pname, "mcview") == 0);
87 if (K8T_DATA(t)->mc_hack_active && !is_mc) {
88 K8T_DATA(t)->mc_hack_active = 0;
89 t->clearOnPartialScrollMode = K8T_DATA(t)->old_scroll_mode;
90 } else if (!K8T_DATA(t)->mc_hack_active && is_mc) {
91 K8T_DATA(t)->mc_hack_active = 1;
92 t->clearOnPartialScrollMode = 2;
95 if ((ppath = get_process_cwd(t->cmdfd)) != NULL && strcmp(K8T_DATA(t)->lastppath, ppath) != 0) {
96 // new path
97 if (K8T_DATA(t)->lastppath != NULL) free(K8T_DATA(t)->lastppath);
98 K8T_DATA(t)->lastppath = strdup(ppath);
99 doupd = 1;
101 if (doupd && !K8T_DATA(t)->titleset) {
102 snprintf(t->title, sizeof(t->title), "%s [%s]", K8T_DATA(t)->lastpname, K8T_DATA(t)->lastppath);
103 } else {
104 doupd = 0;
106 if (doupd || forceupd) {
107 if (t == curterm) fixWindowTitle(t);
108 updateTabBar = 1;
114 // find latest active terminal (but not current %-)
115 static int findTermToSwitch (void) {
116 int idx = -1;
117 K8TTimeMSec maxlat = 0;
118 for (int f = 0; f < term_count; ++f) {
119 if (curterm != term_array[f] && term_array[f]->lastActiveTime > maxlat) {
120 maxlat = term_array[f]->lastActiveTime;
121 idx = f;
124 if (idx < 0) {
125 if (termidx == 0) idx = 0; else idx = termidx+1;
126 if (idx > term_count) idx = term_count-1;
128 return idx;
132 static void fixFirstTab (void) {
133 if (termidx < firstVisibleTab) firstVisibleTab = termidx;
134 else if (termidx > firstVisibleTab+opt_tabcount-1) firstVisibleTab = termidx-opt_tabcount+1;
135 if (firstVisibleTab < 0) firstVisibleTab = 0;
136 updateTabBar = 1;
140 static void switchToTerm (int idx, int redraw) {
141 if (idx >= 0 && idx < term_count && term_array[idx] != NULL && term_array[idx] != curterm) {
142 K8TTimeMSec tt = mclock_ticks();
144 if (curterm != NULL) curterm->lastActiveTime = tt;
145 termidx = idx;
146 curterm = term_array[termidx];
147 curterm->curbhidden = 0;
148 curterm->lastBlinkTime = tt;
149 if (xw.paste_term && xw.paste_term != curterm) k8stx_finish_paste();
151 fixFirstTab();
153 xseturgency(0);
154 k8t_tmFullDirty(curterm);
155 fixWindowTitle(curterm);
156 updateTabBar = 1;
157 XSetWindowBackground(xw.dpy, xw.win, getColor(curterm->defbg));
158 xclearunused();
159 if (redraw) {
160 k8t_tmFullDirty(curterm);
161 k8t_drawTerm(curterm, 1);
162 if (!updateTabBar) updateTabBar = 1;
163 xdrawTabBar();
165 //FIXME: optimize memory allocations
166 if (curterm->sel.clip != NULL && curterm->sel.bx >= 0) {
167 if (lastSelStr != NULL) {
168 if (strlen(lastSelStr) >= strlen(curterm->sel.clip)) {
169 strcpy(lastSelStr, curterm->sel.clip);
170 } else {
171 free(lastSelStr);
172 lastSelStr = NULL;
175 if (lastSelStr == NULL) lastSelStr = strdup(curterm->sel.clip);
176 //fprintf(stderr, "newsel: [%s]\n", lastSelStr);
178 xfixsel();
179 //fprintf(stderr, "curterm #%d\n", termidx);
180 //fprintf(stderr, "needConv: %d\n", curterm->needConv);
185 static K8Term *k8t_termalloc (void) {
186 K8Term *t;
188 if (term_count >= term_array_size) {
189 int newsz = ((term_count==0) ? 1 : term_array_size+64);
190 K8Term **n = realloc(term_array, sizeof(K8Term *)*newsz);
192 if (n == NULL && term_count == 0) k8t_die("out of memory!");
193 term_array = n;
194 term_array_size = newsz;
196 if ((t = calloc(1, sizeof(K8Term))) == NULL) return NULL;
198 t->wrbufsize = K8T_WBUFSIZ;
199 t->deffg = defaultFG;
200 t->defbg = defaultBG;
201 t->dead = 1;
202 t->needConv = (needConversion ? 1 : 0);
203 t->belltype = (opt_audiblebell?K8T_BELL_AUDIO:0)|(opt_urgentbell?K8T_BELL_URGENT:0);
204 t->curblink = opt_cursorBlink;
205 t->curblinkinactive = opt_cursorBlinkInactive;
206 t->fastredraw = opt_fastredraw;
207 t->clearOnPartialScrollMode = opt_scrollclear;
209 termSetCallbacks(t);
210 termSetDefaults(t);
212 term_array[term_count++] = t;
214 return t;
218 // newer delete last terminal!
219 static void termfree (int idx) {
220 if (idx >= 0 && idx < term_count && term_array[idx] != NULL) {
221 K8Term *t = term_array[idx];
222 K8TermData *td = K8T_DATA(t);
223 // abort pasting, if there was any
224 if (xw.paste_term == t) xw.paste_term = NULL;
226 if (K8T_DATA(t)->pid != 0) {
227 kill(K8T_DATA(t)->pid, SIGKILL);
228 return;
231 if (t->cmdfd >= 0) {
232 close(t->cmdfd);
233 t->cmdfd = -1;
236 exitcode = K8T_DATA(t)->exitcode;
238 if (K8T_DATA(t)->waitkeypress) return;
240 if (idx == termidx) {
241 if (term_count > 1) {
242 t->dead = 1;
243 //switchToTerm((idx > 0) ? idx-1 : 1, 0);
244 switchToTerm(findTermToSwitch(), 0);
245 if (curterm == t) {
246 fprintf(stderr, "FATAL: cannot find good terminal to switch onto.\n");
247 curterm = NULL;
249 } else {
250 curterm = NULL;
254 for (int y = 0; y < t->row; ++y) free(t->alt[y]);
255 for (int y = 0; y < t->linecount; ++y) free(t->line[y]);
256 free(t->dirty);
257 free(t->alt);
258 free(t->line);
260 if (td->lastpname != NULL) free(td->lastpname);
261 if (td->lastppath != NULL) free(td->lastppath);
262 if (td->execcmd != NULL) free(td->execcmd);
263 // condense array
264 if (termidx > idx) --termidx; // it's not current, and current is at the right
265 for (int f = idx+1; f < term_count; ++f) term_array[f-1] = term_array[f];
266 --term_count;
268 //if (td->picalloced) XFreePixmap(xw.dpy, td->picbuf);
269 free(td);
270 free(t);
275 static void termcleanup (void) {
276 int f = 0, needredraw = 0;
278 while (f < term_count) {
279 if (term_array[f]->dead) {
280 //fprintf(stderr, "found dead term #%d of %d\n", f, term_count);
281 termfree(f);
282 //fprintf(stderr, "terms left: %d (curterm=%p)\n", term_count, curterm);
283 needredraw = 1;
284 } else {
285 ++f;
289 if (xw.paste_term) {
290 K8TTimeMSec stt = mclock_ticks();
291 if (stt < xw.paste_last_activity || stt-xw.paste_last_activity >= 13000) {
292 //fprintf(stderr, "SELECTION PASTE ABORTED DUE TO TIMEOUT.\n");
293 k8stx_finish_paste();
297 if (needredraw) {
298 updateTabBar = 1;
299 k8t_drawTerm(curterm, 1);