[boinc] 01/12: Imported Upstream version 7.4.14+dfsg

Gianfranco Costamagna locutusofborg-guest at moszumanska.debian.org
Wed Aug 13 20:30:43 UTC 2014


This is an automated email from the git hooks/post-receive script.

locutusofborg-guest pushed a commit to branch master
in repository boinc.

commit efad3c040ba9e9d871672efdefc5cf8af6acdf1d
Author: Gianfranco Costamagna <costamagnagianfranco at yahoo.it>
Date:   Tue Aug 12 11:06:09 2014 +0200

    Imported Upstream version 7.4.14+dfsg
---
 android/BOINC/AndroidManifest.xml                  |   4 +-
 .../attach_project_batch_processing_layout.xml     |  14 ++
 .../layout/attach_project_list_layout_listitem.xml |  10 ++
 android/BOINC/res/menu/projects_menu.xml           |   4 -
 android/BOINC/res/values/strings.xml               |  10 +-
 .../src/edu/berkeley/boinc/ProjectsFragment.java   |  18 --
 .../edu/berkeley/boinc/attach/AcctMgrFragment.java |  20 ++-
 .../boinc/attach/BatchProcessingActivity.java      |  15 +-
 .../boinc/attach/SelectionListActivity.java        |  10 ++
 .../boinc/attach/SelectionListAdapter.java         |  63 +++++--
 client/app.cpp                                     |  22 ++-
 client/app.h                                       |  10 +-
 client/app_config.cpp                              |  34 +++-
 client/app_config.h                                |   2 +
 client/app_control.cpp                             |  91 +++++-----
 client/app_start.cpp                               |  64 ++++---
 client/client_state.cpp                            |  30 +++-
 client/client_state.h                              |   9 +-
 client/client_types.cpp                            |  12 +-
 client/client_types.h                              |   4 +-
 client/cpu_sched.cpp                               |  19 +-
 client/cs_account.cpp                              |   4 +-
 client/cs_cmdline.cpp                              |   4 -
 client/cs_notice.cpp                               |   3 +
 client/cs_scheduler.cpp                            |  36 ++--
 client/cs_trickle.cpp                              |  14 +-
 client/file_names.h                                |   1 +
 client/gpu_detect.cpp                              |  76 ++++++++
 client/gpu_detect.h                                |   9 +
 client/gpu_opencl.cpp                              |  75 ++++++--
 client/project.cpp                                 |   6 +
 client/project.h                                   |   5 +
 client/result.h                                    |  17 +-
 client/rr_sim.cpp                                  |  48 +++---
 client/sandbox.cpp                                 | 192 ++++++++++++++++-----
 client/scheduler_op.cpp                            |   2 +
 client/sim.cpp                                     |  21 +--
 client/switcher.cpp                                |  10 +-
 client/work_fetch.cpp                              |  49 +++++-
 client/work_fetch.h                                |   2 +
 clientgui/DlgAdvPreferences.cpp                    |  28 ++-
 clientgui/DlgAdvPreferencesBase.cpp                | 121 +++++++------
 clientgui/DlgAdvPreferencesBase.h                  |   3 +-
 clientgui/MainDocument.cpp                         |  34 ++++
 clientgui/MainDocument.h                           |   1 +
 clientgui/ViewResources.cpp                        |  30 +---
 clientgui/ViewStatistics.cpp                       |  38 +---
 clientgui/ViewStatistics.h                         |  12 --
 configure.ac                                       |   2 +-
 db/boinc_db_types.h                                |   5 +
 lib/coproc.cpp                                     |  44 +++--
 lib/coproc.h                                       |  41 ++++-
 lib/error_numbers.h                                |   1 +
 lib/gui_rpc_client.h                               |   1 +
 lib/gui_rpc_client_ops.cpp                         |   2 +
 lib/gui_rpc_client_print.cpp                       |   1 +
 lib/opencl_boinc.h                                 |   4 +-
 lib/proc_control.cpp                               |   4 +
 lib/procinfo_mac.cpp                               |   7 +-
 lib/shmem.cpp                                      |   2 +-
 lib/str_util.cpp                                   |   1 +
 lib/util.cpp                                       |  25 +++
 lib/util.h                                         |   2 +
 sched/handle_request.cpp                           |   7 +-
 sched/sched_types.cpp                              |  11 +-
 65 files changed, 1030 insertions(+), 436 deletions(-)

diff --git a/android/BOINC/AndroidManifest.xml b/android/BOINC/AndroidManifest.xml
index 1b230a0..1eba712 100644
--- a/android/BOINC/AndroidManifest.xml
+++ b/android/BOINC/AndroidManifest.xml
@@ -20,8 +20,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="edu.berkeley.boinc"
     android:installLocation="internalOnly"
-    android:versionCode="83"
-    android:versionName="7.4.8" > <!-- installation on SD card would break boot receiver -->
+    android:versionCode="89"
+    android:versionName="7.4.14" > <!-- installation on SD card would break boot receiver -->
 
 
     <!-- Add Google Play store metadata informing the store we can run on tablets and other large screen devices -->
diff --git a/android/BOINC/res/layout/attach_project_batch_processing_layout.xml b/android/BOINC/res/layout/attach_project_batch_processing_layout.xml
index 61e1584..c950bcb 100644
--- a/android/BOINC/res/layout/attach_project_batch_processing_layout.xml
+++ b/android/BOINC/res/layout/attach_project_batch_processing_layout.xml
@@ -113,6 +113,20 @@
 	     </LinearLayout>
 	     
         <Button 
+	        android:id="@+id/share_button"
+	        android:layout_width="wrap_content"
+	        android:layout_height="wrap_content"
+	        android:text="@string/social_invite_button"
+			android:textColor="@android:color/white"
+			android:textAppearance="?android:attr/textAppearanceMedium"
+	        android:background="@drawable/shape_button_blue"
+	        android:minWidth="150dp"
+			android:padding="5dp"
+			android:layout_margin="10dp"
+			android:onClick="shareClicked"
+			android:visibility="gone"/>
+	     
+        <Button 
 	        android:id="@+id/continue_button"
 	        android:layout_width="wrap_content"
 	        android:layout_height="wrap_content"
diff --git a/android/BOINC/res/layout/attach_project_list_layout_listitem.xml b/android/BOINC/res/layout/attach_project_list_layout_listitem.xml
index 081df81..2d5ba7f 100644
--- a/android/BOINC/res/layout/attach_project_list_layout_listitem.xml
+++ b/android/BOINC/res/layout/attach_project_list_layout_listitem.xml
@@ -51,5 +51,15 @@
     	android:layout_height="wrap_content"
     	android:layout_alignParentRight="true"
     	android:layout_centerVertical="true"/>
+	
+    <ImageView 
+    	android:id="@+id/am_button_image"
+    	android:layout_width="wrap_content"
+    	android:layout_height="wrap_content"
+    	android:src="@drawable/nextb"
+    	android:layout_centerVertical="true"
+    	android:layout_alignParentRight="true"
+    	android:visibility="gone"
+    	android:contentDescription="@string/attachproject_acctmgr_header"/>
     
 </RelativeLayout>
diff --git a/android/BOINC/res/menu/projects_menu.xml b/android/BOINC/res/menu/projects_menu.xml
index 80eaaef..3b78310 100644
--- a/android/BOINC/res/menu/projects_menu.xml
+++ b/android/BOINC/res/menu/projects_menu.xml
@@ -25,10 +25,6 @@
          android:title="@string/projects_add"
          android:icon="@drawable/plusw"
          yourapp:showAsAction="ifRoom|withText"></item>
-    <item android:id="@+id/acctmgr_add"
-         android:enabled="true"
-         android:visible="true"
-         android:title="@string/attachproject_list_acctmgr_button"></item>
     <item android:id="@+id/projects_add_url"
          android:enabled="true"
          android:visible="true"
diff --git a/android/BOINC/res/values/strings.xml b/android/BOINC/res/values/strings.xml
index f0fa05f..59d70b3 100644
--- a/android/BOINC/res/values/strings.xml
+++ b/android/BOINC/res/values/strings.xml
@@ -29,7 +29,7 @@
 
     <!-- attach project -->
     <!-- selection list -->
-    <string name="attachproject_list_desc">Select scientific projects you want to contribute in:</string>
+    <string name="attachproject_list_desc">Select scientific projects you want to contribute to:</string>
     <string name="attachproject_list_header">Choose a project</string>
     <string name="attachproject_list_manual_button">Add project by URL</string>
     <string name="attachproject_list_manual_dialog_title">Enter project URL:</string>
@@ -98,6 +98,7 @@
     <string name="attachproject_registration_header_pwd_confirm">… Retype:</string>
     <string name="attachproject_registration_button">Create</string>
     <!-- account manager -->
+    <string name="attachproject_acctmgr_list_desc">Use BOINC account manager to add and manage multiple projects</string>
     <string name="attachproject_acctmgr_header">Add account manager</string>
     <string name="attachproject_acctmgr_header_url">URL</string>
     <string name="attachproject_acctmgr_header_name">User:</string>
@@ -382,5 +383,12 @@
     <string name="nonexcl_dialog_header">Volunteer computing app detected</string>
     <string name="nonexcl_dialog_text">Another volunteer computing app is running on this device. Only one version can run at a time.</string>
     <string name="nonexcl_dialog_exit">Exit</string>
+    
+    <!-- social integration -->
+    <string name="social_invite_button">Invite friends</string>
+    <string name="social_invite_intent_title">How do you want to share?</string>
+    <string name="social_invite_content_title">I\'m doing science on my smartphone!</string>
+    <string name="social_invite_content_body">I\'m using my %1$s to do computing for science. You can too! Download the app from: %2$s</string> <!-- first parameter: device manufacturer, second: URL -->
+    <string name="social_invite_content_url">https://play.google.com/store/apps/details?id=edu.berkeley.boinc</string>
 
 </resources>
diff --git a/android/BOINC/src/edu/berkeley/boinc/ProjectsFragment.java b/android/BOINC/src/edu/berkeley/boinc/ProjectsFragment.java
index 5c4d881..10a225b 100644
--- a/android/BOINC/src/edu/berkeley/boinc/ProjectsFragment.java
+++ b/android/BOINC/src/edu/berkeley/boinc/ProjectsFragment.java
@@ -45,7 +45,6 @@ import android.widget.ListView;
 import android.widget.TextView;
 import edu.berkeley.boinc.adapter.ProjectControlsListAdapter;
 import edu.berkeley.boinc.adapter.ProjectsListAdapter;
-import edu.berkeley.boinc.attach.AcctMgrFragment;
 import edu.berkeley.boinc.attach.ManualUrlInputFragment;
 import edu.berkeley.boinc.rpc.Notice;
 import edu.berkeley.boinc.rpc.AcctMgrInfo;
