1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
16 #include "tig/refdb.h"
18 #include "tig/options.h"
19 #include "tig/watch.h"
21 static struct watch
*watches
;
24 watch_register(struct watch
*watch
, enum watch_trigger triggers
)
26 watch_unregister(watch
);
28 watch
->next
= watches
;
31 watch
->triggers
= triggers
;
32 watch
->changed
= WATCH_NONE
;
33 watch
->state
= WATCH_NONE
;
37 watch_unregister(struct watch
*watch
)
39 struct watch
*pos
, *prev
= NULL
;
41 for (pos
= watches
; pos
; prev
= pos
, pos
= pos
->next
) {
45 watches
= watch
->next
;
47 prev
->next
= watch
->next
;
51 memset(watch
, 0, sizeof(*watch
));
54 struct watch_handler
{
55 enum watch_trigger (*check
)(struct watch_handler
*handler
, enum watch_event event
, enum watch_trigger check
);
56 enum watch_trigger triggers
;
61 check_file_mtime(time_t *last_modified
, const char *path_fmt
, ...)
63 char path
[SIZEOF_STR
];
67 FORMAT_BUFFER(path
, sizeof(path
), path_fmt
, retval
, FALSE
);
70 lstat(path
, &stat
) < 0 ||
71 stat
.st_mtime
<= *last_modified
)
74 *last_modified
= stat
.st_mtime
;
78 static enum watch_trigger
79 watch_head_handler(struct watch_handler
*handler
, enum watch_event event
, enum watch_trigger check
)
83 if (check_file_mtime(&handler
->last_modified
, "%s/HEAD", repo
.git_dir
))
86 // FIXME: check branch
87 if ((head
= get_ref_head()) &&
88 check_file_mtime(&handler
->last_modified
, "%s/refs/head/%s", repo
.git_dir
, head
->name
))
94 static enum watch_trigger
95 watch_stash_handler(struct watch_handler
*handler
, enum watch_event event
, enum watch_trigger check
)
97 if (check_file_mtime(&handler
->last_modified
, "%s/refs/stash", repo
.git_dir
))
103 static enum watch_trigger
104 watch_index_handler(struct watch_handler
*handler
, enum watch_event event
, enum watch_trigger check
)
106 enum watch_trigger changed
= WATCH_NONE
;
107 struct index_diff diff
;
109 if (event
== WATCH_EVENT_AFTER_COMMAND
)
110 return check_file_mtime(&handler
->last_modified
, "%s/index", repo
.git_dir
)
111 ? check
: WATCH_NONE
;
113 if (!check_file_mtime(&handler
->last_modified
, "%s/index", repo
.git_dir
) ||
114 event
== WATCH_EVENT_SWITCH_VIEW
||
115 !index_diff(&diff
, FALSE
, FALSE
))
118 if (check
& WATCH_INDEX_STAGED
) {
120 changed
|= WATCH_INDEX_STAGED_YES
;
122 changed
|= WATCH_INDEX_STAGED_NO
;
125 if (check
& WATCH_INDEX_UNSTAGED
) {
127 changed
|= WATCH_INDEX_UNSTAGED_YES
;
129 changed
|= WATCH_INDEX_UNSTAGED_NO
;
133 handler
->last_modified
= time(NULL
);
138 static enum watch_trigger
139 watch_refs_handler(struct watch_handler
*handler
, enum watch_event event
,
140 enum watch_trigger check
)
142 if (event
== WATCH_EVENT_AFTER_COMMAND
)
148 static struct watch_handler watch_handlers
[] = {
149 { watch_index_handler
, WATCH_INDEX
},
150 { watch_head_handler
, WATCH_HEAD
},
151 { watch_stash_handler
, WATCH_STASH
},
152 { watch_refs_handler
, WATCH_HEAD
| WATCH_REFS
},
156 watch_no_refresh(enum watch_event event
)
158 return opt_refresh_mode
== REFRESH_MODE_MANUAL
||
159 (opt_refresh_mode
== REFRESH_MODE_AFTER_COMMAND
&&
160 event
!= WATCH_EVENT_AFTER_COMMAND
);
164 watch_apply_changes(struct watch
*source
, enum watch_event event
,
165 enum watch_trigger changed
)
169 if (watch_no_refresh(event
))
172 for (watch
= watches
; watch
; watch
= watch
->next
) {
173 enum watch_trigger triggers
= changed
& watch
->triggers
;
175 if (source
== watch
) {
176 source
->state
|= triggers
;
180 if (event
== WATCH_EVENT_AFTER_COMMAND
) {
181 watch
->state
= WATCH_NONE
;
182 triggers
= watch
->triggers
;
185 watch
->changed
|= triggers
;
190 watch_apply(struct watch
*source
, enum watch_trigger changed
)
192 watch_apply_changes(source
, WATCH_EVENT_LOAD
, changed
);
195 static enum watch_trigger
196 watch_update_event(enum watch_event event
, enum watch_trigger trigger
,
197 enum watch_trigger changed
)
199 time_t timestamp
= 0;
202 if (watch_no_refresh(event
))
205 if (event
== WATCH_EVENT_AFTER_COMMAND
)
206 timestamp
= time(NULL
);
208 for (i
= 0; i
< ARRAY_SIZE(watch_handlers
); i
++) {
209 struct watch_handler
*handler
= &watch_handlers
[i
];
211 if (event
== WATCH_EVENT_AFTER_COMMAND
) {
212 changed
= handler
->triggers
;
213 handler
->last_modified
= timestamp
;
218 (trigger
& handler
->triggers
) &&
219 (changed
| handler
->triggers
) != changed
)
220 changed
|= handler
->check(handler
, event
, trigger
);
224 watch_apply_changes(NULL
, event
, changed
);
229 #define watch_trigger_unmask(triggers, set) ((triggers) & ~(set))
231 static inline enum watch_trigger
232 watch_unchanged_triggers(struct watch
*watch
)
234 return watch_trigger_unmask(watch
->triggers
, watch
->changed
);
238 watch_update_single(struct watch
*watch
, enum watch_event event
)
240 enum watch_trigger trigger
= watch_unchanged_triggers(watch
);
242 return watch_update_event(event
, trigger
, watch
->changed
);
246 watch_update(enum watch_event event
)
248 enum watch_trigger trigger
= WATCH_NONE
;
251 /* Collect triggers to check. Skkipping watches that are already
252 * marked dirty to avoid unnecessary checks. */
253 for (watch
= watches
; watch
; watch
= watch
->next
)
254 trigger
|= watch_unchanged_triggers(watch
);
256 return watch_update_event(event
, trigger
, WATCH_NONE
);
260 watch_periodic(int interval
)
262 static time_t last_update
;
265 if (watches
&& interval
> 0) {
266 time_t now
= time(NULL
);
270 if (last_update
+ interval
<= now
) {
271 watch_update(WATCH_EVENT_PERIODIC
);
275 delay
= (now
- last_update
+ interval
) * 1000;
282 watch_dirty(struct watch
*watch
)
284 enum watch_trigger old_index
= watch
->state
& WATCH_INDEX
;
285 enum watch_trigger new_index
= watch
->changed
& WATCH_INDEX
;
286 enum watch_trigger index
= watch_trigger_unmask(new_index
, old_index
);
287 enum watch_trigger other
= watch_trigger_unmask(watch
->changed
, WATCH_INDEX
);
288 bool dirty
= !!(index
| other
);
290 watch
->changed
= WATCH_NONE
;
294 /* vim: set ts=8 sw=8 noexpandtab: */