1 /* 2 * Copyright 2015-2016, Rene Gollent, rene@gollent.com. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ReportUserInterface.h" 8 9 #include <stdio.h> 10 11 #include <Entry.h> 12 #include <FindDirectory.h> 13 #include <Path.h> 14 15 #include <AutoLocker.h> 16 17 #include "MessageCodes.h" 18 #include "UiUtils.h" 19 20 21 ReportUserInterface::ReportUserInterface(thread_id targetThread, 22 const char* reportPath) 23 : 24 fTeam(NULL), 25 fListener(NULL), 26 fTargetThread(targetThread), 27 fReportPath(reportPath), 28 fShowSemaphore(-1), 29 fReportSemaphore(-1), 30 fShown(false), 31 fTerminating(false) 32 { 33 } 34 35 36 ReportUserInterface::~ReportUserInterface() 37 { 38 if (fShowSemaphore >= 0) 39 delete_sem(fShowSemaphore); 40 41 if (fTeam != NULL) 42 fTeam->RemoveListener(this); 43 } 44 45 46 const char* 47 ReportUserInterface::ID() const 48 { 49 return "ReportUserInterface"; 50 } 51 52 53 status_t 54 ReportUserInterface::Init(Team* team, UserInterfaceListener* listener) 55 { 56 fShowSemaphore = create_sem(0, "show report"); 57 if (fShowSemaphore < 0) 58 return fShowSemaphore; 59 60 fReportSemaphore = create_sem(0, "report generator wait"); 61 if (fReportSemaphore < 0) 62 return fReportSemaphore; 63 64 fTeam = team; 65 fListener = listener; 66 67 fTeam->AddListener(this); 68 69 return B_OK; 70 } 71 72 73 void 74 ReportUserInterface::Show() 75 { 76 fShown = true; 77 release_sem(fShowSemaphore); 78 } 79 80 81 void 82 ReportUserInterface::Terminate() 83 { 84 fTerminating = true; 85 } 86 87 88 UserInterface* 89 ReportUserInterface::Clone() const 90 { 91 // the report interface does not support cloning, since 92 // it won't ever be asked to interactively restart. 93 return NULL; 94 } 95 96 97 bool 98 ReportUserInterface::IsInteractive() const 99 { 100 return false; 101 } 102 103 104 status_t 105 ReportUserInterface::LoadSettings(const TeamUiSettings* settings) 106 { 107 return B_OK; 108 } 109 110 111 status_t 112 ReportUserInterface::SaveSettings(TeamUiSettings*& settings) const 113 { 114 return B_OK; 115 } 116 117 118 void 119 ReportUserInterface::NotifyUser(const char* title, const char* message, 120 user_notification_type type) 121 { 122 } 123 124 125 void 126 ReportUserInterface::NotifyBackgroundWorkStatus(const char* message) 127 { 128 } 129 130 131 int32 132 ReportUserInterface::SynchronouslyAskUser(const char* title, 133 const char* message, const char* choice1, const char* choice2, 134 const char* choice3) 135 { 136 return -1; 137 } 138 139 140 status_t 141 ReportUserInterface::SynchronouslyAskUserForFile(entry_ref* _ref) 142 { 143 return B_UNSUPPORTED; 144 } 145 146 147 void 148 ReportUserInterface::Run() 149 { 150 // Wait for the Show() semaphore to be released. 151 status_t error; 152 do { 153 error = acquire_sem(fShowSemaphore); 154 } while (error == B_INTERRUPTED); 155 156 if (error != B_OK) 157 return; 158 159 bool waitNeeded = false; 160 if (fTargetThread > 0) { 161 AutoLocker< ::Team> teamLocker(fTeam); 162 ::Thread* thread = fTeam->ThreadByID(fTargetThread); 163 if (thread == NULL) 164 waitNeeded = true; 165 else if (thread->State() != THREAD_STATE_STOPPED) { 166 waitNeeded = true; 167 fListener->ThreadActionRequested(fTargetThread, MSG_THREAD_STOP); 168 } 169 } 170 171 if (waitNeeded) { 172 do { 173 error = acquire_sem(fShowSemaphore); 174 } while (error == B_INTERRUPTED); 175 176 if (error != B_OK) 177 return; 178 } 179 180 entry_ref ref; 181 if (fReportPath != NULL && fReportPath[0] == '/') { 182 error = get_ref_for_path(fReportPath, &ref); 183 } else { 184 char filename[B_FILE_NAME_LENGTH]; 185 if (fReportPath != NULL) 186 strlcpy(filename, fReportPath, sizeof(filename)); 187 else 188 UiUtils::ReportNameForTeam(fTeam, filename, sizeof(filename)); 189 190 BPath path; 191 error = find_directory(B_DESKTOP_DIRECTORY, &path); 192 if (error == B_OK) 193 error = path.Append(filename); 194 if (error == B_OK) 195 error = get_ref_for_path(path.Path(), &ref); 196 } 197 198 if (error != B_OK) 199 printf("Unable to get ref for report path %s\n", strerror(error)); 200 else { 201 fListener->DebugReportRequested(&ref); 202 203 do { 204 error = acquire_sem(fReportSemaphore); 205 } while (error == B_INTERRUPTED); 206 } 207 208 fListener->UserInterfaceQuitRequested( 209 UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM); 210 } 211 212 213 void 214 ReportUserInterface::ThreadAdded(const Team::ThreadEvent& event) 215 { 216 ::Thread* thread = event.GetThread(); 217 if (thread->ID() != fTargetThread) 218 return; 219 220 if (thread->State() != THREAD_STATE_STOPPED) 221 fListener->ThreadActionRequested(thread->ID(), MSG_THREAD_STOP); 222 else 223 release_sem(fShowSemaphore); 224 } 225 226 227 void 228 ReportUserInterface::ThreadStateChanged(const Team::ThreadEvent& event) 229 { 230 ::Thread* thread = event.GetThread(); 231 if (thread->ID() != fTargetThread) 232 return; 233 else if (thread->State() == THREAD_STATE_STOPPED) 234 release_sem(fShowSemaphore); 235 } 236 237 238 void 239 ReportUserInterface::DebugReportChanged(const Team::DebugReportEvent& event) 240 { 241 if (event.GetFinalStatus() == B_OK) 242 printf("Debug report saved to %s\n", event.GetReportPath()); 243 else { 244 fprintf(stderr, "Failed to write debug report: %s\n", strerror( 245 event.GetFinalStatus())); 246 } 247 release_sem(fReportSemaphore); 248 } 249