* Implemented the BRoster::Shutdown() confirmation feature.
* When an application aborts the shutdown process by refusing to quit, the shutdown window says so for 3 seconds before closing. * Fixed the height of the normal buttons in the shutdown window. They accidentially got the size of a default button. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13564 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <Alert.h>
|
||||||
#include <AppFileInfo.h>
|
#include <AppFileInfo.h>
|
||||||
#include <AppMisc.h>
|
#include <AppMisc.h>
|
||||||
#include <Autolock.h>
|
#include <Autolock.h>
|
||||||
@@ -61,6 +62,10 @@ static const bigtime_t kBackgroundAppQuitTimeout = 3000000; // 3 s
|
|||||||
// to them before they get a KILL signal.
|
// to them before they get a KILL signal.
|
||||||
static const bigtime_t kNonAppQuitTimeout = 500000; // 0.5 s
|
static const bigtime_t kNonAppQuitTimeout = 500000; // 0.5 s
|
||||||
|
|
||||||
|
// The time span the app that has aborted the shutdown shall be displayed in
|
||||||
|
// the shutdown window before closing it automatically.
|
||||||
|
static const bigtime_t kDisplayAbortingAppTimeout = 3000000; // 3 s
|
||||||
|
|
||||||
|
|
||||||
// message what fields
|
// message what fields
|
||||||
enum {
|
enum {
|
||||||
@@ -88,7 +93,8 @@ enum {
|
|||||||
SYSTEM_APP_TERMINATION_PHASE = 1,
|
SYSTEM_APP_TERMINATION_PHASE = 1,
|
||||||
BACKGROUND_APP_TERMINATION_PHASE = 2,
|
BACKGROUND_APP_TERMINATION_PHASE = 2,
|
||||||
OTHER_PROCESSES_TERMINATION_PHASE = 3,
|
OTHER_PROCESSES_TERMINATION_PHASE = 3,
|
||||||
DONE_PHASE = 4,
|
ABORTED_PHASE = 4,
|
||||||
|
DONE_PHASE = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -309,6 +315,20 @@ public:
|
|||||||
fRebootSystemButton->SetMessage(message);
|
fRebootSystemButton->SetMessage(message);
|
||||||
fRebootSystemButton->SetTarget(target);
|
fRebootSystemButton->SetTarget(target);
|
||||||
|
|
||||||
|
// aborted OK button
|
||||||
|
fAbortedOKButton = new(nothrow) BButton(BRect(0, 0, 10, 10),
|
||||||
|
"ok", "OK", NULL, B_FOLLOW_NONE);
|
||||||
|
if (!fAbortedOKButton)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
fAbortedOKButton->Hide();
|
||||||
|
rootView->AddChild(fAbortedOKButton);
|
||||||
|
|
||||||
|
message = new BMessage(MSG_CANCEL_SHUTDOWN);
|
||||||
|
if (!message)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
fAbortedOKButton->SetMessage(message);
|
||||||
|
fAbortedOKButton->SetTarget(target);
|
||||||
|
|
||||||
// compute the sizes
|
// compute the sizes
|
||||||
static const int kHSpacing = 10;
|
static const int kHSpacing = 10;
|
||||||
static const int kVSpacing = 10;
|
static const int kVSpacing = 10;
|
||||||
@@ -320,15 +340,19 @@ public:
|
|||||||
fCancelShutdownButton->ResizeToPreferred();
|
fCancelShutdownButton->ResizeToPreferred();
|
||||||
fRebootSystemButton->MakeDefault(true);
|
fRebootSystemButton->MakeDefault(true);
|
||||||
fRebootSystemButton->ResizeToPreferred();
|
fRebootSystemButton->ResizeToPreferred();
|
||||||
|
fAbortedOKButton->MakeDefault(true);
|
||||||
|
fAbortedOKButton->ResizeToPreferred();
|
||||||
|
|
||||||
BRect rect(fKillAppButton->Frame());
|
BRect rect(fKillAppButton->Frame());
|
||||||
int buttonWidth = rect.IntegerWidth() + 1;
|
int buttonWidth = rect.IntegerWidth() + 1;
|
||||||
|
int buttonHeight = rect.IntegerHeight() + 1;
|
||||||
|
|
||||||
rect = fCancelShutdownButton->Frame();
|
rect = fCancelShutdownButton->Frame();
|
||||||
if (rect.IntegerWidth() >= buttonWidth)
|
if (rect.IntegerWidth() >= buttonWidth)
|
||||||
buttonWidth = rect.IntegerWidth() + 1;
|
buttonWidth = rect.IntegerWidth() + 1;
|
||||||
|
|
||||||
int buttonHeight = fRebootSystemButton->Frame().IntegerHeight() + 1;
|
int defaultButtonHeight
|
||||||
|
= fRebootSystemButton->Frame().IntegerHeight() + 1;
|
||||||
|
|
||||||
// text view
|
// text view
|
||||||
fTextView->SetText("two\nlines");
|
fTextView->SetText("two\nlines");
|
||||||
@@ -345,9 +369,11 @@ public:
|
|||||||
int textX = rightPartX + kInnerHSpacing;
|
int textX = rightPartX + kInnerHSpacing;
|
||||||
int textY = kVSpacing;
|
int textY = kVSpacing;
|
||||||
int buttonsY = textY + textHeight + kInnerVSpacing;
|
int buttonsY = textY + textHeight + kInnerVSpacing;
|
||||||
|
int nonDefaultButtonsY = buttonsY
|
||||||
|
+ (defaultButtonHeight - buttonHeight) / 2;
|
||||||
int rightPartWidth = 2 * buttonWidth + kInnerHSpacing;
|
int rightPartWidth = 2 * buttonWidth + kInnerHSpacing;
|
||||||
int width = rightPartX + rightPartWidth + kHSpacing;
|
int width = rightPartX + rightPartWidth + kHSpacing;
|
||||||
int height = buttonsY + buttonHeight + kVSpacing;
|
int height = buttonsY + defaultButtonHeight + kVSpacing;
|
||||||
|
|
||||||
// now layout the views
|
// now layout the views
|
||||||
|
|
||||||
@@ -362,18 +388,22 @@ public:
|
|||||||
fTextView->SetTextRect(fTextView->Bounds());
|
fTextView->SetTextRect(fTextView->Bounds());
|
||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
fKillAppButton->MoveTo(rightPartX, buttonsY);
|
fKillAppButton->MoveTo(rightPartX, nonDefaultButtonsY);
|
||||||
fKillAppButton->ResizeTo(buttonWidth - 1, buttonHeight - 1);
|
fKillAppButton->ResizeTo(buttonWidth - 1, buttonHeight - 1);
|
||||||
|
|
||||||
fCancelShutdownButton->MoveTo(
|
fCancelShutdownButton->MoveTo(
|
||||||
rightPartX + buttonWidth + kInnerVSpacing - 1,
|
rightPartX + buttonWidth + kInnerVSpacing - 1,
|
||||||
buttonsY);
|
nonDefaultButtonsY);
|
||||||
fCancelShutdownButton->ResizeTo(buttonWidth - 1, buttonHeight - 1);
|
fCancelShutdownButton->ResizeTo(buttonWidth - 1, buttonHeight - 1);
|
||||||
|
|
||||||
fRebootSystemButton->MoveTo(
|
fRebootSystemButton->MoveTo(
|
||||||
(width - fRebootSystemButton->Frame().IntegerWidth()) / 2,
|
(width - fRebootSystemButton->Frame().IntegerWidth()) / 2,
|
||||||
buttonsY);
|
buttonsY);
|
||||||
|
|
||||||
|
fAbortedOKButton->MoveTo(
|
||||||
|
(width - fAbortedOKButton->Frame().IntegerWidth()) / 2,
|
||||||
|
buttonsY);
|
||||||
|
|
||||||
// set the root view and window size
|
// set the root view and window size
|
||||||
rootView->ResizeTo(width - 1, height - 1);
|
rootView->ResizeTo(width - 1, height - 1);
|
||||||
ResizeTo(width - 1, height - 1);
|
ResizeTo(width - 1, height - 1);
|
||||||
@@ -463,6 +493,7 @@ public:
|
|||||||
fCurrentAppIconView->Hide();
|
fCurrentAppIconView->Hide();
|
||||||
fKillAppButton->Hide();
|
fKillAppButton->Hide();
|
||||||
fCancelShutdownButton->Hide();
|
fCancelShutdownButton->Hide();
|
||||||
|
fRebootSystemButton->MakeDefault(true);
|
||||||
fRebootSystemButton->Show();
|
fRebootSystemButton->Show();
|
||||||
// TODO: Temporary work-around for a Haiku bug.
|
// TODO: Temporary work-around for a Haiku bug.
|
||||||
fRebootSystemButton->Invalidate();
|
fRebootSystemButton->Invalidate();
|
||||||
@@ -471,6 +502,19 @@ public:
|
|||||||
fTextView->SetText("It's now safe to turn off the computer.");
|
fTextView->SetText("It's now safe to turn off the computer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetWaitForAbortedOK()
|
||||||
|
{
|
||||||
|
fCurrentAppIconView->Hide();
|
||||||
|
fKillAppButton->Hide();
|
||||||
|
fCancelShutdownButton->Hide();
|
||||||
|
fAbortedOKButton->MakeDefault(true);
|
||||||
|
fAbortedOKButton->Show();
|
||||||
|
// TODO: Temporary work-around for a Haiku bug.
|
||||||
|
fAbortedOKButton->Invalidate();
|
||||||
|
|
||||||
|
SetTitle("Shutdown Aborted");
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AppInfo {
|
struct AppInfo {
|
||||||
team_id team;
|
team_id team;
|
||||||
@@ -544,6 +588,7 @@ private:
|
|||||||
BButton *fKillAppButton;
|
BButton *fKillAppButton;
|
||||||
BButton *fCancelShutdownButton;
|
BButton *fCancelShutdownButton;
|
||||||
BButton *fRebootSystemButton;
|
BButton *fRebootSystemButton;
|
||||||
|
BButton *fAbortedOKButton;
|
||||||
BMessage *fKillAppMessage;
|
BMessage *fKillAppMessage;
|
||||||
team_id fCurrentApp;
|
team_id fCurrentApp;
|
||||||
};
|
};
|
||||||
@@ -567,6 +612,7 @@ ShutdownProcess::ShutdownProcess(TRoster *roster, EventQueue *eventQueue)
|
|||||||
fCurrentPhase(INVALID_PHASE),
|
fCurrentPhase(INVALID_PHASE),
|
||||||
fShutdownError(B_ERROR),
|
fShutdownError(B_ERROR),
|
||||||
fHasGUI(false),
|
fHasGUI(false),
|
||||||
|
fReboot(false),
|
||||||
fWindow(NULL)
|
fWindow(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -686,6 +732,9 @@ ShutdownProcess::Init(BMessage *request)
|
|||||||
// everything went fine: now we own the request
|
// everything went fine: now we own the request
|
||||||
fRequest = request;
|
fRequest = request;
|
||||||
|
|
||||||
|
if (fRequest->FindBool("reboot", &fReboot) != B_OK)
|
||||||
|
fReboot = false;
|
||||||
|
|
||||||
resume_thread(fWorker);
|
resume_thread(fWorker);
|
||||||
|
|
||||||
PRINT(("ShutdownProcess::Init() done\n"));
|
PRINT(("ShutdownProcess::Init() done\n"));
|
||||||
@@ -742,6 +791,7 @@ ShutdownProcess::MessageReceived(BMessage *message)
|
|||||||
// get the phase the event is intended for
|
// get the phase the event is intended for
|
||||||
int32 phase = TimeoutEvent::GetMessagePhase(message);
|
int32 phase = TimeoutEvent::GetMessagePhase(message);
|
||||||
team_id team = TimeoutEvent::GetMessageTeam(message);;
|
team_id team = TimeoutEvent::GetMessageTeam(message);;
|
||||||
|
PRINT(("MSG_PHASE_TIMED_OUT: phase: %ld, team: %ld\n", phase, team));
|
||||||
|
|
||||||
BAutolock _(fWorkerLock);
|
BAutolock _(fWorkerLock);
|
||||||
|
|
||||||
@@ -1021,6 +1071,17 @@ ShutdownProcess::_SetShutdownWindowWaitForShutdown()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _SetShutdownWindowWaitForAbortedOK
|
||||||
|
void
|
||||||
|
ShutdownProcess::_SetShutdownWindowWaitForAbortedOK()
|
||||||
|
{
|
||||||
|
if (fHasGUI) {
|
||||||
|
BAutolock _(fWindow);
|
||||||
|
|
||||||
|
fWindow->SetWaitForAbortedOK();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// _NegativeQuitRequestReply
|
// _NegativeQuitRequestReply
|
||||||
void
|
void
|
||||||
ShutdownProcess::_NegativeQuitRequestReply(thread_id thread)
|
ShutdownProcess::_NegativeQuitRequestReply(thread_id thread)
|
||||||
@@ -1047,13 +1108,8 @@ ShutdownProcess::_PrepareShutdownMessage(BMessage &message) const
|
|||||||
status_t
|
status_t
|
||||||
ShutdownProcess::_ShutDown()
|
ShutdownProcess::_ShutDown()
|
||||||
{
|
{
|
||||||
// get the reboot flag
|
|
||||||
bool reboot;
|
|
||||||
if (fRequest->FindBool("reboot", &reboot) != B_OK)
|
|
||||||
reboot = false;
|
|
||||||
|
|
||||||
#ifdef __HAIKU__
|
#ifdef __HAIKU__
|
||||||
return _kern_shutdown(reboot);
|
return _kern_shutdown(fReboot);
|
||||||
#else
|
#else
|
||||||
// we can't do anything on R5
|
// we can't do anything on R5
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -1166,6 +1222,18 @@ ShutdownProcess::_WorkerDoShutdown()
|
|||||||
{
|
{
|
||||||
PRINT(("ShutdownProcess::_WorkerDoShutdown()\n"));
|
PRINT(("ShutdownProcess::_WorkerDoShutdown()\n"));
|
||||||
|
|
||||||
|
// ask the user to confirm the shutdown, if desired
|
||||||
|
bool askUser;
|
||||||
|
if (fHasGUI && fRequest->FindBool("confirm", &askUser) == B_OK && askUser) {
|
||||||
|
BAlert *alert = new BAlert("Shut Down?",
|
||||||
|
"Do you really want to shut down the system?",
|
||||||
|
"Shut Down", "Cancel", NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
||||||
|
int32 result = alert->Go();
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
throw_error(B_SHUTDOWN_CANCELLED);
|
||||||
|
}
|
||||||
|
|
||||||
// make the shutdown window ready and show it
|
// make the shutdown window ready and show it
|
||||||
_InitShutdownWindow();
|
_InitShutdownWindow();
|
||||||
_SetShutdownWindowCurrentApp(-1);
|
_SetShutdownWindowCurrentApp(-1);
|
||||||
@@ -1256,6 +1324,7 @@ ShutdownProcess::_QuitApps(AppInfoList &list, bool disableCancel)
|
|||||||
PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
|
PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
|
||||||
"team %ld (-1 => user)\n", team));
|
"team %ld (-1 => user)\n", team));
|
||||||
|
|
||||||
|
_DisplayAbortingApp(team);
|
||||||
throw_error(B_SHUTDOWN_CANCELLED);
|
throw_error(B_SHUTDOWN_CANCELLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1281,6 +1350,7 @@ ShutdownProcess::_QuitApps(AppInfoList &list, bool disableCancel)
|
|||||||
PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
|
PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
|
||||||
"team %ld (-1 => user)\n", team));
|
"team %ld (-1 => user)\n", team));
|
||||||
|
|
||||||
|
_DisplayAbortingApp(team);
|
||||||
throw_error(B_SHUTDOWN_CANCELLED);
|
throw_error(B_SHUTDOWN_CANCELLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1348,6 +1418,7 @@ ShutdownProcess::_QuitApps(AppInfoList &list, bool disableCancel)
|
|||||||
PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled "
|
PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled "
|
||||||
"by team %ld (-1 => user)\n", eventTeam));
|
"by team %ld (-1 => user)\n", eventTeam));
|
||||||
|
|
||||||
|
_DisplayAbortingApp(team);
|
||||||
throw_error(B_SHUTDOWN_CANCELLED);
|
throw_error(B_SHUTDOWN_CANCELLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1599,3 +1670,68 @@ ShutdownProcess::_QuitBlockingApp(AppInfoList &list, team_id team,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _DisplayAbortingApp
|
||||||
|
void
|
||||||
|
ShutdownProcess::_DisplayAbortingApp(team_id team)
|
||||||
|
{
|
||||||
|
// find the app that cancelled the shutdown
|
||||||
|
char appName[B_FILE_NAME_LENGTH];
|
||||||
|
bool foundApp = false;
|
||||||
|
{
|
||||||
|
BAutolock _(fWorkerLock);
|
||||||
|
|
||||||
|
RosterAppInfo *info = fUserApps.InfoFor(team);
|
||||||
|
if (!info)
|
||||||
|
info = fSystemApps.InfoFor(team);
|
||||||
|
if (!info)
|
||||||
|
fBackgroundApps.InfoFor(team);
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
foundApp = true;
|
||||||
|
strcpy(appName, info->ref.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundApp) {
|
||||||
|
PRINT(("ShutdownProcess::_DisplayAbortingApp(): Didn't find the app "
|
||||||
|
"that has cancelled the shutdown.\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compose the text to be displayed
|
||||||
|
char buffer[1024];
|
||||||
|
snprintf(buffer, sizeof(buffer), "Application \"%s\" has aborted the shutdown "
|
||||||
|
"process.", appName);
|
||||||
|
|
||||||
|
// set up the window
|
||||||
|
_SetShutdownWindowCurrentApp(team);
|
||||||
|
_SetShutdownWindowText(buffer);
|
||||||
|
_SetShutdownWindowWaitForAbortedOK();
|
||||||
|
|
||||||
|
// schedule the timeout event
|
||||||
|
_SetPhase(ABORTED_PHASE);
|
||||||
|
_ScheduleTimeoutEvent(kDisplayAbortingAppTimeout);
|
||||||
|
|
||||||
|
// wait for the timeout or the user to press the cancel button
|
||||||
|
while (true) {
|
||||||
|
uint32 event;
|
||||||
|
team_id eventTeam;
|
||||||
|
int32 phase;
|
||||||
|
status_t error = _GetNextEvent(event, eventTeam, phase, true);
|
||||||
|
if (error != B_OK)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// stop waiting when the timeout occurs
|
||||||
|
if (event == TIMEOUT_EVENT)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// stop waiting when the user hit the cancel button
|
||||||
|
if (event == ABORT_EVENT && phase == ABORTED_PHASE && eventTeam < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// also stop when the responsible app quit
|
||||||
|
if ((event == APP_QUIT_EVENT) && eventTeam == team)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ private:
|
|||||||
void _SetShutdownWindowCancelButtonEnabled(bool enabled);
|
void _SetShutdownWindowCancelButtonEnabled(bool enabled);
|
||||||
void _SetShutdownWindowKillButtonEnabled(bool enabled);
|
void _SetShutdownWindowKillButtonEnabled(bool enabled);
|
||||||
void _SetShutdownWindowWaitForShutdown();
|
void _SetShutdownWindowWaitForShutdown();
|
||||||
|
void _SetShutdownWindowWaitForAbortedOK();
|
||||||
|
|
||||||
static status_t _WorkerEntry(void *data);
|
static status_t _WorkerEntry(void *data);
|
||||||
status_t _Worker();
|
status_t _Worker();
|
||||||
@@ -66,8 +67,9 @@ private:
|
|||||||
void _WaitForBackgroundApps();
|
void _WaitForBackgroundApps();
|
||||||
void _KillBackgroundApps();
|
void _KillBackgroundApps();
|
||||||
void _QuitNonApps();
|
void _QuitNonApps();
|
||||||
void _QuitBlockingApp(AppInfoList &list, team_id app, const char *appName,
|
void _QuitBlockingApp(AppInfoList &list, team_id team, const char *appName,
|
||||||
bool cancelAllowed);
|
bool cancelAllowed);
|
||||||
|
void _DisplayAbortingApp(team_id team);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class TimeoutEvent;
|
class TimeoutEvent;
|
||||||
@@ -95,6 +97,7 @@ private:
|
|||||||
int32 fCurrentPhase;
|
int32 fCurrentPhase;
|
||||||
status_t fShutdownError;
|
status_t fShutdownError;
|
||||||
bool fHasGUI;
|
bool fHasGUI;
|
||||||
|
bool fReboot;
|
||||||
ShutdownWindow *fWindow;
|
ShutdownWindow *fWindow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user