From 3328351a156b6c8cdd00409d0bc8854f9de28c6e Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Fri, 11 Mar 2005 13:08:52 +0000 Subject: [PATCH] Changes for Mac GUI BOINC manager and core client. Major rework of XCode project file. BOINC Manager has standard Mac application bundle with icon. Client doesn't ask for User ID or Project URL on command line. Added code for Mac to determine idle time (user inactivity). Core Client is embedded in BOINC Manager application bundle. BOINC Manager sets working directory to ~/Application Support/Boinc_Data/ and then launches embedded Core Client. Restored functions boinc_close_socket() and get_socket_error() to net_xfer.C to eliminate link errors. Modified Files: client/hostinfo_unix.C client/main.C client/net_xfer.C clientgui/BOINCGUIApp.cpp lib/hostinfo.h Added Files: mac_build/boinc.pbproj.zip svn path=/trunk/boinc/; revision=5628 --- client/hostinfo_unix.C | 8 ++++++ client/main.C | 10 ++++++++ client/net_xfer.C | 20 +++++++++++++++ clientgui/BOINCGUIApp.cpp | 49 ++++++++++++++++++++++++++++++++++++- lib/hostinfo.h | 15 ++++++++++++ mac_build/boinc.pbproj.zip | Bin 0 -> 11829 bytes 6 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 mac_build/boinc.pbproj.zip diff --git a/client/hostinfo_unix.C b/client/hostinfo_unix.C index 7313974e2b..d5a6cdf3d0 100644 --- a/client/hostinfo_unix.C +++ b/client/hostinfo_unix.C @@ -71,6 +71,10 @@ using std::string; +#ifdef __APPLE__ +NXEventHandle gEventHandle; +#endif // __APPLE__ + // anyone know how to see if this host has physical network connection? // int get_connected_state() { @@ -580,6 +584,10 @@ bool HOST_INFO::users_idle(bool check_all_logins, double idle_time_to_run) { #ifdef HAVE__DEV_TTY1 && (check_all_logins || all_tty_idle(idle_time, device_tty, '1', 7)) #endif +#ifdef __APPLE__ + && (NXIdleTime(gEventHandle) > (60 * idle_time_to_run)) +#endif // __APPLE__ + ; } diff --git a/client/main.C b/client/main.C index 5264bcd62a..cfdacb3c83 100644 --- a/client/main.C +++ b/client/main.C @@ -140,6 +140,9 @@ void show_message(PROJECT *p, char* msg, int priority) { // and create an account file // int add_new_project() { +#if defined(__WXMAC__) // If we have a GUI BOINC Manager + return 0; +#else PROJECT project; printf("Enter the URL of the project: "); @@ -158,6 +161,7 @@ int add_new_project() { project.tentative = true; return project.write_account_file(); +#endif } #ifdef WIN32 @@ -418,6 +422,12 @@ int main(int argc, char** argv) { retval = boinc_main_loop(); } #else + +#ifdef __APPLE__ + // Initialize Mac OS X idle time measurement / idle detection + gEventHandle = NXOpenEventStatus(); +#endif // __APPLE__ + retval = boinc_main_loop(); #endif diff --git a/client/net_xfer.C b/client/net_xfer.C index 7a7e95da74..aa7cf56912 100644 --- a/client/net_xfer.C +++ b/client/net_xfer.C @@ -68,6 +68,26 @@ using std::vector; // in this many seconds, error out #define NET_XFER_TIMEOUT 600 +static void boinc_close_socket(int sock) { +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif +} + +int get_socket_error(int fd) { + socklen_t intsize = sizeof(int); + int n; +#ifdef WIN32 + getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&n, &intsize); +#elif __APPLE__ + getsockopt(fd, SOL_SOCKET, SO_ERROR, &n, (int *)&intsize); +#else + getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&n, &intsize); +#endif + return n; +} int NET_XFER::get_ip_addr(int &ip_addr) { diff --git a/clientgui/BOINCGUIApp.cpp b/clientgui/BOINCGUIApp.cpp index bb8ad5ef41..4d8435aae8 100644 --- a/clientgui/BOINCGUIApp.cpp +++ b/clientgui/BOINCGUIApp.cpp @@ -34,6 +34,9 @@ typedef void (CALLBACK* IdleTrackerTerm)(); typedef DWORD (CALLBACK* IdleTrackerGetIdleTickCount)(); #endif +#ifdef __WXMAC__ +#include +#endif IMPLEMENT_APP(CBOINCGUIApp) IMPLEMENT_DYNAMIC_CLASS(CBOINCGUIApp, wxApp) @@ -304,6 +307,43 @@ void CBOINCGUIApp::StartupBOINCCore() #endif +#ifdef __WXMAC__ + + { + wxChar buf[1024]; + bool success; + ProcessSerialNumber ourPSN; + FSRef ourFSRef; + OSErr err; + + // Set the current directory ahead of the application launch so the core + // client can find its files + wxExpandPath(buf, "~/Library/Application Support"); + strDirectory = wxT(buf); + success = ::wxSetWorkingDirectory( strDirectory ); + if (success) // If SetWD failed, don't create a directory in wrong place + success = wxMkdir( wxT("BOINC Data"), 0777); // Does nothing if dir exists + strDirectory += wxT("/BOINC Data"); + success = ::wxSetWorkingDirectory( strDirectory ); +// wxChar *wd = wxGetWorkingDirectory(buf, 1000); // For debugging + // Get the full path to core client inside this application's bundle + err = GetCurrentProcess (&ourPSN); + if (err == noErr) + err = GetProcessBundleLocation(&ourPSN, &ourFSRef); + if (err == noErr) + err = FSRefMakePath (&ourFSRef, (UInt8*)buf, sizeof(buf)); + if (err == noErr) + { + strExecute = wxT("\""); + strExecute += wxT(buf); + strExecute += wxT("/Contents/Resources/boinc_client\" -redirectio"); + } + else + buf[0] = '\0'; + } + +#else // ! __WXMAC__ + // We are only interested in the path component of the fully qualified path. wxFileName::SplitPath( szExecutableDirectory, &strDirectory, NULL, NULL ); @@ -311,6 +351,8 @@ void CBOINCGUIApp::StartupBOINCCore() // client can find its files ::wxSetWorkingDirectory( strDirectory ); +#endif // ! __WXMAC__ + #ifdef __WXMSW__ // Append boinc.exe to the end of the strExecute string and get ready to rock @@ -347,11 +389,16 @@ void CBOINCGUIApp::StartupBOINCCore() #else +#ifndef __WXMAC__ + // Append boinc.exe to the end of the strExecute string and get ready to rock strExecute += wxT("/boinc"); + +#endif // ! __WXMAC__ + m_lBOINCCoreProcessId = ::wxExecute( strExecute ); -#endif +#endif // ! __WXMSW__ if ( 0 != m_lBOINCCoreProcessId ) m_bBOINCStartedByManager = true; diff --git a/lib/hostinfo.h b/lib/hostinfo.h index d8b0186df3..8aacd4d665 100644 --- a/lib/hostinfo.h +++ b/lib/hostinfo.h @@ -74,4 +74,19 @@ struct HOST_INFO { extern HINSTANCE g_hIdleDetectionDll; // handle to DLL for user idle #endif +#ifdef __APPLE__ +#ifdef __cplusplus +extern "C" { +#endif +#include +typedef mach_port_t NXEventHandle; +NXEventHandle NXOpenEventStatus(void); +extern double NXIdleTime(NXEventHandle handle); +#ifdef __cplusplus +} // extern "C" +#endif + +extern NXEventHandle gEventHandle; +#endif + #endif diff --git a/mac_build/boinc.pbproj.zip b/mac_build/boinc.pbproj.zip new file mode 100644 index 0000000000000000000000000000000000000000..95fcf744fe3fd1199687fa5b4d8e85e91d397caa GIT binary patch literal 11829 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