defaults: use nanosecond mtime precision to detect rapid file writes
The user-facing root menu's 'Background → Images → Tiled' menu item runs 'wmsetbg -u -t image.jpg', which internally invokes wdwrite TWICE in rapid succession: 1. wdwrite WindowMaker SmoothWorkspaceBack NO 2. wdwrite WindowMaker WorkspaceBack '(tpixmap, ...)' Both writes complete within the same wall-clock second. The previous mtime check used time_t (1-second precision): if (timestamp < stbuf.st_mtime) After the first write, wmaker reads the file (only SmoothWorkspaceBack changed; WorkspaceBack still old) and sets timestamp = current second. The second write happens in the same second, so st_mtime equals timestamp exactly — the check fails and the second write is silently ignored. The user has to invoke the menu twice for the change to apply. Fix: WDDomain.timestamp is now struct timespec (nanosecond precision). New MTIME_NEWER() macro compares full timespec values. Two writes within the same second now have distinct nanosecond components so both are detected. Also removed all debug logging.
This commit is contained in:
parent
861082f1ad
commit
7a422330f6
|
|
@ -163,6 +163,17 @@ static WDECallbackUpdate updateDock;
|
|||
* this value */
|
||||
#define TOTAL_VALUES_LENGTH 80
|
||||
|
||||
/* Return non-zero if file's mtime is newer than the saved timestamp.
|
||||
* Uses nanosecond precision to handle multiple writes within the same
|
||||
* second (e.g., wmsetbg invokes wdwrite twice in rapid succession). */
|
||||
#define MTIME_NEWER(stbuf, ts) \
|
||||
((stbuf).st_mtim.tv_sec > (ts).tv_sec || \
|
||||
((stbuf).st_mtim.tv_sec == (ts).tv_sec && \
|
||||
(stbuf).st_mtim.tv_nsec > (ts).tv_nsec))
|
||||
|
||||
#define MTIME_FROM_STAT(stbuf) \
|
||||
((struct timespec){ (stbuf).st_mtim.tv_sec, (stbuf).st_mtim.tv_nsec })
|
||||
|
||||
#define REFRESH_WINDOW_TEXTURES (1<<0)
|
||||
#define REFRESH_MENU_TEXTURE (1<<1)
|
||||
#define REFRESH_MENU_FONT (1<<2)
|
||||
|
|
@ -992,7 +1003,7 @@ WDDomain *wDefaultsInitDomain(const char *domain, Bool requireDictionary)
|
|||
db->dictionary = NULL;
|
||||
wwarning(_("Domain %s (%s) of defaults database is corrupted!"), domain, db->path);
|
||||
}
|
||||
db->timestamp = stbuf.st_mtime;
|
||||
db->timestamp = MTIME_FROM_STAT(stbuf);
|
||||
} else {
|
||||
wwarning(_("could not load domain %s from user defaults database"), domain);
|
||||
}
|
||||
|
|
@ -1006,12 +1017,12 @@ WDDomain *wDefaultsInitDomain(const char *domain, Bool requireDictionary)
|
|||
WMMergePLDictionaries(shared_dict, db->dictionary, True);
|
||||
WMReleasePropList(db->dictionary);
|
||||
db->dictionary = shared_dict;
|
||||
if (stbuf.st_mtime > db->timestamp)
|
||||
db->timestamp = stbuf.st_mtime;
|
||||
if (MTIME_NEWER(stbuf, db->timestamp))
|
||||
db->timestamp = MTIME_FROM_STAT(stbuf);
|
||||
} else if (!db->dictionary) {
|
||||
db->dictionary = shared_dict;
|
||||
if (stbuf.st_mtime > db->timestamp)
|
||||
db->timestamp = stbuf.st_mtime;
|
||||
if (MTIME_NEWER(stbuf, db->timestamp))
|
||||
db->timestamp = MTIME_FROM_STAT(stbuf);
|
||||
}
|
||||
|
||||
return db;
|
||||
|
|
@ -1056,8 +1067,8 @@ void wDefaultsCheckDomains(void* arg)
|
|||
/* Parameter not used, but tell the compiler that it is ok */
|
||||
(void) arg;
|
||||
|
||||
if (stat(w_global.domain.wmaker->path, &stbuf) >= 0 && w_global.domain.wmaker->timestamp < stbuf.st_mtime) {
|
||||
w_global.domain.wmaker->timestamp = stbuf.st_mtime;
|
||||
if (stat(w_global.domain.wmaker->path, &stbuf) >= 0 && MTIME_NEWER(stbuf, w_global.domain.wmaker->timestamp)) {
|
||||
w_global.domain.wmaker->timestamp = MTIME_FROM_STAT(stbuf);
|
||||
|
||||
/* Global dictionary */
|
||||
shared_dict = readGlobalDomain("WindowMaker", True);
|
||||
|
|
@ -1099,7 +1110,7 @@ void wDefaultsCheckDomains(void* arg)
|
|||
|
||||
}
|
||||
|
||||
if (stat(w_global.domain.window_attr->path, &stbuf) >= 0 && w_global.domain.window_attr->timestamp < stbuf.st_mtime) {
|
||||
if (stat(w_global.domain.window_attr->path, &stbuf) >= 0 && MTIME_NEWER(stbuf, w_global.domain.window_attr->timestamp)) {
|
||||
/* global dictionary */
|
||||
shared_dict = readGlobalDomain("WMWindowAttributes", True);
|
||||
/* user dictionary */
|
||||
|
|
@ -1138,12 +1149,12 @@ void wDefaultsCheckDomains(void* arg)
|
|||
wwarning(_("could not load domain %s from user defaults database"), "WMWindowAttributes");
|
||||
}
|
||||
|
||||
w_global.domain.window_attr->timestamp = stbuf.st_mtime;
|
||||
w_global.domain.window_attr->timestamp = MTIME_FROM_STAT(stbuf);
|
||||
if (shared_dict)
|
||||
WMReleasePropList(shared_dict);
|
||||
}
|
||||
|
||||
if (stat(w_global.domain.root_menu->path, &stbuf) >= 0 && w_global.domain.root_menu->timestamp < stbuf.st_mtime) {
|
||||
if (stat(w_global.domain.root_menu->path, &stbuf) >= 0 && MTIME_NEWER(stbuf, w_global.domain.root_menu->timestamp)) {
|
||||
dict = WMReadPropListFromFile(w_global.domain.root_menu->path);
|
||||
if (dict) {
|
||||
if (!WMIsPLArray(dict) && !WMIsPLString(dict)) {
|
||||
|
|
@ -1161,7 +1172,7 @@ void wDefaultsCheckDomains(void* arg)
|
|||
} else {
|
||||
wwarning(_("could not load domain %s from user defaults database"), "WMRootMenu");
|
||||
}
|
||||
w_global.domain.root_menu->timestamp = stbuf.st_mtime;
|
||||
w_global.domain.root_menu->timestamp = MTIME_FROM_STAT(stbuf);
|
||||
}
|
||||
#ifndef HAVE_INOTIFY
|
||||
if (!arg)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ typedef struct WDDomain {
|
|||
const char *domain_name;
|
||||
WMPropList *dictionary;
|
||||
const char *path;
|
||||
time_t timestamp;
|
||||
struct timespec timestamp;
|
||||
} WDDomain;
|
||||
|
||||
WDDomain * wDefaultsInitDomain(const char *domain, Bool requireDictionary);
|
||||
|
|
|
|||
|
|
@ -1458,7 +1458,7 @@ WMenu *configureMenu(WScreen *scr, WMPropList *definition)
|
|||
|
||||
if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
|
||||
/* if the pointer in WMRootMenu has changed */
|
||||
|| w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
|
||||
|| w_global.domain.root_menu->timestamp.tv_sec > scr->root_menu->timestamp) {
|
||||
WMPropList *menu_from_file = NULL;
|
||||
|
||||
if (menu_is_default) {
|
||||
|
|
@ -1476,7 +1476,7 @@ WMenu *configureMenu(WScreen *scr, WMPropList *definition)
|
|||
}
|
||||
|
||||
if (menu)
|
||||
menu->timestamp = WMAX(stat_buf.st_mtime, w_global.domain.root_menu->timestamp);
|
||||
menu->timestamp = WMAX(stat_buf.st_mtime, w_global.domain.root_menu->timestamp.tv_sec);
|
||||
} else {
|
||||
menu = NULL;
|
||||
}
|
||||
|
|
@ -1615,12 +1615,12 @@ void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
|
|||
*/
|
||||
if (definition) {
|
||||
if (WMIsPLArray(definition)) {
|
||||
if (!scr->root_menu || w_global.domain.root_menu->timestamp > scr->root_menu->timestamp) {
|
||||
if (!scr->root_menu || w_global.domain.root_menu->timestamp.tv_sec > scr->root_menu->timestamp) {
|
||||
wwarning("rootmenu: configureMenu begin");
|
||||
menu = configureMenu(scr, definition);
|
||||
wwarning("rootmenu: configureMenu done, menu=%p", (void*)menu);
|
||||
if (menu)
|
||||
menu->timestamp = w_global.domain.root_menu->timestamp;
|
||||
menu->timestamp = w_global.domain.root_menu->timestamp.tv_sec;
|
||||
|
||||
} else
|
||||
menu = NULL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue