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