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