Commit | Line | Data |
---|---|---|
b801d8b2 JF |
1 | /** |
2 | * TIG(1) | |
3 | * ====== | |
4 | * | |
5 | * NAME | |
6 | * ---- | |
7 | * tig - text-mode interface for git | |
8 | * | |
9 | * SYNOPSIS | |
10 | * -------- | |
11 | * [verse] | |
12 | * tig | |
13 | * tig log [git log options] | |
14 | * tig diff [git diff options] | |
15 | * tig < [git log or git diff output] | |
16 | * | |
17 | * DESCRIPTION | |
18 | * ----------- | |
19 | * Browse changes in a git repository. | |
20 | * | |
21 | * OPTIONS | |
22 | * ------- | |
23 | * | |
24 | * None. | |
25 | * | |
26 | **/ | |
27 | ||
28 | #define DEBUG | |
29 | ||
30 | #ifndef DEBUG | |
31 | #define NDEBUG | |
32 | #endif | |
33 | ||
34 | #include <stdarg.h> | |
35 | #include <stdlib.h> | |
36 | #include <stdio.h> | |
37 | #include <string.h> | |
38 | #include <signal.h> | |
39 | #include <assert.h> | |
40 | ||
41 | #include <curses.h> | |
42 | #include <form.h> | |
43 | ||
44 | static void die(const char *err, ...); | |
45 | static void report(const char *msg, ...); | |
46 | ||
47 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) | |
48 | ||
b801d8b2 | 49 | #define KEY_TAB 9 |
fd85fef1 JF |
50 | #define KEY_ESC 27 |
51 | #define KEY_DEL 127 | |
b801d8b2 JF |
52 | |
53 | #define REQ_OFFSET (MAX_COMMAND + 1) | |
54 | ||
55 | /* Requests for switching between the different views. */ | |
56 | #define REQ_DIFF (REQ_OFFSET + 0) | |
57 | #define REQ_LOG (REQ_OFFSET + 1) | |
58 | #define REQ_MAIN (REQ_OFFSET + 2) | |
59 | ||
60 | #define REQ_QUIT (REQ_OFFSET + 11) | |
61 | #define REQ_VERSION (REQ_OFFSET + 12) | |
62 | #define REQ_STOP (REQ_OFFSET + 13) | |
63 | #define REQ_UPDATE (REQ_OFFSET + 14) | |
64 | #define REQ_REDRAW (REQ_OFFSET + 15) | |
fd85fef1 JF |
65 | #define REQ_FIRST_LINE (REQ_OFFSET + 16) |
66 | #define REQ_LAST_LINE (REQ_OFFSET + 17) | |
b801d8b2 | 67 | |
fd85fef1 | 68 | #define COLOR_CURSOR 42 |
b801d8b2 JF |
69 | |
70 | /** | |
71 | * KEYS | |
72 | * ---- | |
73 | * | |
74 | * d:: | |
75 | * diff | |
76 | * l:: | |
77 | * log | |
78 | * q:: | |
79 | * quit | |
80 | * r:: | |
81 | * redraw screen | |
82 | * s:: | |
83 | * stop all background loading | |
84 | * j:: | |
85 | * down | |
86 | * k:: | |
87 | * up | |
88 | * h, ?:: | |
89 | * help | |
90 | * v:: | |
91 | * version | |
92 | * | |
93 | **/ | |
94 | ||
95 | #define HELP "(d)iff, (l)og, (m)ain, (q)uit, (v)ersion, (h)elp" | |
96 | ||
97 | struct keymap { | |
98 | int alias; | |
99 | int request; | |
100 | }; | |
101 | ||
102 | struct keymap keymap[] = { | |
fd85fef1 | 103 | /* Cursor navigation */ |
b801d8b2 JF |
104 | { KEY_UP, REQ_PREV_LINE }, |
105 | { 'k', REQ_PREV_LINE }, | |
106 | { KEY_DOWN, REQ_NEXT_LINE }, | |
107 | { 'j', REQ_NEXT_LINE }, | |
fd85fef1 JF |
108 | { KEY_HOME, REQ_FIRST_LINE }, |
109 | { KEY_END, REQ_LAST_LINE }, | |
110 | ||
111 | /* Scrolling */ | |
112 | { KEY_IC, REQ_SCR_BLINE }, /* scroll field backward a line */ | |
113 | { KEY_DC, REQ_SCR_FLINE }, /* scroll field forward a line */ | |
114 | { KEY_NPAGE, REQ_SCR_FPAGE }, /* scroll field forward a page */ | |
115 | { KEY_PPAGE, REQ_SCR_BPAGE }, /* scroll field backward a page */ | |
116 | { 'w', REQ_SCR_FHPAGE }, /* scroll field forward half page */ | |
117 | { 's', REQ_SCR_BHPAGE }, /* scroll field backward half page */ | |
b801d8b2 JF |
118 | |
119 | { 'd', REQ_DIFF }, | |
120 | { 'l', REQ_LOG }, | |
121 | { 'm', REQ_MAIN }, | |
122 | ||
123 | /* No input from wgetch() with nodelay() enabled. */ | |
124 | { ERR, REQ_UPDATE }, | |
125 | ||
126 | { KEY_ESC, REQ_QUIT }, | |
127 | { 'q', REQ_QUIT }, | |
128 | { 's', REQ_STOP }, | |
129 | { 'v', REQ_VERSION }, | |
130 | { 'r', REQ_REDRAW }, | |
131 | }; | |
132 | ||
133 | static int | |
134 | get_request(int request) | |
135 | { | |
136 | int i; | |
137 | ||
138 | for (i = 0; i < ARRAY_SIZE(keymap); i++) | |
139 | if (keymap[i].alias == request) | |
140 | return keymap[i].request; | |
141 | ||
142 | return request; | |
143 | } | |
144 | ||
145 | ||
146 | /* | |
147 | * Viewer | |
148 | */ | |
149 | ||
150 | struct view { | |
151 | char *name; | |
152 | char *cmd; | |
fd85fef1 | 153 | char *id; |
b801d8b2 JF |
154 | |
155 | /* Rendering */ | |
fd85fef1 | 156 | int (*render)(struct view *, unsigned int); |
b801d8b2 | 157 | WINDOW *win; |
fd85fef1 | 158 | int height, width; |
b801d8b2 JF |
159 | |
160 | /* Navigation */ | |
161 | unsigned long offset; /* Offset of the window top */ | |
162 | unsigned long lineno; /* Current line number */ | |
163 | ||
164 | /* Buffering */ | |
165 | unsigned long lines; /* Total number of lines */ | |
166 | char **line; /* Line index */ | |
167 | ||
168 | /* Loading */ | |
169 | FILE *pipe; | |
170 | }; | |
171 | ||
fd85fef1 | 172 | static int default_renderer(struct view *view, unsigned int lineno); |
b801d8b2 | 173 | |
fd85fef1 | 174 | #define DIFF_CMD \ |
b801d8b2 JF |
175 | "git log --stat -n1 %s ; echo; " \ |
176 | "git diff --find-copies-harder -B -C %s^ %s" | |
177 | ||
178 | #define LOG_CMD \ | |
179 | "git log --stat -n100 %s" | |
180 | ||
fd85fef1 JF |
181 | #define MAIN_CMD \ |
182 | "git log --stat --pretty=raw %s" | |
183 | ||
b801d8b2 JF |
184 | /* The status window at the bottom. Used for polling keystrokes. */ |
185 | static WINDOW *status_win; | |
186 | ||
fd85fef1 JF |
187 | #define SIZEOF_ID 1024 |
188 | ||
189 | char head_id[SIZEOF_ID] = "HEAD"; | |
190 | char commit_id[SIZEOF_ID] = "HEAD"; | |
191 | ||
192 | static unsigned int current_view; | |
193 | static unsigned int nloading; | |
194 | ||
b801d8b2 | 195 | static struct view views[] = { |
fd85fef1 JF |
196 | { "diff", DIFF_CMD, commit_id, default_renderer }, |
197 | { "log", LOG_CMD, head_id, default_renderer }, | |
198 | { "main", MAIN_CMD, head_id, default_renderer }, | |
b801d8b2 JF |
199 | }; |
200 | ||
201 | static struct view *display[ARRAY_SIZE(views)]; | |
b801d8b2 JF |
202 | |
203 | #define foreach_view(view, i) \ | |
204 | for (i = 0; i < sizeof(display) && (view = display[i]); i++) | |
205 | ||
206 | static void | |
207 | redraw_view(struct view *view) | |
208 | { | |
209 | int lineno; | |
b801d8b2 JF |
210 | |
211 | wclear(view->win); | |
212 | wmove(view->win, 0, 0); | |
213 | ||
fd85fef1 JF |
214 | for (lineno = 0; lineno < view->height; lineno++) { |
215 | if (!view->render(view, lineno)) | |
216 | break; | |
b801d8b2 JF |
217 | } |
218 | ||
219 | redrawwin(view->win); | |
220 | wrefresh(view->win); | |
221 | } | |
222 | ||
223 | /* FIXME: Fix percentage. */ | |
224 | static void | |
225 | report_position(struct view *view, int all) | |
226 | { | |
227 | report(all ? "line %d of %d (%d%%) viewing from %d" | |
228 | : "line %d of %d", | |
229 | view->lineno + 1, | |
230 | view->lines, | |
231 | view->lines ? view->offset * 100 / view->lines : 0, | |
232 | view->offset); | |
233 | } | |
234 | ||
235 | static void | |
fd85fef1 | 236 | move_view(struct view *view, int lines) |
b801d8b2 | 237 | { |
fd85fef1 JF |
238 | /* The rendering expects the new offset. */ |
239 | view->offset += lines; | |
240 | ||
241 | assert(0 <= view->offset && view->offset < view->lines); | |
242 | assert(lines); | |
b801d8b2 | 243 | |
fd85fef1 JF |
244 | { |
245 | int from = lines > 0 ? view->height - lines : 0; | |
246 | int to = from + (lines > 0 ? lines : -lines); | |
247 | ||
248 | wscrl(view->win, lines); | |
249 | ||
250 | for (; from < to; from++) { | |
251 | if (!view->render(view, from)) | |
252 | break; | |
253 | } | |
254 | } | |
255 | ||
256 | /* Move current line into the view. */ | |
257 | if (view->lineno < view->offset) { | |
258 | view->lineno = view->offset; | |
259 | view->render(view, 0); | |
260 | ||
261 | } else if (view->lineno >= view->offset + view->height) { | |
262 | view->lineno = view->offset + view->height - 1; | |
263 | view->render(view, view->lineno - view->offset); | |
264 | } | |
265 | ||
266 | assert(view->offset <= view->lineno && view->lineno <= view->lines); | |
267 | ||
268 | redrawwin(view->win); | |
269 | wrefresh(view->win); | |
270 | ||
271 | report_position(view, lines); | |
272 | } | |
273 | static void | |
274 | scroll_view(struct view *view, int request) | |
275 | { | |
276 | int lines = 1; | |
b801d8b2 JF |
277 | |
278 | switch (request) { | |
fd85fef1 JF |
279 | case REQ_SCR_FPAGE: |
280 | lines = view->height; | |
281 | case REQ_SCR_FLINE: | |
b801d8b2 | 282 | if (view->offset + lines > view->lines) |
bde3653a | 283 | lines = view->lines - view->offset; |
b801d8b2 | 284 | |
fd85fef1 | 285 | if (lines == 0 || view->offset + view->height >= view->lines) { |
b801d8b2 JF |
286 | report("already at last line"); |
287 | return; | |
288 | } | |
289 | break; | |
290 | ||
fd85fef1 JF |
291 | case REQ_SCR_BPAGE: |
292 | lines = view->height; | |
293 | case REQ_SCR_BLINE: | |
b801d8b2 JF |
294 | if (lines > view->offset) |
295 | lines = view->offset; | |
296 | ||
297 | if (lines == 0) { | |
298 | report("already at first line"); | |
299 | return; | |
300 | } | |
301 | ||
fd85fef1 | 302 | lines = -lines; |
b801d8b2 | 303 | break; |
b801d8b2 JF |
304 | } |
305 | ||
fd85fef1 JF |
306 | move_view(view, lines); |
307 | } | |
b801d8b2 | 308 | |
b801d8b2 | 309 | |
fd85fef1 JF |
310 | static void |
311 | navigate_view(struct view *view, int request) | |
312 | { | |
313 | int steps; | |
b801d8b2 | 314 | |
fd85fef1 JF |
315 | switch (request) { |
316 | case REQ_PREV_LINE: | |
317 | if (view->lineno == 0) { | |
318 | report("already at first line"); | |
319 | return; | |
320 | } | |
321 | steps = -1; | |
322 | break; | |
b801d8b2 | 323 | |
fd85fef1 JF |
324 | case REQ_NEXT_LINE: |
325 | if (view->lineno + 1 >= view->lines) { | |
326 | report("already at last line"); | |
327 | return; | |
328 | } | |
329 | steps = 1; | |
330 | break; | |
b801d8b2 | 331 | |
fd85fef1 JF |
332 | case REQ_FIRST_LINE: |
333 | steps = -view->lineno; | |
334 | break; | |
b801d8b2 | 335 | |
fd85fef1 JF |
336 | case REQ_LAST_LINE: |
337 | steps = view->lines - view->lineno - 1; | |
338 | } | |
339 | ||
340 | view->lineno += steps; | |
341 | view->render(view, view->lineno - steps - view->offset); | |
342 | ||
343 | if (view->lineno < view->offset || | |
344 | view->lineno >= view->offset + view->height) { | |
345 | if (steps < 0 && -steps > view->offset) { | |
346 | steps = -view->offset; | |
b801d8b2 | 347 | } |
fd85fef1 JF |
348 | move_view(view, steps); |
349 | return; | |
b801d8b2 JF |
350 | } |
351 | ||
fd85fef1 JF |
352 | view->render(view, view->lineno - view->offset); |
353 | ||
b801d8b2 JF |
354 | redrawwin(view->win); |
355 | wrefresh(view->win); | |
356 | ||
fd85fef1 | 357 | report_position(view, view->height); |
b801d8b2 JF |
358 | } |
359 | ||
360 | static void | |
361 | resize_view(struct view *view) | |
362 | { | |
363 | int lines, cols; | |
364 | ||
365 | getmaxyx(stdscr, lines, cols); | |
366 | ||
367 | if (view->win) { | |
368 | mvwin(view->win, 0, 0); | |
369 | wresize(view->win, lines - 1, cols); | |
370 | ||
371 | } else { | |
372 | view->win = newwin(lines - 1, 0, 0, 0); | |
373 | if (!view->win) { | |
fd85fef1 | 374 | report("failed to create %s view", view->name); |
b801d8b2 JF |
375 | return; |
376 | } | |
377 | scrollok(view->win, TRUE); | |
378 | } | |
fd85fef1 JF |
379 | |
380 | getmaxyx(view->win, view->height, view->width); | |
b801d8b2 JF |
381 | } |
382 | ||
383 | ||
384 | static bool | |
385 | begin_update(struct view *view) | |
386 | { | |
387 | char buf[1024]; | |
388 | ||
389 | if (view->cmd) { | |
fd85fef1 JF |
390 | char *id = view->id; |
391 | ||
392 | if (snprintf(buf, sizeof(buf), view->cmd, id, id, id) < sizeof(buf)) | |
b801d8b2 JF |
393 | view->pipe = popen(buf, "r"); |
394 | ||
395 | if (!view->pipe) | |
396 | return FALSE; | |
397 | ||
398 | if (nloading++ == 0) | |
399 | nodelay(status_win, TRUE); | |
400 | } | |
401 | ||
402 | display[current_view] = view; | |
403 | ||
404 | view->offset = 0; | |
405 | view->lines = 0; | |
406 | view->lineno = 0; | |
407 | ||
408 | return TRUE; | |
409 | } | |
410 | ||
411 | static void | |
412 | end_update(struct view *view) | |
413 | { | |
414 | wattrset(view->win, A_NORMAL); | |
415 | pclose(view->pipe); | |
416 | view->pipe = NULL; | |
417 | ||
418 | if (nloading-- == 1) | |
419 | nodelay(status_win, FALSE); | |
420 | } | |
421 | ||
422 | static int | |
423 | update_view(struct view *view) | |
424 | { | |
425 | char buffer[BUFSIZ]; | |
426 | char *line; | |
b801d8b2 JF |
427 | char **tmp; |
428 | int redraw; | |
fd85fef1 | 429 | int lines = view->height; |
b801d8b2 JF |
430 | |
431 | if (!view->pipe) | |
432 | return TRUE; | |
433 | ||
fd85fef1 | 434 | /* Only redraw after the first reading session. */ |
b801d8b2 JF |
435 | redraw = !view->line; |
436 | ||
437 | tmp = realloc(view->line, sizeof(*view->line) * (view->lines + lines)); | |
438 | if (!tmp) | |
439 | goto alloc_error; | |
440 | ||
441 | view->line = tmp; | |
442 | ||
443 | while ((line = fgets(buffer, sizeof(buffer), view->pipe))) { | |
444 | int linelen; | |
445 | ||
b801d8b2 JF |
446 | linelen = strlen(line); |
447 | if (linelen) | |
448 | line[linelen - 1] = 0; | |
449 | ||
450 | view->line[view->lines] = strdup(line); | |
451 | if (!view->line[view->lines]) | |
452 | goto alloc_error; | |
453 | view->lines++; | |
fd85fef1 JF |
454 | |
455 | if (lines-- == 1) | |
456 | break; | |
b801d8b2 JF |
457 | } |
458 | ||
459 | if (redraw) | |
460 | redraw_view(view); | |
461 | ||
462 | if (ferror(view->pipe)) { | |
fd85fef1 | 463 | report("failed to read %s", view->cmd); |
b801d8b2 JF |
464 | goto end; |
465 | ||
466 | } else if (feof(view->pipe)) { | |
467 | report_position(view, 0); | |
468 | goto end; | |
469 | } | |
470 | ||
471 | return TRUE; | |
472 | ||
473 | alloc_error: | |
fd85fef1 | 474 | report("allocation failure"); |
b801d8b2 JF |
475 | |
476 | end: | |
477 | end_update(view); | |
478 | return FALSE; | |
479 | } | |
480 | ||
481 | ||
482 | static struct view * | |
483 | switch_view(struct view *prev, int request) | |
484 | { | |
485 | struct view *view = &views[request - REQ_OFFSET]; | |
486 | struct view *displayed; | |
487 | int i; | |
488 | ||
489 | if (view == prev) { | |
490 | foreach_view (displayed, i) ; | |
491 | ||
492 | if (i == 1) | |
fd85fef1 | 493 | report("already in %s view", view->name); |
b801d8b2 JF |
494 | else |
495 | report("FIXME: Maximize"); | |
496 | ||
497 | return view; | |
498 | ||
499 | } else { | |
500 | foreach_view (displayed, i) { | |
501 | if (view == displayed) { | |
502 | current_view = i; | |
fd85fef1 | 503 | report("new current view"); |
b801d8b2 JF |
504 | return view; |
505 | } | |
506 | } | |
507 | } | |
508 | ||
509 | if (!view->win) | |
510 | resize_view(view); | |
511 | ||
512 | /* Reload */ | |
513 | ||
514 | if (view->line) { | |
515 | for (i = 0; i < view->lines; i++) | |
516 | if (view->line[i]) | |
517 | free(view->line[i]); | |
518 | ||
519 | free(view->line); | |
520 | view->line = NULL; | |
521 | } | |
522 | ||
523 | if (prev && prev->pipe) | |
524 | end_update(prev); | |
525 | ||
526 | if (begin_update(view)) { | |
527 | if (!view->cmd) | |
528 | report("%s", HELP); | |
529 | else | |
530 | report("loading..."); | |
531 | } | |
532 | ||
533 | return view; | |
534 | } | |
535 | ||
536 | ||
537 | /* Process a keystroke */ | |
538 | static int | |
539 | view_driver(struct view *view, int key) | |
540 | { | |
541 | int request = get_request(key); | |
542 | int i; | |
543 | ||
544 | switch (request) { | |
545 | case REQ_NEXT_LINE: | |
b801d8b2 | 546 | case REQ_PREV_LINE: |
fd85fef1 JF |
547 | case REQ_FIRST_LINE: |
548 | case REQ_LAST_LINE: | |
549 | if (view) | |
550 | navigate_view(view, request); | |
551 | break; | |
552 | ||
553 | case REQ_SCR_FLINE: | |
554 | case REQ_SCR_BLINE: | |
555 | case REQ_SCR_FPAGE: | |
556 | case REQ_SCR_BPAGE: | |
b801d8b2 JF |
557 | if (view) |
558 | scroll_view(view, request); | |
559 | break; | |
560 | ||
561 | case REQ_MAIN: | |
562 | case REQ_LOG: | |
563 | case REQ_DIFF: | |
564 | view = switch_view(view, request); | |
565 | break; | |
566 | ||
567 | case REQ_REDRAW: | |
568 | redraw_view(view); | |
569 | break; | |
570 | ||
571 | case REQ_STOP: | |
572 | foreach_view (view, i) { | |
573 | if (view->pipe) { | |
574 | end_update(view); | |
575 | scroll_view(view, 0); | |
576 | } | |
577 | } | |
578 | break; | |
579 | ||
580 | case REQ_VERSION: | |
581 | report("version %s", VERSION); | |
582 | return TRUE; | |
583 | ||
584 | case REQ_UPDATE: | |
585 | doupdate(); | |
586 | return TRUE; | |
587 | ||
588 | case REQ_QUIT: | |
589 | return FALSE; | |
590 | ||
591 | default: | |
592 | report(HELP); | |
593 | return TRUE; | |
594 | } | |
595 | ||
596 | return TRUE; | |
597 | } | |
598 | ||
599 | ||
600 | /* | |
601 | * Rendering | |
602 | */ | |
603 | ||
604 | #define ATTR(line, attr) { (line), sizeof(line) - 1, (attr) } | |
605 | ||
606 | struct attr { | |
607 | char *line; | |
608 | int linelen; | |
609 | int attr; | |
610 | }; | |
611 | ||
612 | static struct attr attrs[] = { | |
613 | ATTR("commit ", COLOR_PAIR(COLOR_GREEN)), | |
614 | ATTR("Author: ", COLOR_PAIR(COLOR_CYAN)), | |
615 | ATTR("Date: ", COLOR_PAIR(COLOR_YELLOW)), | |
616 | ATTR("diff --git ", COLOR_PAIR(COLOR_YELLOW)), | |
617 | ATTR("diff-tree ", COLOR_PAIR(COLOR_BLUE)), | |
618 | ATTR("index ", COLOR_PAIR(COLOR_BLUE)), | |
619 | ATTR("-", COLOR_PAIR(COLOR_RED)), | |
620 | ATTR("+", COLOR_PAIR(COLOR_GREEN)), | |
621 | ATTR("@", COLOR_PAIR(COLOR_MAGENTA)), | |
622 | }; | |
623 | ||
624 | static int | |
fd85fef1 | 625 | default_renderer(struct view *view, unsigned int lineno) |
b801d8b2 JF |
626 | { |
627 | char *line; | |
628 | int linelen; | |
629 | int attr = A_NORMAL; | |
630 | int i; | |
631 | ||
fd85fef1 JF |
632 | if (view->offset + lineno >= view->lines) |
633 | return FALSE; | |
634 | ||
b801d8b2 JF |
635 | line = view->line[view->offset + lineno]; |
636 | if (!line) return FALSE; | |
637 | ||
638 | linelen = strlen(line); | |
639 | ||
640 | for (i = 0; i < ARRAY_SIZE(attrs); i++) { | |
641 | if (linelen < attrs[i].linelen | |
642 | || strncmp(attrs[i].line, line, attrs[i].linelen)) | |
643 | continue; | |
644 | ||
645 | attr = attrs[i].attr; | |
646 | break; | |
647 | } | |
648 | ||
fd85fef1 JF |
649 | if (view->offset + lineno == view->lineno) { |
650 | if (i == 0) | |
651 | strncpy(commit_id, line + 7, SIZEOF_ID); | |
652 | attr = COLOR_PAIR(COLOR_CURSOR) | A_BOLD; | |
653 | } | |
654 | ||
b801d8b2 | 655 | wattrset(view->win, attr); |
fd85fef1 JF |
656 | //mvwprintw(view->win, lineno, 0, "%4d: %s", view->offset + lineno, line); |
657 | mvwaddstr(view->win, lineno, 0, line); | |
b801d8b2 JF |
658 | |
659 | return TRUE; | |
660 | } | |
661 | ||
662 | /* | |
663 | * Main | |
664 | */ | |
665 | ||
666 | static void | |
667 | quit(int sig) | |
668 | { | |
669 | if (status_win) | |
670 | delwin(status_win); | |
671 | endwin(); | |
672 | ||
673 | /* FIXME: Shutdown gracefully. */ | |
674 | ||
675 | exit(0); | |
676 | } | |
677 | ||
678 | static void die(const char *err, ...) | |
679 | { | |
680 | va_list args; | |
681 | ||
682 | endwin(); | |
683 | ||
684 | va_start(args, err); | |
685 | fputs("tig: ", stderr); | |
686 | vfprintf(stderr, err, args); | |
687 | fputs("\n", stderr); | |
688 | va_end(args); | |
689 | ||
690 | exit(1); | |
691 | } | |
692 | ||
693 | static void | |
694 | report(const char *msg, ...) | |
695 | { | |
696 | va_list args; | |
697 | ||
698 | va_start(args, msg); | |
699 | ||
700 | werase(status_win); | |
701 | wmove(status_win, 0, 0); | |
702 | ||
fd85fef1 | 703 | #if 0 |
b801d8b2 | 704 | if (display[current_view]) |
fd85fef1 JF |
705 | wprintw(status_win, "%s %4s: ", commit_id, display[current_view]->name); |
706 | #endif | |
b801d8b2 JF |
707 | vwprintw(status_win, msg, args); |
708 | wrefresh(status_win); | |
709 | ||
710 | va_end(args); | |
711 | } | |
712 | ||
713 | static void | |
714 | init_colors(void) | |
715 | { | |
716 | int bg = COLOR_BLACK; | |
717 | ||
718 | start_color(); | |
719 | ||
720 | if (use_default_colors() != ERR) | |
721 | bg = -1; | |
722 | ||
723 | init_pair(COLOR_BLACK, COLOR_BLACK, bg); | |
724 | init_pair(COLOR_GREEN, COLOR_GREEN, bg); | |
725 | init_pair(COLOR_RED, COLOR_RED, bg); | |
726 | init_pair(COLOR_CYAN, COLOR_CYAN, bg); | |
727 | init_pair(COLOR_WHITE, COLOR_WHITE, bg); | |
728 | init_pair(COLOR_MAGENTA, COLOR_MAGENTA, bg); | |
729 | init_pair(COLOR_BLUE, COLOR_BLUE, bg); | |
730 | init_pair(COLOR_YELLOW, COLOR_YELLOW, bg); | |
fd85fef1 | 731 | init_pair(COLOR_CURSOR, COLOR_WHITE, COLOR_GREEN); |
b801d8b2 JF |
732 | } |
733 | ||
734 | int | |
735 | main(int argc, char *argv[]) | |
736 | { | |
737 | int request = REQ_MAIN; | |
738 | int x, y; | |
739 | ||
740 | signal(SIGINT, quit); | |
741 | ||
742 | initscr(); /* initialize the curses library */ | |
743 | nonl(); /* tell curses not to do NL->CR/NL on output */ | |
744 | cbreak(); /* take input chars one at a time, no wait for \n */ | |
745 | noecho(); /* don't echo input */ | |
746 | leaveok(stdscr, TRUE); | |
747 | /* curs_set(0); */ | |
748 | ||
749 | if (has_colors()) | |
750 | init_colors(); | |
751 | ||
752 | getmaxyx(stdscr, y, x); | |
753 | status_win = newwin(1, 0, y - 1, 0); | |
754 | if (!status_win) | |
755 | die("Failed to create status window"); | |
756 | ||
757 | /* Enable keyboard mapping */ | |
758 | keypad(status_win, TRUE); | |
759 | wattrset(status_win, COLOR_PAIR(COLOR_GREEN)); | |
760 | ||
761 | while (view_driver(display[current_view], request)) { | |
762 | struct view *view; | |
763 | int i; | |
764 | ||
765 | foreach_view (view, i) { | |
766 | if (view->pipe) { | |
767 | update_view(view); | |
768 | } | |
769 | } | |
770 | ||
771 | /* Refresh, accept single keystroke of input */ | |
772 | request = wgetch(status_win); | |
773 | if (request == KEY_RESIZE) { | |
774 | int lines, cols; | |
775 | ||
776 | getmaxyx(stdscr, lines, cols); | |
777 | mvwin(status_win, lines - 1, 0); | |
778 | wresize(status_win, 1, cols - 1); | |
779 | } | |
780 | } | |
781 | ||
782 | quit(0); | |
783 | ||
784 | return 0; | |
785 | } | |
786 | ||
787 | /** | |
788 | * COPYRIGHT | |
789 | * --------- | |
790 | * Copyright (c) Jonas Fonseca, 2006 | |
791 | * | |
792 | * This program is free software; you can redistribute it and/or modify | |
793 | * it under the terms of the GNU General Public License as published by | |
794 | * the Free Software Foundation; either version 2 of the License, or | |
795 | * (at your option) any later version. | |
796 | * | |
797 | * SEE ALSO | |
798 | * -------- | |
799 | * link:http://www.kernel.org/pub/software/scm/git/docs/[git(7)], | |
800 | * link:http://www.kernel.org/pub/software/scm/cogito/docs/[cogito(7)] | |
801 | **/ |