1 /*
2  *  runinbg.cpp
3  *  PHD Guiding
4  *
5  *  Created by Andy Galasso.
6  *  Copyright (c) 2014-2017 Andy Galasso
7  *  All rights reserved.
8  *
9  *  This source code is distributed under the following "BSD" license
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions are met:
12  *    Redistributions of source code must retain the above copyright notice,
13  *     this list of conditions and the following disclaimer.
14  *    Redistributions in binary form must reproduce the above copyright notice,
15  *     this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *    Neither the name of Craig Stark, Stark Labs nor the names of its
18  *     contributors may be used to endorse or promote products derived from
19  *     this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  *  POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 #include "phd.h"
36 
37 #include <wx/progdlg.h>
38 
39 struct ProgressWindow : public wxProgressDialog
40 {
ProgressWindowProgressWindow41     ProgressWindow(wxWindow *parent, const wxString& title, const wxString& message)
42         : wxProgressDialog(title, message, 100, parent, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT)
43     {
44     }
45 };
46 
47 struct RunInBgImpl : public wxTimer, public wxThreadHelper
48 {
49     RunInBg *m_bg;
50     wxWindow *m_parent;
51     wxString m_title;
52     wxString m_message;
53     wxCriticalSection m_updateMsgCS;
54     wxString *m_updateMsg;
55     ProgressWindow *m_win;
56     bool m_shown;
57     volatile bool m_done;
58     volatile bool m_canceled;
59     wxDateTime m_showTime;
60     wxString m_errorMsg;
61     unsigned int m_popupDelayMillis;
62 
RunInBgImplRunInBgImpl63     RunInBgImpl(RunInBg *bg, wxWindow *parent, const wxString& title, const wxString& message)
64         : m_bg(bg),
65         m_parent(parent),
66         m_title(title),
67         m_message(message),
68         m_updateMsg(nullptr),
69         m_win(0),
70         m_shown(false),
71         m_done(false),
72         m_canceled(false),
73         m_popupDelayMillis(2500)
74     {
75     }
76 
RunRunInBgImpl77     bool Run()
78     {
79         bool err = false;
80 
81         wxBusyCursor busy;
82         if (m_parent)
83             m_parent->SetCursor(wxCURSOR_WAIT); // need to do this too!
84 #ifndef __APPLE__
85         // this makes the progress window inaccessible on OSX
86         wxWindowDisabler wd;
87 #endif // __APPLE__
88 
89         CreateThread();
90         wxThread *thread = GetThread();
91         thread->Run();
92         m_showTime = wxDateTime::UNow() + wxTimeSpan(0, 0, m_popupDelayMillis / 1000, m_popupDelayMillis % 1000);
93         Start(250); // start timer
94         while (!m_done && !m_canceled)
95         {
96             wxYield();
97             wxMilliSleep(20);
98         }
99         Stop(); // stop timer
100         if (m_canceled && !m_done)
101         {
102             // give it a bit of time to respond to cancel before killing it
103             for (int i = 0; i < 50 && !m_done; i++)
104             {
105                 wxYield();
106                 wxMilliSleep(20);
107             }
108             if (!m_done)
109             {
110                 Debug.AddLine("Background thread did not respond to cancel... kill it");
111                 m_bg->OnKill();
112                 thread->Kill();
113                 thread = 0;
114                 err = true;
115             }
116         }
117         if (m_canceled && m_errorMsg.empty())
118             m_errorMsg = _("The operation was canceled");
119         if (m_win)
120         {
121             delete m_win;
122             m_win = 0;
123         }
124         if (thread)
125             err = thread->Wait() ? true : false;
126 
127         if (m_parent)
128             m_parent->SetCursor(wxCURSOR_ARROW);
129 
130         return err;
131     }
132 
SetMessageRunInBgImpl133     void SetMessage(const wxString& message)
134     {
135         if (m_win)
136         {
137             wxCriticalSectionLocker _lck(m_updateMsgCS);
138             m_updateMsg = new wxString(message);
139         }
140         else
141             m_message = message;
142     }
143 
NotifyRunInBgImpl144     void Notify() // timer notification
145     {
146         if (!m_shown && wxDateTime::UNow() >= m_showTime)
147         {
148             m_win = new ProgressWindow(m_parent, m_title, m_message);
149             m_shown = true;
150         }
151         if (m_win)
152         {
153             wxString *updateMsg;
154             { // lock scope
155                 wxCriticalSectionLocker _lck(m_updateMsgCS);
156                 updateMsg = m_updateMsg;
157                 m_updateMsg = nullptr;
158             }
159 
160             bool cont;
161             if (updateMsg)
162             {
163                 cont = m_win->Pulse(*updateMsg);
164                 m_message = *updateMsg;
165                 delete updateMsg;
166             }
167             else
168                 cont = m_win->Pulse();
169 
170             if (!cont)
171             {
172                 m_canceled = true;
173                 Debug.AddLine("Canceled");
174                 m_bg->OnCancel();
175                 delete m_win;
176                 m_win = 0;
177             }
178         }
179     }
180 
EntryRunInBgImpl181     wxThread::ExitCode Entry()
182     {
183         bool err = m_bg->Entry();
184         m_done = true;
185         return (wxThread::ExitCode) err;
186     }
187 };
188 
RunInBg(wxWindow * parent,const wxString & title,const wxString & message)189 RunInBg::RunInBg(wxWindow *parent, const wxString& title, const wxString& message)
190     : m_impl(new RunInBgImpl(this, parent, title, message))
191 {
192 }
193 
~RunInBg(void)194 RunInBg::~RunInBg(void)
195 {
196     delete m_impl;
197 }
198 
SetPopupDelay(unsigned int millis)199 void RunInBg::SetPopupDelay(unsigned int millis)
200 {
201     m_impl->m_popupDelayMillis = millis;
202 }
203 
Run(void)204 bool RunInBg::Run(void)
205 {
206     return m_impl->Run();
207 }
208 
SetMessage(const wxString & message)209 void RunInBg::SetMessage(const wxString& message)
210 {
211     m_impl->SetMessage(message);
212 }
213 
SetErrorMsg(const wxString & msg)214 void RunInBg::SetErrorMsg(const wxString& msg)
215 {
216     m_impl->m_errorMsg = msg;
217 }
218 
GetErrorMsg(void)219 wxString RunInBg::GetErrorMsg(void)
220 {
221     return m_impl->m_errorMsg;
222 }
223 
IsCanceled(void)224 bool RunInBg::IsCanceled(void)
225 {
226     return m_impl->m_canceled;
227 }
228 
OnCancel()229 void RunInBg::OnCancel()
230 {
231 }
232 
OnKill()233 void RunInBg::OnKill()
234 {
235 }
236