AmiTimeKeeper/broker.c

559 lines
14 KiB
C

/*-
* Copyright (c) 2017-2021 Carsten Sonne Larsen <cs@innolan.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "config.h"
#include "global.h"
#include "notify.h"
#include "message.h"
#include "setting.h"
#include "shutdown.h"
#include "text.h"
#include "sync.h"
#include "mem.h"
#include "win.h"
#include "tz.h"
#if defined(SCREENNOTIFY)
#include "libraries/screennotify.h"
#endif
#include "logmod.h"
#define MODULENAME "Main"
#define EVT_HOTKEY 1L
static void MsgLoop(void);
static void CleanupBroker(void);
struct AppBroker
{
CxObj *Object;
struct MsgPort *CommodityPort;
struct MsgPort *NotifyPort;
struct MsgPort *ScreenPort;
APTR screenNotifyWB;
APTR screenNotifyPub;
};
static struct NewBroker newBroker = {
NB_VERSION,
(STRPTR)APP_SHORT_NAME,
(STRPTR)APP_TITLE_VERSION,
(STRPTR) "Synchronize Clock With Internet Servers",
NBU_UNIQUE | NBU_NOTIFY,
COF_SHOW_HIDE,
0, 0, 0};
static struct AppBroker Broker = {NULL, NULL, NULL, NULL, NULL, NULL};
static volatile bool BrokerRunning = false;
void StartBroker(void)
{
LONG error;
CxObj *filter, *sender, *translate;
bool init = SetupPorts();
if (!init)
{
return;
}
LogInfo(TextInitComponent, "commodity");
Broker.CommodityPort = CreateMsgPort();
if (Broker.CommodityPort == NULL)
{
LogError(TextPortInitError, "commodity");
CleanupBroker();
return;
}
Broker.NotifyPort = CreateMsgPort();
if (Broker.NotifyPort == NULL)
{
LogError(TextPortInitError, "notify");
CleanupBroker();
return;
}
Broker.ScreenPort = CreateMsgPort();
if (Broker.ScreenPort == NULL)
{
LogError(TextPortInitError, "screen");
CleanupBroker();
return;
}
newBroker.nb_Port = Broker.CommodityPort;
newBroker.nb_Pri = Settings->Priority;
Broker.Object = CxBroker(&newBroker, &error);
if (!Broker.Object)
{
switch (error)
{
case CBERR_SYSERR:
LogError("System problems (CBERR_SYSERR). Could not allocate broker object");
break;
case CBERR_DUP:
LogWarn(Text2P, APP_SHORT_NAME, TextAlreadyRunning);
break;
default:
LogError("Could not allocate broker object (error code: %ld)", error);
break;
}
CleanupBroker();
return;
}
filter = CxFilter(Settings->PopKey);
if (filter == NULL)
{
LogError("Could not allocate broker filter object");
CleanupBroker();
return;
}
AttachCxObj(Broker.Object, filter);
sender = CxSender(Broker.CommodityPort, EVT_HOTKEY);
if (sender == NULL)
{
LogError("Could not allocate broker sender object");
CleanupBroker();
return;
}
AttachCxObj(filter, sender);
translate = CxTranslate(NULL);
if (translate == NULL)
{
LogError("Could not allocate broker translate object");
CleanupBroker();
return;
}
AttachCxObj(filter, translate);
error = CxObjError(filter);
if (error != 0)
{
switch (error)
{
case CBERR_SYSERR:
LogWarn("Commodity filter problems (CBERR_SYSERR)");
break;
case COERR_BADFILTER:
LogWarn("Commodity HOTKEY error");
break;
default:
LogWarn("Commodity filter error (error code: %ld)", error);
break;
}
}
#if defined(SCREENNOTIFY)
if (ScreenNotifyBase != NULL)
{
Broker.screenNotifyWB = AddWorkbenchClient(Broker.ScreenPort, 0);
Broker.screenNotifyPub = AddPubScreenClient(Broker.ScreenPort, 0);
LogTrace("AddWorkbenchClient");
}
#endif
ActivateNotifyPort(Broker.NotifyPort);
if (Settings->Active)
{
ActivateCxObj(Broker.Object, 1);
StartSynchronizer();
}
MsgLoop();
CleanupBroker();
CleanupPorts();
}
bool IsBrokerRunning(void)
{
return BrokerRunning;
}
void SetBrokerPriority(int priority)
{
SetCxObjPri(Broker.Object, priority);
}
void Deactivate(void)
{
if (SynchronizerRunning)
{
LogNotice(KEYWORD_ACTIVE " changed: 1 -> 0");
LogWarn("Disabling time synchronization");
SendMessageWait(MSGPORT_SYNCER, ATK_DISABLE);
ActivateCxObj(Broker.Object, 0);
}
else
{
LogError(TextSyncAlreadyOff);
}
}
void Activate(void)
{
if (!SynchronizerRunning)
{
LogNotice(KEYWORD_ACTIVE " changed: 0 -> 1");
LogWarn("Enabling time synchronization");
ActivateCxObj(Broker.Object, 1);
StartSynchronizer();
}
else
{
LogError(TextSyncAlreadyOn);
}
}
static void ChangeTimezone(void)
{
InitTimezone();
SendWindowMessage(ATK_TZ_CHANGED);
}
static void ChangeTimezoneDelayedProc(void)
{
Delay(15 * 50);
ChangeTimezone();
SendWindowMessage(ATK_TZ_CHANGED);
}
static void ChangeTimezoneDelayed(void)
{
CreateNewProcTags(
NP_Entry, (IPTR)ChangeTimezoneDelayedProc,
NP_StackSize, 16 * 1024,
NP_Name, 0,
NP_Input, 0,
NP_Output, 0,
NP_Error, 0,
NP_CloseInput, FALSE,
NP_CloseOutput, FALSE,
NP_CloseError, FALSE,
NP_WindowPtr, 0,
NP_ConsoleTask, 0,
NP_Cli, FALSE,
TAG_DONE);
}
static bool HandleBrokerMessage(LONG msgid, ULONG msgtype)
{
switch (msgtype)
{
case CXM_IEVENT:
switch (msgid)
{
case EVT_HOTKEY:
LogDebug("Show window");
ShowSettingWindow();
break;
default:
break;
}
break;
case CXM_COMMAND:
switch (msgid)
{
case CXCMD_DISABLE:
Deactivate();
break;
case CXCMD_ENABLE:
Activate();
break;
case CXCMD_KILL:
LogWarn("Received shutdown from commodity");
return false;
break;
case CXCMD_UNIQUE:
LogWarn("Commodity duplicate detected");
LogDebug("Show window");
ShowSettingWindow();
break;
case CXCMD_APPEAR:
LogDebug("Show window");
ShowSettingWindow();
break;
case CXCMD_DISAPPEAR:
LogDebug("Hide window");
SendWindowMessage(ATK_SHUTDOWN);
break;
default:
break;
}
}
return true;
}
static bool HandleUserMessage(long msg)
{
switch (msg)
{
case ATK_STORE:
SaveSettings(true);
SaveSettings(false);
SendWindowMessage(ATK_SHUTDOWN);
break;
case ATK_APPLY:
SaveSettings(false);
SendWindowMessage(ATK_SHUTDOWN);
break;
case ATK_UNDO:
ApplyAppSettings(WindowSettings, true);
SendWindowMessage(ATK_SHUTDOWN);
break;
case ATK_RESTART:
SendMessageWait(MSGPORT_SYNCER, ATK_RESTART);
break;
case ATK_DISABLE:
Activate();
break;
case ATK_ENABLE:
Deactivate();
break;
case ATK_READONLY:
LogDebug("READONLY changed: 1 -> 0");
LogWarn("Entering read only mode. Clock is not adjusted in read-only mode");
break;
case ATK_READWRITE:
LogDebug("READONLY changed: 0 -> 1");
LogWarn("Leaving read only mode. Clock is adjusted again");
break;
case ATK_SHUTDOWN:
return false;
break;
default:
break;
}
return true;
}
static void MsgLoop(void)
{
bool loop = true;
long shutdown = LOOP_RUNNING;
ULONG notifySigMask = 0;
ULONG commoditySigMask = (1 << Broker.CommodityPort->mp_SigBit);
ULONG screenSigMask = (1 << Broker.ScreenPort->mp_SigBit);
#if defined(SCREENNOTIFY)
bool reopenWindow = false;
notifySigMask = (1 << Broker.NotifyPort->mp_SigBit);
#endif
ULONG brokerSigMask = GetPortSignalMask(MSGPORT_BROKER);
ULONG memorySigMask = GetPortSignalMask(MSGPORT_MEMORY);
ULONG controlSigMask = GetPortSignalMask(MSGPORT_CONTROL);
ULONG arexxSigMask = GetPortSignalMask(MSGPORT_AREXX);
ULONG sigMask = commoditySigMask | screenSigMask | notifySigMask | brokerSigMask |
memorySigMask | controlSigMask | arexxSigMask | SIGBREAKF_CTRL_C;
BrokerRunning = true;
LogTrace("Loop started");
if (Settings->Popup || Settings->PopupOnStart)
{
ShowSettingWindow();
}
do
{
ULONG sigrcvd = Wait(sigMask);
if (sigrcvd & commoditySigMask)
{
CxMsg *msg;
while ((msg = (CxMsg *)GetMsg(Broker.CommodityPort)))
{
bool cont;
LONG msgId = CxMsgID(msg);
ULONG msgType = CxMsgType(msg);
ReplyMsg((struct Message *)msg);
cont = HandleBrokerMessage(msgId, msgType);
if (!cont)
shutdown = SHUTDOWN_REQUEST;
}
}
if (sigrcvd & brokerSigMask)
{
struct ApplicationMesage *msg;
while ((msg = (struct ApplicationMesage *)GetNewMessage(MSGPORT_BROKER)))
{
bool cont;
long msgId = msg->MsgId;
ReplyMsg((struct Message *)msg);
cont = HandleUserMessage(msgId);
if (!cont)
loop = false; // Now exit loop safely
}
}
if (sigrcvd & controlSigMask)
{
bool cont = HandleControlMessages();
if (!cont)
shutdown = SHUTDOWN_REQUEST;
}
if (sigrcvd & arexxSigMask)
{
bool cont = HandleARexxMessages();
if (!cont)
shutdown = SHUTDOWN_REQUEST;
}
if (sigrcvd & notifySigMask)
{
struct NotifyMessage *msg;
while ((msg = (struct NotifyMessage *)GetMsg(Broker.NotifyPort)))
{
long msgType = msg->nm_NReq->nr_UserData;
ReplyMsg((struct Message *)msg);
switch (msgType)
{
case ATK_TZ_CHANGED:
LogDebug("TZ changed");
ChangeTimezone();
break;
case ATK_TZONE_CHANGED:
LogDebug("TZONE changed");
ChangeTimezone();
break;
case ATK_LOCALE_CHANGED:
LogError("Locale changed");
ChangeTimezoneDelayed();
break;
}
break;
}
}
#if defined(SCREENNOTIFY)
if (sigrcvd & screenSigMask)
{
struct ScreenNotifyMessage *msg;
while ((msg = (struct ScreenNotifyMessage *)GetMsg(Broker.ScreenPort)))
{
LogDebug("ScreenNotifyMessage");
if (msg->snm_Type == SCREENNOTIFY_TYPE_WORKBENCH)
{
if (msg->snm_Value == NULL)
{
LogDebug("WB Close");
LogDebug("Hide window");
SendWindowMessage(ATK_DISABLE);
reopenWindow = WindowProcRunning;
}
else
{
LogDebug("WB Open");
if (reopenWindow)
{
ShowSettingWindow();
}
ChangeTimezone();
reopenWindow = false;
}
}
ReplyMsg((struct Message *)msg);
}
}
#endif
if (sigrcvd & memorySigMask)
{
HandleMemoryMessages();
}
if (sigrcvd & SIGBREAKF_CTRL_C)
{
LogWarn("Received CTRL + C");
shutdown = SHUTDOWN_REQUEST;
}
if (shutdown == SHUTDOWN_REQUEST)
{
shutdown = SHUTTING_DOWN;
StartShutdown();
}
} while (loop);
BrokerRunning = false;
LogTrace("Loop exited");
}
static void CleanupBroker(void)
{
#if defined(SCREENNOTIFY)
if (Broker.screenNotifyWB != NULL)
{
LogTrace("ScreenNotify WB cleared");
while (!RemWorkbenchClient(Broker.screenNotifyWB))
{
Delay(10);
}
}
if (Broker.screenNotifyPub != NULL)
{
LogTrace("ScreenNotify PubScreen cleared");
while (!RemPubScreenClient(Broker.screenNotifyPub))
{
Delay(10);
}
}
#endif
CleanupNotifications();
if (Broker.Object != NULL)
{
DeleteCxObjAll(Broker.Object);
Broker.Object = NULL;
LogTrace("CxObj cleared");
}
CleanupMsgPort(&Broker.CommodityPort);
LogTrace(TextPortCleaned, "Commodity");
CleanupMsgPort(&Broker.NotifyPort);
LogTrace(TextPortCleaned, "Notify");
CleanupMsgPort(&Broker.ScreenPort);
LogTrace(TextPortCleaned, "Screen");
}