@@ -58,7 +57,6 @@ public class ProjectsFragment extends Fragment {
 	private ListView lv;
 	private ProjectsListAdapter listAdapter;
 	private ArrayList<ProjectsListData> data = new ArrayList<ProjectsListData>();
-	private Boolean acctMgrPresent = false;
 
 	// controls popup dialog
 	Dialog dialogControls;
@@ -117,25 +115,10 @@ public class ProjectsFragment extends Fragment {
 	}
 	
 	@Override
-	public void onPrepareOptionsMenu(Menu menu) {
-		super.onPrepareOptionsMenu(menu);
-
-        // disable "add account manager" button, if account manager already present
-        if(acctMgrPresent) {
-        	MenuItem item = menu.findItem(R.id.acctmgr_add);
-        	item.setVisible(false);
-        }
-	}
-	
-	@Override
 	public boolean onOptionsItemSelected(MenuItem item) {
 	    if(Logging.VERBOSE) Log.v(Logging.TAG, "AttachProjectListActivity onOptionsItemSelected()");
 
 	    switch (item.getItemId()) {
-	    	case R.id.acctmgr_add:
-	    		AcctMgrFragment dialog = new AcctMgrFragment();
-	    		dialog.show(getFragmentManager(), getActivity().getString(R.string.attachproject_acctmgr_header));
-	    		return true;
 	    	case R.id.projects_add_url:
 	    		ManualUrlInputFragment dialog2 = new ManualUrlInputFragment();
 	    		dialog2.show(getFragmentManager(), getActivity().getString(R.string.attachproject_list_manual_button));
@@ -149,7 +132,6 @@ public class ProjectsFragment extends Fragment {
 		try {
 			// read projects from state saved in ClientStatus
 			ArrayList<Project> statusProjects = (ArrayList<Project>) BOINCActivity.monitor.getProjects();
-			acctMgrPresent = BOINCActivity.monitor.getAcctMgrInfoPresent();
 			AcctMgrInfo statusAcctMgr = BOINCActivity.monitor.getClientAcctMgrInfo();
 			ArrayList<Transfer> statusTransfers = (ArrayList<Transfer>) BOINCActivity.monitor.getTransfers();
 			
diff --git a/android/BOINC/src/edu/berkeley/boinc/attach/AcctMgrFragment.java b/android/BOINC/src/edu/berkeley/boinc/attach/AcctMgrFragment.java
index 3526383..634c599 100644
--- a/android/BOINC/src/edu/berkeley/boinc/attach/AcctMgrFragment.java
+++ b/android/BOINC/src/edu/berkeley/boinc/attach/AcctMgrFragment.java
@@ -19,6 +19,7 @@
 
 package edu.berkeley.boinc.attach;
 
+import edu.berkeley.boinc.BOINCActivity;
 import edu.berkeley.boinc.R;
 import edu.berkeley.boinc.utils.*;
 import android.app.Dialog;
@@ -59,6 +60,8 @@ public class AcctMgrFragment extends DialogFragment{
 	private LinearLayout ongoingWrapper;
 	private Button continueB;
 	
+	private boolean returnToMainActivity = false;
+	
 	private AttachProjectAsyncTask asyncTask;
 
     @Override
@@ -132,6 +135,10 @@ public class AcctMgrFragment extends DialogFragment{
 		  return dialog;
 	}
 	
+	public void setReturnToMainActivity() {
+		returnToMainActivity = true;
+	}
+	
 	private int verifyInput(String url, String name, String pwd) {
 		int stringResource = 0;
 		
@@ -254,7 +261,18 @@ public class AcctMgrFragment extends DialogFragment{
 		protected void onPostExecute(Integer result) {
 			super.onPostExecute(result);
 			if(Logging.DEBUG) Log.d(Logging.TAG, "AcctMgrFragment.AttachProjectAsyncTask onPostExecute, returned: " + result); 
-			if(result == BOINCErrors.ERR_OK) dismiss();
+			if(result == BOINCErrors.ERR_OK) {
+				dismiss();
+				if(returnToMainActivity) {
+					if(Logging.DEBUG) Log.d(Logging.TAG, "AcctMgrFragment.AttachProjectAsyncTask onPostExecute, start main activity"); 
+					Intent intent = new Intent(getActivity(), BOINCActivity.class);
+					// add flags to return to main activity and clearing all others and clear the back stack
+					intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
+					intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+					intent.putExtra("targetFragment", R.string.tab_projects); // make activity display projects fragment
+					startActivity(intent);
+				}
+			}
 			else {
 	        	ongoingWrapper.setVisibility(View.GONE);
 	        	continueB.setVisibility(View.VISIBLE);
diff --git a/android/BOINC/src/edu/berkeley/boinc/attach/BatchProcessingActivity.java b/android/BOINC/src/edu/berkeley/boinc/attach/BatchProcessingActivity.java
index 44bce50..11231e2 100644
--- a/android/BOINC/src/edu/berkeley/boinc/attach/BatchProcessingActivity.java
+++ b/android/BOINC/src/edu/berkeley/boinc/attach/BatchProcessingActivity.java
@@ -116,7 +116,6 @@ public class BatchProcessingActivity extends FragmentActivity{
     }
 
 	// triggered by continue button
-	// button only visible if no conflicts occured.
 	public void continueClicked(View v) {
 		boolean conflicts = attachService.unresolvedConflicts();
 		if(Logging.DEBUG) Log.d(Logging.TAG, "BatchProcessingActivity.continueClicked: conflicts? " + conflicts);
@@ -137,6 +136,19 @@ public class BatchProcessingActivity extends FragmentActivity{
 			startActivity(intent);
 		}
 	}
+
+	// triggered by share button
+	public void shareClicked(View v) {
+		if(Logging.DEBUG) Log.d(Logging.TAG, "BatchProcessingActivity.shareClicked.");
+		Intent intent=new Intent(android.content.Intent.ACTION_SEND);
+		intent.setType("text/plain");
+		intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+
+		// Add data to the intent, the receiving app will decide what to do with it.
+		intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.social_invite_content_title));
+		intent.putExtra(Intent.EXTRA_TEXT, String.format(getString(R.string.social_invite_content_body), android.os.Build.MANUFACTURER, getString(R.string.social_invite_content_url)));
+		startActivity(Intent.createChooser(intent, getString(R.string.social_invite_intent_title)));
+	}
 	
 	// adapts header text and icons when hint selection changes
 	private void adaptHintHeader() {
@@ -240,6 +252,7 @@ public class BatchProcessingActivity extends FragmentActivity{
 		protected void onPostExecute(Void result) {
 			((LinearLayout) findViewById(R.id.attach_status_ongoing_wrapper)).setVisibility(View.GONE);
 			((Button) findViewById(R.id.continue_button)).setVisibility(View.VISIBLE);
+			((Button) findViewById(R.id.share_button)).setVisibility(View.VISIBLE);
 			super.onPostExecute(result);
 		}
 	}
diff --git a/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListActivity.java b/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListActivity.java
index 523a4f0..b47fb59 100644
--- a/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListActivity.java
+++ b/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListActivity.java
@@ -203,6 +203,8 @@ public class SelectionListActivity extends FragmentActivity{
 	        	for(ProjectInfo tmp: result) {
 	        		entries.add(new ProjectListEntry(tmp));
 	        	}
+
+	        	entries.add(new ProjectListEntry()); // add account manager option to bottom of list
 		        SelectionListAdapter listAdapter = new SelectionListAdapter(SelectionListActivity.this,R.id.listview,entries);
 		        lv.setAdapter(listAdapter);
 	         } 
@@ -212,10 +214,18 @@ public class SelectionListActivity extends FragmentActivity{
 	class ProjectListEntry {
 		public ProjectInfo info;
 		public boolean checked;
+		public boolean am; //indicates that element is account manager entry
 		
 		public ProjectListEntry(ProjectInfo info) {
 			this.info = info;
 			this.checked = false;
 		}
+		
+		/**
+		 * Creates Account manager list object
+		 */
+		public ProjectListEntry() {
+			this.am = true;
+		}
 	}
 }
diff --git a/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListAdapter.java b/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListAdapter.java
index d0254a3..9fac9db 100644
--- a/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListAdapter.java
+++ b/android/BOINC/src/edu/berkeley/boinc/attach/SelectionListAdapter.java
@@ -31,6 +31,7 @@ import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.CheckBox;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -56,29 +57,55 @@ public class SelectionListAdapter extends ArrayAdapter<ProjectListEntry>{
 		
         v = vi.inflate(R.layout.attach_project_list_layout_listitem, null);
 		TextView name = (TextView) v.findViewById(R.id.name);
-		name.setText(listItem.info.name);
 		TextView description = (TextView) v.findViewById(R.id.description);
-		description.setText(listItem.info.generalArea);
 		TextView summary = (TextView) v.findViewById(R.id.summary);
-		summary.setText(listItem.info.summary);
 		CheckBox cb = (CheckBox) v.findViewById(R.id.cb);
-		cb.setChecked(listItem.checked);
-		cb.setOnClickListener(new OnClickListener() {
-			@Override
-			public void onClick(View v) {
-				listItem.checked = !listItem.checked;
-			}
-		});
 		LinearLayout textWrapper = (LinearLayout) v.findViewById(R.id.text_wrapper);
-		textWrapper.setOnClickListener(new OnClickListener() {
-			@Override
-			public void onClick(View v) {
-				if(Logging.DEBUG) Log.d(Logging.TAG, "SelectionListAdapter: onProjectClick open info for: " + listItem.info.name);
+        
+        if(listItem.am) {
+        	// element is account manager
+    		name.setText(activity.getString(R.string.attachproject_acctmgr_header));
+    		description.setText(activity.getString(R.string.attachproject_acctmgr_list_desc));
+    		cb.setVisibility(View.GONE);
+    		summary.setVisibility(View.GONE);
+    		ImageView button = (ImageView) v.findViewById(R.id.am_button_image);
+    		button.setVisibility(View.VISIBLE);
+    		OnClickListener listener = new OnClickListener() {
+    			@Override
+    			public void onClick(View v) {
+    				if(Logging.DEBUG) Log.d(Logging.TAG, "SelectionListAdapter: account manager clicked.");
+    	    		AcctMgrFragment dialog = new AcctMgrFragment();
+    	    		dialog.setReturnToMainActivity(); // configure, so dialog returns to main activity when finished
+    	    		dialog.show(activity.getSupportFragmentManager(), activity.getString(R.string.attachproject_acctmgr_header));
+    			}
+    		};
+    		v.setOnClickListener(listener);
+    		name.setOnClickListener(listener);
+    		description.setOnClickListener(listener);
+    		button.setOnClickListener(listener);
+        } else {
+        	// element is project option
+    		name.setText(listItem.info.name);
+    		description.setText(listItem.info.generalArea);
+    		summary.setText(listItem.info.summary);
+    		cb.setChecked(listItem.checked);
+    		cb.setOnClickListener(new OnClickListener() {
+    			@Override
+    			public void onClick(View v) {
+    				listItem.checked = !listItem.checked;
+    			}
+    		});
+    		textWrapper.setOnClickListener(new OnClickListener() {
+    			@Override
+    			public void onClick(View v) {
+    				if(Logging.DEBUG) Log.d(Logging.TAG, "SelectionListAdapter: onProjectClick open info for: " + listItem.info.name);
 
-				ProjectInfoFragment dialog = ProjectInfoFragment.newInstance(listItem.info);
-				dialog.show(activity.getSupportFragmentManager(), "ProjectInfoFragment");
-			}
-		});
+    				ProjectInfoFragment dialog = ProjectInfoFragment.newInstance(listItem.info);
+    				dialog.show(activity.getSupportFragmentManager(), "ProjectInfoFragment");
+    			}
+    		});
+        	
+        }
         return v;
     }
 	
diff --git a/client/app.cpp b/client/app.cpp
index 1e6ab4c..3c85200 100644
--- a/client/app.cpp
+++ b/client/app.cpp
@@ -140,6 +140,17 @@ ACTIVE_TASK::ACTIVE_TASK() {
     finish_file_time = 0;
 }
 
+bool ACTIVE_TASK::process_exists() {
+    switch (task_state()) {
+    case PROCESS_EXECUTING:
+    case PROCESS_SUSPENDED:
+    case PROCESS_ABORT_PENDING:
+    case PROCESS_QUIT_PENDING:
+        return true;
+    }
+    return false;
+}
+
 // preempt this task;
 // called from the CLIENT_STATE::enforce_schedule()
 // and ACTIVE_TASK_SET::suspend_all()
@@ -198,7 +209,9 @@ int ACTIVE_TASK::preempt(int preempt_type, int reason) {
 
 #ifndef SIM
 
-// called when a process has exited
+// called when the task's main process has exited.
+// delete the shared memory used to communicate with it,
+// and kill any remaining subsidiary processes.
 //
 void ACTIVE_TASK::cleanup_task() {
 #ifdef _WIN32
@@ -218,7 +231,7 @@ void ACTIVE_TASK::cleanup_task() {
 
     if (app_client_shm.shm) {
 #ifndef __EMX__
-        if (app_version->api_major_version() >= 6) {
+        if (app_version->api_version_at_least(6, 0)) {
             retval = detach_shmem_mmap(app_client_shm.shm, sizeof(SHARED_MEM));
         } else
 #endif
@@ -241,6 +254,8 @@ void ACTIVE_TASK::cleanup_task() {
     }
 #endif
 
+    kill_subsidiary_processes();
+
     if (cc_config.exit_after_finish) {
         gstate.write_state_file();
         exit(0);
@@ -343,7 +358,8 @@ void ACTIVE_TASK_SET::get_memory_usage() {
     }
     PROCINFO boinc_total;
     if (log_flags.mem_usage_debug) {
-        memset(&boinc_total, 0, sizeof(boinc_total));
+        boinc_total.clear();
+        boinc_total.working_set_size_smoothed = 0;
     }
     for (i=0; i<active_tasks.size(); i++) {
         ACTIVE_TASK* atp = active_tasks[i];
diff --git a/client/app.h b/client/app.h
index 03263bf..5c73500 100644
--- a/client/app.h
+++ b/client/app.h
@@ -207,7 +207,6 @@ struct ACTIVE_TASK {
     // Termination stuff.
     // Terminology:
     // "kill": forcibly kill the main process and all its descendants.
-    //    (note: on Windows secure mode, we can't kill the descendants)
     // "request exit": send a request-exit message, and enumerate descendants.
     //      If after 15 secs any processes remain, kill them
     //      called from:
@@ -226,9 +225,12 @@ struct ACTIVE_TASK {
     //
     int request_exit();
     int request_abort();
-    int kill_task(bool will_restart);
-        // Kill process and descendants forcibly.
+    int kill_running_task(bool will_restart);
+        // Kill process and subsidiary processes forcibly.
         // Unix: send a SIGKILL signal, Windows: TerminateProcess()
+    int kill_subsidiary_processes();
+        // kill subsidiary processes of a job
+        // whose main process has already exited
     int abort_task(int exit_status, const char*);
         // can be called whether or not process exists
 
@@ -242,6 +244,8 @@ struct ACTIVE_TASK {
     // Implementation stuff related to termination
     //
     std::vector<int> descendants;
+        // PIDs of descendants, computed every 10 sec or so
+        // during resource usage computation.
     bool process_exists();
     bool has_task_exited();
         // return true if this task has exited
diff --git a/client/app_config.cpp b/client/app_config.cpp
index e65bee0..5e01e7e 100644
--- a/client/app_config.cpp
+++ b/client/app_config.cpp
@@ -29,6 +29,7 @@ bool have_max_concurrent = false;
 
 int APP_CONFIG::parse(XML_PARSER& xp, PROJECT* p) {
     memset(this, 0, sizeof(APP_CONFIG));
+    double x;
 
     while (!xp.get_tag()) {
         if (xp.match_tag("/app")) return 0;
@@ -40,8 +41,26 @@ int APP_CONFIG::parse(XML_PARSER& xp, PROJECT* p) {
         if (xp.match_tag("gpu_versions")) {
             while (!xp.get_tag()) {
                 if (xp.match_tag("/gpu_versions")) break;
-                if (xp.parse_double("gpu_usage", gpu_gpu_usage)) continue;
-                if (xp.parse_double("cpu_usage", gpu_cpu_usage)) continue;
+                if (xp.parse_double("gpu_usage", x)) {
+                    if (x <= 0) {
+                        msg_printf(p, MSG_USER_ALERT,
+                            "gpu_usage must be positive in app_config.xml"
+                        );
+                    } else {
+                        gpu_gpu_usage = x;
+                    }
+                    continue;
+                }
+                if (xp.parse_double("cpu_usage", x)) {
+                    if (x < 0) {
+                        msg_printf(p, MSG_USER_ALERT,
+                            "cpu_usage must be non-negative in app_config.xml"
+                        );
+                    } else {
+                        gpu_cpu_usage = x;
+                    }
+                    continue;
+                }
             }
             continue;
         }
@@ -81,6 +100,7 @@ int APP_VERSION_CONFIG::parse(XML_PARSER& xp, PROJECT* p) {
 }
 
 int APP_CONFIGS::parse(XML_PARSER& xp, PROJECT* p) {
+    int n;
     app_configs.clear();
     if (!xp.parse_start("app_config")) return ERR_XML_PARSE;
     while (!xp.get_tag()) {
@@ -101,6 +121,13 @@ int APP_CONFIGS::parse(XML_PARSER& xp, PROJECT* p) {
             }
             continue;
         }
+        if (xp.parse_int("project_max_concurrent", n)) {
+            if (n >= 0) {
+                have_max_concurrent = true;
+                project_max_concurrent = n;
+            }
+            continue;
+        }
         if (log_flags.unparsed_xml) {
             msg_printf(p, MSG_INFO,
                 "Unparsed line in app_config.xml: %s",
@@ -183,6 +210,9 @@ void max_concurrent_init() {
     for (unsigned int i=0; i<gstate.apps.size(); i++) {
         gstate.apps[i]->n_concurrent = 0;
     }
+    for (unsigned int i=0; i<gstate.projects.size(); i++) {
+        gstate.projects[i]->n_concurrent = 0;
+    }
 }
 
 // undo the effects of an app_config.xml that no longer exists
diff --git a/client/app_config.h b/client/app_config.h
index 81c6c11..3d54570 100644
--- a/client/app_config.h
+++ b/client/app_config.h
@@ -50,6 +50,7 @@ struct APP_VERSION_CONFIG {
 struct APP_CONFIGS {
     std::vector<APP_CONFIG> app_configs;
     std::vector<APP_VERSION_CONFIG> app_version_configs;
+    int project_max_concurrent;
 
     int parse(XML_PARSER&, PROJECT*);
     int parse_file(FILE*, PROJECT*);
@@ -57,6 +58,7 @@ struct APP_CONFIGS {
     void clear() {
         app_configs.clear();
         app_version_configs.clear();
+        project_max_concurrent = 0;
     }
 };
 
diff --git a/client/app_control.cpp b/client/app_control.cpp
index a8f6010..ee64e5a 100644
--- a/client/app_control.cpp
+++ b/client/app_control.cpp
@@ -105,7 +105,7 @@ bool ACTIVE_TASK_SET::poll() {
                         atp->result->name
                     );
                 }
-                atp->kill_task(false);
+                atp->kill_running_task(false);
             }
         }
         if (atp->task_state() == PROCESS_QUIT_PENDING) {
@@ -116,7 +116,7 @@ bool ACTIVE_TASK_SET::poll() {
                         atp->result->name
                     );
                 }
-                atp->kill_task(true);
+                atp->kill_running_task(true);
             }
         }
     }
@@ -211,29 +211,34 @@ int ACTIVE_TASK::request_abort() {
 
 #ifdef _WIN32
 static void kill_app_process(int pid, bool will_restart) {
-    HANDLE h = OpenProcess(READ_CONTROL | PROCESS_TERMINATE, false, pid);
-    if (h == NULL) return;
-    TerminateProcess(h, will_restart?0:EXIT_ABORTED_BY_CLIENT);
-    CloseHandle(h);
-}
-#else
-static void kill_app_process(int pid, bool) {
     int retval = 0;
-#ifdef SANDBOX
-    retval = kill_via_switcher(pid);
+    retval = kill_program(pid, will_restart?0:EXIT_ABORTED_BY_CLIENT);
     if (retval && log_flags.task_debug) {
         msg_printf(0, MSG_INFO,
-            "[task] kill_via_switcher() failed: %s",
-            boincerror(retval)
+            "[task] kill_app_process() failed: %s",
+            strerror(retval)
         );
     }
-#endif
-    retval = kill(pid, SIGKILL);
-    if (retval && log_flags.task_debug) {
-        msg_printf(0, MSG_INFO,
-            "[task] kill() failed: %s",
-            boincerror(retval)
-        );
+}
+#else
+static void kill_app_process(int pid, bool) {
+    int retval = 0;
+    if (g_use_sandbox) {
+        retval = kill_via_switcher(pid);
+        if (retval && log_flags.task_debug) {
+            msg_printf(0, MSG_INFO,
+                "[task] kill_via_switcher() failed: %s (%d)",
+                (retval>=0) ? strerror(errno) : boincerror(retval), retval
+            );
+        }
+    } else {
+        retval = kill(pid, SIGKILL);
+        if (retval && log_flags.task_debug) {
+            msg_printf(0, MSG_INFO,
+                "[task] kill() failed: %s",
+                strerror(errno)
+            );
+        }
     }
 }
 #endif
@@ -244,27 +249,24 @@ static inline void kill_processes(vector<int> pids, bool will_restart) {
     }
 }
 
-// Kill the task (and descendants) by OS-specific means.
+// Kill a task whose main process is still running
+// Just kill the main process; shared mem and subsidiary processes
+// will be cleaned up after it exits, by cleanup_task();
 //
-int ACTIVE_TASK::kill_task(bool will_restart) {
-    vector<int>pids;
-#ifdef _WIN32
-    // On Win, in protected mode we won't be able to get
-    // handles for the descendant processes;
-    // all we can do is terminate the main process,
-    // using the handle we got when we created it.
-    //
-    if (g_use_sandbox) {
-        TerminateProcess(process_handle, will_restart?0:EXIT_ABORTED_BY_CLIENT);
-        return 0;
-    }
-#endif
-    get_descendants(pid, pids);
-    pids.push_back(pid);
-    for (unsigned int i=0; i<other_pids.size(); i++) {
-        pids.push_back(other_pids[i]);
-    }
-    kill_processes(pids, will_restart);
+int ACTIVE_TASK::kill_running_task(bool will_restart) {
+    kill_app_process(pid, will_restart);
+    return 0;
+}
+
+// Clean up the subsidiary processes of a task whose main process has exited,
+// namely:
+// - its descendants (as recently enumerated; it's too late to do that now)
+//   This list will be populated only in the quit and abort cases.
+// - its "other" processes, e.g. VMs
+//
+int ACTIVE_TASK::kill_subsidiary_processes() {
+    kill_processes(other_pids, true);
+    kill_processes(descendants, true);
     return 0;
 }
 
@@ -430,10 +432,8 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
     //
     if (task_state() == PROCESS_ABORT_PENDING) {
         set_task_state(PROCESS_ABORTED, "handle_exited_app");
-        kill_processes(descendants, false);
     } else if (task_state() == PROCESS_QUIT_PENDING) {
         set_task_state(PROCESS_UNINITIALIZED, "handle_exited_app");
-        kill_processes(descendants, true);
         will_restart = true;
     } else {
 #ifdef _WIN32
@@ -562,6 +562,8 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
 #endif
     }
 
+    // get rid of shared-mem segment and kill subsidiary processes
+    //
     cleanup_task();
 
     if (gstate.run_test_app) {
@@ -694,7 +696,7 @@ void ACTIVE_TASK_SET::process_control_poll() {
                     "Restarting %s - message timeout", atp->result->name
                 );
             }
-            atp->kill_task(true);
+            atp->kill_running_task(true);
         } else {
             atp->process_control_queue.msg_queue_poll(
                 atp->app_client_shm.shm->process_control_request
@@ -737,6 +739,7 @@ bool ACTIVE_TASK_SET::check_app_exited() {
             // The process doesn't seem to be there.
             // Mark task as aborted so we don't check it again.
             //
+            atp->cleanup_task();
             atp->set_task_state(PROCESS_ABORTED, "check_app_exited");
         }
     }
@@ -1241,7 +1244,7 @@ void ACTIVE_TASK_SET::kill_tasks(PROJECT* proj) {
         atp = active_tasks[i];
         if (proj && atp->wup->project != proj) continue;
         if (!atp->process_exists()) continue;
-        atp->kill_task(true);
+        atp->kill_running_task(true);
     }
 }
 
diff --git a/client/app_start.cpp b/client/app_start.cpp
index 58fb736..2f1826b 100644
--- a/client/app_start.cpp
+++ b/client/app_start.cpp
@@ -163,7 +163,7 @@ int ACTIVE_TASK::get_shmem_seg_name() {
     char init_data_path[MAXPATHLEN];
 #ifndef __EMX__
     // shmem_seg_name is not used with mmap() shared memory
-    if (app_version->api_major_version() >= 6) {
+    if (app_version->api_version_at_least(6, 0)) {
         shmem_seg_name = -1;
         return 0;
     }
@@ -253,7 +253,13 @@ void ACTIVE_TASK::init_app_init_data(APP_INIT_DATA& aid) {
     int rt = app_version->gpu_usage.rsc_type;
     if (rt) {
         COPROC& cp = coprocs.coprocs[rt];
-        safe_strcpy(aid.gpu_type, cp.type);
+        if (coproc_type_name_to_num(cp.type) >= 0) {
+            // Standardized vendor name ("NVIDIA", "ATI" or "intel_gpu")
+            safe_strcpy(aid.gpu_type, cp.type);
+        } else {
+            // For other vendors, use vendor name as returned by OpenCL
+            safe_strcpy(aid.gpu_type, cp.opencl_prop.vendor);
+        }
         int k = result->coproc_indices[0];
         if (k<0 || k>=cp.count) {
             msg_printf(0, MSG_INTERNAL_ERROR,
@@ -433,9 +439,7 @@ int ACTIVE_TASK::setup_file(
     }
     if (retval) return retval;
 #endif
-#ifdef SANDBOX
-    return set_to_project_group(link_path);
-#endif
+    if (g_use_sandbox) set_to_project_group(link_path);
     return 0;
 }
 
@@ -508,7 +512,7 @@ int ACTIVE_TASK::start(bool test) {
     unsigned int i;
     FILE_REF fref;
     FILE_INFO* fip;
-    int retval, rt;
+    int retval;
     APP_INIT_DATA aid;
 #ifdef _WIN32
     bool success = false;
@@ -665,7 +669,8 @@ int ACTIVE_TASK::start(bool test) {
     char slotdirpath[MAXPATHLEN];
     char error_msg[1024];
     char error_msg2[1024];
-
+    DWORD last_error;
+    
     memset(&process_info, 0, sizeof(process_info));
     memset(&startup_info, 0, sizeof(startup_info));
     startup_info.cb = sizeof(startup_info);
@@ -688,9 +693,11 @@ int ACTIVE_TASK::start(bool test) {
     sprintf(cmdline, "%s %s %s",
         exec_path, wup->command_line.c_str(), app_version->cmdline
     );
-    rt = app_version->gpu_usage.rsc_type;
-    if (rt) {
-        coproc_cmdline(rt, result, app_version->gpu_usage.usage, cmdline);
+    if (!app_version->api_version_at_least(7, 5)) {
+        int rt = app_version->gpu_usage.rsc_type;
+        if (rt) {
+            coproc_cmdline(rt, result, app_version->gpu_usage.usage, cmdline);
+        }
     }
 
     relative_to_absolute(slot_dir, slotdirpath);
@@ -704,6 +711,7 @@ int ACTIVE_TASK::start(bool test) {
     }
 
     for (i=0; i<5; i++) {
+        last_error = 0;
         if (sandbox_account_service_token != NULL) {
 
             if (!CreateEnvironmentBlock(&environment_block, sandbox_account_service_token, FALSE)) {
@@ -731,9 +739,11 @@ int ACTIVE_TASK::start(bool test) {
                 success = true;
                 break;
             } else {
-                windows_format_error_string(GetLastError(), error_msg, sizeof(error_msg));
+                last_error = GetLastError();
+                windows_format_error_string(last_error, error_msg, sizeof(error_msg));
                 msg_printf(wup->project, MSG_INTERNAL_ERROR,
-                    "Process creation failed: %s", error_msg
+                    "Process creation failed: %s - error code %d (0x%x)",
+                    error_msg, last_error, last_error
                 );
             }
 
@@ -763,9 +773,11 @@ int ACTIVE_TASK::start(bool test) {
                 success = true;
                 break;
             } else {
-                windows_format_error_string(GetLastError(), error_msg, sizeof(error_msg));
+                last_error = GetLastError();
+                windows_format_error_string(last_error, error_msg, sizeof(error_msg));
                 msg_printf(wup->project, MSG_INTERNAL_ERROR,
-                    "Process creation failed: %s", error_msg
+                    "Process creation failed: %s - error code %d (0x%x)",
+                    error_msg, last_error, last_error
                 );
             }
         }
@@ -774,6 +786,16 @@ int ACTIVE_TASK::start(bool test) {
 
     if (!success) {
         sprintf(buf, "CreateProcess() failed - %s", error_msg);
+
+        if (last_error == ERROR_NOT_ENOUGH_MEMORY) {
+            // if CreateProcess() failed because system is low on memory,
+            // treat this like a temporary exit;
+            // retry in 10 min, and give up after 100 times
+            //
+            bool will_restart;
+            handle_temporary_exit(will_restart, 600, "not enough memory", false);
+            if (will_restart) return 0;
+        }
         retval = ERR_EXEC;
         goto error;
     }
@@ -864,9 +886,11 @@ int ACTIVE_TASK::start(bool test) {
         wup->command_line.c_str(), app_version->cmdline
     );
 
-    rt = app_version->gpu_usage.rsc_type;
-    if (rt) {
-        coproc_cmdline(rt, result, app_version->gpu_usage.usage, cmdline);
+    if (!app_version->api_version_at_least(7, 5)) {
+        int rt = app_version->gpu_usage.rsc_type;
+        if (rt) {
+            coproc_cmdline(rt, result, app_version->gpu_usage.usage, cmdline);
+        }
     }
 
     // Set up client/app shared memory seg if needed
@@ -875,7 +899,7 @@ int ACTIVE_TASK::start(bool test) {
 #ifdef ANDROID
         if (true) {
 #else
-        if (app_version->api_major_version() >= 6) {
+        if (app_version->api_version_at_least(6, 0)) {
 #endif
             // Use mmap() shared memory
             sprintf(buf, "%s/%s", slot_dir, MMAPPED_FILE_NAME);
@@ -884,9 +908,7 @@ int ACTIVE_TASK::start(bool test) {
                     int fd = open(buf, O_RDWR | O_CREAT, 0660);
                     if (fd >= 0) {
                         close (fd);
-#ifdef SANDBOX
-                        set_to_project_group(buf);
-#endif
+                        if (g_use_sandbox) set_to_project_group(buf);
                     }
                 }
             }
diff --git a/client/client_state.cpp b/client/client_state.cpp
index e333eb4..73be34d 100644
--- a/client/client_state.cpp
+++ b/client/client_state.cpp
@@ -114,6 +114,7 @@ CLIENT_STATE::CLIENT_STATE()
     core_client_version.prerelease = false;
 #endif
     strcpy(language, "");
+    strcpy(client_brand, "");
     exit_after_app_start_secs = 0;
     app_started = 0;
     exit_before_upload = false;
@@ -242,6 +243,17 @@ void CLIENT_STATE::show_host_info() {
             "VirtualBox version: %s",
             host_info.virtualbox_version
         );
+    } else {
+#if defined (_WIN32) && !defined(_WIN64)
+        if (!strcmp(get_primary_platform(), "windows_x86_64")) {
+            msg_printf(NULL, MSG_INFO,
+                "VirtualBox: can't detect because this is a 32-bit client"
+            );
+            msg_printf(NULL, MSG_INFO,
+                "  (to use VirtualBox, install a 64-bit BOINC client)."
+            );
+        }
+#endif
     }
 }
 
@@ -265,7 +277,9 @@ const char* rsc_name(int i) {
 // user-friendly version
 //
 const char* rsc_name_long(int i) {
-    return proc_type_name(coproc_type_name_to_num(coprocs.coprocs[i].type));
+    int num = coproc_type_name_to_num(coprocs.coprocs[i].type);
+    if (num >= 0) return proc_type_name(num);   // CPU, NVIDIA GPU, AMD GPU or Intel GPU
+    return coprocs.coprocs[i].type;             // Some other type
 }
 
 // alert user if any jobs need more RAM than available
@@ -394,6 +408,14 @@ int CLIENT_STATE::init() {
     msg_printf(NULL, MSG_INFO, "Running under account %s", pbuf);
 #endif
 
+    FILE* f = fopen(CLIENT_BRAND_FILENAME, "r");
+    if (f) {
+        fgets(client_brand, sizeof(client_brand), f);
+        strip_whitespace(client_brand);
+        msg_printf(NULL, MSG_INFO, "Client brand: %s", client_brand);
+        fclose(f);
+    }
+
     parse_account_files();
     parse_statistics_files();
 
@@ -458,6 +480,8 @@ int CLIENT_STATE::init() {
             coprocs.add(coprocs.intel_gpu);
         }
     }
+    coprocs.add_other_coproc_types();
+    
     host_info.coprocs = coprocs;
     
     if (coprocs.none() ) {
@@ -659,9 +683,7 @@ int CLIENT_STATE::init() {
         if (retval) return retval;
     }
 
-#ifdef SANDBOX
-    get_project_gid();
-#endif
+    if (g_use_sandbox) get_project_gid();
 #ifdef _WIN32
     get_sandbox_account_service_token();
     if (sandbox_account_service_token != NULL) g_use_sandbox = true;
diff --git a/client/client_state.h b/client/client_state.h
index 2bcb395..8d6d3ad 100644
--- a/client/client_state.h
+++ b/client/client_state.h
@@ -110,6 +110,9 @@ struct CLIENT_STATE {
     double device_status_time;
 
     char language[16];                // ISO language code reported by GUI
+    char client_brand[256];
+        // contents of client_brand.txt, e.g. "HTP Power to Give"
+        // reported to scheduler
     VERSION_INFO core_client_version;
     string statefile_platform_name;
     int file_xfer_giveup_period;
@@ -138,11 +141,11 @@ struct CLIENT_STATE {
         // Determine when it is safe to leave the quit_client() handler
         // and to finish cleaning up.
     char detach_project_url[256];
-        // stores URL for -detach_project option
+        // stores URL for --detach_project option
     char reset_project_url[256];
-        // stores URL for -reset_project option
+        // stores URL for --reset_project option
     char update_prefs_url[256];
-        // stores URL for -update_prefs option
+        // stores URL for --update_prefs option
     char main_host_venue[256];
         // venue from project or AMS that gave us general prefs
     char attach_project_url[256];
diff --git a/client/client_types.cpp b/client/client_types.cpp
index a641e3d..28036ab 100644
--- a/client/client_types.cpp
+++ b/client/client_types.cpp
@@ -983,11 +983,13 @@ void APP_VERSION::clear_errors() {
     }
 }
 
-int APP_VERSION::api_major_version() {
-    int v, n;
-    n = sscanf(api_version, "%d", &v);
-    if (n != 1) return 0;
-    return v;
+bool APP_VERSION::api_version_at_least(int major, int minor) {
+    int maj, min, n;
+    n = sscanf(api_version, "%d.%d", &maj, &min);
+    if (n != 2) return false;
+    if (maj < major) return false;
+    if (maj > major) return true;
+    return min >= minor;
 }
 
 int FILE_REF::parse(XML_PARSER& xp) {
diff --git a/client/client_types.h b/client/client_types.h
index 325dd10..908b21e 100644
--- a/client/client_types.h
+++ b/client/client_types.h
@@ -282,7 +282,7 @@ struct APP_VERSION {
     char api_version[16];
     double avg_ncpus;
     double max_ncpus;
-    GPU_USAGE gpu_usage;    // can only use 1 GPUtype
+    GPU_USAGE gpu_usage;    // can only use 1 GPU type
     double gpu_ram;
     double flops;
     char cmdline[256];
@@ -326,7 +326,7 @@ struct APP_VERSION {
     bool had_download_failure(int& failnum);
     void get_file_errors(std::string&);
     void clear_errors();
-    int api_major_version();
+    bool api_version_at_least(int major, int minor);
     inline bool uses_coproc(int rt) {
         return (gpu_usage.rsc_type == rt);
     }
diff --git a/client/cpu_sched.cpp b/client/cpu_sched.cpp
index dd4177d..b54a515 100644
--- a/client/cpu_sched.cpp
+++ b/client/cpu_sched.cpp
@@ -1011,10 +1011,12 @@ static inline bool more_important(RESULT* r0, RESULT* r1) {
     if (unfin0 && !unfin1) return true;
     if (!unfin0 && unfin1) return false;
 
-    // favor jobs that use more CPUs
+    // for CPU jobs, favor jobs that use more CPUs
     //
-    if (r0->avp->avg_ncpus > r1->avp->avg_ncpus) return true;
-    if (r1->avp->avg_ncpus > r0->avp->avg_ncpus) return false;
+    if (!cp0) {
+        if (r0->avp->avg_ncpus > r1->avp->avg_ncpus) return true;
+        if (r1->avp->avg_ncpus > r0->avp->avg_ncpus) return false;
+    }
 
     // favor jobs selected first by schedule_cpus()
     // (e.g., because their project has high sched priority)
@@ -1552,17 +1554,6 @@ double CLIENT_STATE::nearly_runnable_resource_share() {
     return x;
 }
 
-bool ACTIVE_TASK::process_exists() {
-    switch (task_state()) {
-    case PROCESS_EXECUTING:
-    case PROCESS_SUSPENDED:
-    case PROCESS_ABORT_PENDING:
-    case PROCESS_QUIT_PENDING:
-        return true;
-    }
-    return false;
-}
-
 // if there's not an active task for the result, make one
 //
 ACTIVE_TASK* CLIENT_STATE::get_task(RESULT* rp) {
diff --git a/client/cs_account.cpp b/client/cs_account.cpp
index 15ed20e..90858f2 100644
--- a/client/cs_account.cpp
+++ b/client/cs_account.cpp
@@ -206,7 +206,7 @@ int PROJECT::parse_account_file_venue() {
     FILE* in = boinc_fopen(path, "r");
     if (!in) return ERR_FOPEN;
 
-    //msg_printf(this, MSG_INFO, "parsing project prefs, looking for venue %s", host_venue);
+	//msg_printf(this, MSG_INFO, "parsing project prefs, looking for venue %s", host_venue);
     MIOFILE mf;
     XML_PARSER xp(&mf);
     mf.init_file(in);
@@ -217,7 +217,7 @@ int PROJECT::parse_account_file_venue() {
         } else if (xp.match_tag("venue")) {
             parse_attr(attr_buf, "name", venue, sizeof(venue));
             if (!strcmp(venue, host_venue)) {
-                //msg_printf(this, MSG_INFO, "found venue %s", host_venue);
+				//msg_printf(this, MSG_INFO, "found venue %s", host_venue);
                 using_venue_specific_prefs = true;
                 in_right_venue = true;
 
diff --git a/client/cs_cmdline.cpp b/client/cs_cmdline.cpp
index 4194df5..21520ba 100644
--- a/client/cs_cmdline.cpp
+++ b/client/cs_cmdline.cpp
@@ -68,9 +68,7 @@ static void print_options(char* prog) {
         "    --gui_rpc_port <port>          port for GUI RPCs\n"
         "    --gui_rpc_unix_domain          use Unix domain for GUI RPCs\n"
         "    --help                         show options\n"
-#ifdef SANDBOX
         "    --insecure                     disable app sandboxing (Unix)\n"
-#endif
 #ifdef __APPLE__
         "    --launched_by_manager          client was launched by Manager\n"
 #endif
@@ -183,9 +181,7 @@ void CLIENT_STATE::parse_cmdline(int argc, char** argv) {
             print_options(argv[0]);
             exit(0);
         } else if (ARG(insecure)) {
-#ifdef SANDBOX
             g_use_sandbox = false;
-#endif
         } else if (ARG(launched_by_manager)) {
             launched_by_manager = true;
         } else if (ARG(master_fetch_interval)) {
diff --git a/client/cs_notice.cpp b/client/cs_notice.cpp
index ff1cb34..416d01c 100644
--- a/client/cs_notice.cpp
+++ b/client/cs_notice.cpp
@@ -345,9 +345,12 @@ bool NOTICES::remove_dups(NOTICE& n) {
         ) {
             i = notices.erase(i);
             removed_something = true;
+#if 0
+        // this check prevents news item edits from showing; skip it
         } else if (same_guid(n, n2)) {
             n2.keep = true;
             return false;
+#endif
         } else if (same_text(n, n2)) {
             int min_diff = 0;
 
diff --git a/client/cs_scheduler.cpp b/client/cs_scheduler.cpp
index ad7f007..9a27525 100644
--- a/client/cs_scheduler.cpp
+++ b/client/cs_scheduler.cpp
@@ -125,7 +125,8 @@ int CLIENT_STATE::make_scheduler_request(PROJECT* p) {
         "    <prrs_fraction>%f</prrs_fraction>\n"
         "    <duration_correction_factor>%f</duration_correction_factor>\n"
         "    <allow_multiple_clients>%d</allow_multiple_clients>\n"
-        "    <sandbox>%d</sandbox>\n",
+        "    <sandbox>%d</sandbox>\n"
+        "    <dont_send_work>%d</dont_send_work>\n",
         p->authenticator,
         p->hostid,
         p->rpc_seqno,
@@ -137,7 +138,8 @@ int CLIENT_STATE::make_scheduler_request(PROJECT* p) {
         prrs_fraction,
         p->duration_correction_factor,
         cc_config.allow_multiple_clients?1:0,
-        g_use_sandbox?1:0
+        g_use_sandbox?1:0,
+        p->dont_request_more_work?1:0
     );
     work_fetch.write_request(f, p);
 
@@ -225,28 +227,8 @@ int CLIENT_STATE::make_scheduler_request(PROJECT* p) {
         total_disk_usage, p->disk_usage, p->disk_share
     );
 
-    // copy request values from RSC_WORK_FETCH to COPROC
-    //
-    int j = rsc_index(GPU_TYPE_NVIDIA);
-    if (j > 0) {
-        coprocs.nvidia.req_secs = rsc_work_fetch[j].req_secs;
-        coprocs.nvidia.req_instances = rsc_work_fetch[j].req_instances;
-        coprocs.nvidia.estimated_delay = rsc_work_fetch[j].req_secs?rsc_work_fetch[j].busy_time_estimator.get_busy_time():0;
-    }
-    j = rsc_index(GPU_TYPE_ATI);
-    if (j > 0) {
-        coprocs.ati.req_secs = rsc_work_fetch[j].req_secs;
-        coprocs.ati.req_instances = rsc_work_fetch[j].req_instances;
-        coprocs.ati.estimated_delay = rsc_work_fetch[j].req_secs?rsc_work_fetch[j].busy_time_estimator.get_busy_time():0;
-    }
-    j = rsc_index(GPU_TYPE_INTEL);
-    if (j > 0) {
-        coprocs.intel_gpu.req_secs = rsc_work_fetch[j].req_secs;
-        coprocs.intel_gpu.req_instances = rsc_work_fetch[j].req_instances;
-        coprocs.intel_gpu.estimated_delay = rsc_work_fetch[j].req_secs?rsc_work_fetch[j].busy_time_estimator.get_busy_time():0;
-    }
-
     if (coprocs.n_rsc > 1) {
+        work_fetch.copy_requests();
         coprocs.write_xml(mf, true);
     }
 
@@ -301,7 +283,7 @@ int CLIENT_STATE::make_scheduler_request(PROJECT* p) {
     // send descriptions of app versions
     //
     fprintf(f, "<app_versions>\n");
-    j=0;
+    int j=0;
     for (i=0; i<app_versions.size(); i++) {
         APP_VERSION* avp = app_versions[i];
         if (avp->project != p) continue;
@@ -393,6 +375,10 @@ int CLIENT_STATE::make_scheduler_request(PROJECT* p) {
         fclose(cof);
     }
 
+    if (strlen(client_brand)) {
+        fprintf(f, "    <client_brand>%s</client_brand>\n", client_brand);
+    }
+
     fprintf(f, "</scheduler_request>\n");
 
     fclose(f);
@@ -680,7 +666,7 @@ int CLIENT_STATE::handle_scheduler_reply(
     if (sr.project_is_down) {
         if (sr.request_delay) {
             double x = now + sr.request_delay;
-            project->set_min_rpc_time(x, "project is down");
+            project->set_min_rpc_time(x, "project requested delay");
         }
         return ERR_PROJECT_DOWN;
     }
diff --git a/client/cs_trickle.cpp b/client/cs_trickle.cpp
index 24be21d..a8c84a7 100644
--- a/client/cs_trickle.cpp
+++ b/client/cs_trickle.cpp
@@ -70,7 +70,19 @@ int CLIENT_STATE::read_trickle_files(PROJECT* project, FILE* f) {
 
         sprintf(path, "%s/%s", project->project_dir(), fname);
         retval = read_file_malloc(path, file_contents);
-        if (retval) continue;
+        if (retval) {
+            if (log_flags.trickle_debug) {
+                msg_printf(project, MSG_INFO,
+                    "[trickle] can't read trickle file %s", path
+                );
+            }
+            continue;
+        }
+        if (log_flags.trickle_debug) {
+            msg_printf(project, MSG_INFO,
+                "[trickle] read trickle file %s", path
+            );
+        }
         fprintf(f,
             "  <msg_from_host>\n"
             "      <result_name>%s</result_name>\n"
diff --git a/client/file_names.h b/client/file_names.h
index 0aba40f..bf729f2 100644
--- a/client/file_names.h
+++ b/client/file_names.h
@@ -62,6 +62,7 @@ extern void send_log_after(const char* filename, double t, MIOFILE& mf);
 #define CA_BUNDLE_FILENAME          "ca-bundle.crt"
 #define CERTIFICATE_DIRECTORY       "certificates"
 #define CLIENT_AUTH_FILENAME        "client_auth.xml"
+#define CLIENT_BRAND_FILENAME       "client_brand.txt"
 #define CLIENT_OPAQUE_FILENAME      "client_opaque.txt"
 #define CONFIG_FILE                 "cc_config.xml"
 #define COPROC_INFO_FILENAME        "coproc_info.xml"
diff --git a/client/gpu_detect.cpp b/client/gpu_detect.cpp
index d604d97..f17380f 100644
--- a/client/gpu_detect.cpp
+++ b/client/gpu_detect.cpp
@@ -37,6 +37,7 @@
 #endif
 
 #include "coproc.h"
+#include "gpu_detect.h"
 #include "file_names.h"
 #include "util.h"
 #include "str_replace.h"
@@ -60,6 +61,7 @@ vector<COPROC_INTEL> intel_gpus;
 vector<OPENCL_DEVICE_PROP> ati_opencls;
 vector<OPENCL_DEVICE_PROP> nvidia_opencls;
 vector<OPENCL_DEVICE_PROP> intel_gpu_opencls;
+vector<OPENCL_DEVICE_PROP> other_opencls;
 vector<OPENCL_CPU_PROP> cpu_opencls;
 
 static char* client_path;
@@ -251,6 +253,23 @@ void COPROCS::correlate_gpus(
         descs.push_back(string(buf));
     }
 
+    // Create descriptions for other OpenCL GPUs
+    //
+    int max_other_coprocs = MAX_RSC-1;  // coprocs[0] is reserved for CPU
+    if (have_nvidia()) max_other_coprocs--;
+    if (have_ati()) max_other_coprocs--;
+    if (have_intel_gpu()) max_other_coprocs--;
+
+    // TODO: Should we implement cc_config ignore vectors for other (future) OpenCL coprocessors?
+
+    for (i=0; i<other_opencls.size(); i++) {
+        if ((int)i > max_other_coprocs) {
+            other_opencls[i].is_used = COPROC_UNUSED;
+        }
+        other_opencls[i].description(buf, sizeof(buf), other_opencls[i].name);
+        descs.push_back(string(buf));
+    }
+    
     // Create descriptions for OpenCL CPUs
     //
     for (i=0; i<cpu_opencls.size(); i++) {
@@ -265,7 +284,43 @@ void COPROCS::correlate_gpus(
     nvidia_opencls.clear();
     intel_gpu_opencls.clear();
     cpu_opencls.clear();
+}
 
+// This is called from CLIENT_STATE::init() after adding NVIDIA, ATI and Intel GPUs
+// If we don't care about the order of GPUs in COPROCS::coprocs[], 
+// this code could be included at the end of COPROCS::correlate_gpus().
+int COPROCS::add_other_coproc_types() {
+    int retval = 0;
+    
+    for (unsigned int i=0; i<other_opencls.size(); i++) {
+        if (other_opencls[i].is_used != COPROC_USED) continue;
+        if (n_rsc >= MAX_RSC) {
+            retval = ERR_BUFFER_OVERFLOW;
+            break;
+        }
+        
+        COPROC c;
+        // For other device types other than NVIDIA, ATI or Intel GPU coprocessor.
+        // we put each instance into a separate other_opencls element, so count=1.
+        c.count = 1;
+        c.opencl_device_count = 1;
+        c.opencl_prop = other_opencls[i];
+        c.available_ram = c.opencl_prop.global_mem_size;
+        c.device_num = c.opencl_prop.device_num;
+        c.peak_flops = c.opencl_prop.peak_flops;
+        c.have_opencl = true;
+        c.opencl_device_indexes[0] = c.opencl_prop.opencl_device_index;
+        c.opencl_device_ids[0] = c.opencl_prop.device_id;
+        c.instance_has_opencl[0] = true;
+        safe_strcpy(c.type, other_opencls[i].name);
+
+        // Don't call COPROCS::add() because duplicate type is legal here
+        coprocs[n_rsc++] = c;
+        
+    }
+    
+    other_opencls.clear();
+    return retval;
 }
 
 // Some dual-GPU laptops (e.g., Macbook Pro) don't 
@@ -316,6 +371,9 @@ int COPROCS::write_coproc_info_file(vector<string> &warnings) {
     for (i=0; i<intel_gpu_opencls.size(); ++i) {
         intel_gpu_opencls[i].write_xml(mf, "intel_gpu_opencl", true);
     }
+    for (i=0; i<other_opencls.size(); i++) {
+        other_opencls[i].write_xml(mf, "other_opencl", true);
+    }
     for (i=0; i<cpu_opencls.size(); i++) {
         cpu_opencls[i].write_xml(mf);
     }
@@ -340,6 +398,7 @@ int COPROCS::read_coproc_info_file(vector<string> &warnings) {
     OPENCL_DEVICE_PROP ati_opencl;
     OPENCL_DEVICE_PROP nvidia_opencl;
     OPENCL_DEVICE_PROP intel_gpu_opencl;
+    OPENCL_DEVICE_PROP other_opencl;
     OPENCL_CPU_PROP cpu_opencl;
 
     ati_gpus.clear();
@@ -348,6 +407,7 @@ int COPROCS::read_coproc_info_file(vector<string> &warnings) {
     ati_opencls.clear();
     nvidia_opencls.clear();
     intel_gpu_opencls.clear();
+    other_opencls.clear();
     cpu_opencls.clear();
 
     f = boinc_fopen(COPROC_INFO_FILENAME, "r");
@@ -434,6 +494,18 @@ int COPROCS::read_coproc_info_file(vector<string> &warnings) {
             continue;
         }
 
+        if (xp.match_tag("other_opencl")) {
+            memset(&other_opencl, 0, sizeof(other_opencl));
+            retval = other_opencl.parse(xp, "/other_opencl");
+            if (retval) {
+                memset(&other_opencl, 0, sizeof(other_opencl));
+            } else {
+                other_opencl.is_used = COPROC_USED;
+                other_opencls.push_back(other_opencl);
+            }
+            continue;
+        }
+
         if (xp.match_tag("opencl_cpu_prop")) {
             memset(&cpu_opencl, 0, sizeof(cpu_opencl));
             retval = cpu_opencl.parse(xp);
@@ -528,7 +600,11 @@ int COPROCS::launch_child_process_to_detect_gpus() {
         client_path,
         argc,
         argv, 
+#ifdef _DEBUG
+        1,
+#else
         0,
+#endif
         prog
     );
 
diff --git a/client/gpu_detect.h b/client/gpu_detect.h
index 366a1bc..75b32a6 100644
--- a/client/gpu_detect.h
+++ b/client/gpu_detect.h
@@ -15,11 +15,20 @@
 // You should have received a copy of the GNU Lesser General Public License
 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
 
+#ifndef _GPU_DETECT_H_
+#define _GPU_DETECT_H_
+
+#include "coproc.h"
+
+using std::vector;
+
 extern vector<COPROC_ATI> ati_gpus;
 extern vector<COPROC_NVIDIA> nvidia_gpus;
 extern vector<COPROC_INTEL> intel_gpus;
 extern vector<OPENCL_DEVICE_PROP> nvidia_opencls;
 extern vector<OPENCL_DEVICE_PROP> ati_opencls;
 extern vector<OPENCL_DEVICE_PROP> intel_gpu_opencls;
+extern vector<OPENCL_DEVICE_PROP> other_opencls;
 extern vector<OPENCL_CPU_PROP> cpu_opencls;
 
+#endif
diff --git a/client/gpu_opencl.cpp b/client/gpu_opencl.cpp
index 3902b3a..2ba3a14 100644
--- a/client/gpu_opencl.cpp
+++ b/client/gpu_opencl.cpp
@@ -17,6 +17,8 @@
 
 // Detection of GPUs using OpenCL
 
+#define TEST_OTHER_COPROC_LOGIC 0
+
 #ifdef _WIN32
 #include "boinc_win.h"
 #ifdef _MSC_VER
@@ -176,7 +178,6 @@ void COPROCS::get_opencl(
 #ifdef __APPLE__
     opencl_lib = dlopen("/System/Library/Frameworks/OpenCL.framework/Versions/Current/OpenCL", RTLD_NOW);
 #else
-//TODO: Is this correct?
     opencl_lib = dlopen("libOpenCL.so", RTLD_NOW);
 #endif
     if (!opencl_lib) {
@@ -289,10 +290,11 @@ void COPROCS::get_opencl(
             cpu_opencls.push_back(c);
         }
 
-        //////////// GPUs //////////////
+        //////////// GPUs and Accelerators //////////////
         
         ciErrNum = (*__clGetDeviceIDs)(
-            platforms[platform_index], (CL_DEVICE_TYPE_GPU),
+            platforms[platform_index],
+            (CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_ACCELERATOR),
             MAX_COPROC_INSTANCES, devices, &num_devices
         );
 
@@ -367,6 +369,17 @@ void COPROCS::get_opencl(
             ciErrNum = get_opencl_info(prop, device_index, warnings);
             if (ciErrNum != CL_SUCCESS) continue;
 
+// TODO: Eliminate this, or improve it
+#if TEST_OTHER_COPROC_LOGIC
+            if (is_NVIDIA(prop.vendor)) {
+                safe_strcpy(prop.vendor, "FAKE VENDOR X");
+            } else if (is_AMD(prop.vendor)) {
+                safe_strcpy(prop.vendor, "FAKE VENDOR Y");
+            } else {
+                safe_strcpy(prop.vendor, "FAKE VENDOR Z");
+            }
+#endif
+
             prop.is_used = COPROC_UNUSED;
             prop.get_device_version_int();
 
@@ -432,7 +445,7 @@ void COPROCS::get_opencl(
             }
             
             //////////// AMD / ATI //////////////
-            if (is_AMD(prop.vendor)) {
+            else if (is_AMD(prop.vendor)) {
                 prop.opencl_device_index = device_index;
 
                 if (ati.have_cal) {
@@ -490,8 +503,7 @@ void COPROCS::get_opencl(
             }
 
             //////////// INTEL GPU //////////////
-            //
-            if (is_intel(prop.vendor)) {
+            else if (is_intel(prop.vendor)) {
                 prop.device_num = (int)(intel_gpu_opencls.size());
                 prop.opencl_device_index = device_index;
 
@@ -515,8 +527,47 @@ void COPROCS::get_opencl(
                 //
                 intel_gpus.push_back(c);
             }
+
+            //////////// OTHER GPU OR ACCELERTOR //////////////
+            else {
+                // Put each coprocessor instance into a separate other_opencls element
+
+                // opencl_device_index is passed to project apps via init_data.xml
+                // to differentiate among OpenCL devices from the same vendor. It is
+                // used by boinc_get_opencl_ids() to select the correct OpenCL device.
+                int opencl_device_index = 0;
+                for (unsigned int coproc_index=0; coproc_index<other_opencls.size(); coproc_index++) {
+                    if (!strcmp(other_opencls[coproc_index].vendor, prop.vendor)) {
+                        opencl_device_index++;  // Another OpenCL device from same vendor
+                    }
+                }
+                
+                prop.device_num = 0;    // Each vector entry has only one device
+                prop.opencl_device_index = opencl_device_index;
+                prop.opencl_available_ram = prop.global_mem_size;
+                prop.is_used = COPROC_USED;
+
+                // TODO: Find a better way to calculate / estimate peak_flops for future coprocessors?
+                prop.peak_flops = 0;
+                if (prop.max_compute_units) {
+                    prop.peak_flops = prop.max_compute_units * prop.max_clock_frequency * MEGA;
+                }
+                if (prop.peak_flops <= 0) prop.peak_flops = 45e9;
+
+                other_opencls.push_back(prop);
+            }
         }
     }
+    
+    int max_other_coprocs = MAX_RSC-1;  // coprocs[0] is reserved for CPU
+    // Neither nvidia.count, ati.count nor intel_gpu.count have been set yet, 
+    // so we can't test have_nvidia(), have_ati() or have_intel_gpu() here.
+    if ((nvidia_opencls.size() > 0) || nvidia.have_cuda) max_other_coprocs--;
+    if ((ati_opencls.size() > 0) || ati.have_cal) max_other_coprocs--;
+    if (intel_gpu_opencls.size() > 0) max_other_coprocs--;
+    if ((int)other_opencls.size() > max_other_coprocs) {
+        warnings.push_back("Too many OpenCL device types found");
+    }
 
 
 #ifdef __APPLE__
@@ -532,10 +583,12 @@ void COPROCS::get_opencl(
 
     if ((nvidia_opencls.size() == 0) &&
         (ati_opencls.size() == 0) &&
-        (intel_gpu_opencls.size() == 0)
+        (intel_gpu_opencls.size() == 0) &&
+        (cpu_opencls.size() == 0) &&
+        (other_opencls.size() == 0)
     ) {
         warnings.push_back(
-            "OpenCL library present but no OpenCL-capable GPUs found"
+            "OpenCL library present but no OpenCL-capable devices found"
         );
     }
 }
@@ -577,9 +630,7 @@ void COPROCS::correlate_opencl(
         intel_gpu.available_ram = intel_gpu.opencl_prop.global_mem_size;
         safe_strcpy(intel_gpu.name, intel_gpu.opencl_prop.name);
     }
-    
-// TODO: Add code to allow adding other GPU vendors
-}
+ }
 
 cl_int COPROCS::get_opencl_info(
     OPENCL_DEVICE_PROP& prop,
@@ -839,7 +890,7 @@ void COPROC::find_best_opencls(
 ) {
     unsigned int i;
 
-    // identify the most capable ATI or NVIDIA OpenCL GPU
+    // identify the most capable ATI, NVIDIA or Intel OpenCL GPU
     //
     bool first = true;
     for (i=0; i<opencls.size(); i++) {
diff --git a/client/project.cpp b/client/project.cpp
index 906c05e..555a0d7 100644
--- a/client/project.cpp
+++ b/client/project.cpp
@@ -60,6 +60,7 @@ void PROJECT::init() {
     strcpy(team_name, "");
     strcpy(email_hash, "");
     strcpy(cross_project_id, "");
+    strcpy(external_cpid, "");
     cpid_time = 0;
     user_total_credit = 0;
     user_expavg_credit = 0;
@@ -106,6 +107,7 @@ void PROJECT::init() {
     too_many_uploading_results = false;
     njobs_success = 0;
     njobs_error = 0;
+    app_configs.clear();
 
 #ifdef SIM
     idle_time = 0;
@@ -190,6 +192,7 @@ int PROJECT::parse_state(XML_PARSER& xp) {
         if (xp.parse_str("host_venue", host_venue, sizeof(host_venue))) continue;
         if (xp.parse_str("email_hash", email_hash, sizeof(email_hash))) continue;
         if (xp.parse_str("cross_project_id", cross_project_id, sizeof(cross_project_id))) continue;
+        if (xp.parse_str("external_cpid", external_cpid, sizeof(external_cpid))) continue;
         if (xp.parse_double("cpid_time", cpid_time)) continue;
         if (xp.parse_double("user_total_credit", user_total_credit)) continue;
         if (xp.parse_double("user_expavg_credit", user_expavg_credit)) continue;
@@ -352,6 +355,7 @@ int PROJECT::write_state(MIOFILE& out, bool gui_rpc) {
         "    <host_venue>%s</host_venue>\n"
         "    <email_hash>%s</email_hash>\n"
         "    <cross_project_id>%s</cross_project_id>\n"
+        "    <external_cpid>%s</external_cpid>\n"
         "    <cpid_time>%f</cpid_time>\n"
         "    <user_total_credit>%f</user_total_credit>\n"
         "    <user_expavg_credit>%f</user_expavg_credit>\n"
@@ -386,6 +390,7 @@ int PROJECT::write_state(MIOFILE& out, bool gui_rpc) {
         host_venue,
         email_hash,
         cross_project_id,
+        external_cpid,
         cpid_time,
         user_total_credit,
         user_expavg_credit,
@@ -521,6 +526,7 @@ void PROJECT::copy_state_fields(PROJECT& p) {
     safe_strcpy(host_venue, p.host_venue);
     safe_strcpy(email_hash, p.email_hash);
     safe_strcpy(cross_project_id, p.cross_project_id);
+    safe_strcpy(external_cpid, p.external_cpid);
     user_total_credit = p.user_total_credit;
     user_expavg_credit = p.user_expavg_credit;
     user_create_time = p.user_create_time;
diff --git a/client/project.h b/client/project.h
index 70cb63b..1b42f90 100644
--- a/client/project.h
+++ b/client/project.h
@@ -95,6 +95,9 @@ struct PROJECT : PROJ_AM {
     char team_name[256];
     char email_hash[MD5_LEN];
     char cross_project_id[MD5_LEN];
+        // the "internal" user CPID
+    char external_cpid[MD5_LEN];
+        // the "external" user CPID (as exported to stats sites)
     double cpid_time;
     double user_total_credit;
     double user_expavg_credit;
@@ -229,6 +232,8 @@ struct PROJECT : PROJ_AM {
     bool some_result_suspended();
     bool uploading();
     bool has_results();
+    int n_concurrent;
+        // used to enforce APP_CONFIGS::max_concurrent
 
     struct RESULT *next_runnable_result;
         // the next result to run for this project
diff --git a/client/result.h b/client/result.h
index 0a9c91d..aa39d88 100644
--- a/client/result.h
+++ b/client/result.h
@@ -167,6 +167,7 @@ struct RESULT {
     bool already_selected;
         // used to keep cpu scheduler from scheduling a result twice
         // transient; used only within schedule_cpus()
+        // also used in round-robin simulation
     double computation_deadline();
         // report deadline - prefs.work_buf_min - time slice
     bool rr_sim_misses_deadline;
@@ -190,13 +191,23 @@ struct RESULT {
 
 inline bool max_concurrent_exceeded(RESULT* rp) {
     APP* app = rp->app;
-    if (!app->max_concurrent) return false;
-    return (app->n_concurrent >= app->max_concurrent);
-
+    if (app->max_concurrent) {
+        if (app->n_concurrent >= app->max_concurrent) {
+            return true;
+        }
+    }
+    PROJECT* p = rp->project;
+    if (p->app_configs.project_max_concurrent) {
+        if (p->n_concurrent >= p->app_configs.project_max_concurrent) {
+            return true;
+        }
+    }
+    return false;
 }
 
 inline void max_concurrent_inc(RESULT* rp) {
     rp->app->n_concurrent++;
+    rp->project->n_concurrent++;
 }
 
 // a completed result, for which the RESULT record no longer exists.
diff --git a/client/rr_sim.cpp b/client/rr_sim.cpp
index 9027669..d0f1c0f 100644
--- a/client/rr_sim.cpp
+++ b/client/rr_sim.cpp
@@ -180,6 +180,7 @@ void RR_SIM::init_pending_lists() {
     for (unsigned int i=0; i<gstate.results.size(); i++) {
         RESULT* rp = gstate.results[i];
         rp->rr_sim_misses_deadline = false;
+        rp->already_selected = false;
         if (!rp->nearly_runnable()) continue;
         if (rp->some_download_stalled()) continue;
         if (rp->project->non_cpu_intensive) continue;
@@ -268,13 +269,14 @@ void RR_SIM::pick_jobs_to_run(double reltime) {
                 //
                 activate(rp);
                 adjust_rec_sched(rp);
-                if (log_flags.rrsim_detail) {
+                if (log_flags.rrsim_detail && !rp->already_selected) {
                     char buf[256];
                     rsc_string(rp, buf);
                     msg_printf(rp->project, MSG_INFO,
-                        "[rr_sim_detail] %.2f: starting %s (%s)",
-                        reltime, rp->name, buf
+                        "[rr_sim_detail] %.2f: starting %s (%s) (%.2fG/%.2fG)",
+                        reltime, rp->name, buf, rp->rrsim_flops_left/1e9, rp->rrsim_flops/1e9
                     );
+                    rp->already_selected = true;
                 }
 
                 // check whether resource is saturated
@@ -412,6 +414,13 @@ void RR_SIM::simulate() {
         // see if we finish a time slice before first job ends
         //
         double delta_t = rpbest->rrsim_finish_delay;
+        if (log_flags.rrsim_detail) {
+            msg_printf(NULL, MSG_INFO,
+                "[rrsim_detail] rpbest: %s (finish delay %.2f)",
+                rpbest->name,
+                delta_t
+            );
+        }
         if (delta_t > 3600) {
             rpbest = 0;
 
@@ -422,14 +431,22 @@ void RR_SIM::simulate() {
             } else {
                 delta_t = 3600;
             }
+            if (log_flags.rrsim_detail) {
+                msg_printf(NULL, MSG_INFO,
+                    "[rrsim_detail] time-slice step of %.2f sec", delta_t
+                );
+            }
         } else {
             rpbest->rrsim_done = true;
             pbest = rpbest->project;
             if (log_flags.rr_simulation) {
+                char buf[256];
+                rsc_string(rpbest, buf);
                 msg_printf(pbest, MSG_INFO,
-                    "[rr_sim] %.2f: %s finishes (%.2fG/%.2fG)",
-                    sim_now - gstate.now,
+                    "[rr_sim] %.2f: %s finishes (%s) (%.2fG/%.2fG)",
+                    sim_now + delta_t - gstate.now,
                     rpbest->name,
+                    buf,
                     rpbest->estimated_flops_remaining()/1e9, rpbest->rrsim_flops/1e9
                 );
             }
@@ -472,30 +489,9 @@ void RR_SIM::simulate() {
             }
         }
 
-#if 1
         for (int i=0; i<coprocs.n_rsc; i++) {
             rsc_work_fetch[i].update_stats(sim_now, delta_t, buf_end);
         }
-#else
-        // update saturated time
-        //
-        double end_time = sim_now + delta_t;
-        double x = end_time - gstate.now;
-        for (int i=0; i<coprocs.n_rsc; i++) {
-            rsc_work_fetch[i].update_saturated_time(x);
-        }
-
-        // increment resource shortfalls
-        //
-        if (sim_now < buf_end) {
-            if (end_time > buf_end) end_time = buf_end;
-            double d_time = end_time - sim_now;
-
-            for (int i=0; i<coprocs.n_rsc; i++) {
-                rsc_work_fetch[i].accumulate_shortfall(d_time);
-            }
-        }
-#endif
 
         // update project REC
         //
diff --git a/client/sandbox.cpp b/client/sandbox.cpp
index ff6ff93..df215e8 100644
--- a/client/sandbox.cpp
+++ b/client/sandbox.cpp
@@ -23,8 +23,10 @@
 #include "config.h"
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <grp.h>
 #include <errno.h>
+#include <fcntl.h> 
+#include <unistd.h>
+#include <grp.h>
 #endif
 
 #include "error_numbers.h"
@@ -42,49 +44,6 @@
 bool g_use_sandbox = false;
 
 #ifndef _WIN32
-#ifndef _DEBUG
-static int lookup_group(const char* name, gid_t& gid) {
-    struct group* gp = getgrnam(name);
-    if (!gp) return ERR_GETGRNAM;
-    gid = gp->gr_gid;
-    return 0;
-}
-#endif
-
-int kill_via_switcher(int pid) {
-    char cmd[1024];
-    
-    if (!g_use_sandbox) return 0;
-
-    // if project application is running as user boinc_project and 
-    // client is running as user boinc_master,
-    // we cannot send a signal directly, so use switcher.
-    //
-    sprintf(cmd, "/bin/kill kill -s KILL %d", pid);
-    return switcher_exec(SWITCHER_FILE_NAME, cmd);
-}
-
-int get_project_gid() {
-    if (g_use_sandbox) {
-#ifdef _DEBUG
-        gstate.boinc_project_gid = getegid();
-#else
-        return lookup_group(BOINC_PROJECT_GROUP_NAME, gstate.boinc_project_gid);
-#endif  // _DEBUG
-    } else {
-        gstate.boinc_project_gid = 0;
-    }
-    return 0;
-}
-
-int set_to_project_group(const char* path) {
-    if (g_use_sandbox) {
-        if (switcher_exec(SETPROJECTGRP_FILE_NAME, path)) {
-            return ERR_CHOWN;
-        }
-    }
-    return 0;
-}
 
 // POSIX requires that shells run from an application will use the 
 // real UID and GID if different from the effective UID and GID.  
@@ -95,10 +54,30 @@ int set_to_project_group(const char* path) {
 int switcher_exec(const char *util_filename, const char* cmdline) {
     char* argv[100];
     char util_path[MAXPATHLEN];
+    char command [1024];
+    char buffer[1024];
+    int fds_out[2], fds_err[2];
+    int stat;
+    int retval;
+    std::string output_out, output_err;
 
     sprintf(util_path, "%s/%s", SWITCHER_DIR, util_filename);
     argv[0] = const_cast<char*>(util_filename);
+    // Make a copy of cmdline because parse_command_line modifies it
+    safe_strcpy(command, cmdline);
     parse_command_line(const_cast<char*>(cmdline), argv+1);
+
+    // Create the output pipes
+    if (pipe(fds_out) == -1) {
+        perror("pipe() for fds_out failed in switcher_exec");
+        return ERR_PIPE;
+    }
+    
+    if (pipe(fds_err) == -1) {
+        perror("pipe() for fds_err failed in switcher_exec");
+        return ERR_PIPE;
+    }
+
     int pid = fork();
     if (pid == -1) {
         perror("fork() failed in switcher_exec");
@@ -106,15 +85,105 @@ int switcher_exec(const char *util_filename, const char* cmdline) {
     }
     if (pid == 0) {
         // This is the new (forked) process
+
+        // Setup pipe redirects
+        while ((dup2(fds_out[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+        while ((dup2(fds_err[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
+        // Child only needs one-way (write) pipes so close read pipes
+        close(fds_out[0]);
+        close(fds_err[0]);
+
         execv(util_path, argv);
         fprintf(stderr, "execv failed in switcher_exec(%s, %s): %s", util_path, cmdline, strerror(errno));
-        return ERR_EXEC;
+
+        _exit(EXIT_FAILURE);
+    }
+    // Parent only needs one-way (read) pipes so close write pipes
+    close(fds_out[1]);
+    close(fds_err[1]);
+    
+    // Capture stdout output
+    while (1) {
+        ssize_t count = read(fds_out[0], buffer, sizeof(buffer));
+        if (count == -1) {
+            if (errno == EINTR) {
+                continue;
+            } else {
+                break;
+            }
+        } else if (count == 0) {
+            break;
+        } else {
+            buffer[count] = '\0';
+            output_out += buffer;
+        }
+    }
+
+    // Capture stderr output
+    while (1) {
+        ssize_t count = read(fds_err[0], buffer, sizeof(buffer));
+        if (count == -1) {
+            if (errno == EINTR) {
+                continue;
+            } else {
+                break;
+            }
+        } else if (count == 0) {
+            break;
+        } else {
+            buffer[count] = '\0';
+            output_err += buffer;
+        }
     }
+
     // Wait for command to complete, like system() does.
-    waitpid(pid, 0, 0); 
+    waitpid(pid, &stat, 0);
+
+    // Close pipe descriptors
+    close(fds_out[0]);
+    close(fds_err[0]);
+
+    if (WIFEXITED(stat)) {
+        retval = WEXITSTATUS(stat);
+
+        if (retval) {
+            if (log_flags.task_debug) {
+                msg_printf(0, MSG_INTERNAL_ERROR, "[task_debug] failure in switcher_exec");
+                msg_printf(0, MSG_INTERNAL_ERROR, "[task_debug]   switcher: %s", util_path);
+                msg_printf(0, MSG_INTERNAL_ERROR, "[task_debug]    command: %s", command);
+                msg_printf(0, MSG_INTERNAL_ERROR, "[task_debug]  exit code: %d", retval);
+                msg_printf(0, MSG_INTERNAL_ERROR, "[task_debug]     stdout: %s", output_out.c_str());
+                msg_printf(0, MSG_INTERNAL_ERROR, "[task_debug]     stderr: %s", output_err.c_str());
+            }
+        }
+        return retval;
+    }
+
     return 0;
 }
 
+int kill_via_switcher(int pid) {
+    char cmd[1024];
+    
+    if (!g_use_sandbox) return 0;
+
+    // if project application is running as user boinc_project and 
+    // client is running as user boinc_master,
+    // we cannot send a signal directly, so use switcher.
+    //
+    sprintf(cmd, "/bin/kill kill -s KILL %d", pid);
+    return switcher_exec(SWITCHER_FILE_NAME, cmd);
+}
+
+#ifndef _DEBUG
+static int lookup_group(const char* name, gid_t& gid) {
+    struct group* gp = getgrnam(name);
+    if (!gp) return ERR_GETGRNAM;
+    gid = gp->gr_gid;
+    return 0;
+}
+#endif
+
 int remove_project_owned_file_or_dir(const char* path) {
     char cmd[1024];
 
@@ -129,6 +198,37 @@ int remove_project_owned_file_or_dir(const char* path) {
     return ERR_UNLINK;
 }
 
+int get_project_gid() {
+    if (g_use_sandbox) {
+#ifdef _DEBUG
+        // GDB can't attach to applications which are running as a different user   
+        //  or group, so fix up data with current user and group during debugging
+        gstate.boinc_project_gid = getegid();
+#else
+        return lookup_group(BOINC_PROJECT_GROUP_NAME, gstate.boinc_project_gid);
+#endif  // _DEBUG
+    } else {
+        gstate.boinc_project_gid = 0;
+    }
+    return 0;
+}
+
+int set_to_project_group(const char* path) {
+    if (g_use_sandbox) {
+        if (switcher_exec(SETPROJECTGRP_FILE_NAME, path)) {
+            return ERR_CHOWN;
+        }
+    }
+    return 0;
+}
+
+#else
+int get_project_gid() {
+    return 0;
+}
+int set_to_project_group(const char* path) {
+    return 0;
+}
 #endif // ! _WIN32
 
 static int delete_project_owned_file_aux(const char* path) {
diff --git a/client/scheduler_op.cpp b/client/scheduler_op.cpp
index 6a05cfa..edc39fc 100644
--- a/client/scheduler_op.cpp
+++ b/client/scheduler_op.cpp
@@ -824,6 +824,8 @@ int SCHEDULER_REPLY::parse(FILE* in, PROJECT* project) {
             continue;
         } else if (xp.parse_str("cross_project_id", project->cross_project_id, sizeof(project->cross_project_id))) {
             continue;
+        } else if (xp.parse_str("external_cpid", project->external_cpid, sizeof(project->external_cpid))) {
+            continue;
         } else if (xp.match_tag("trickle_down")) {
             retval = gstate.handle_trickle_down(project, in);
             if (retval) {
diff --git a/client/sim.cpp b/client/sim.cpp
index 4bf0c3c..1056a64 100644
--- a/client/sim.cpp
+++ b/client/sim.cpp
@@ -897,8 +897,6 @@ void show_resource(int rsc_type) {
     fprintf(html_out, "</table></td>");
 }
 
-int nproc_types = 1;
-
 void html_start() {
     char buf[256];
 
@@ -921,13 +919,16 @@ void html_start() {
     fprintf(html_out,
         "<th width=%d>CPU</th>", WIDTH2
     );
-    if (coprocs.have_nvidia()) {
-        fprintf(html_out, "<th width=%d>NVIDIA GPU</th>", WIDTH2);
-        nproc_types++;
-    }
-    if (coprocs.have_ati()) {
-        fprintf(html_out, "<th width=%d>ATI GPU</th>", WIDTH2);
-        nproc_types++;
+    for (int i=1; i<coprocs.n_rsc; i++) {
+        int pt = coproc_type_name_to_num(coprocs.coprocs[i].type);
+        const char* name;
+        if (pt) {
+            name = proc_type_name(pt);
+        } else {
+            name = coprocs.coprocs[i].type;
+        }
+        fprintf(html_out, "<th width=%d>%s</th>\n", WIDTH2, name);
+
     }
     fprintf(html_out, "</tr></table>\n");
 }
@@ -940,7 +941,7 @@ void html_rec() {
         );
         fprintf(html_out,
             "<td width=%d valign=top><font size=-2>%s</font></td></tr></table>\n",
-            nproc_types*WIDTH2,
+            coprocs.n_rsc*WIDTH2,
             html_msg.c_str()
         );
         html_msg = "";
diff --git a/client/switcher.cpp b/client/switcher.cpp
index 7bc1aa8..1902350 100644
--- a/client/switcher.cpp
+++ b/client/switcher.cpp
@@ -130,9 +130,11 @@ int main(int /*argc*/, char** argv) {
 #endif
     }
 
-    execv(argv[1], argv+2);
-
-    // If we got here execv failed
-    fprintf(stderr, "Process creation (%s) failed: errno=%d\n", argv[1], errno);
+    retval = execv(argv[1], argv+2);
+    if (retval == -1) {
+        // If we got here execv failed
+        fprintf(stderr, "Process creation (%s) failed: %s (errno = %d)\n", argv[1], strerror(errno), retval);
+    }
 
+    return retval;
 }
diff --git a/client/work_fetch.cpp b/client/work_fetch.cpp
index ee7ed06..1fc6078 100644
--- a/client/work_fetch.cpp
+++ b/client/work_fetch.cpp
@@ -15,6 +15,8 @@
 // You should have received a copy of the GNU Lesser General Public License
 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
 
+#include <cmath>
+
 #include "cpp.h"
 
 #ifdef _WIN32
@@ -146,6 +148,12 @@ void RSC_PROJECT_WORK_FETCH::resource_backoff(PROJECT* p, const char* name) {
 
 ///////////////  RSC_WORK_FETCH  ///////////////
 
+void RSC_WORK_FETCH::copy_request(COPROC& c) {
+    c.req_secs = req_secs;
+    c.req_instances = req_instances;
+    c.estimated_delay =  req_secs?busy_time_estimator.get_busy_time():0;
+}
+
 RSC_PROJECT_WORK_FETCH& RSC_WORK_FETCH::project_state(PROJECT* p) {
     return p->rsc_pwf[rsc_type];
 }
@@ -421,6 +429,27 @@ void WORK_FETCH::set_all_requests(PROJECT* p) {
 }
 #endif
 
+// copy request fields from RSC_WORK_FETCH to COPROCS
+//
+void WORK_FETCH::copy_requests() {
+    for (int i=0; i<coprocs.n_rsc; i++) {
+        switch (coproc_type_name_to_num(coprocs.coprocs[i].type)) {
+        case PROC_TYPE_NVIDIA_GPU:
+            rsc_work_fetch[i].copy_request(coprocs.nvidia);
+            break;
+        case PROC_TYPE_AMD_GPU:
+            rsc_work_fetch[i].copy_request(coprocs.ati);
+            break;
+        case PROC_TYPE_INTEL_GPU:
+            rsc_work_fetch[i].copy_request(coprocs.intel_gpu);
+            break;
+        default:
+            rsc_work_fetch[i].copy_request(coprocs.coprocs[i]);
+            break;
+        }
+    }
+}
+
 void WORK_FETCH::print_state() {
     msg_printf(0, MSG_INFO, "[work_fetch] ------- start work fetch state -------");
     msg_printf(0, MSG_INFO, "[work_fetch] target work buffer: %.2f + %.2f sec",
@@ -530,7 +559,7 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
             for (unsigned int j=0; j<gstate.projects.size(); j++) {
                 p2 = gstate.projects[j];
                 if (p2 == p) break;
-                if (p2->sched_priority == p->sched_priority) continue;
+				if (p2->sched_priority == p->sched_priority) continue;
                 if (p2->pwf.cant_fetch_work_reason) {
                     WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: %s can't fetch work", p2->project_name);)
                     continue;
@@ -1054,13 +1083,25 @@ void CLIENT_STATE::compute_nuploading_results() {
 double ACTIVE_TASK::est_dur() {
     if (fraction_done >= 1) return elapsed_time;
     double wu_est = result->estimated_runtime();
-    if (wu_est < elapsed_time) wu_est = elapsed_time;
-    if (fraction_done <= 0) return wu_est;
+    if (fraction_done <= 0) {
+        if (elapsed_time > 0) {
+            // if app is running but hasn't reported fraction done,
+            // use the fraction-done guesstimate from ACTIVE_TASK::write_gui()
+            //
+            double fd = 1 - exp(-elapsed_time/wu_est);
+            return elapsed_time/fd;
+        } else {
+            return wu_est;
+        }
+    }
+    bool exceeded_wu_est = (elapsed_time > wu_est);
+    if (exceeded_wu_est) wu_est = elapsed_time;
     double frac_est = fraction_done_elapsed_time / fraction_done;
 
     // if app says fraction done is accurate, just use it
+    // also use it if static estimate has already been exceeded
     //
-    if (result->app->fraction_done_exact) return frac_est;
+    if (result->app->fraction_done_exact || exceeded_wu_est) return frac_est;
 
     // weighting of dynamic estimate is the fraction done
     // i.e. when fraction done is 0.5, weighting is 50/50
diff --git a/client/work_fetch.h b/client/work_fetch.h
index b58a771..a2d0e23 100644
--- a/client/work_fetch.h
+++ b/client/work_fetch.h
@@ -240,6 +240,7 @@ struct RSC_WORK_FETCH {
     void print_state(const char*);
     void clear_request();
     void set_request(PROJECT*);
+    void copy_request(COPROC&);
     void set_request_excluded(PROJECT*);
     bool may_have_work(PROJECT*);
     int cant_fetch(PROJECT*);
@@ -311,6 +312,7 @@ struct WORK_FETCH {
     void clear_backoffs(APP_VERSION&);
     void request_string(char*);
     bool requested_work();
+    void copy_requests();
 };
 
 extern RSC_WORK_FETCH rsc_work_fetch[MAX_RSC];
diff --git a/clientgui/DlgAdvPreferences.cpp b/clientgui/DlgAdvPreferences.cpp
index 8a4b23a..715d7a3 100644
--- a/clientgui/DlgAdvPreferences.cpp
+++ b/clientgui/DlgAdvPreferences.cpp
@@ -100,6 +100,29 @@ CDlgAdvPreferences::CDlgAdvPreferences(wxWindow* parent) : CDlgAdvPreferencesBas
     ReadPreferenceSettings();
     //
     RestoreState();
+
+#ifdef __WXMSW__
+    int margin = 0, tabwidth = 0;
+    RECT r;
+    BOOL success = TabCtrl_GetItemRect(m_Notebook->GetHWND(), 0, &r);
+    if (success) {
+        margin = r.left;
+    }
+
+    success = TabCtrl_GetItemRect(m_Notebook->GetHWND(), m_Notebook->GetPageCount()-1, &r);
+    if (success) {
+        tabwidth += r.right;
+    }
+    tabwidth += margin;
+    wxSize sz = m_Notebook->GetBestSize();
+    if (sz.x < tabwidth) {
+        sz.x = tabwidth;
+        m_Notebook->SetMinSize(sz);
+    }
+#endif
+
+    this->Layout();
+    Fit();
 }
 
 /* destructor */
@@ -779,7 +802,10 @@ bool CDlgAdvPreferences::EnsureTabPageVisible(wxTextCtrl* txtCtrl) {
 
 /* show an error message and set the focus to the control that caused the error */
 void CDlgAdvPreferences::ShowErrorMessage(wxString& message,wxTextCtrl* errorCtrl) {
-    bool visibleOK = this->EnsureTabPageVisible(errorCtrl);
+#if wxDEBUG_LEVEL   // Prevent compiler warning (unused variable)
+    bool visibleOK =
+#endif
+    this->EnsureTabPageVisible(errorCtrl);
     wxASSERT(visibleOK);
     //
     if(message.IsEmpty()){
diff --git a/clientgui/DlgAdvPreferencesBase.cpp b/clientgui/DlgAdvPreferencesBase.cpp
index 9876dfd..e8deae1 100644
--- a/clientgui/DlgAdvPreferencesBase.cpp
+++ b/clientgui/DlgAdvPreferencesBase.cpp
@@ -98,6 +98,7 @@ CDlgAdvPreferencesBase::CDlgAdvPreferencesBase( wxWindow* parent, int id, wxStri
     m_panelControls->SetSizer( notebookSizer );
     m_panelControls->Layout();
     notebookSizer->Fit( m_panelControls );
+
     dialogSizer->Add( m_panelControls, 1, wxALL|wxEXPAND, 1 );
 
     m_panelButtons = new wxPanel( this, ID_DEFAULT, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
@@ -126,11 +127,12 @@ CDlgAdvPreferencesBase::CDlgAdvPreferencesBase( wxWindow* parent, int id, wxStri
 
     dialogSizer->Fit( this );
     this->SetSizer( dialogSizer );
-    this->Layout();
 }
 
 wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
 {
+    wxSize textCtrlSize = getTextCtrlSize(wxT("999.99"));
+    
     wxPanel* processorTab = new wxPanel( notebook, ID_TABPAGE_PROC, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
     processorTab->SetExtraStyle( wxWS_EX_VALIDATE_RECURSIVELY );
 
@@ -180,8 +182,7 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
         0, wxALL, 5
     );
     m_txtProcIdleFor = new wxTextCtrl(
-        computingAllowedStaticBox, ID_TXTPROCIDLEFOR, wxT(""), wxDefaultPosition,
-        wxSize( 50,-1 ), wxTE_RIGHT
+        computingAllowedStaticBox, ID_TXTPROCIDLEFOR, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("999.99")), wxTE_RIGHT
     );
     m_txtProcIdleFor->SetToolTip(
         _("do work only after you haven't used the computer for this number of minutes")
@@ -214,8 +215,7 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
         0, wxALL, 5
     );
     m_txtMaxLoad = new wxTextCtrl(
-        computingAllowedStaticBox, ID_TXTMAXLOAD, wxT(""), wxDefaultPosition,
-        wxSize( 50,-1 ), wxTE_RIGHT
+        computingAllowedStaticBox, ID_TXTMAXLOAD, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("100.00")), wxTE_RIGHT
     );
     m_txtMaxLoad->SetToolTip(
         _("suspend work if processor usage exceeds this level")
@@ -240,7 +240,7 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
     m_staticText351 = new wxStaticText( computingAllowedStaticBox, ID_DEFAULT, _("Every day between hours of"), wxDefaultPosition, wxDefaultSize, 0 );
     cpuTimesSizer->Add( m_staticText351, 0, wxALL, 5 );
 
-    m_txtProcEveryDayStart = new wxTextCtrl( computingAllowedStaticBox, ID_TXTPROCEVERYDAYSTART, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT );
+    m_txtProcEveryDayStart = new wxTextCtrl( computingAllowedStaticBox, ID_TXTPROCEVERYDAYSTART, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("23:59")), wxTE_RIGHT );
     m_txtProcEveryDayStart->SetToolTip( _("start work at this time") );
 
     cpuTimesSizer->Add( m_txtProcEveryDayStart, 0, wxALL, 1 );
@@ -248,7 +248,7 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
     m_staticText25 = new wxStaticText( computingAllowedStaticBox, ID_DEFAULT, _("and"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
     cpuTimesSizer->Add( m_staticText25, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtProcEveryDayStop = new wxTextCtrl( computingAllowedStaticBox, ID_TXTPROCEVERYDAYSTOP, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT );
+    m_txtProcEveryDayStop = new wxTextCtrl( computingAllowedStaticBox, ID_TXTPROCEVERYDAYSTOP, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("23:59")), wxTE_RIGHT );
     m_txtProcEveryDayStop->SetToolTip( _("stop work at this time") );
 
     cpuTimesSizer->Add( m_txtProcEveryDayStop, 0, wxALL, 1 );
@@ -273,49 +273,49 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
 
     procDaysSizer->Add( m_chkProcMonday, 0, wxALL, 5 );
 
-    m_txtProcMonday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCMONDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtProcMonday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCMONDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     procDaysSizer->Add( m_txtProcMonday, 0, wxALL, 1 );
 
     m_chkProcTuesday = new wxCheckBox( m_panelProcSpecialTimes, ID_CHKPROCTUESDAY, _("Tuesday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     procDaysSizer->Add( m_chkProcTuesday, 0, wxALL, 5 );
 
-    m_txtProcTuesday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCTUESDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtProcTuesday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCTUESDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     procDaysSizer->Add( m_txtProcTuesday, 0, wxALL, 1 );
 
     m_chkProcWednesday = new wxCheckBox( m_panelProcSpecialTimes, ID_CHKPROCWEDNESDAY, _("Wednesday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     procDaysSizer->Add( m_chkProcWednesday, 0, wxALL, 5 );
 
-    m_txtProcWednesday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCWEDNESDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtProcWednesday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCWEDNESDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     procDaysSizer->Add( m_txtProcWednesday, 0, wxALL, 1 );
 
     m_chkProcThursday = new wxCheckBox( m_panelProcSpecialTimes, ID_CHKPROCTHURSDAY, _("Thursday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     procDaysSizer->Add( m_chkProcThursday, 0, wxALL, 5 );
 
-    m_txtProcThursday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCTHURSDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtProcThursday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCTHURSDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     procDaysSizer->Add( m_txtProcThursday, 0, wxALL, 1 );
 
     m_chkProcFriday = new wxCheckBox( m_panelProcSpecialTimes, ID_CHKPROCFRIDAY, _("Friday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     procDaysSizer->Add( m_chkProcFriday, 0, wxALL, 5 );
 
-    m_txtProcFriday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCFRIDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtProcFriday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCFRIDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     procDaysSizer->Add( m_txtProcFriday, 0, wxALL, 1 );
 
     m_chkProcSaturday = new wxCheckBox( m_panelProcSpecialTimes, ID_CHKPROCSATURDAY, _("Saturday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     procDaysSizer->Add( m_chkProcSaturday, 0, wxALL, 5 );
 
-    m_txtProcSaturday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCSATURDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtProcSaturday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCSATURDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     procDaysSizer->Add( m_txtProcSaturday, 0, wxALL, 1 );
 
     m_chkProcSunday = new wxCheckBox( m_panelProcSpecialTimes, ID_CHKPROCSUNDAY, _("Sunday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     procDaysSizer->Add( m_chkProcSunday, 0, wxALL, 5 );
 
-    m_txtProcSunday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCSUNDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtProcSunday = new wxTextCtrl( m_panelProcSpecialTimes, ID_TXTPROCSUNDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     procDaysSizer->Add( m_txtProcSunday, 0, wxALL, 1 );
 
     m_panelProcSpecialTimes->SetSizer( procDaysSizer );
@@ -335,8 +335,8 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
 
     m_staticText18 = new wxStaticText( miscProcStaticBox, ID_DEFAULT, _("Switch between applications every"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     miscProcGridSizer->Add( m_staticText18, 0, wxALL|wxEXPAND, 5 );
-
-    m_txtProcSwitchEvery = new wxTextCtrl( miscProcStaticBox, ID_TXTPROCSWITCHEVERY, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    
+    m_txtProcSwitchEvery = new wxTextCtrl( miscProcStaticBox, ID_TXTPROCSWITCHEVERY, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     miscProcGridSizer->Add( m_txtProcSwitchEvery, 0, wxALL, 1 );
 
     m_staticText19 = new wxStaticText( miscProcStaticBox, ID_DEFAULT, _("minutes"), wxDefaultPosition, wxDefaultSize, 0 );
@@ -345,7 +345,7 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
     m_staticText20 = new wxStaticText( miscProcStaticBox, ID_DEFAULT, _("On multiprocessor systems, use at most"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     miscProcGridSizer->Add( m_staticText20, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtProcUseProcessors = new wxTextCtrl( miscProcStaticBox, ID_TXTPROCUSEPROCESSORS, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtProcUseProcessors = new wxTextCtrl( miscProcStaticBox, ID_TXTPROCUSEPROCESSORS, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     miscProcGridSizer->Add( m_txtProcUseProcessors, 0, wxALL, 1 );
 
     /*xgettext:no-c-format*/ 
@@ -355,7 +355,7 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
     m_staticText22 = new wxStaticText( miscProcStaticBox, ID_DEFAULT, _("Use at most"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     miscProcGridSizer->Add( m_staticText22, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtProcUseCPUTime = new wxTextCtrl( miscProcStaticBox, ID_TXTPOCUSECPUTIME, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtProcUseCPUTime = new wxTextCtrl( miscProcStaticBox, ID_TXTPOCUSECPUTIME, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     miscProcGridSizer->Add( m_txtProcUseCPUTime, 0, wxALL, 1 );
 
     /*xgettext:no-c-format*/
@@ -376,6 +376,8 @@ wxPanel* CDlgAdvPreferencesBase::createProcessorTab(wxNotebook* notebook)
 
 wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
 {
+    wxSize textCtrlSize = getTextCtrlSize(wxT("9999.99"));
+
     wxPanel* networkTab = new wxPanel( notebook, ID_TABPAGE_NET, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
     networkTab->SetExtraStyle( wxWS_EX_VALIDATE_RECURSIVELY );
 
@@ -391,18 +393,18 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
     // upload/download rates
 
     m_staticText32 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("Maximum download rate"), wxDefaultPosition, wxDefaultSize, 0 );
-    networkGeneralGridSizer->Add( m_staticText32, 0, wxALL, 5 );
+    networkGeneralGridSizer->Add( m_staticText32, 0, wxALIGN_RIGHT|wxALL, 5 );
 
-    m_txtNetDownloadRate = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETDOWNLOADRATE, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT );
+    m_txtNetDownloadRate = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETDOWNLOADRATE, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     networkGeneralGridSizer->Add( m_txtNetDownloadRate, 0, wxALL, 1 );
 
     m_staticText33 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("KBytes/second (0 means no restriction)"), wxDefaultPosition, wxDefaultSize, 0 );
     networkGeneralGridSizer->Add( m_staticText33, 0, wxALL, 5 );
-
+    
     m_staticText34 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("Maximum upload rate"), wxDefaultPosition, wxDefaultSize, 0 );
     networkGeneralGridSizer->Add( m_staticText34, 0, wxALIGN_RIGHT|wxALL, 5 );
 
-    m_txtNetUploadRate = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETUPLOADRATE, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT );
+    m_txtNetUploadRate = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETUPLOADRATE, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     networkGeneralGridSizer->Add( m_txtNetUploadRate, 0, wxALL, 1 );
 
     m_staticText35 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("KBytes/second (0 means no restriction)"), wxDefaultPosition, wxDefaultSize, 0 );
@@ -414,11 +416,10 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
         networkGeneralStaticBox, ID_DEFAULT,
         _("Minimum work buffer"), wxDefaultPosition, wxDefaultSize, 0
     );
-    networkGeneralGridSizer->Add( m_staticText30, 0, wxALL, 5 );
+    networkGeneralGridSizer->Add( m_staticText30, 0, wxALIGN_RIGHT|wxALL, 5 );
 
     m_txtNetConnectInterval = new wxTextCtrl(
-        networkGeneralStaticBox, ID_TXTNETCONNECTINTERVAL, wxT(""),
-        wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT
+        networkGeneralStaticBox, ID_TXTNETCONNECTINTERVAL, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT
     );
     m_txtNetConnectInterval->SetToolTip(
         _("Try to maintain enough tasks to keep busy for this many days")
@@ -438,8 +439,7 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
     networkGeneralGridSizer->Add( m_staticText331, 0, wxALIGN_RIGHT|wxALL, 5 );
 
     m_txtNetAdditionalDays = new wxTextCtrl(
-        networkGeneralStaticBox, ID_TXTNETADDITIONALDAYS, wxT(""),
-        wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT
+        networkGeneralStaticBox, ID_TXTNETADDITIONALDAYS, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT
     );
     m_txtNetAdditionalDays->SetToolTip(
         _("In addition, maintain enough tasks for up to this many days")
@@ -449,28 +449,29 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
     m_staticText341 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("days (maximum value: 10)"), wxDefaultPosition, wxDefaultSize, 0 );
     networkGeneralGridSizer->Add( m_staticText341, 0, wxALL, 5 );
 
-    networkGeneralBoxSizer->Add( networkGeneralGridSizer, 0, wxEXPAND, 1 );
-
     // long-term quota
 
-    wxBoxSizer* networkTransferLimitSizer = new wxBoxSizer( wxHORIZONTAL );
-
     m_staticText_daily_xfer1 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("Transfer at most"), wxDefaultPosition, wxDefaultSize, 0 );
-    networkTransferLimitSizer->Add( m_staticText_daily_xfer1, 0, wxALL, 5 );
+    networkGeneralGridSizer->Add( m_staticText_daily_xfer1, 0, wxALIGN_RIGHT|wxALL, 5 );
+
+    m_txt_daily_xfer_limit_mb = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETDOWNLOADRATE, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
+    networkGeneralGridSizer->Add( m_txt_daily_xfer_limit_mb, 0, wxALL, 1 );
 
-    m_txt_daily_xfer_limit_mb = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETDOWNLOADRATE, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT );
-    networkTransferLimitSizer->Add( m_txt_daily_xfer_limit_mb, 0, wxALL, 1 );
+    wxBoxSizer* networkTransferLimitSizer = new wxBoxSizer( wxHORIZONTAL );
 
     m_staticText_daily_xfer2 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("MBytes every"), wxDefaultPosition, wxDefaultSize, 0 );
     networkTransferLimitSizer->Add( m_staticText_daily_xfer2, 0, wxALL, 5 );
 
-    m_txt_daily_xfer_period_days = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETUPLOADRATE, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), wxTE_RIGHT );
+    m_txt_daily_xfer_period_days = new wxTextCtrl( networkGeneralStaticBox, ID_TXTNETUPLOADRATE, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("999.99")), wxTE_RIGHT );
     networkTransferLimitSizer->Add( m_txt_daily_xfer_period_days, 0, wxALL, 1 );
 
     m_staticText_daily_xfer4 = new wxStaticText( networkGeneralStaticBox, ID_DEFAULT, _("days (0 means no restriction)"), wxDefaultPosition, wxDefaultSize, 0 );
     networkTransferLimitSizer->Add( m_staticText_daily_xfer4, 0, wxALL, 5 );
 
-    networkGeneralBoxSizer->Add( networkTransferLimitSizer, 0, wxALL, 0 );
+    networkGeneralGridSizer->Add( networkTransferLimitSizer, 0, wxALL, 0 );
+
+    networkGeneralBoxSizer->Add( networkGeneralGridSizer, 0, wxEXPAND, 1 );
+
 
     m_chkNetSkipImageVerification = new wxCheckBox( networkGeneralStaticBox, ID_CHKNETSKIPIMAGEVERIFICATION, _("Skip image file verification"), wxDefaultPosition, wxDefaultSize, 0 );
 
@@ -505,7 +506,7 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
     m_staticText38 = new wxStaticText( networkTimesStaticBox, ID_DEFAULT, _("Every day between hours of"), wxDefaultPosition, wxDefaultSize, 0 );
     networkTimesSizer->Add( m_staticText38, 0, wxALL, 5 );
 
-    m_txtNetEveryDayStart = new wxTextCtrl( networkTimesStaticBox, ID_TXTNETEVERYDAYSTART, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), 0 );
+    m_txtNetEveryDayStart = new wxTextCtrl( networkTimesStaticBox, ID_TXTNETEVERYDAYSTART, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("23:59")), 0 );
     m_txtNetEveryDayStart->SetToolTip( _("network usage start hour") );
 
     networkTimesSizer->Add( m_txtNetEveryDayStart, 0, wxALL, 1 );
@@ -513,7 +514,7 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
     m_staticText37 = new wxStaticText( networkTimesStaticBox, ID_DEFAULT, _("and"), wxDefaultPosition, wxDefaultSize, 0 );
     networkTimesSizer->Add( m_staticText37, 0, wxALL, 5 );
 
-    m_txtNetEveryDayStop = new wxTextCtrl( networkTimesStaticBox, ID_TXTNETEVERYDAYSTOP, wxT(""), wxDefaultPosition, wxSize( 50,-1 ), 0 );
+    m_txtNetEveryDayStop = new wxTextCtrl( networkTimesStaticBox, ID_TXTNETEVERYDAYSTOP, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("23:59")), 0 );
     m_txtNetEveryDayStop->SetToolTip( _("network usage stop hour") );
 
     networkTimesSizer->Add( m_txtNetEveryDayStop, 0, wxALL, 1 );
@@ -538,49 +539,49 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
 
     netDaysGridSizer->Add( m_chkNetMonday, 0, wxALL, 5 );
 
-    m_txtNetMonday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETMONDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtNetMonday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETMONDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     netDaysGridSizer->Add( m_txtNetMonday, 0, wxALL, 1 );
 
     m_chkNetTuesday = new wxCheckBox( m_panelNetSpecialTimes, ID_CHKNETTUESDAY, _("Tuesday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     netDaysGridSizer->Add( m_chkNetTuesday, 0, wxALL, 5 );
 
-    m_txtNetTuesday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETTUESDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtNetTuesday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETTUESDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     netDaysGridSizer->Add( m_txtNetTuesday, 0, wxALL, 1 );
 
     m_chkNetWednesday = new wxCheckBox( m_panelNetSpecialTimes, ID_CHKNETWEDNESDAY, _("Wednesday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     netDaysGridSizer->Add( m_chkNetWednesday, 0, wxALL, 5 );
 
-    m_txtNetWednesday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETWEDNESDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtNetWednesday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETWEDNESDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     netDaysGridSizer->Add( m_txtNetWednesday, 0, wxALL, 1 );
 
     m_chkNetThursday = new wxCheckBox( m_panelNetSpecialTimes, ID_CHKNETTHURSDAY, _("Thursday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     netDaysGridSizer->Add( m_chkNetThursday, 0, wxALL, 5 );
 
-    m_txtNetThursday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETTHURSDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtNetThursday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETTHURSDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     netDaysGridSizer->Add( m_txtNetThursday, 0, wxALL, 1 );
 
     m_chkNetFriday = new wxCheckBox( m_panelNetSpecialTimes, ID_CHKNETFRIDAY, _("Friday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     netDaysGridSizer->Add( m_chkNetFriday, 0, wxALL, 5 );
 
-    m_txtNetFriday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETFRIDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtNetFriday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETFRIDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     netDaysGridSizer->Add( m_txtNetFriday, 0, wxALL, 1 );
 
     m_chkNetSaturday = new wxCheckBox( m_panelNetSpecialTimes, ID_CHKNETSATURDAY, _("Saturday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     netDaysGridSizer->Add( m_chkNetSaturday, 0, wxALL, 5 );
 
-    m_txtNetSaturday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETSATURDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtNetSaturday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETSATURDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     netDaysGridSizer->Add( m_txtNetSaturday, 0, wxALL, 1 );
 
     m_chkNetSunday = new wxCheckBox( m_panelNetSpecialTimes, ID_CHKNETSUNDAY, _("Sunday"), wxDefaultPosition, wxDefaultSize, 0 );
 
     netDaysGridSizer->Add( m_chkNetSunday, 0, wxALL, 5 );
 
-    m_txtNetSunday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETSUNDAY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_txtNetSunday = new wxTextCtrl( m_panelNetSpecialTimes, ID_TXTNETSUNDAY, wxT(""), wxDefaultPosition, getTextCtrlSize(wxT("00:00-23:59")), 0 );
     netDaysGridSizer->Add( m_txtNetSunday, 0, wxALL, 1 );
 
     m_panelNetSpecialTimes->SetSizer( netDaysGridSizer );
@@ -599,6 +600,8 @@ wxPanel* CDlgAdvPreferencesBase::createNetworkTab(wxNotebook* notebook)
 
 wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
 {
+    wxSize textCtrlSize = getTextCtrlSize(wxT("9999.99"));
+    
     wxPanel* diskMemoryTab = new wxPanel( notebook, ID_TABPAGE_DISK, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
     diskMemoryTab->SetExtraStyle( wxWS_EX_VALIDATE_RECURSIVELY );
 
@@ -615,7 +618,7 @@ wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
     m_staticText40 = new wxStaticText( diskUsageStaticBox, ID_DEFAULT, _("Use at most"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     diskUsageGridSizer->Add( m_staticText40, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtDiskMaxSpace = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKMAXSPACE, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtDiskMaxSpace = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKMAXSPACE, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     m_txtDiskMaxSpace->SetToolTip( _("the maximum disk space used by BOINC (in Gigabytes)") );
 
     diskUsageGridSizer->Add( m_txtDiskMaxSpace, 0, wxALL, 1 );
@@ -626,7 +629,7 @@ wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
     m_staticText42 = new wxStaticText( diskUsageStaticBox, ID_DEFAULT, _("Leave at least"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     diskUsageGridSizer->Add( m_staticText42, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtDiskLeastFree = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKLEASTFREE, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtDiskLeastFree = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKLEASTFREE, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     m_txtDiskLeastFree->SetToolTip( _("BOINC leaves at least this amount of disk space free (in Gigabytes)") );
 
     diskUsageGridSizer->Add( m_txtDiskLeastFree, 0, wxALL, 1 );
@@ -637,7 +640,7 @@ wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
     m_staticText44 = new wxStaticText( diskUsageStaticBox, ID_DEFAULT, _("Use at most"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     diskUsageGridSizer->Add( m_staticText44, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtDiskMaxOfTotal = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKMAXOFTOTAL, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtDiskMaxOfTotal = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKMAXOFTOTAL, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     m_txtDiskMaxOfTotal->SetToolTip( _("BOINC uses at most this percentage of total disk space") );
 
     diskUsageGridSizer->Add( m_txtDiskMaxOfTotal, 0, wxALL, 1 );
@@ -649,7 +652,7 @@ wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
     m_staticText46 = new wxStaticText( diskUsageStaticBox, ID_DEFAULT, _("Tasks checkpoint to disk at most every"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     diskUsageGridSizer->Add( m_staticText46, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtDiskWriteToDisk = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKWRITETODISK, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtDiskWriteToDisk = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKWRITETODISK, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     diskUsageGridSizer->Add( m_txtDiskWriteToDisk, 0, wxALL, 1 );
 
     m_staticText47 = new wxStaticText( diskUsageStaticBox, ID_DEFAULT, _("seconds"), wxDefaultPosition, wxDefaultSize, 0 );
@@ -658,7 +661,7 @@ wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
     m_staticText48 = new wxStaticText( diskUsageStaticBox, ID_DEFAULT, _("Use at most"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     diskUsageGridSizer->Add( m_staticText48, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtDiskMaxSwap = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKWRITETODISK, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtDiskMaxSwap = new wxTextCtrl( diskUsageStaticBox, ID_TXTDISKWRITETODISK, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     diskUsageGridSizer->Add( m_txtDiskMaxSwap, 0, wxALL, 1 );
 
     /*xgettext:no-c-format*/
@@ -680,7 +683,8 @@ wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
     m_staticText50 = new wxStaticText( memoryUsageStaticBox, ID_DEFAULT, _("Use at most"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     memoryUsageGridSizer->Add( m_staticText50, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtMemoryMaxInUse = new wxTextCtrl( memoryUsageStaticBox, ID_TXTMEMORYMAXINUSE, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    textCtrlSize = getTextCtrlSize(wxT("100.00"));
+    m_txtMemoryMaxInUse = new wxTextCtrl( memoryUsageStaticBox, ID_TXTMEMORYMAXINUSE, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     memoryUsageGridSizer->Add( m_txtMemoryMaxInUse, 0, wxALL, 1 );
 
     /*xgettext:no-c-format*/ 
@@ -690,7 +694,7 @@ wxPanel* CDlgAdvPreferencesBase::createDiskAndMemoryTab(wxNotebook* notebook)
     m_staticText52 = new wxStaticText( memoryUsageStaticBox, ID_DEFAULT, _("Use at most"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
     memoryUsageGridSizer->Add( m_staticText52, 0, wxALL|wxEXPAND, 5 );
 
-    m_txtMemoryMaxOnIdle = new wxTextCtrl( memoryUsageStaticBox, ID_TXTMEMORYMAXONIDLE, wxT(""), wxDefaultPosition, wxSize( 75,-1 ), wxTE_RIGHT );
+    m_txtMemoryMaxOnIdle = new wxTextCtrl( memoryUsageStaticBox, ID_TXTMEMORYMAXONIDLE, wxT(""), wxDefaultPosition, textCtrlSize, wxTE_RIGHT );
     memoryUsageGridSizer->Add( m_txtMemoryMaxOnIdle, 0, wxALL, 1 );
 
     /*xgettext:no-c-format*/ 
@@ -779,3 +783,16 @@ wxPanel* CDlgAdvPreferencesBase::createExclusiveAppsTab(wxNotebook* notebook)
 
     return exclusiveAppsTab;
 }
+
+wxSize CDlgAdvPreferencesBase::getTextCtrlSize(wxString maxText) {
+    int w, h, margin;
+    wxSize sz;
+    wxFont f = GetParent()->GetFont();
+    GetTextExtent(maxText, &w, &h, NULL, NULL, &f);
+    margin = w/3;
+    if (margin < 9) margin = 9;
+    sz.x = w + margin;
+    sz.y = wxDefaultCoord;
+    return sz;
+}
+
diff --git a/clientgui/DlgAdvPreferencesBase.h b/clientgui/DlgAdvPreferencesBase.h
index 0fa81ef..0d03c3e 100644
--- a/clientgui/DlgAdvPreferencesBase.h
+++ b/clientgui/DlgAdvPreferencesBase.h
@@ -235,13 +235,14 @@ protected:
     wxButton* m_btnHelp;
 
 public:
-    CDlgAdvPreferencesBase( wxWindow* parent, int id = -1, wxString title = wxT(""), wxPoint pos = wxDefaultPosition, wxSize size = wxSize( 547,526 ), int style = wxDEFAULT_DIALOG_STYLE );
+    CDlgAdvPreferencesBase( wxWindow* parent, int id = -1, wxString title = wxT(""), wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize, int style = wxDEFAULT_DIALOG_STYLE );
 
 private:
     wxPanel* createProcessorTab(wxNotebook* notebook);
     wxPanel* createNetworkTab(wxNotebook* notebook);
     wxPanel* createDiskAndMemoryTab(wxNotebook* notebook);
     wxPanel* createExclusiveAppsTab(wxNotebook* notebook);
+    wxSize getTextCtrlSize(wxString maxText);
 };
 
 #endif //__DlgAdvPreferencesBase__
diff --git a/clientgui/MainDocument.cpp b/clientgui/MainDocument.cpp
index 5535a95..2f8dd65 100644
--- a/clientgui/MainDocument.cpp
+++ b/clientgui/MainDocument.cpp
@@ -2601,3 +2601,37 @@ wxString result_description(RESULT* result, bool show_resources) {
     return strBuffer;
 }
 
+static void hsv2rgb(
+    double h, double s, double v, double& r, double& g, double& b
+) {
+    double m, n, f;
+    int i = floor(h);
+    f = h - i;
+    if (!(i&1)) f = 1 - f;
+    m = v * (1 - s);
+    n = v * (1 - s*f);
+    switch (i) {
+    case 6:
+    case 0: r = v; g = n; b = m; return;
+    case 1: r = n; g = v; b = m; return;
+    case 2: r = m; g = v; b = n; return;
+    case 3: r = m; g = n; b = v; return;
+    case 4: r = n; g = m; b = v; return;
+    case 5: r = v; g = m; b = n; return;
+    }
+}
+
+// return the ith out of n maximally distinct colors
+//
+void color_cycle(int i, int n, wxColour& color) {
+    double h = (double)i/(double)n;
+    double r, g, b;
+    double v = .6;
+    if (n > 6) v = .5 + (i % 3)*.125;
+        // cycle through 3 different brightnesses
+    hsv2rgb(h*6, .5, v, r, g, b);
+    unsigned char cr = (unsigned char) (r*256);
+    unsigned char cg = (unsigned char) (g*256);
+    unsigned char cb = (unsigned char) (b*256);
+    color = wxColour(cr, cg, cb);
+}
diff --git a/clientgui/MainDocument.h b/clientgui/MainDocument.h
index 11d1d5c..0348f45 100644
--- a/clientgui/MainDocument.h
+++ b/clientgui/MainDocument.h
@@ -415,6 +415,7 @@ extern void localize(wxString& strMessage);
 extern void eol_to_br(wxString& strMessage);
 extern void remove_eols(wxString& strMessage);
 extern void https_to_http(wxString& strMessage);
+extern void color_cycle(int i, int n, wxColour& color);
 
 #ifdef SANDBOX
 #define BOINC_MASTER_GROUP_NAME "boinc_master"
diff --git a/clientgui/ViewResources.cpp b/clientgui/ViewResources.cpp
index 5d8d555..72e8022 100644
--- a/clientgui/ViewResources.cpp
+++ b/clientgui/ViewResources.cpp
@@ -165,24 +165,6 @@ bool CViewResources::OnRestoreState(wxConfigBase* /*pConfig*/) {
     return true;
 }
 
-void hsv2rgb(double h, double s, double v, double& r, double& g, double& b) {
-    double m, n, f;
-    int i = floor(h);
-    f = h - i;
-    if (!(i&1)) f = 1 - f;
-    m = v * (1 - s);
-    n = v * (1 - s*f);
-    switch (i) {
-    case 6:
-    case 0: r = v; g = n; b = m; return;
-    case 1: r = n; g = v; b = m; return;
-    case 2: r = m; g = v; b = n; return;
-    case 3: r = m; g = n; b = v; return;
-    case 4: r = n; g = m; b = v; return;
-    case 5: r = v; g = m; b = n; return;
-    }
-}
-
 void CViewResources::OnListRender( wxTimerEvent& WXUNUSED(event) ) {
     CMainDocument* pDoc = wxGetApp().GetDocument();
     wxString diskspace;
@@ -228,15 +210,9 @@ void CViewResources::OnListRender( wxTimerEvent& WXUNUSED(event) ) {
 				wxPiePart part;
                 part.SetLabel(projectname + wxT(": ") + diskspace);
 				part.SetValue(usage);
-                double h = (double)i/(double)pDoc->disk_usage.projects.size();
-                double r, g, b;
-                double v = .5 + (i % 3)*.2;
-                    // cycle through 3 different brightnesses
-                hsv2rgb(h*6, .5, v, r, g, b);
-                unsigned char cr = (unsigned char) (r*256);
-                unsigned char cg = (unsigned char) (g*256);
-                unsigned char cb = (unsigned char) (b*256);
-                part.SetColour(wxColour(cr, cg, cb));
+                wxColour color;
+                color_cycle(i, pDoc->disk_usage.projects.size(), color);
+                part.SetColour(color);
 				m_pieCtrlBOINC->m_Series.Add(part);
 			}
 			m_pieCtrlBOINC->Refresh();
diff --git a/clientgui/ViewStatistics.cpp b/clientgui/ViewStatistics.cpp
index 04b2aa2..00fef81 100644
--- a/clientgui/ViewStatistics.cpp
+++ b/clientgui/ViewStatistics.cpp
@@ -36,7 +36,8 @@ enum {  // Command buttons (m_SelectedStatistic)
     show_user_total = 0,
     show_user_average,
     show_host_total,
-    show_host_average
+    show_host_average,
+    n_command_buttons
 };
 
 enum {  // Mode buttons (m_ModeViewStatistic)
@@ -184,17 +185,6 @@ CPaintStatistics::CPaintStatistics(wxWindow* parent, wxWindowID id, const wxPoin
 	m_pen_GraphRACColour = wxColour(0, 160, 0);
 	m_pen_GraphTotalHostColour = wxColour(0, 0, 255);
 	m_pen_GraphRACHostColour = wxColour(0, 0, 0);
-	                            
-	m_pen_GraphColour00 = wxColour(255, 0, 0);
-	m_pen_GraphColour01 = wxColour(0, 160, 0);
-	m_pen_GraphColour02 = wxColour(0, 0, 255);
-	m_pen_GraphColour03 = wxColour(0, 0, 0);
-	m_pen_GraphColour04 = wxColour(255, 0, 255);
-	m_pen_GraphColour05 = wxColour(255, 128, 0);
-	m_pen_GraphColour06 = wxColour(192, 192, 0);
-	m_pen_GraphColour07 = wxColour(0, 192, 192);
-	m_pen_GraphColour08 = wxColour(160, 160, 160);
-	m_pen_GraphColour09 = wxColour(160, 0, 0);
 
 	m_dc_bmp.Create(1, 1);
 	m_full_repaint = true;
@@ -239,20 +229,6 @@ static bool CrossTwoLine(const double X1_1, const double Y1_1, const double X1_2
 	}
 }
 
-void CPaintStatistics::getDrawColour(wxColour &graphColour, int number) {
-	switch (number % 10){
-	case 1:	graphColour = m_pen_GraphColour01;	break;
-	case 2:	graphColour = m_pen_GraphColour02;	break;
-	case 3:	graphColour = m_pen_GraphColour03;	break;
-	case 4:	graphColour = m_pen_GraphColour04;	break;
-	case 5: graphColour = m_pen_GraphColour05;	break;
-	case 6:	graphColour = m_pen_GraphColour06;	break;
-	case 7:	graphColour = m_pen_GraphColour07;	break;
-	case 8:	graphColour = m_pen_GraphColour08;	break;
-	case 9: graphColour = m_pen_GraphColour09;	break;
-	default:graphColour = m_pen_GraphColour00;
-	}
-}
 //----Draw "Point"
 static void myDrawPoint(wxDC &dc,int X, int Y, wxColour graphColour,int numberTypePoint, int PointWidth) {
 	dc.SetPen(wxPen(graphColour , 1 , wxSOLID));
@@ -632,7 +608,7 @@ void CPaintStatistics::DrawLegend(wxDC &dc, PROJECTS* proj, CMainDocument* pDoc,
 		int  typePoint = 0;
 		if (bColour){
 			getTypePoint(typePoint, count);
-			getDrawColour(graphColour, count);
+			color_cycle(count, proj->projects.size(), graphColour);
 		} else if (SelProj == count) {
 				graphColour = m_pen_LegendSelectTextColour;
 			} else {
@@ -1317,7 +1293,7 @@ void CPaintStatistics::DrawAll(wxDC &dc) {
 				DrawAxis(dc, max_val_y, min_val_y,max_val_x, min_val_x, m_pen_AxisColour, max_val_y_all, min_val_y_all);
 			//Draw graph
 				wxColour graphColour=wxColour(0,0,0);
-				getDrawColour(graphColour,m_SelectedStatistic);
+				color_cycle(m_SelectedStatistic, n_command_buttons, graphColour);
 				DrawGraph(dc, i, graphColour, 0, m_SelectedStatistic);
 			//Change row/col
 				if (col == nb_proj_col) {
@@ -1384,7 +1360,7 @@ void CPaintStatistics::DrawAll(wxDC &dc) {
 			DrawAxis(dc, max_val_y, min_val_y, max_val_x, min_val_x, pen_AxisColour1, max_val_y, min_val_y);
 		    // Draw graph
 			wxColour graphColour=wxColour(0,0,0);
-			getDrawColour(graphColour,m_SelectedStatistic);
+			color_cycle(m_SelectedStatistic, n_command_buttons, graphColour);
 			DrawGraph(dc, i, graphColour, 0, m_SelectedStatistic);
 		    // Draw marker
 			DrawMarker(dc);
@@ -1454,7 +1430,7 @@ void CPaintStatistics::DrawAll(wxDC &dc) {
 				wxColour graphColour = wxColour(0,0,0);
 				int  typePoint = 0;
 				getTypePoint(typePoint,count);
-				getDrawColour(graphColour,count);
+				color_cycle(count, proj->projects.size(), graphColour);
 				DrawGraph(dc, i, graphColour, typePoint, m_SelectedStatistic);
 			}
 		}
@@ -1591,7 +1567,7 @@ void CPaintStatistics::DrawAll(wxDC &dc) {
         wxColour graphColour = wxColour(0,0,0);
         int  typePoint = 0;
         getTypePoint(typePoint,count);
-        getDrawColour(graphColour,m_SelectedStatistic);
+        color_cycle(m_SelectedStatistic, n_command_buttons, graphColour);
         DrawGraph2(dc, sumstats, graphColour, typePoint, m_SelectedStatistic);
 //        sumstats.clear();
 	//Draw marker
diff --git a/clientgui/ViewStatistics.h b/clientgui/ViewStatistics.h
index c88a954..274f8e8 100644
--- a/clientgui/ViewStatistics.h
+++ b/clientgui/ViewStatistics.h
@@ -47,8 +47,6 @@ public:
 	
 	void DrawMarker(wxDC &dc);
 
-	void getDrawColour(wxColour &graphColour, int number);
-
 	void ClearXY();
 
 	void ClearLegendXY();
@@ -185,16 +183,6 @@ public:
     wxColour                m_pen_GraphTotalHostColour;
     wxColour                m_pen_GraphRACHostColour;
 
-    wxColour                m_pen_GraphColour00;
-    wxColour                m_pen_GraphColour01;
-    wxColour                m_pen_GraphColour02;
-    wxColour                m_pen_GraphColour03;
-    wxColour                m_pen_GraphColour04;
-    wxColour                m_pen_GraphColour05;
-    wxColour                m_pen_GraphColour06;
-    wxColour                m_pen_GraphColour07;
-    wxColour                m_pen_GraphColour08;
-    wxColour                m_pen_GraphColour09;
 protected:
     void OnPaint(wxPaintEvent& event);
 	void OnEraseBackground(wxEraseEvent & /*event*/){};
diff --git a/configure.ac b/configure.ac
index 44dcd1e..57543f1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@ dnl not sure exactly what the minimum version is (but 2.13 wont work)
 AC_PREREQ(2.58)
 
 dnl Set the BOINC version here.  You can also use the set-version script.
-AC_INIT(BOINC, 7.4.8)
+AC_INIT(BOINC, 7.4.14)
 AC_CONFIG_MACRO_DIR([m4])
 LIBBOINC_VERSION=`echo ${PACKAGE_VERSION} | sed 's/\./:/g'`
 AC_SUBST([LIBBOINC_VERSION])
diff --git a/db/boinc_db_types.h b/db/boinc_db_types.h
index 1580ac3..26179c8 100644
--- a/db/boinc_db_types.h
+++ b/db/boinc_db_types.h
@@ -201,6 +201,9 @@ struct USER {
         // is being verified.
     bool has_profile;
     char cross_project_id[256];
+        // the "internal" cross-project ID;
+        // the "external CPID" that  gets exported to stats sites
+        // is MD5(cpid, email)
     char passwd_hash[256];
     bool email_validated;           // deprecated
     int donated;
@@ -347,6 +350,8 @@ struct HOST {
     //
     char p_features[1024];
     char virtualbox_version[256];
+    char client_brand[256];
+        // as specific in client_brand.txt config file on client
     bool p_vm_extensions_disabled;
     int num_opencl_cpu_platforms;
     OPENCL_CPU_PROP opencl_cpu_prop[MAX_OPENCL_CPU_PLATFORMS];
diff --git a/lib/coproc.cpp b/lib/coproc.cpp
index 42bfd8e..9f581f0 100644
--- a/lib/coproc.cpp
+++ b/lib/coproc.cpp
@@ -102,14 +102,23 @@ void PCI_INFO::write(MIOFILE& f) {
     );
 }
 
-void COPROC::write_xml(MIOFILE& f) {
+void COPROC::write_xml(MIOFILE& f, bool scheduler_rpc) {
     f.printf(
         "<coproc>\n"
         "   <type>%s</type>\n"
-        "   <count>%d</count>\n"
-        "</coproc>\n",
+        "   <count>%d</count>\n",
         type, count
     );
+    
+    if (scheduler_rpc) {
+        write_request(f);
+    }
+
+    if (have_opencl) {
+        opencl_prop.write_xml(f, "coproc_opencl");
+    }
+    
+    f.printf("</coproc>\n");
 }
 
 void COPROC::write_request(MIOFILE& f) {
@@ -232,17 +241,24 @@ int COPROCS::parse(XML_PARSER& xp) {
 
 void COPROCS::write_xml(MIOFILE& mf, bool scheduler_rpc) {
 #ifndef _USING_FCGI_
-//TODO: Write coprocs[0] through coprocs[n_rsc]
     mf.printf("    <coprocs>\n");
-    if (nvidia.count) {
-        nvidia.write_xml(mf, scheduler_rpc);
-    }
-    if (ati.count) {
-        ati.write_xml(mf, scheduler_rpc);
-    }
-    if (intel_gpu.count) {
-        intel_gpu.write_xml(mf, scheduler_rpc);
+    
+    for (int i=1; i<n_rsc; i++) {
+        switch (coproc_type_name_to_num(coprocs[i].type)) {
+        case PROC_TYPE_NVIDIA_GPU:
+            nvidia.write_xml(mf, scheduler_rpc);
+            break;
+        case PROC_TYPE_AMD_GPU:
+            ati.write_xml(mf, scheduler_rpc);
+            break;
+        case PROC_TYPE_INTEL_GPU:
+            intel_gpu.write_xml(mf, scheduler_rpc);
+            break;
+        default:
+            coprocs[i].write_xml(mf, scheduler_rpc);
+        }
     }
+    
     mf.printf("    </coprocs>\n");
 #endif
 }
@@ -883,7 +899,7 @@ void COPROC_INTEL::fake(double ram, double avail_ram, int n) {
 // <coproc>
 //    <type>xxx</type>
 //
-// Don't confused this with the element names used for GPUS within <coprocs>,
+// Don't confuse this with the element names used for GPUS within <coprocs>,
 // namely:
 // coproc_cuda
 // coproc_ati
@@ -914,5 +930,5 @@ int coproc_type_name_to_num(const char* name) {
     if (!strcmp(name, "NVIDIA")) return PROC_TYPE_NVIDIA_GPU;
     if (!strcmp(name, "ATI")) return PROC_TYPE_AMD_GPU;
     if (!strcmp(name, "intel_gpu")) return PROC_TYPE_INTEL_GPU;
-    return 0;
+    return -1;      // Some other type
 }
diff --git a/lib/coproc.h b/lib/coproc.h
index 792b99f..2d699df 100644
--- a/lib/coproc.h
+++ b/lib/coproc.h
@@ -57,6 +57,15 @@
 //  that are incapable of handling them, and it involves no server changes.
 //  Its drawback is that, on systems with multiple and differing GPUs,
 //  it may not use some GPUs that actually could be used.
+//
+//  Modified (as of 23 July 14) to allow coprocessors (OpenCL GPUs and OpenCL
+//  accelerators) from vendors other than original 3: NVIDIA, AMD and Intel.  
+//  For these original 3 GPU vendors, we still use the above approach, and the
+//  COPROC::type field contains a standardized vendor name "NVIDIA", "ATI" or
+//  "intel_gpu".  But for other, "new" vendors, we treat each device as a
+//  separate resource, creating an entry for each instance in the
+//  COPROCS::coprocs[] array and copying the device name COPROC::opencl_prop.name 
+//  into the COPROC::type field (instead of the vendor name.)
 
 #ifndef _COPROC_
 #define _COPROC_
@@ -88,11 +97,23 @@
 
 // arguments to proc_type_name() and proc_type_name_xml().
 //
-#define PROC_TYPE_CPU        0
-#define PROC_TYPE_NVIDIA_GPU 1
-#define PROC_TYPE_AMD_GPU    2
-#define PROC_TYPE_INTEL_GPU  3
-#define NPROC_TYPES          4
+enum {
+    PROC_TYPE_CPU=0,
+    PROC_TYPE_NVIDIA_GPU,
+    PROC_TYPE_AMD_GPU,
+    PROC_TYPE_INTEL_GPU,
+    PROC_TYPE_A,
+    PROC_TYPE_B,
+    PROC_TYPE_C,
+    PROC_TYPE_D,
+    PROC_TYPE_E,
+    PROC_TYPE_F,
+    PROC_TYPE_G,
+    NPROC_TYPES
+};
+
+extern const char* proc_type_names_xml[NPROC_TYPES];
+extern const char* proc_type_names[NPROC_TYPES];
 
 extern const char* proc_type_name(int);
     // user-readable name
@@ -104,6 +125,13 @@ extern int coproc_type_name_to_num(const char* name);
 #define GPU_TYPE_NVIDIA proc_type_name_xml(PROC_TYPE_NVIDIA_GPU)
 #define GPU_TYPE_ATI proc_type_name_xml(PROC_TYPE_AMD_GPU)
 #define GPU_TYPE_INTEL proc_type_name_xml(PROC_TYPE_INTEL_GPU)
+#define COPROC_TYPE_A proc_type_name_xml(PROC_TYPE_A)
+#define COPROC_TYPE_B proc_type_name_xml(PROC_TYPE_B)
+#define COPROC_TYPE_C proc_type_name_xml(PROC_TYPE_C)
+#define COPROC_TYPE_D proc_type_name_xml(PROC_TYPE_D)
+#define COPROC_TYPE_E proc_type_name_xml(PROC_TYPE_E)
+#define COPROC_TYPE_F proc_type_name_xml(PROC_TYPE_F)
+#define COPROC_TYPE_G proc_type_name_xml(PROC_TYPE_G)
 
 // represents a requirement for a coproc.
 // This is a parsed version of the <coproc> elements in an <app_version>
@@ -184,7 +212,7 @@ struct COPROC {
     OPENCL_DEVICE_PROP opencl_prop;
 
 #ifndef _USING_FCGI_
-    void write_xml(MIOFILE&);
+    void write_xml(MIOFILE&, bool scheduler_rpc=false);
     void write_request(MIOFILE&);
 #endif
     int parse(XML_PARSER&);
@@ -399,6 +427,7 @@ struct COPROCS {
     void set_path_to_client(char *path);
     int write_coproc_info_file(std::vector<std::string> &warnings);
     int read_coproc_info_file(std::vector<std::string> &warnings);
+    int add_other_coproc_types();
     
 #ifdef __APPLE__
     void opencl_get_ati_mem_size_from_opengl(std::vector<std::string> &warnings);
diff --git a/lib/error_numbers.h b/lib/error_numbers.h
index 2de17d1..fc7d14e 100644
--- a/lib/error_numbers.h
+++ b/lib/error_numbers.h
@@ -202,6 +202,7 @@
 #define ERR_ABORTED_ON_EXIT -232
 #define ERR_PROC_PARSE      -235
 #define ERR_STATFS          -236
+#define ERR_PIPE            -237
 
 // PLEASE: add a text description of your error to 
 // the text description function boincerror() in str_util.cpp.
diff --git a/lib/gui_rpc_client.h b/lib/gui_rpc_client.h
index 65a70ce..757aab4 100644
--- a/lib/gui_rpc_client.h
+++ b/lib/gui_rpc_client.h
@@ -170,6 +170,7 @@ struct PROJECT {
     char venue[256];
     int njobs_success;
     int njobs_error;
+    char external_cpid[64];
 
     // NOTE: if you add any data items above,
     // update parse(), and clear() to include them!!
diff --git a/lib/gui_rpc_client_ops.cpp b/lib/gui_rpc_client_ops.cpp
index ec45bed..2a90055 100644
--- a/lib/gui_rpc_client_ops.cpp
+++ b/lib/gui_rpc_client_ops.cpp
@@ -459,6 +459,7 @@ int PROJECT::parse(XML_PARSER& xp) {
         if (xp.parse_str("venue", venue, sizeof(venue))) continue;
         if (xp.parse_int("njobs_success", njobs_success)) continue;
         if (xp.parse_int("njobs_error", njobs_error)) continue;
+        if (xp.parse_str("external_cpid", external_cpid, sizeof(external_cpid))) continue;
     }
     return ERR_XML_PARSE;
 }
@@ -512,6 +513,7 @@ void PROJECT::clear() {
     strcpy(venue, "");
     njobs_success = 0;
     njobs_error = 0;
+    strcpy(external_cpid, "");
 }
 
 APP::APP() {
diff --git a/lib/gui_rpc_client_print.cpp b/lib/gui_rpc_client_print.cpp
index b2472a8..4f576c1 100644
--- a/lib/gui_rpc_client_print.cpp
+++ b/lib/gui_rpc_client_print.cpp
@@ -108,6 +108,7 @@ void PROJECT::print() {
     }
     printf("   jobs succeeded: %d\n", njobs_success);
     printf("   jobs failed: %d\n", njobs_error);
+    printf("   cross-project ID: %s\n", external_cpid);
 }
 
 void APP::print() {
diff --git a/lib/opencl_boinc.h b/lib/opencl_boinc.h
index e2ac403..7e16aa6 100644
--- a/lib/opencl_boinc.h
+++ b/lib/opencl_boinc.h
@@ -38,7 +38,7 @@ struct OPENCL_DEVICE_PROP {
     cl_device_id device_id;
     char name[256];                     // Device name
     char vendor[256];                   // Device vendor (NVIDIA, ATI, AMD, etc.)
-    cl_uint vendor_id;                  // OpenCL ID of device vendor
+    cl_uint vendor_id;                  // Vendor's unique ID for this device on this host
     cl_bool available;                  // Is this device available?
     cl_device_fp_config half_fp_config; // Half precision capabilities
     cl_device_fp_config single_fp_config;   // Single precision
@@ -63,7 +63,7 @@ struct OPENCL_DEVICE_PROP {
     double peak_flops;                  // temp used in scan process
     COPROC_USAGE is_used;               // temp used in scan process
     double opencl_available_ram;        // temp used in scan process
-    int opencl_device_index;            // temp used in scan process
+    int opencl_device_index;            // zero-based device number within this COPROC type
 
     void write_xml(MIOFILE&, const char* tag, bool temp_file=false);
     int parse(XML_PARSER&, const char* end_tag);
diff --git a/lib/proc_control.cpp b/lib/proc_control.cpp
index 8ef8775..0eeacb6 100644
--- a/lib/proc_control.cpp
+++ b/lib/proc_control.cpp
@@ -49,6 +49,10 @@ using std::vector;
 
 //#define DEBUG
 
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
 static void get_descendants_aux(PROC_MAP& pm, int pid, vector<int>& pids) {
     PROC_MAP::iterator i = pm.find(pid);
     if (i == pm.end()) return;
diff --git a/lib/procinfo_mac.cpp b/lib/procinfo_mac.cpp
index 705c534..639a463 100644
--- a/lib/procinfo_mac.cpp
+++ b/lib/procinfo_mac.cpp
@@ -101,8 +101,11 @@ int procinfo_setup(PROC_MAP& pm) {
 // This eliminates the need to install our own application which runs setuid 
 // root; this was perceived by some users as a security risk.
 
-
-    fd = popen("ps -axcopid,ppid,rss,vsz,pagein,pri,time,command", "r");
+// Under OS 10.8.x (only) ps writes a spurious warning to stderr if called
+// from a process that has the DYLD_LIBRARY_PATH environment variable set.
+// "env -i command" prevents the command from inheriting the caller's 
+// environment, which avoids the spurious warning.
+    fd = popen("env -i ps -axcopid,ppid,rss,vsz,pagein,pri,time,command", "r");
     if (!fd) return ERR_FOPEN;
 
     // Skip over the header line
diff --git a/lib/shmem.cpp b/lib/shmem.cpp
index 8d90468..00b3cb7 100644
--- a/lib/shmem.cpp
+++ b/lib/shmem.cpp
@@ -418,7 +418,7 @@ int create_shmem(key_t key, int size, gid_t gid, void** pp) {
     }
     if (id < 0) {
         perror("shmget");
-        fprintf(stderr,"shmem size: %d\n", size);
+        fprintf(stderr, "shmem size: %d\n", size);
         return ERR_SHMGET;
     }
 
diff --git a/lib/str_util.cpp b/lib/str_util.cpp
index a573620..041a57b 100644
--- a/lib/str_util.cpp
+++ b/lib/str_util.cpp
@@ -527,6 +527,7 @@ const char* boincerror(int which_error) {
         case ERR_CRYPTO: return "encryption error";
         case ERR_ABORTED_ON_EXIT: return "job was aborted on client exit";
         case ERR_PROC_PARSE: return "a /proc entry was not parsed correctly";
+        case ERR_PIPE: return "pipe() failed";
         case 404: return "HTTP file not found";
         case 407: return "HTTP proxy authentication failure";
         case 416: return "HTTP range request error";
diff --git a/lib/util.cpp b/lib/util.cpp
index d44fea5..5ec56a3 100644
--- a/lib/util.cpp
+++ b/lib/util.cpp
@@ -461,10 +461,35 @@ int run_program(
 #endif
 
 #ifdef _WIN32
+int kill_program(int pid, int exit_code) {
+    int retval;
+
+    HANDLE h = OpenProcess(PROCESS_TERMINATE, false, pid);
+    if (h == NULL && GetLastError() == ERROR_ACCESS_DENIED) return EPERM;
+    if (h == NULL && GetLastError() == ERROR_INVALID_PARAMETER) return EINVAL;
+    if (h == NULL && GetLastError() == ERROR_FILE_NOT_FOUND) return ESRCH;
+    if (h == NULL) return EIO;
+    if (TerminateProcess(h, exit_code)) {
+        retval = 0;
+    } else {
+        retval = 1;
+    }
+    CloseHandle(h);
+
+    return retval;
+}
 void kill_program(HANDLE pid) {
     TerminateProcess(pid, 0);
 }
 #else
+int kill_program(int pid, int exit_code) {
+    int retval;
+    retval = kill(pid, SIGKILL);
+    if (-1 == retval) {
+        retval = errno;
+    }
+    return retval;
+}
 void kill_program(int pid) {
     kill(pid, SIGKILL);
 }
diff --git a/lib/util.h b/lib/util.h
index 3b89817..0db7588 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -91,6 +91,7 @@ extern int run_program(
 );
 
 extern void kill_program(HANDLE);
+extern int kill_program(int, int);
 extern int get_exit_status(HANDLE);
 extern bool process_exists(HANDLE);
 
@@ -100,6 +101,7 @@ extern int run_program(
     const char* dir, const char* file, int argc, char *const argv[], double, int&
 );
 extern void kill_program(int);
+extern int kill_program(int, int);
 extern int get_exit_status(int);
 extern bool process_exists(int);
 #endif
diff --git a/sched/handle_request.cpp b/sched/handle_request.cpp
index e886f70..8a1f93b 100644
--- a/sched/handle_request.cpp
+++ b/sched/handle_request.cpp
@@ -500,11 +500,16 @@ static int modify_host_struct(HOST& host) {
     host.timezone = g_request->host.timezone;
     strncpy(host.domain_name, g_request->host.domain_name, sizeof(host.domain_name));
     char buf[1024], buf2[1024];
-    sprintf(buf, "[BOINC|%d.%d.%d]",
+    sprintf(buf, "[BOINC|%d.%d.%d",
         g_request->core_client_major_version,
         g_request->core_client_minor_version,
         g_request->core_client_release
     );
+    if (strlen(host.client_brand)) {
+        strcat(buf, "|");
+        strcat(buf, host.client_brand);
+    }
+    strcat(buf, "]");
     g_request->coprocs.summary_string(buf2, sizeof(buf2));
     strlcpy(host.serialnum, buf, sizeof(host.serialnum));
     strlcat(host.serialnum, buf2, sizeof(host.serialnum));
diff --git a/sched/sched_types.cpp b/sched/sched_types.cpp
index decdc94..2a8e64a 100644
--- a/sched/sched_types.cpp
+++ b/sched/sched_types.cpp
@@ -772,9 +772,15 @@ int SCHEDULER_REPLY::write(FILE* fout, SCHEDULER_REQUEST& sreq) {
             );
         }
         if (strlen(user.cross_project_id)) {
+            char external_cpid[MD5_LEN];
+            safe_strcpy(buf, user.cross_project_id);
+            safe_strcat(buf, user.email_addr);
+            md5_block((unsigned char*)buf, strlen(buf), external_cpid);
             fprintf(fout,
-                "<cross_project_id>%s</cross_project_id>\n",
-                user.cross_project_id
+                "<cross_project_id>%s</cross_project_id>\n"
+                "<external_cpid>%s</external_cpid>\n",
+                user.cross_project_id,
+                external_cpid
             );
         }
 
@@ -1247,6 +1253,7 @@ int HOST::parse(XML_PARSER& xp) {
         if (xp.parse_double("n_bwdown", n_bwdown)) continue;
         if (xp.parse_str("p_features", p_features, sizeof(p_features))) continue;
         if (xp.parse_str("virtualbox_version", virtualbox_version, sizeof(virtualbox_version))) continue;
+        if (xp.parse_str("client_brand", client_brand, sizeof(client_brand))) continue;
         if (xp.parse_bool("p_vm_extensions_disabled", p_vm_extensions_disabled)) continue;
         if (xp.match_tag("opencl_cpu_prop")) {
             int retval = opencl_cpu_prop[num_opencl_cpu_platforms].parse(xp);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-boinc/boinc.git



More information about the pkg-boinc-commits mailing list