diff --git a/ChangeLog b/ChangeLog index ccba92e0..05be18ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ Changes since version 0.64.0: - added WINDOWS_MENU submenu type for root menu (Bastien Nocera ) - added kbd shortcuts for icon chooser - use Hermes in wrlib +- removed MOUSE_WS_WHEEL #defines +- fixed bug with multibyte text on libc5 systems (Osamu Ajiki ) +- fixed race conditions on signal handlers +- SIGINT will gently exit, SIGTERM will not be handled Changes since version 0.63.1: ............................. diff --git a/WINGs/ChangeLog b/WINGs/ChangeLog index 071483e1..1d27b548 100644 --- a/WINGs/ChangeLog +++ b/WINGs/ChangeLog @@ -4,6 +4,7 @@ Changes since wmaker 0.64.0: - made programmatic scroller changes send notifications - replaced WMSetBoxExpandsToParent with WMSetViewExpands... - added WMGetLabelFont() +- added WMAddEternalTimerHandler() changes since wmaker 0.63.1: ............................ diff --git a/WINGs/WINGs/WINGs.h b/WINGs/WINGs/WINGs.h index af6a6ec1..e358292f 100644 --- a/WINGs/WINGs/WINGs.h +++ b/WINGs/WINGs/WINGs.h @@ -604,7 +604,7 @@ char *WMGetApplicationName(); char *WMPathForResourceOfType(char *resource, char *ext); -WMScreen *WMOpenScreen(); +WMScreen *WMOpenScreen(const char *display); WMScreen *WMCreateScreenWithRContext(Display *display, int screen, RContext *context); @@ -658,6 +658,9 @@ void WMMaskEvent(Display *dpy, long mask, XEvent *event); WMHandlerID WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata); +WMHandlerID WMAddEternalTimerHandler(int milliseconds, WMCallback *callback, + void *cdata); + void WMDeleteTimerWithClientData(void *cdata); void WMDeleteTimerHandler(WMHandlerID handlerID); diff --git a/WINGs/WINGs/WINGsP.h b/WINGs/WINGs/WINGsP.h index 24caa833..8dde6f18 100644 --- a/WINGs/WINGs/WINGsP.h +++ b/WINGs/WINGs/WINGsP.h @@ -528,9 +528,9 @@ void W_HandleSelectionEvent(XEvent *event); void W_HandleDNDClientMessage(WMView *toplevel, XClientMessageEvent *event); -void W_FlushASAPNotificationQueue(); +void W_FlushASAPNotificationQueue(void); -void W_FlushIdleNotificationQueue(); +void W_FlushIdleNotificationQueue(void); struct W_Balloon *W_CreateBalloon(WMScreen *scr); diff --git a/WINGs/wevent.c b/WINGs/wevent.c index 201f3e02..89d4df5c 100644 --- a/WINGs/wevent.c +++ b/WINGs/wevent.c @@ -38,6 +38,7 @@ typedef struct TimerHandler { struct timeval when; /* when to call the callback */ void *clientData; struct TimerHandler *next; + Bool permanent; } TimerHandler; @@ -125,6 +126,9 @@ rightNow(struct timeval *tv) { (((t1).tv_sec == (t2).tv_sec) \ && ((t1).tv_usec > (t2).tv_usec))) +#define IS_ZERO(tv) (tv.tv_sec == 0 && tv.tv_usec == 0) + +#define SET_ZERO(tv) tv.tv_sec = 0, tv.tv_usec = 0 static void addmillisecs(struct timeval *tv, int milliseconds) @@ -136,19 +140,11 @@ addmillisecs(struct timeval *tv, int milliseconds) } -WMHandlerID -WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata) +static void +enqueueTimerHandler(TimerHandler *handler) { - TimerHandler *handler, *tmp; + TimerHandler *tmp; - handler = malloc(sizeof(TimerHandler)); - if (!handler) - return NULL; - - rightNow(&handler->when); - addmillisecs(&handler->when, milliseconds); - handler->callback = callback; - handler->clientData = cdata; /* insert callback in queue, sorted by time left */ if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) { /* first in the queue */ @@ -161,7 +157,39 @@ WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata) } handler->next = tmp->next; tmp->next = handler; - } + } +} + + +WMHandlerID +WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata) +{ + TimerHandler *handler; + + handler = malloc(sizeof(TimerHandler)); + if (!handler) + return NULL; + + rightNow(&handler->when); + addmillisecs(&handler->when, milliseconds); + handler->callback = callback; + handler->clientData = cdata; + handler->permanent = False; + + enqueueTimerHandler(handler); + + return handler; +} + + +WMHandlerID +WMAddEternalTimerHandler(int milliseconds, WMCallback *callback, void *cdata) +{ + TimerHandler *handler = WMAddTimerHandler(milliseconds, callback, cdata); + + if (handler != NULL) + handler->permanent = True; + return handler; } @@ -174,15 +202,21 @@ WMDeleteTimerWithClientData(void *cdata) if (!cdata || !timerHandler) return; - + tmp = timerHandler; if (tmp->clientData==cdata) { - timerHandler = tmp->next; - wfree(tmp); + tmp->permanent = False; + if (!IS_ZERO(tmp->when)) { + timerHandler = tmp->next; + wfree(tmp); + } } else { while (tmp->next) { if (tmp->next->clientData==cdata) { handler = tmp->next; + handler->permanent = False; + if (IS_ZERO(handler->when)) + break; tmp->next = handler->next; wfree(handler); break; @@ -203,6 +237,12 @@ WMDeleteTimerHandler(WMHandlerID handlerID) return; tmp = timerHandler; + + handler->permanent = False; + + if (IS_ZERO(handler->when)) + return; + if (tmp==handler) { timerHandler = handler->next; wfree(handler); @@ -241,7 +281,6 @@ WMAddIdleHandler(WMCallback *callback, void *cdata) } - void WMDeleteIdleHandler(WMHandlerID handlerID) { @@ -306,6 +345,7 @@ checkIdleHandlers() WMBag *handlerCopy; WMBagIterator iter; + if (!idleHandler || WMGetBagItemCount(idleHandler)==0) { W_FlushIdleNotificationQueue(); /* make sure an observer in queue didn't added an idle handler */ @@ -349,14 +389,26 @@ checkTimerHandlers() rightNow(&now); - while (timerHandler && IS_AFTER(now, timerHandler->when)) { - handler = timerHandler; - timerHandler = timerHandler->next; - handler->next = NULL; + handler = timerHandler; + while (handler && IS_AFTER(now, handler->when)) { + SET_ZERO(handler->when); (*handler->callback)(handler->clientData); - wfree(handler); + handler = handler->next; } + while (timerHandler && IS_ZERO(handler->when)) { + handler = timerHandler; + timerHandler = timerHandler->next; + + if (handler->permanent) { + rightNow(&handler->when); + addmillisecs(&handler->when, milliseconds); + enqueueTimerHandler(handler); + } else { + wfree(handler); + } + } + W_FlushASAPNotificationQueue(); } @@ -932,7 +984,7 @@ WMNextEvent(Display *dpy, XEvent *event) while (XPending(dpy) == 0) { /* Do idle stuff */ /* Do idle and timer stuff while there are no timer or X events */ - while (!XPending(dpy) && checkIdleHandlers()) { + while (XPending(dpy) == 0 && checkIdleHandlers()) { /* dispatch timer events */ checkTimerHandlers(); } @@ -942,7 +994,7 @@ WMNextEvent(Display *dpy, XEvent *event) * timer/idle stuff. Or we might block forever waiting for * an event that already arrived. */ - /* wait to something happen */ + /* wait for something to happen or a timer to expire */ W_WaitForEvent(dpy, 0); /* Check any expired timers */ diff --git a/WINGs/wfont.c b/WINGs/wfont.c index 9a81d662..c764dd02 100644 --- a/WINGs/wfont.c +++ b/WINGs/wfont.c @@ -42,9 +42,10 @@ generalize_xlfd (const char *xlfd) char *slant = xlfd_get_element(xlfd, 4); char *pxlsz = xlfd_get_element(xlfd, 7); - len = snprintf(NULL, 0, "%s,-*-*-%s-%s-*-*-%s-*-*-*-*-*-*-*," - "-*-*-*-*-*-*-%s-*-*-*-*-*-*-*,*", - xlfd, weight, slant, pxlsz, pxlsz); +#define Xstrlen(A) ((A)?strlen(A):0) + len = Xstrlen(xlfd)+Xstrlen(weight)+Xstrlen(slant)+Xstrlen(pxlsz)*2+50; +#undef Xstrlen + buf = wmalloc(len + 1); snprintf(buf, len + 1, "%s,-*-*-%s-%s-*-*-%s-*-*-*-*-*-*-*," "-*-*-*-*-*-*-%s-*-*-*-*-*-*-*,*", diff --git a/WINGs/widgets.c b/WINGs/widgets.c index 9a9051fc..dda50776 100644 --- a/WINGs/widgets.c +++ b/WINGs/widgets.c @@ -516,13 +516,13 @@ loadPixmaps(WMScreen *scr) WMScreen* -WMOpenScreen() +WMOpenScreen(const char *display) { - Display *dpy = XOpenDisplay(""); + Display *dpy = XOpenDisplay(display); if (!dpy) { wwarning("WINGs: could not open display %s", - XDisplayName("")); + XDisplayName(display)); return NULL; } diff --git a/src/WindowMaker.h b/src/WindowMaker.h index ca89941b..91b0e27a 100644 --- a/src/WindowMaker.h +++ b/src/WindowMaker.h @@ -247,10 +247,22 @@ typedef enum { #define WCHECK_STATE(state) (state == WProgramState) -#define WCHANGE_STATE(nstate) \ - if (WProgramState == WSTATE_NORMAL\ - || nstate != WSTATE_MODAL)\ - WProgramState = (nstate) + + +#define WCHANGE_STATE(nstate) {\ + if (WProgramState == WSTATE_NORMAL\ + || nstate != WSTATE_MODAL)\ + WProgramState = (nstate); \ + if (WProgramSigState != 0)\ + WProgramState = WProgramSigState;\ +} + + +/* only call inside signal handlers, with signals blocked */ +#define SIG_WCHANGE_STATE(nstate) {\ + WProgramSigState = (nstate);\ + WProgramState = (nstate);\ +} /* notifications */ diff --git a/src/dialog.c b/src/dialog.c index a77835a5..c61cbc82 100644 --- a/src/dialog.c +++ b/src/dialog.c @@ -1062,8 +1062,7 @@ handleLogoPush(XEvent *event, void *data) static char *msgs[] = { "Have a nice day!", "Focus follow mouse users will burn in hell!!!", - "F'ck Canada!!!!", - "F'ck Bastard Imperialists!!!", + "Mooo Canada!!!!", "Hi! My name is bobby...", "AHH! The neurotic monkeys are after me!", "WHAT YOU SAY??", diff --git a/src/main.c b/src/main.c index b53dc54d..f2861609 100644 --- a/src/main.c +++ b/src/main.c @@ -138,9 +138,11 @@ int wXkbEventBase; #endif /* special flags */ +char WProgramSigState = 0; char WProgramState = WSTATE_NORMAL; char WDelayedActionSet = 0; + /* temporary stuff */ int wVisualID = -1; diff --git a/src/startup.c b/src/startup.c index 87b64d05..224ae4a6 100644 --- a/src/startup.c +++ b/src/startup.c @@ -228,6 +228,8 @@ handleXIO(Display *xio_dpy) static void delayedAction(void *cdata) { + if (WDelatedActionSet == 0) + return; WDelayedActionSet = 0; /* * Make the event dispatcher do whatever it needs to do, @@ -238,6 +240,58 @@ delayedAction(void *cdata) } +/* + *---------------------------------------------------------------------- + * handleExitSig-- + * User generated exit signal handler. + *---------------------------------------------------------------------- + */ +static RETSIGTYPE +handleExitSig(int sig) +{ + sigset_t sigs; + + sigfillset(&sigs); + sigprocmask(SIG_BLOCK, &sigs, NULL); + + if (sig == SIGUSR1) { +#ifdef SYS_SIGLIST_DECLARED + wwarning(_("got signal %i (%s) - restarting\n"), sig, sys_siglist[sig]); +#else + wwarning(_("got signal %i - restarting\n"), sig); +#endif + + SIG_WCHANGE_STATE(WSTATE_NEED_RESTART); + /* setup idle handler, so that this will be handled when + * the select() is returned becaused of the signal, even if + * there are no X events in the queue */ + WDelayedActionSet = 1; + } else if (sig == SIGUSR2) { +#ifdef SYS_SIGLIST_DECLARED + wwarning(_("got signal %i (%s) - rereading defaults\n"), sig, sys_siglist[sig]); +#else + wwarning(_("got signal %i - rereading defaults\n"), sig); +#endif + + SIG_WCHANGE_STATE(WSTATE_NEED_REREAD); + /* setup idle handler, so that this will be handled when + * the select() is returned becaused of the signal, even if + * there are no X events in the queue */ + WDelayedActionSet = 1; + } else if (sig == SIGINT || sig == SIGHUP) { +#ifdef SYS_SIGLIST_DECLARED + wwarning(_("got signal %i (%s) - exiting...\n"), sig, sys_siglist[sig]); +#else + wwarning(_("got signal %i - exiting...\n"), sig); +#endif + + SIG_WCHANGE_STATE(WSTATE_NEED_EXIT); + + WDelayedActionSet = 1; + } + + sigprocmask(SIG_UNBLOCK, &sigs, NULL); +} /* *---------------------------------------------------------------------- @@ -262,53 +316,6 @@ handleSig(int sig) * here. Xlib calls are not reentrant so the integrity of Xlib is * not guaranteed if a Xlib call is made from a signal handler. */ - if (sig == SIGUSR1) { -#ifdef SYS_SIGLIST_DECLARED - wwarning(_("got signal %i (%s) - restarting\n"), sig, sys_siglist[sig]); -#else - wwarning(_("got signal %i - restarting\n"), sig); -#endif - - WCHANGE_STATE(WSTATE_NEED_RESTART); - /* setup idle handler, so that this will be handled when - * the select() is returned becaused of the signal, even if - * there are no X events in the queue */ - if (!WDelayedActionSet) { - WDelayedActionSet = 1; - WMAddIdleHandler(delayedAction, NULL); - } - return; - } else if (sig == SIGUSR2) { -#ifdef SYS_SIGLIST_DECLARED - wwarning(_("got signal %i (%s) - rereading defaults\n"), sig, sys_siglist[sig]); -#else - wwarning(_("got signal %i - rereading defaults\n"), sig); -#endif - - WCHANGE_STATE(WSTATE_NEED_REREAD); - /* setup idle handler, so that this will be handled when - * the select() is returned becaused of the signal, even if - * there are no X events in the queue */ - if (!WDelayedActionSet) { - WDelayedActionSet = 1; - WMAddIdleHandler(delayedAction, NULL); - } - return; - } else if (sig == SIGTERM || sig == SIGHUP) { -#ifdef SYS_SIGLIST_DECLARED - wwarning(_("got signal %i (%s) - exiting...\n"), sig, sys_siglist[sig]); -#else - wwarning(_("got signal %i - exiting...\n"), sig); -#endif - - WCHANGE_STATE(WSTATE_NEED_EXIT); - - if (!WDelayedActionSet) { - WDelayedActionSet = 1; - WMAddIdleHandler(delayedAction, NULL); - } - return; - } #ifdef SYS_SIGLIST_DECLARED wfatal(_("got signal %i (%s)\n"), sig, sys_siglist[sig]); @@ -333,6 +340,10 @@ handleSig(int sig) dumpcore = 1; + /* + * Yeah, we shouldn't do this, but it's already crashed anyway :P + */ + #ifndef NO_EMERGENCY_AUTORESTART /* Close the X connection and open a new one. This is to avoid messing * Xlib because we call to Xlib functions in a signal handler. @@ -384,18 +395,17 @@ handleSig(int sig) } -static RETSIGTYPE -ignoreSig(int signal) -{ - return; -} - - static RETSIGTYPE buryChild(int foo) { pid_t pid; int status; + int save_errno = errno; + sigset_t sigs; + + sigfillset(&sigs); + /* Block signals so that NotifyDeadProcess() doesn't get fux0red */ + sigprocmask(SIG_BLOCK, &sigs, NULL); /* R.I.P. */ /* If 2 or more kids exit in a small time window, before this handler gets @@ -406,15 +416,13 @@ buryChild(int foo) */ while ((pid=waitpid(-1, &status, WNOHANG))>0 || (pid<0 && errno==EINTR)) { NotifyDeadProcess(pid, WEXITSTATUS(status)); - /* - * Make sure that the kid will be buried even if there are - * no events in the X event queue - */ - if (!WDelayedActionSet) { - WDelayedActionSet = 1; - WMAddIdleHandler(delayedAction, NULL); - } } + + WDelayedActionSet = 1; + + sigprocmask(SIG_UNBLOCK, &sigs, NULL); + + errno = save_errno; } @@ -790,32 +798,34 @@ StartUp(Bool defaultScreenOnly) wCursor[WCUR_TEXT] = XCreateFontCursor(dpy, XC_xterm); /* odd name???*/ wCursor[WCUR_SELECT] = XCreateFontCursor(dpy, XC_cross); + /* signal handler stuff that gets called when a signal is caught */ + WMAddEternalTimerHandler(500, delayedAction, NULL); + /* emergency exit... */ sig_action.sa_handler = handleSig; sigemptyset(&sig_action.sa_mask); - /* Here we don't care about SA_RESTART since these signals will close - * wmaker anyway. - * -Dan */ - sig_action.sa_flags = 0; - sigaction(SIGINT, &sig_action, NULL); - sigaction(SIGTERM, &sig_action, NULL); - sigaction(SIGHUP, &sig_action, NULL); + sig_action.sa_flags = SA_RESTART; sigaction(SIGQUIT, &sig_action, NULL); sigaction(SIGSEGV, &sig_action, NULL); sigaction(SIGBUS, &sig_action, NULL); sigaction(SIGFPE, &sig_action, NULL); sigaction(SIGABRT, &sig_action, NULL); + sig_action.sa_handler = handleExitSig; + /* Here we set SA_RESTART for safety, because SIGUSR1 may not be handled * immediately. * -Dan */ sig_action.sa_flags = SA_RESTART; +/* sigaction(SIGTERM, &sig_action, NULL);*/ + sigaction(SIGINT, &sig_action, NULL); + sigaction(SIGHUP, &sig_action, NULL); sigaction(SIGUSR1, &sig_action, NULL); sigaction(SIGUSR2, &sig_action, NULL); /* ignore dead pipe */ - sig_action.sa_handler = ignoreSig; + sig_action.sa_handler = SIG_IGN; sig_action.sa_flags = SA_RESTART; sigaction(SIGPIPE, &sig_action, NULL);