Move src files to standard java folder structure
This commit is contained in:
501
src/main/java/com/sheepit/client/standalone/GuiSwing.java
Normal file
501
src/main/java/com/sheepit/client/standalone/GuiSwing.java
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone;
|
||||
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.SettingsLoader;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.standalone.swing.activity.Settings;
|
||||
import com.sheepit.client.standalone.swing.activity.Working;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.MenuItem;
|
||||
import java.awt.PopupMenu;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowStateListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class GuiSwing extends JFrame implements Gui {
|
||||
public static final String type = "swing";
|
||||
private static final String logoPath = "/sheepit-logo.png";
|
||||
|
||||
public static void drawVersionStringOnImage(final BufferedImage image, String versionString) {
|
||||
var watermarkWidth = image.getWidth();
|
||||
var watermarkHeight = image.getHeight();
|
||||
|
||||
Graphics2D gph = (Graphics2D) image.getGraphics();
|
||||
// use anti aliasing to smooth version string
|
||||
gph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
gph.setFont(gph.getFont().deriveFont(12f));
|
||||
|
||||
FontMetrics fontMetrics = gph.getFontMetrics();
|
||||
// move text to the very right of the image respecting its length
|
||||
int x = watermarkWidth - fontMetrics.stringWidth(versionString);
|
||||
// the bottom of the image, but give enough headroom for descending letters like g
|
||||
int y = watermarkHeight - fontMetrics.getMaxDescent();
|
||||
|
||||
gph.drawString(versionString, x, y);
|
||||
gph.dispose();
|
||||
}
|
||||
|
||||
@NotNull public static JLabel createLogoWithWatermark() {
|
||||
final URL logoURL = GuiSwing.class.getResource(logoPath);
|
||||
|
||||
// If logo cannot be found, return dummy image
|
||||
if (logoURL == null) {
|
||||
System.err.println("Error: Unable to find logo " + logoPath);
|
||||
return new JLabel();
|
||||
}
|
||||
|
||||
JLabel labelImage;
|
||||
try {
|
||||
// Include the version of the app as a watermark in the SheepIt logo.
|
||||
final BufferedImage watermark = ImageIO.read(logoURL);
|
||||
String versionString = "v" + Configuration.jarVersion;
|
||||
drawVersionStringOnImage(watermark, versionString);
|
||||
|
||||
labelImage = new JLabel(new ImageIcon(watermark));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// If something fails, we just show the SheepIt logo (without any watermark)
|
||||
ImageIcon image = new ImageIcon(logoURL);
|
||||
labelImage = new JLabel(image);
|
||||
}
|
||||
return labelImage;
|
||||
}
|
||||
|
||||
public enum ActivityType {
|
||||
WORKING, SETTINGS
|
||||
}
|
||||
|
||||
private SystemTray sysTray;
|
||||
private JPanel panel;
|
||||
private Working activityWorking;
|
||||
private Settings activitySettings;
|
||||
private TrayIcon trayIcon;
|
||||
private boolean useSysTray;
|
||||
private String title;
|
||||
|
||||
private int framesRendered;
|
||||
|
||||
private boolean waitingForAuthentication;
|
||||
private Client client;
|
||||
|
||||
private BufferedImage iconSprites;
|
||||
private BufferedImage[] trayIconSprites;
|
||||
|
||||
@Getter @Setter private SettingsLoader settingsLoader;
|
||||
|
||||
private ThreadClient threadClient;
|
||||
|
||||
public GuiSwing(boolean useSysTray_, String title_) {
|
||||
framesRendered = 0;
|
||||
useSysTray = useSysTray_;
|
||||
title = title_;
|
||||
waitingForAuthentication = true;
|
||||
|
||||
new Timer().scheduleAtFixedRate(new TimerTask() {
|
||||
@Override public void run() {
|
||||
if (activityWorking != null) {
|
||||
activityWorking.updateTime();
|
||||
}
|
||||
}
|
||||
}, 2 * 1000, 2 * 1000);
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
if (useSysTray) {
|
||||
try {
|
||||
sysTray = SystemTray.getSystemTray();
|
||||
if (SystemTray.isSupported()) {
|
||||
addWindowStateListener(new WindowStateListener() {
|
||||
public void windowStateChanged(WindowEvent e) {
|
||||
if (e.getNewState() == ICONIFIED) {
|
||||
hideToTray();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
sysTray = null;
|
||||
}
|
||||
}
|
||||
|
||||
// load the images sprite and split into individual images
|
||||
URL spriteSequenceUrl = getClass().getResource("/icon-sprites.png");
|
||||
|
||||
if (spriteSequenceUrl != null) {
|
||||
try {
|
||||
iconSprites = ImageIO.read(spriteSequenceUrl);
|
||||
trayIconSprites = new BufferedImage[101 * 1]; // sprite sheet has 101 images in 1 column
|
||||
|
||||
setIconImage(extractImageFromSprite(-1)); // sprite 0 is standard Sheep It! icon
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
setTitle(title);
|
||||
setSize(520, 760);
|
||||
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
panel = new JPanel();
|
||||
panel.setLayout(new GridBagLayout());
|
||||
Dimension panelPrefSize = new Dimension(this.getSize().width - 20, this.getSize().height);
|
||||
panel.setPreferredSize(panelPrefSize);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(panel);
|
||||
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
//adjust scrollbar thickness
|
||||
scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(13, 0));
|
||||
scrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(0, 13));
|
||||
|
||||
//adjust the speed of scrolling
|
||||
scrollPane.getVerticalScrollBar().setUnitIncrement(50);
|
||||
scrollPane.getHorizontalScrollBar().setUnitIncrement(50);
|
||||
|
||||
setContentPane(scrollPane);
|
||||
panel.setBorder(new EmptyBorder(20, 20, 20, 20));
|
||||
|
||||
activityWorking = new Working(this);
|
||||
activitySettings = new Settings(this);
|
||||
|
||||
this.showActivity(ActivityType.SETTINGS);
|
||||
|
||||
try {
|
||||
if (client.getConfiguration().getTheme().equals("light")) {
|
||||
UIManager.setLookAndFeel(new FlatLightLaf());
|
||||
}
|
||||
else if (client.getConfiguration().getTheme().equals("dark")) {
|
||||
UIManager.setLookAndFeel(new FlatDarkLaf());
|
||||
}
|
||||
|
||||
// Apply the selected theme to swing components
|
||||
SwingUtilities.invokeAndWait(() -> FlatLaf.updateUI());
|
||||
}
|
||||
catch (UnsupportedLookAndFeelException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
while (waitingForAuthentication) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_) {
|
||||
status(msg_, false);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_, boolean overwriteSuspendedMsg) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setStatus(msg_, overwriteSuspendedMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void status(String msg, int progress) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setStatus(String.format("%s %d%%", msg, progress));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void status(String msg, int progress, long size) {
|
||||
this.status(msg, progress);
|
||||
}
|
||||
|
||||
@Override public void setRenderingProjectName(String name_) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setRenderingProjectName(name_);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void error(String msg_) {
|
||||
status(msg_, true);
|
||||
}
|
||||
|
||||
@Override public void setRemainingTime(String time_) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setRemainingTime(time_);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setRenderingTime(String time_) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setRenderingTime(time_);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public synchronized void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
this.activityWorking.displayTransferStats(downloads, uploads);
|
||||
}
|
||||
|
||||
@Override public void AddFrameRendered() {
|
||||
framesRendered++;
|
||||
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.setRenderedFrame(framesRendered);
|
||||
}
|
||||
else {
|
||||
System.out.println("GuiSwing::AddFrameRendered() error: no working activity");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void displayStats(Stats stats) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.displayStats(stats);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
if (activityWorking != null) {
|
||||
this.activityWorking.displayUploadQueueStats(queueSize, queueVolume);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override public void setClient(Client cli) {
|
||||
client = cli;
|
||||
}
|
||||
|
||||
@Override public void setComputeMethod(String computeMethod) {
|
||||
this.activityWorking.setComputeMethod(computeMethod);
|
||||
}
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return client.getConfiguration();
|
||||
}
|
||||
|
||||
@Override public void successfulAuthenticationEvent(String publickey) {
|
||||
if (settingsLoader != null) {
|
||||
if (publickey != null && settingsLoader.getLogin().isLaunchCommand() == false) {
|
||||
settingsLoader.getPassword().setValue(publickey);
|
||||
}
|
||||
settingsLoader.saveFile();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCredentials(String contentLogin, String contentPassword) {
|
||||
client.getConfiguration().setLogin(contentLogin);
|
||||
client.getConfiguration().setPassword(contentPassword);
|
||||
|
||||
waitingForAuthentication = false;
|
||||
synchronized (this) {
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
if (threadClient == null || threadClient.isAlive() == false) {
|
||||
threadClient = new ThreadClient();
|
||||
threadClient.start();
|
||||
}
|
||||
|
||||
showActivity(ActivityType.WORKING);
|
||||
}
|
||||
|
||||
public void showActivity(ActivityType type) {
|
||||
panel.removeAll();
|
||||
panel.doLayout();
|
||||
|
||||
if (type == ActivityType.WORKING) {
|
||||
activityWorking.show();
|
||||
}
|
||||
else if (type == ActivityType.SETTINGS) {
|
||||
activitySettings.show();
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
panel.repaint();
|
||||
}
|
||||
|
||||
public void hideToTray() {
|
||||
if (sysTray == null || SystemTray.isSupported() == false) {
|
||||
System.out.println("GuiSwing::hideToTray SystemTray not supported!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
trayIcon = getTrayIcon();
|
||||
sysTray.add(trayIcon);
|
||||
}
|
||||
catch (AWTException e) {
|
||||
System.out.println("GuiSwing::hideToTray an error occured while trying to add system tray icon (exception: " + e + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
setVisible(false);
|
||||
|
||||
}
|
||||
|
||||
public void restoreFromTray() {
|
||||
if (sysTray != null && SystemTray.isSupported()) {
|
||||
sysTray.remove(trayIcon);
|
||||
setVisible(true);
|
||||
setExtendedState(getExtendedState() & ~JFrame.ICONIFIED & JFrame.NORMAL); // for toFront and requestFocus to actually work
|
||||
toFront();
|
||||
requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
public TrayIcon getTrayIcon() {
|
||||
final PopupMenu trayMenu = new PopupMenu();
|
||||
|
||||
// on start, show the base icon
|
||||
Image img = extractImageFromSprite(-1);
|
||||
final TrayIcon icon = new TrayIcon(img);
|
||||
|
||||
MenuItem exit = new MenuItem("Exit");
|
||||
exit.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
trayMenu.add(exit);
|
||||
|
||||
MenuItem open = new MenuItem("Open...");
|
||||
open.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
restoreFromTray();
|
||||
}
|
||||
});
|
||||
trayMenu.add(open);
|
||||
|
||||
MenuItem settings = new MenuItem("Settings...");
|
||||
settings.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
restoreFromTray();
|
||||
showActivity(ActivityType.SETTINGS);
|
||||
}
|
||||
});
|
||||
trayMenu.add(settings);
|
||||
|
||||
icon.setPopupMenu(trayMenu);
|
||||
icon.setImageAutoSize(true);
|
||||
icon.setToolTip("SheepIt! Client");
|
||||
|
||||
icon.addActionListener(new ActionListener() {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
restoreFromTray();
|
||||
}
|
||||
});
|
||||
|
||||
return icon;
|
||||
|
||||
}
|
||||
|
||||
public JPanel getContentPanel() {
|
||||
return this.panel;
|
||||
}
|
||||
|
||||
private Image extractImageFromSprite(int spriteNumber) {
|
||||
// Sprite structure
|
||||
// Image 0: base sprite
|
||||
// Images 1-101: progress bar percentage from 0 to 100
|
||||
//
|
||||
// Always add +1 to the icon requested.
|
||||
// -1 turns into 0 (base sprite with no progress bar)
|
||||
// 0 to 101 turns into 1 to 101 (progress sequence starts in sprite 1 and ends on sprite 101)
|
||||
ImageIcon img = new ImageIcon(iconSprites.getSubimage(0, (spriteNumber + 1) * 114, 114, 114));
|
||||
|
||||
return img.getImage();
|
||||
}
|
||||
|
||||
@Override public void updateTrayIcon(Integer percentage) {
|
||||
// update the app icon on the app bar
|
||||
Image img = extractImageFromSprite(percentage);
|
||||
setIconImage(img);
|
||||
|
||||
// if the app supports the system tray, update as well
|
||||
if (sysTray != null && SystemTray.isSupported()) {
|
||||
if (trayIcon != null) {
|
||||
trayIcon.setImage(img);
|
||||
trayIcon.setImageAutoSize(true); // use this method to ensure that icon is refreshed when on
|
||||
// the tray
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadClient extends Thread {
|
||||
@Override public void run() {
|
||||
if (GuiSwing.this.client != null) {
|
||||
GuiSwing.this.client.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
220
src/main/java/com/sheepit/client/standalone/GuiText.java
Normal file
220
src/main/java/com/sheepit/client/standalone/GuiText.java
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.Log;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.standalone.text.CLIInputActionHandler;
|
||||
import com.sheepit.client.standalone.text.CLIInputObserver;
|
||||
|
||||
import sun.misc.Signal;
|
||||
import sun.misc.SignalHandler;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
public class GuiText implements Gui {
|
||||
public static final String type = "text";
|
||||
|
||||
private int framesRendered;
|
||||
|
||||
private int sigIntCount = 0;
|
||||
private Log log;
|
||||
private DateFormat df;
|
||||
private String eta;
|
||||
|
||||
private Client client;
|
||||
|
||||
public GuiText() {
|
||||
this.framesRendered = 0;
|
||||
this.log = Log.getInstance(null);
|
||||
this.df = new SimpleDateFormat("MMM dd HH:mm:ss");
|
||||
this.eta = "";
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
if (client != null) {
|
||||
|
||||
CLIInputObserver cli_input_observer = new CLIInputObserver(client);
|
||||
cli_input_observer.addListener(new CLIInputActionHandler());
|
||||
Thread cli_input_observer_thread = new Thread(cli_input_observer);
|
||||
cli_input_observer_thread.start();
|
||||
|
||||
Signal.handle(new Signal("INT"), new SignalHandler() {
|
||||
@Override public void handle(Signal signal) {
|
||||
sigIntCount++;
|
||||
|
||||
if (sigIntCount == 4) {
|
||||
// This is only for ugly issues that might occur
|
||||
System.out.println("WARNING: Hitting Ctrl-C again will force close the application.");
|
||||
}
|
||||
else if (sigIntCount == 5) {
|
||||
Signal.raise(new Signal("INT"));
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
else if (client.isRunning() && client.isSuspended() == false) {
|
||||
client.askForStop();
|
||||
System.out.println("Will exit after current frame... Press Ctrl+C again to exit now.");
|
||||
}
|
||||
else {
|
||||
client.stop();
|
||||
GuiText.this.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.run();
|
||||
client.stop();
|
||||
}
|
||||
// stop if there is no client or client has been stopped
|
||||
this.stop();
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
|
||||
@Override public void updateTrayIcon(Integer percentage) {
|
||||
}
|
||||
|
||||
@Override public void status(String msg_) {
|
||||
status(msg_, false);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_, boolean overwriteSuspendedMsg) {
|
||||
log.debug("GUI " + msg_);
|
||||
|
||||
if (client != null && client.isSuspended()) {
|
||||
if (overwriteSuspendedMsg) {
|
||||
System.out.println(String.format("%s %s", this.df.format(new Date()), msg_));
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.out.println(String.format("%s %s", this.df.format(new Date()), msg_));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void status(String msg, int progress) {
|
||||
this.status(msg, progress, 0);
|
||||
}
|
||||
|
||||
@Override public void status(String msg, int progress, long size) {
|
||||
System.out.print("\r");
|
||||
System.out.print(String.format("%s %s", this.df.format(new Date()), showProgress(msg, progress, size)));
|
||||
}
|
||||
|
||||
@Override public void error(String err_) {
|
||||
System.out.println(String.format("ERROR: %s %s", this.df.format(new Date()), err_));
|
||||
log.error("Error " + err_);
|
||||
}
|
||||
|
||||
@Override public void AddFrameRendered() {
|
||||
this.framesRendered += 1;
|
||||
System.out.println(String.format("%s Frames rendered: %d", this.df.format(new Date()), this.framesRendered));
|
||||
}
|
||||
|
||||
@Override public synchronized void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
System.out.println(String
|
||||
.format("%s Session downloads: %s @ %s/s / Uploads: %s @ %s/s", this.df.format(new Date()), downloads.getSessionTraffic(),
|
||||
downloads.getAverageSessionSpeed(), uploads.getSessionTraffic(), uploads.getAverageSessionSpeed()));
|
||||
}
|
||||
|
||||
@Override public void displayStats(Stats stats) {
|
||||
System.out.println(String.format("%s Frames remaining: %d", this.df.format(new Date()), stats.getRemainingFrame()));
|
||||
System.out.println(String.format("%s Credits earned: %d", this.df.format(new Date()), stats.getCreditsEarnedDuringSession()));
|
||||
}
|
||||
|
||||
@Override public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
// No need to check if the queue is not empty to show the volume bc this line is always shown at the end
|
||||
// of the render process in text GUI (unless an error occurred, where the file is uploaded synchronously)
|
||||
System.out.println(String.format("%s Queued uploads: %d (%.2fMB)", this.df.format(new Date()), queueSize, (queueVolume / 1024.0 / 1024.0)));
|
||||
}
|
||||
|
||||
@Override public void setRenderingProjectName(String name_) {
|
||||
if (name_ != null && name_.isEmpty() == false) {
|
||||
System.out.println(String.format("%s Rendering project \"%s\"", this.df.format(new Date()), name_));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void setRemainingTime(String time_) {
|
||||
this.eta = time_;
|
||||
}
|
||||
|
||||
@Override public void setRenderingTime(String time_) {
|
||||
System.out.println(String.format("%s Rendering %s", this.df.format(new Date()), time_));
|
||||
}
|
||||
|
||||
@Override public void setClient(Client cli) {
|
||||
client = cli;
|
||||
}
|
||||
|
||||
@Override public void setComputeMethod(String computeMethod) {
|
||||
System.out.println(String.format("%s Compute method: %s", this.df.format(new Date()), computeMethod));
|
||||
}
|
||||
|
||||
@Override public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override public void successfulAuthenticationEvent(String publickey) {
|
||||
|
||||
}
|
||||
|
||||
private String showProgress(String message, int progress, long size) {
|
||||
StringBuilder progressBar = new StringBuilder(140);
|
||||
|
||||
if (progress < 100) {
|
||||
progressBar
|
||||
.append(message)
|
||||
.append(" ")
|
||||
.append(String.join("", Collections.nCopies(progress == 0 ? 2 : 2 - (int) (Math.log10(progress)), " ")))
|
||||
.append(String.format("%d%% [", progress))
|
||||
.append(String.join("", Collections.nCopies((int)(progress/5), "=")))
|
||||
.append('>')
|
||||
.append(String.join("", Collections.nCopies(20 - (int)(progress / 5), " ")))
|
||||
.append(']');
|
||||
|
||||
if (size > 0) {
|
||||
progressBar.append(String.format(" %dMB", (size / 1024 / 1024)));
|
||||
}
|
||||
|
||||
if (!this.eta.equals("")) {
|
||||
progressBar.append(String.format(" ETA %s", this.eta));
|
||||
}
|
||||
|
||||
progressBar.append(String.join("", Collections.nCopies(60 - progressBar.length(), " ")));
|
||||
}
|
||||
// If progress has reached 100%
|
||||
else {
|
||||
progressBar
|
||||
.append(message)
|
||||
.append(" done")
|
||||
.append(String.join("", Collections.nCopies(60 - progressBar.length(), " ")))
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
return progressBar.toString();
|
||||
}
|
||||
}
|
||||
248
src/main/java/com/sheepit/client/standalone/GuiTextOneLine.java
Normal file
248
src/main/java/com/sheepit/client/standalone/GuiTextOneLine.java
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.standalone.text.CLIInputActionHandler;
|
||||
import com.sheepit.client.standalone.text.CLIInputObserver;
|
||||
|
||||
import sun.misc.Signal;
|
||||
import sun.misc.SignalHandler;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
public class GuiTextOneLine implements Gui {
|
||||
public static final String type = "oneLine";
|
||||
|
||||
private String project;
|
||||
private int rendered;
|
||||
private int remaining;
|
||||
private String creditsEarned;
|
||||
private int sigIntCount = 0;
|
||||
private DateFormat df;
|
||||
|
||||
private String computeMethod;
|
||||
private String status;
|
||||
private String line;
|
||||
private String eta;
|
||||
|
||||
private int uploadQueueSize;
|
||||
private long uploadQueueVolume;
|
||||
|
||||
private boolean exiting = false;
|
||||
|
||||
private Client client;
|
||||
|
||||
public GuiTextOneLine() {
|
||||
project = "";
|
||||
rendered = 0;
|
||||
remaining = 0;
|
||||
creditsEarned = null;
|
||||
status = "";
|
||||
computeMethod = "";
|
||||
line = "";
|
||||
uploadQueueSize = 0;
|
||||
uploadQueueVolume = 0;
|
||||
df = new SimpleDateFormat("MMM dd HH:mm:ss");
|
||||
eta = "";
|
||||
}
|
||||
|
||||
@Override public void start() {
|
||||
if (client != null) {
|
||||
|
||||
CLIInputObserver cli_input_observer = new CLIInputObserver(client);
|
||||
cli_input_observer.addListener(new CLIInputActionHandler());
|
||||
Thread cli_input_observer_thread = new Thread(cli_input_observer);
|
||||
cli_input_observer_thread.start();
|
||||
|
||||
Signal.handle(new Signal("INT"), new SignalHandler() {
|
||||
@Override public void handle(Signal signal) {
|
||||
sigIntCount++;
|
||||
|
||||
if (sigIntCount == 5) {
|
||||
Signal.raise(new Signal("INT"));
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
else if (client.isRunning() && client.isSuspended() == false) {
|
||||
client.askForStop();
|
||||
exiting = true;
|
||||
}
|
||||
else {
|
||||
client.stop();
|
||||
GuiTextOneLine.this.stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.run();
|
||||
client.stop();
|
||||
}
|
||||
// stop if there is no client or client has been stopped
|
||||
this.stop();
|
||||
}
|
||||
|
||||
@Override public void stop() {
|
||||
Runtime.getRuntime().halt(0);
|
||||
}
|
||||
|
||||
@Override public void updateTrayIcon(Integer percentage) {
|
||||
}
|
||||
|
||||
@Override public void status(String msg_) {
|
||||
status(msg_, false);
|
||||
}
|
||||
|
||||
@Override public void status(String msg_, boolean overwriteSuspendedMsg) {
|
||||
if (client != null && client.isSuspended()) {
|
||||
if (overwriteSuspendedMsg) {
|
||||
status = msg_;
|
||||
updateLine();
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = msg_;
|
||||
updateLine();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void status(String msg, int progress) {
|
||||
this.status(msg, progress, 0);
|
||||
}
|
||||
|
||||
@Override public void status(String msg, int progress, long size) {
|
||||
status = showProgress(msg, progress, size);
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void setRenderingProjectName(String name_) {
|
||||
if (name_ == null || name_.isEmpty()) {
|
||||
project = "";
|
||||
}
|
||||
else {
|
||||
project = name_ + " |";
|
||||
}
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void error(String msg_) {
|
||||
status = "Error " + msg_;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void AddFrameRendered() {
|
||||
rendered += 1;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public synchronized void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
// Session traffic stats not shown in the 1 line UI to avoid increasing the line length above 120 chars
|
||||
}
|
||||
|
||||
@Override public void displayStats(Stats stats) {
|
||||
remaining = stats.getRemainingFrame();
|
||||
creditsEarned = String.valueOf(stats.getCreditsEarnedDuringSession());
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
this.uploadQueueSize = queueSize;
|
||||
this.uploadQueueVolume = queueVolume;
|
||||
}
|
||||
|
||||
@Override public void setRemainingTime(String time_) {
|
||||
this.eta = time_;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void setRenderingTime(String time_) {
|
||||
status = "Rendering " + time_;
|
||||
updateLine();
|
||||
}
|
||||
|
||||
@Override public void setClient(Client cli) {
|
||||
client = cli;
|
||||
}
|
||||
|
||||
@Override public void setComputeMethod(String computeMethod_) {
|
||||
computeMethod = computeMethod_;
|
||||
}
|
||||
|
||||
@Override public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override public void successfulAuthenticationEvent(String publickey) {
|
||||
|
||||
}
|
||||
|
||||
private void updateLine() {
|
||||
int charToRemove = line.length();
|
||||
|
||||
System.out.print("\r");
|
||||
|
||||
line = String.format("%s Frames: %d Points: %s | Upload Queue: %d%s | %%s %s %s", df.format(new Date()), rendered,
|
||||
creditsEarned != null ? creditsEarned : "unknown", this.uploadQueueSize,
|
||||
(this.uploadQueueSize > 0 ? String.format(" (%.2fMB)", (this.uploadQueueVolume / 1024.0 / 1024.0)) : ""), computeMethod,
|
||||
status + (exiting ? " (Exiting after all frames are uploaded)" : ""));
|
||||
|
||||
if (line.length() + project.length() > 120) {
|
||||
// If the line without the project name is already >120 characters (might happen if the user has thousands of frames and millions of points in the
|
||||
// session + is exiting after all frames are uploaded) then set the line to 117c to avoid a negative number exception in substring function
|
||||
int lineLength = Math.min(line.length(), 117);
|
||||
line = String.format(line, project.substring(0, 117 - lineLength) + "...");
|
||||
}
|
||||
else {
|
||||
line = String.format(line, project);
|
||||
}
|
||||
|
||||
System.out.print(line);
|
||||
for (int i = line.length(); i <= charToRemove; i++) {
|
||||
System.out.print(" ");
|
||||
}
|
||||
}
|
||||
|
||||
private String showProgress(String message, int progress, long size) {
|
||||
StringBuilder progressBar = new StringBuilder(140);
|
||||
progressBar
|
||||
.append(message)
|
||||
.append(String.join("", Collections.nCopies(progress == 0 ? 2 : 2 - (int) (Math.log10(progress)), " ")))
|
||||
.append(String.format(" %d%%%% [", progress))
|
||||
.append(String.join("", Collections.nCopies((int) (progress / 10), "=")))
|
||||
.append('>')
|
||||
.append(String.join("", Collections.nCopies(10 - (int) (progress / 10), " ")))
|
||||
.append(']');
|
||||
|
||||
if (size > 0) {
|
||||
progressBar.append(String.format(" %dMB", (size / 1024 / 1024)));
|
||||
}
|
||||
|
||||
if (!this.eta.equals("")) {
|
||||
progressBar.append(String.format(" ETA %s", this.eta));
|
||||
}
|
||||
|
||||
return progressBar.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
import org.kohsuke.args4j.OptionDef;
|
||||
import org.kohsuke.args4j.spi.OptionHandler;
|
||||
import org.kohsuke.args4j.spi.Parameters;
|
||||
import org.kohsuke.args4j.spi.Setter;
|
||||
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.hardware.gpu.GPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
|
||||
public class ListGpuParameterHandler<T> extends OptionHandler<T> {
|
||||
public ListGpuParameterHandler(CmdLineParser parser, OptionDef option, Setter<? super T> setter) {
|
||||
super(parser, option, setter);
|
||||
}
|
||||
|
||||
@Override public int parseArguments(Parameters params) throws CmdLineException {
|
||||
List<GPUDevice> gpus = GPU.listDevices(new Configuration(null, null, null));
|
||||
if (gpus != null) {
|
||||
for (GPUDevice gpu : gpus) {
|
||||
System.out.println("GPU_ID : " + gpu.getOldId());
|
||||
System.out.println("Long ID : " + gpu.getId());
|
||||
System.out.println("Model : " + gpu.getModel());
|
||||
System.out.println("Memory, MB: " + (int) (gpu.getMemory() / (1024 * 1024)));
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public String getDefaultMetaVariable() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
import org.kohsuke.args4j.OptionDef;
|
||||
import org.kohsuke.args4j.spi.OptionHandler;
|
||||
import org.kohsuke.args4j.spi.Parameters;
|
||||
import org.kohsuke.args4j.spi.Setter;
|
||||
|
||||
import com.sheepit.client.Configuration;
|
||||
|
||||
public class VersionParameterHandler<T> extends OptionHandler<T> {
|
||||
public VersionParameterHandler(CmdLineParser parser, OptionDef option, Setter<? super T> setter) {
|
||||
super(parser, option, setter);
|
||||
}
|
||||
|
||||
@Override public int parseArguments(Parameters params) throws CmdLineException {
|
||||
Configuration config = new Configuration(null, "", "");
|
||||
System.out.println("Version: " + Configuration.jarVersion);
|
||||
System.exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public String getDefaultMetaVariable() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
514
src/main/java/com/sheepit/client/standalone/Worker.java
Normal file
514
src/main/java/com/sheepit/client/standalone/Worker.java
Normal file
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone;
|
||||
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
import org.kohsuke.args4j.Option;
|
||||
import org.kohsuke.args4j.OptionHandlerFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Calendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Configuration.ComputeType;
|
||||
import com.sheepit.client.Error;
|
||||
import com.sheepit.client.Gui;
|
||||
import com.sheepit.client.Log;
|
||||
import com.sheepit.client.Pair;
|
||||
import com.sheepit.client.SettingsLoader;
|
||||
import com.sheepit.client.ShutdownHook;
|
||||
import com.sheepit.client.Utils;
|
||||
import com.sheepit.client.hardware.gpu.GPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.hardware.gpu.nvidia.Nvidia;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCL;
|
||||
import com.sheepit.client.network.Proxy;
|
||||
import com.sheepit.client.os.OS;
|
||||
|
||||
public class Worker {
|
||||
@Option(name = SettingsLoader.ARG_SERVER, usage = "Render-farm server, default https://client.sheepit-renderfarm.com", metaVar = "URL", required = false) private String server = "https://client.sheepit-renderfarm.com";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_LOGIN, usage = "User's login", metaVar = "LOGIN", required = false) private String login = "";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_PASSWORD, usage = "User's password or public key (accessible under the Keys tab of the profile page)", metaVar = "PASSWORD", required = false) private String password = "";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_CACHE_DIR, usage = "Cache/Working directory. Caution, everything in it not related to the render-farm will be removed", metaVar = "/tmp/cache", required = false) private String cache_dir = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHARED_ZIP, usage = "Shared directory for downloaded binaries and scenes. Useful when running two or more clients in the same computer/network to download once and render many times. IMPORTANT: This option and value must be identical in ALL clients sharing the directory.", required = false) private String sharedDownloadsDir = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_GPU, usage = "Name of the GPU used for the render, for example CUDA_0 for Nvidia or OPENCL_0 for AMD/Intel card", metaVar = "CUDA_0", required = false) private String gpu_device = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_NO_GPU, usage = "Don't detect GPUs", required = false) private boolean no_gpu_detection = false;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_COMPUTE_METHOD, usage = "CPU: only use cpu, GPU: only use gpu, CPU_GPU: can use cpu and gpu (not at the same time) if -gpu is not use it will not use the gpu", metaVar = "CPU", required = false) private String method = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_CORES, usage = "Number of cores/threads to use for the render. The minimum is two cores unless your system only has one", metaVar = "3", required = false) private int nb_cores = -1;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_MEMORY, usage = "Maximum memory allow to be used by renderer, number with unit (800M, 2G, ...)", required = false) private String max_ram = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_RENDERTIME, usage = "Maximum time allow for each frame (in minutes)", required = false) private int max_rendertime = -1;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_VERBOSE, usage = "Display log", required = false) private boolean print_log = false;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_REQUEST_TIME, usage = "H1:M1-H2:M2,H3:M3-H4:M4 Use the 24h format. For example to request job between 2am-8.30am and 5pm-11pm you should do --request-time 2:00-8:30,17:00-23:00 Caution, it's the requesting job time to get a project, not the working time", metaVar = "2:00-8:30,17:00-23:00", required = false) private String request_time = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHUTDOWN, usage = "Specify when the client will close and the host computer will shut down in a proper way. The time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now.", metaVar = "DATETIME or +N", required = false) private String shutdown = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHUTDOWN_MODE, usage = "Indicates if the shutdown process waits for the upload queue to finish (wait) or interrupt all the pending tasks immediately (hard). The default shutdown mode is wait.", metaVar = "MODE", required = false) private String shutdownMode = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_PROXY, usage = "URL of the proxy", metaVar = "http://login:password@host:port", required = false) private String proxy = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_EXTRAS, usage = "Extras data push on the authentication request", required = false) private String extras = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_UI, usage = "Specify the user interface to use, default '" + GuiSwing.type + "', available '" + GuiTextOneLine.type + "', '"
|
||||
+ GuiText.type + "', '" + GuiSwing.type + "' (graphical)", required = false) private String ui_type = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_CONFIG, usage = "Specify the configuration file", required = false) private String config_file = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_VERSION, usage = "Display application version", required = false, handler = VersionParameterHandler.class) private VersionParameterHandler versionHandler;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_SHOW_GPU, usage = "Print available GPU devices and exit", required = false, handler = ListGpuParameterHandler.class) private ListGpuParameterHandler listGpuParameterHandler;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_NO_SYSTRAY, usage = "Don't use SysTray", required = false) private boolean useSysTray = false;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_PRIORITY, usage = "Set render process priority (19 lowest to -19 highest)", required = false) private int priority = 19;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_TITLE, usage = "Custom title for the GUI Client", required = false) private String title = "SheepIt Render Farm";
|
||||
|
||||
@Option(name = SettingsLoader.ARG_THEME, usage = "Specify the theme to use for the graphical client, default 'light', available 'light', 'dark'", required = false) private String theme = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_HOSTNAME, usage = "Set a custom hostname name (name change will be lost when client is closed)", required = false) private String hostname = null;
|
||||
|
||||
@Option(name = SettingsLoader.ARG_HEADLESS, usage = "Mark your client manually as headless to block Eevee projects", required = false) private boolean headless = java.awt.GraphicsEnvironment.isHeadless();
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (OS.getOS() == null) {
|
||||
System.err.println(Error.humanString(Error.Type.OS_NOT_SUPPORTED));
|
||||
System.exit(1);
|
||||
}
|
||||
new Worker().doMain(args);
|
||||
}
|
||||
|
||||
public void doMain(String[] args) {
|
||||
CmdLineParser parser = new CmdLineParser(this);
|
||||
try {
|
||||
parser.parseArgument(args);
|
||||
}
|
||||
catch (CmdLineException e) {
|
||||
System.err.println(e.getMessage());
|
||||
System.err.println("Usage: ");
|
||||
parser.printUsage(System.err);
|
||||
System.err.println();
|
||||
System.err.println("Example: java " + this.getClass().getName() + " " + parser.printExample(OptionHandlerFilter.REQUIRED));
|
||||
return;
|
||||
}
|
||||
|
||||
ComputeType compute_method = null;
|
||||
Configuration config = new Configuration(null, login, password);
|
||||
config.setPrintLog(print_log);
|
||||
config.setUsePriority(priority);
|
||||
config.setDetectGPUs(!no_gpu_detection);
|
||||
|
||||
if (sharedDownloadsDir != null) {
|
||||
File dir = new File(sharedDownloadsDir);
|
||||
if (dir.exists() == false && dir.mkdirs()) {
|
||||
Log.getInstance(config).debug("created shared-zip directory " + dir);
|
||||
}
|
||||
else if (dir.exists() == false) {
|
||||
System.err.println("ERROR: The shared-zip directory " + dir + " does not exist and cannot be automatically created");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir.canWrite() == false) {
|
||||
System.err.println("ERROR: The shared-zip directory " + dir + " must be writeable");
|
||||
return;
|
||||
}
|
||||
config.setSharedDownloadsDirectory(dir);
|
||||
}
|
||||
|
||||
if (cache_dir != null) {
|
||||
Pattern cache_dirValidator = Pattern.compile("^(\\/|\\\\|[a-z]:)?[a-z0-9\\/\\\\\\s-_.]+$",Pattern.CASE_INSENSITIVE);
|
||||
Matcher cache_dirCandidate = cache_dirValidator.matcher(cache_dir);
|
||||
|
||||
if (cache_dirCandidate.find()) {
|
||||
|
||||
File a_dir = new File(cache_dir);
|
||||
a_dir.mkdirs();
|
||||
if (a_dir.isDirectory() && a_dir.canWrite()) {
|
||||
config.setCacheDir(a_dir);
|
||||
}
|
||||
else {
|
||||
System.err.println("ERROR: The entered cache path is either not a directory or is not writable!");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
System.err.println("ERROR: The entered cache path (-cache-dir parameter) contains invalid characters. Allowed characters are a-z, A-Z, 0-9, /, \\, ., - and _");
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
// We set a hard limit of 3 concurrent uploads. As the server doesn't allocate more than 3 concurrent jobs to
|
||||
// a single session to avoid any client taking too many frames and not validating them, we throttle the uploads.
|
||||
// If we don't set this limit, in a computer with slow uploads the server will return a "no job available" when
|
||||
// the 4th concurrent job is requested and that will put the client in "wait" mode for some random time. To
|
||||
// avoid that situation we set this limit.
|
||||
config.setMaxUploadingJob(3);
|
||||
|
||||
// Store the SysTray preference from the user. Please note that we must ! the value of the variable because the way args4j works. If the --no-systray
|
||||
// parameter is detected, args4j will store (boolean)true in the useSysTray variable but we want to store (boolean)false in the configuration class
|
||||
// for further checks.
|
||||
config.setUseSysTray(!useSysTray);
|
||||
|
||||
config.setHeadless(headless);
|
||||
|
||||
if (gpu_device != null) {
|
||||
if (gpu_device.startsWith(Nvidia.TYPE) == false && gpu_device.startsWith(OpenCL.TYPE) == false) {
|
||||
System.err.println("ERROR: The entered GPU_ID is invalid. The GPU_ID should look like '" + Nvidia.TYPE + "_#' or '" + OpenCL.TYPE
|
||||
+ "_#'. Please use the proper GPU_ID from the GPU list below\n");
|
||||
showGPUList(parser);
|
||||
}
|
||||
|
||||
GPUDevice gpu = GPU.getGPUDevice(gpu_device);
|
||||
if (gpu == null) {
|
||||
System.err.println("ERROR: The entered GPU_ID is invalid. Please use the proper GPU_ID from the GPU list below\n");
|
||||
showGPUList(parser);
|
||||
}
|
||||
config.setGPUDevice(gpu);
|
||||
}
|
||||
|
||||
if (request_time != null) {
|
||||
String[] intervals = request_time.split(",");
|
||||
if (intervals != null) {
|
||||
config.setRequestTime(new LinkedList<Pair<Calendar, Calendar>>());
|
||||
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
|
||||
for (String interval : intervals) {
|
||||
String[] times = interval.split("-");
|
||||
if (times != null && times.length == 2) {
|
||||
Calendar start = Calendar.getInstance();
|
||||
Calendar end = Calendar.getInstance();
|
||||
|
||||
try {
|
||||
start.setTime(timeFormat.parse(times[0]));
|
||||
end.setTime(timeFormat.parse(times[1]));
|
||||
}
|
||||
catch (ParseException e) {
|
||||
System.err.println(String.format(
|
||||
"ERROR: The entered time slot (-request-time parameter) doesn't seem to be valid. Please check the format is correct [%s]",
|
||||
e.getMessage()));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (start.before(end)) {
|
||||
config.getRequestTime().add(new Pair<Calendar, Calendar>(start, end));
|
||||
}
|
||||
else {
|
||||
System.err.println(String.format("ERROR: The start (%s) time must be earlier than the finish (%s) time", times[0], times[1]));
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nb_cores < -1 || nb_cores == 0) { // -1 is the default
|
||||
System.err.println("ERROR: The entered number of CPU cores (-cores parameter) is not valid. Please enter a number greater than zero");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
config.setNbCores(nb_cores);
|
||||
}
|
||||
|
||||
if (max_ram != null) {
|
||||
try {
|
||||
config.setMaxAllowedMemory(Utils.parseNumber(max_ram) / 1024); // internal value is in KiB
|
||||
}
|
||||
catch (java.lang.IllegalStateException e) {
|
||||
System.err.println(
|
||||
String.format("ERROR: The entered value of maximum memory (-memory parameter) doesn't seem to be a valid number [%s]", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_rendertime > 0) {
|
||||
config.setMaxRenderTime(max_rendertime * 60);
|
||||
}
|
||||
|
||||
if (method != null) {
|
||||
try {
|
||||
compute_method = ComputeType.valueOf(method);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
System.err.println(String.format(
|
||||
"ERROR: The entered compute method (-compute-method parameter) is not valid. Available values are CPU, GPU or CPU_GPU [%s]",
|
||||
e.getMessage()));
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (config.getGPUDevice() != null) {
|
||||
compute_method = ComputeType.GPU;
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy != null) {
|
||||
try {
|
||||
Proxy.set(proxy);
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
System.err.println(String.format("ERROR: The entered proxy URL (-proxy parameter) doesn't seem to have the right format. Please check it [%s]",
|
||||
e.getMessage()));
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (extras != null) {
|
||||
config.setExtras(extras);
|
||||
}
|
||||
|
||||
if (compute_method != null) {
|
||||
if (compute_method == ComputeType.CPU && config.getGPUDevice() != null) {
|
||||
System.err.println(
|
||||
"ERROR: The compute method is set to use CPU only, but a GPU has also been specified. Change the compute method to CPU_GPU or remove the GPU");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (compute_method == ComputeType.CPU_GPU && config.getGPUDevice() == null) {
|
||||
System.err.println(
|
||||
"ERROR: The compute method is set to use both CPU and GPU, but no GPU has been specified. Change the compute method to CPU or add a GPU (via -gpu parameter)");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (compute_method == ComputeType.GPU && config.getGPUDevice() == null) {
|
||||
System.err.println("ERROR: The compute method is set to use GPU only, but not GPU has been specified. Please add a GPU (via -gpu parameter)");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (compute_method == ComputeType.CPU) {
|
||||
config.setGPUDevice(null); // remove the GPU
|
||||
}
|
||||
}
|
||||
|
||||
config.setComputeMethod(compute_method);
|
||||
|
||||
if (ui_type != null) {
|
||||
config.setUIType(ui_type);
|
||||
}
|
||||
|
||||
if (theme != null) {
|
||||
if (!theme.equals("light") && !theme.equals("dark")) {
|
||||
System.err.println("ERROR: The entered theme (-theme parameter) doesn't exist. Please choose either 'light' or 'dark'");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
config.setTheme(this.theme);
|
||||
}
|
||||
|
||||
// Shutdown process block
|
||||
if (shutdown != null) {
|
||||
Pattern absoluteTimePattern = Pattern.compile("^([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))T([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$");
|
||||
Pattern relativeTimePattern = Pattern.compile("^\\+([0-9]{2,4})$");
|
||||
LocalDateTime shutdownTime = null;
|
||||
|
||||
Matcher timeAbsolute = absoluteTimePattern.matcher(shutdown);
|
||||
Matcher timeRelative = relativeTimePattern.matcher(shutdown);
|
||||
|
||||
if (timeAbsolute.find()) {
|
||||
if ((shutdownTime = shutdownTimeParse(shutdown)) != null) {
|
||||
long diffInMillies = ChronoUnit.MILLIS.between(LocalDateTime.now(), shutdownTime);
|
||||
|
||||
if (diffInMillies < 0) {
|
||||
System.err.println(String
|
||||
.format("\nERROR: The entered shutdown time (%s) is a date on the past. Shutdown time must be at least 30 minutes from now",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
else if (diffInMillies < 10 * 60 * 1000) { // 10 minutes
|
||||
System.err.println(String.format(
|
||||
"\nERROR: The specified shutdown time (%s) is expected to happen in less than 10 minutes. Shutdown time must be at least 30 minutes from now",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
config.setShutdownTime(diffInMillies);
|
||||
}
|
||||
else {
|
||||
System.err.println(String.format(
|
||||
"\nERROR: The format of the entered shutdown time (%s) is not correct.\nThe time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now (min. +10 minutes, max. +9999 minutes)",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
else if (timeRelative.find()) {
|
||||
int minutesUntilShutdown = Integer.parseInt(timeRelative.group(1));
|
||||
config.setShutdownTime(minutesUntilShutdown * 60 * 1000);
|
||||
shutdownTime = LocalDateTime.now().plusMinutes(minutesUntilShutdown);
|
||||
}
|
||||
else {
|
||||
System.err.println(String.format(
|
||||
"\nERROR: The time especified (%s) is less than 10 minutes or the format is not correct.\nThe time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now (min. +10 minutes, max. +9999 minutes)",
|
||||
shutdown));
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (shutdownMode != null) {
|
||||
if (shutdownMode.toLowerCase().equals("wait") || shutdownMode.toLowerCase().equals("hard")) {
|
||||
config.setShutdownMode(shutdownMode.toLowerCase());
|
||||
}
|
||||
else {
|
||||
System.err
|
||||
.println(String.format("ERROR: The entered shutdown-mode (%s) is invalid. Please enter wait or hard shutdown mode.", shutdownMode));
|
||||
System.err.println(" - Wait: the shutdown process is initiated once the current job and all the queued uploads are finished.");
|
||||
System.err
|
||||
.println(" - Hard: Then shutdown process is executed immediately. Any ongoing rendering process or upload queues will be cancelled.");
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if no shutdown mode specified, then set "wait" mode by default
|
||||
config.setShutdownMode("wait");
|
||||
}
|
||||
|
||||
System.out.println("==============================================================================");
|
||||
if (config.getShutdownMode().equals("wait")) {
|
||||
System.out.println(String.format(
|
||||
"WARNING!\n\nThe client will stop requesting new jobs at %s.\nTHE EFFECTIVE SHUTDOWN MIGHT OCCUR LATER THAN THE REQUESTED TIME AS THE UPLOAD\nQUEUE MUST BE FULLY UPLOADED BEFORE THE SHUTDOWN PROCESS STARTS.\n\nIf you want to shutdown the computer sharp at the specified time, please\ninclude the '-shutdown-mode hard' parameter in the application call",
|
||||
shutdownTime));
|
||||
}
|
||||
else {
|
||||
System.out.println(String.format(
|
||||
"WARNING!\n\nThe client will initiate the shutdown process at %s.\nALL RENDERS IN PROGRESS AND UPLOAD QUEUES WILL BE CANCELED.\n\nIf you prefer to shutdown the computer once the pending jobs are completed,\nplease include the '-shutdown-mode wait' parameter in the application call",
|
||||
shutdownTime));
|
||||
}
|
||||
System.out.println("==============================================================================\n");
|
||||
}
|
||||
else if (shutdown == null && shutdownMode != null) {
|
||||
System.err.println(
|
||||
"ERROR: The shutdown-mode parameter cannot be entered alone. Please make sure that you also enter a valid shutdown time (using -shutdown parameter)");
|
||||
System.err.println("Aborting");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
if (config_file != null) {
|
||||
if (new File(config_file).exists() == false) {
|
||||
System.err.println(
|
||||
"ERROR: The entered configuration file (-config parameter) cannot be loaded. Please check that you've entered an existing filename");
|
||||
System.exit(2);
|
||||
}
|
||||
config.setConfigFilePath(config_file);
|
||||
}
|
||||
|
||||
SettingsLoader settingsLoader = new SettingsLoader(config_file);
|
||||
settingsLoader.merge(config, true);
|
||||
|
||||
if (args.length > 0) {
|
||||
settingsLoader.markLaunchSettings(List.of(args));
|
||||
}
|
||||
|
||||
Log.getInstance(config).debug("client version " + Configuration.jarVersion);
|
||||
|
||||
// Hostname change will overwrite the existing one (default or read from configuration file) but changes will be lost when the client closes
|
||||
if (hostname != null) {
|
||||
Pattern hostnameValidator = Pattern.compile("[^a-z0-9-_]", Pattern.CASE_INSENSITIVE);
|
||||
Matcher hostnameCandidate = hostnameValidator.matcher(hostname);
|
||||
|
||||
if (hostnameCandidate.find()) {
|
||||
System.err.println(
|
||||
"ERROR: The entered hostname (-hostname parameter) contains invalid characters. Allowed hostname characters are a-z, A-Z, 0-9, - and _");
|
||||
System.exit(2);
|
||||
}
|
||||
else {
|
||||
config.setHostname(hostname);
|
||||
}
|
||||
}
|
||||
|
||||
Gui gui;
|
||||
String type = config.getUIType();
|
||||
if (type == null) {
|
||||
type = "swing";
|
||||
}
|
||||
switch (type) {
|
||||
case GuiTextOneLine.type:
|
||||
if (config.isPrintLog()) {
|
||||
System.err.println(
|
||||
"ERROR: The oneLine UI and the --verbose parameter cannot be used at the same time. Please either change the ui to text or remove the verbose mode");
|
||||
System.exit(2);
|
||||
}
|
||||
gui = new GuiTextOneLine();
|
||||
break;
|
||||
case GuiText.type:
|
||||
gui = new GuiText();
|
||||
break;
|
||||
default:
|
||||
case GuiSwing.type:
|
||||
if (java.awt.GraphicsEnvironment.isHeadless()) {
|
||||
System.err.println("ERROR: Your current configuration doesn't support graphical UI.");
|
||||
System.err.println("Please use one of the text-based UIs provided (using -ui " + GuiTextOneLine.type + " or -ui " + GuiText.type + ")");
|
||||
System.exit(3);
|
||||
}
|
||||
gui = new GuiSwing(config.isUseSysTray(), title);
|
||||
((GuiSwing) gui).setSettingsLoader(settingsLoader);
|
||||
break;
|
||||
}
|
||||
Client cli = new Client(gui, config, server);
|
||||
gui.setClient(cli);
|
||||
ShutdownHook hook = new ShutdownHook(cli);
|
||||
hook.attachShutDownHook();
|
||||
|
||||
gui.start();
|
||||
}
|
||||
|
||||
private void showGPUList(CmdLineParser parser) {
|
||||
try {
|
||||
parser.parseArgument("--show-gpu");
|
||||
}
|
||||
catch (CmdLineException e) {
|
||||
System.err.println(String.format("ERROR: Unable to parse the provided parameter [%s]", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime shutdownTimeParse(String shutdownTime) {
|
||||
try {
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
|
||||
return LocalDateTime.parse(shutdownTime, df);
|
||||
}
|
||||
catch (DateTimeParseException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.sheepit.client.standalone.swing;
|
||||
|
||||
public enum SwingTooltips {
|
||||
WORKING_DIRECTORY(
|
||||
"Where Sheepit stores things like downloaded projects, finished frames etc. Unless you want to free up your C: drive, Auto detected will do just fine."),
|
||||
|
||||
COMPUTE_DEVICES("What Sheepit will use to render. Note that only one device can be active at a time, e.g. sometimes you get jobs for your \n"
|
||||
+ "GPU so your CPU takes a break, sometimes it's the other way around. The only way to always use 100% of your system \n"
|
||||
+ "is to setup 2 clients, but for that you need to use the command line to give them different configs."),
|
||||
|
||||
CPU_CORES("How many (logical) cores of your CPU, often called threads, Sheepit may use. This doesn't apply to GPU-jobs.\n"
|
||||
+ "(Note that GPU jobs will also use CPU cores for scene building and feeding the GPU)\n"),
|
||||
|
||||
MEMORY("How much RAM Sheepit may use. This isn't a 100% safe limit, since Blender can erroneously use more, \n"
|
||||
+ "but Sheepit will try its best to give you jobs that require less than what you've entered here."),
|
||||
|
||||
PRIORITY("Which priority in your system the rendering process should have."),
|
||||
|
||||
MINIMIZE_TO_SYSTRAY("Whether Sheepit should vanish into your system tray (the icons next to the clock in the bottom right) when you minimize the window."),
|
||||
|
||||
PROXY("If you don't know what this does, you don't need it. Useful for example if you're in a company network with restricted access."),
|
||||
|
||||
COMPUTER_NAME("What this machine will be displayed as on your Sheepit profile page. Only you and admins can see this."),
|
||||
|
||||
MAX_TIME_PER_FRAME("How much time a frame should take at most. Sheepit will try to assign jobs to your machine that take less time to compute.");
|
||||
|
||||
|
||||
private final String explanation;
|
||||
|
||||
private SwingTooltips(String explanation) {
|
||||
this.explanation = explanation;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return explanation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone.swing.activity;
|
||||
|
||||
public interface Activity {
|
||||
|
||||
public void show();
|
||||
|
||||
public void resizeWindow();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone.swing.activity;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPasswordField;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
|
||||
import com.formdev.flatlaf.FlatLaf;
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Configuration.ComputeType;
|
||||
import com.sheepit.client.hardware.cpu.CPU;
|
||||
import com.sheepit.client.hardware.gpu.GPU;
|
||||
import com.sheepit.client.hardware.gpu.GPUDevice;
|
||||
import com.sheepit.client.hardware.gpu.GPULister;
|
||||
import com.sheepit.client.hardware.gpu.nvidia.Nvidia;
|
||||
import com.sheepit.client.hardware.gpu.opencl.OpenCL;
|
||||
import com.sheepit.client.network.Proxy;
|
||||
import com.sheepit.client.os.OS;
|
||||
import com.sheepit.client.standalone.GuiSwing;
|
||||
import com.sheepit.client.standalone.swing.SwingTooltips;
|
||||
import com.sheepit.client.standalone.swing.components.CollapsibleJPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class Settings implements Activity {
|
||||
private static final String DUMMY_CACHE_DIR = "Auto detected";
|
||||
|
||||
private GuiSwing parent;
|
||||
|
||||
private JTextField login;
|
||||
private JPasswordField password;
|
||||
private JLabel cacheDirText;
|
||||
private File cacheDir;
|
||||
private JFileChooser cacheDirChooser;
|
||||
private JCheckBox useCPU;
|
||||
private List<JCheckBoxGPU> useGPUs;
|
||||
private JCheckBox useSysTray;
|
||||
private JCheckBox headlessCheckbox;
|
||||
private JSlider cpuCores;
|
||||
private JSlider ram;
|
||||
private JSpinner renderTime;
|
||||
private JSlider priority;
|
||||
private JTextField proxy;
|
||||
private JTextField hostname;
|
||||
|
||||
private ButtonGroup themeOptionsGroup;
|
||||
private JRadioButton lightMode;
|
||||
private JRadioButton darkMode;
|
||||
|
||||
private JCheckBox saveFile;
|
||||
private JCheckBox autoSignIn;
|
||||
JButton saveButton;
|
||||
|
||||
private boolean haveAutoStarted;
|
||||
private boolean useSysTrayPrevState;
|
||||
private boolean isHeadlessPrevState;
|
||||
private boolean sessionStarted; //indicates whether the settings activity gets shown on launch or mid-session.
|
||||
// it should only be false when the settings activity gets shown right after launch
|
||||
|
||||
public Settings(GuiSwing parent_) {
|
||||
parent = parent_;
|
||||
cacheDir = null;
|
||||
useGPUs = new LinkedList<JCheckBoxGPU>();
|
||||
haveAutoStarted = false;
|
||||
sessionStarted = false;
|
||||
}
|
||||
|
||||
@Override public void show() {
|
||||
Configuration config = parent.getConfiguration();
|
||||
parent.getSettingsLoader().merge(config, false);
|
||||
useSysTrayPrevState = config.isUseSysTray();
|
||||
isHeadlessPrevState = config.isHeadless();
|
||||
|
||||
applyTheme(config.getTheme()); // apply the proper theme (light/dark)
|
||||
|
||||
List<GPUDevice> gpus = GPU.listDevices(config);
|
||||
useGPUs.clear(); // Empty the auxiliary list (used in the list of checkboxes)
|
||||
|
||||
GridBagConstraints constraints = new GridBagConstraints();
|
||||
int currentRow = 0;
|
||||
|
||||
JLabel labelImage = GuiSwing.createLogoWithWatermark();
|
||||
|
||||
constraints.fill = GridBagConstraints.CENTER;
|
||||
|
||||
constraints.gridwidth = 2;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(labelImage, constraints);
|
||||
|
||||
++currentRow;
|
||||
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(new JLabel(" "), constraints); // Add a separator between logo and first panel
|
||||
|
||||
currentRow++;
|
||||
|
||||
// authentication
|
||||
CollapsibleJPanel authentication_panel = new CollapsibleJPanel(new GridLayout(2, 2), this);
|
||||
authentication_panel.setBorder(BorderFactory.createTitledBorder("Authentication"));
|
||||
|
||||
JLabel loginLabel = new JLabel("Username:");
|
||||
login = new JTextField();
|
||||
login.setText(parent.getConfiguration().getLogin());
|
||||
login.setColumns(20);
|
||||
login.addKeyListener(new CheckCanStart());
|
||||
JLabel passwordLabel = new JLabel("Password:");
|
||||
password = new JPasswordField();
|
||||
password.setText(parent.getConfiguration().getPassword());
|
||||
password.setColumns(10);
|
||||
password.addKeyListener(new CheckCanStart());
|
||||
|
||||
authentication_panel.add(loginLabel);
|
||||
authentication_panel.add(login);
|
||||
|
||||
authentication_panel.add(passwordLabel);
|
||||
authentication_panel.add(password);
|
||||
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
parent.getContentPanel().add(authentication_panel, constraints);
|
||||
|
||||
// Theme selection panel
|
||||
CollapsibleJPanel themePanel = new CollapsibleJPanel(new GridLayout(1, 3), this);
|
||||
themePanel.setBorder(BorderFactory.createTitledBorder("Theme"));
|
||||
|
||||
themeOptionsGroup = new ButtonGroup();
|
||||
|
||||
lightMode = new JRadioButton("Light");
|
||||
lightMode.setActionCommand("light");
|
||||
lightMode.setSelected(config.getTheme().equals("light"));
|
||||
lightMode.addActionListener(new ApplyThemeAction());
|
||||
|
||||
darkMode = new JRadioButton("Dark");
|
||||
darkMode.setActionCommand("dark");
|
||||
darkMode.setSelected(config.getTheme().equals("dark"));
|
||||
darkMode.addActionListener(new ApplyThemeAction());
|
||||
|
||||
themePanel.add(lightMode);
|
||||
themePanel.add(darkMode);
|
||||
|
||||
// Group both radio buttons to allow only one selected
|
||||
themeOptionsGroup.add(lightMode);
|
||||
themeOptionsGroup.add(darkMode);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
|
||||
parent.getContentPanel().add(themePanel, constraints);
|
||||
|
||||
// directory
|
||||
CollapsibleJPanel directory_panel = new CollapsibleJPanel(new GridLayout(1, 3), this);
|
||||
directory_panel.setBorder(BorderFactory.createTitledBorder("Cache"));
|
||||
JLabel cacheLabel = new JLabel("Working directory:");
|
||||
cacheLabel.setToolTipText(SwingTooltips.WORKING_DIRECTORY.getText());
|
||||
directory_panel.add(cacheLabel);
|
||||
String destination = DUMMY_CACHE_DIR;
|
||||
if (config.isUserHasSpecifiedACacheDir()) {
|
||||
destination = config.getCacheDirForSettings().getName();
|
||||
}
|
||||
|
||||
JPanel cacheDirWrapper = new JPanel();
|
||||
cacheDirWrapper.setLayout(new BoxLayout(cacheDirWrapper, BoxLayout.LINE_AXIS));
|
||||
cacheDirText = new JLabel(destination);
|
||||
cacheDirWrapper.add(cacheDirText);
|
||||
|
||||
cacheDirWrapper.add(Box.createHorizontalGlue());
|
||||
|
||||
cacheDirChooser = new JFileChooser();
|
||||
cacheDirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
JButton openButton = new JButton("...");
|
||||
openButton.addActionListener(new ChooseFileAction());
|
||||
cacheDirWrapper.add(openButton);
|
||||
|
||||
directory_panel.add(cacheDirWrapper);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
|
||||
parent.getContentPanel().add(directory_panel, constraints);
|
||||
|
||||
// compute devices
|
||||
GridBagLayout gridbag = new GridBagLayout();
|
||||
GridBagConstraints compute_devices_constraints = new GridBagConstraints();
|
||||
CollapsibleJPanel compute_devices_panel = new CollapsibleJPanel(gridbag, this);
|
||||
|
||||
compute_devices_panel.setBorder(BorderFactory.createTitledBorder("Compute devices"));
|
||||
compute_devices_panel.setToolTipText(SwingTooltips.COMPUTE_DEVICES.getText());
|
||||
|
||||
ComputeType method = config.getComputeMethod();
|
||||
useCPU = new JCheckBox("CPU");
|
||||
boolean gpuChecked = false;
|
||||
|
||||
if (method == ComputeType.CPU_GPU) {
|
||||
useCPU.setSelected(true);
|
||||
gpuChecked = true;
|
||||
}
|
||||
else if (method == ComputeType.CPU) {
|
||||
useCPU.setSelected(true);
|
||||
gpuChecked = false;
|
||||
}
|
||||
else if (method == ComputeType.GPU) {
|
||||
useCPU.setSelected(false);
|
||||
gpuChecked = true;
|
||||
}
|
||||
useCPU.addActionListener(new CpuChangeAction());
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.gridy = 0;
|
||||
compute_devices_constraints.fill = GridBagConstraints.BOTH;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
compute_devices_constraints.weighty = 1.0;
|
||||
|
||||
gridbag.setConstraints(useCPU, compute_devices_constraints);
|
||||
compute_devices_panel.add(useCPU);
|
||||
|
||||
if (gpus.size() > 0) {
|
||||
for (GPUDevice gpu : gpus) {
|
||||
JCheckBoxGPU gpuCheckBox = new JCheckBoxGPU(gpu);
|
||||
gpuCheckBox.setToolTipText(gpu.getId());
|
||||
if (gpuChecked) {
|
||||
GPUDevice config_gpu = config.getGPUDevice();
|
||||
if (config_gpu != null && config_gpu.getId().equals(gpu.getId())) {
|
||||
gpuCheckBox.setSelected(gpuChecked);
|
||||
}
|
||||
}
|
||||
gpuCheckBox.addActionListener(new GpuChangeAction());
|
||||
|
||||
compute_devices_constraints.gridy++;
|
||||
gridbag.setConstraints(gpuCheckBox, compute_devices_constraints);
|
||||
compute_devices_panel.add(gpuCheckBox);
|
||||
useGPUs.add(gpuCheckBox);
|
||||
}
|
||||
|
||||
//When replacing gpus it can happen that the client can't find the one specified in the config anymore in which case config.getGPUDevice()
|
||||
//returns null
|
||||
if ((config.getComputeMethod() == ComputeType.GPU || config.getComputeMethod() == ComputeType.CPU_GPU) && config.getGPUDevice() != null) {
|
||||
GPULister gpu;
|
||||
|
||||
if (config.getGPUDevice().getType().equals("CUDA")) {
|
||||
gpu = new Nvidia();
|
||||
}
|
||||
else if (config.getGPUDevice().getType().equals("OPENCL")) {
|
||||
gpu = new OpenCL();
|
||||
}
|
||||
}
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
compute_devices_panel.add(new JLabel(" "), compute_devices_constraints); // Add a space between lines
|
||||
}
|
||||
|
||||
CPU cpu = new CPU();
|
||||
if (cpu.cores() > 1) { // if only one core is available, no need to show the choice
|
||||
double step = 1;
|
||||
double display = (double) cpu.cores() / step;
|
||||
while (display > 10) {
|
||||
step += 1.0;
|
||||
display = (double) cpu.cores() / step;
|
||||
}
|
||||
|
||||
cpuCores = new JSlider(CPU.MIN_CORES, cpu.cores());
|
||||
cpuCores.setMajorTickSpacing((int) (step));
|
||||
cpuCores.setMinorTickSpacing(1);
|
||||
cpuCores.setPaintTicks(true);
|
||||
cpuCores.setSnapToTicks(true);
|
||||
cpuCores.addChangeListener(new ChangeListener() {
|
||||
@Override public void stateChanged(ChangeEvent e) {
|
||||
cpuCores.setToolTipText(String.valueOf(cpuCores.getValue()));
|
||||
}
|
||||
});
|
||||
cpuCores.setPaintLabels(true);
|
||||
cpuCores.setValue(config.getNbCores() != -1 ? (Math.max(config.getNbCores(), CPU.MIN_CORES)): cpuCores.getMaximum());
|
||||
JLabel coreLabel = new JLabel("CPU cores:");
|
||||
coreLabel.setToolTipText(SwingTooltips.CPU_CORES.getText());
|
||||
|
||||
compute_devices_constraints.weightx = 1.0 / gpus.size();
|
||||
compute_devices_constraints.gridx = 0;
|
||||
compute_devices_constraints.gridy++;
|
||||
|
||||
gridbag.setConstraints(coreLabel, compute_devices_constraints);
|
||||
compute_devices_panel.add(coreLabel);
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
gridbag.setConstraints(cpuCores, compute_devices_constraints);
|
||||
compute_devices_panel.add(new JLabel(" "), compute_devices_constraints); // Add a space between lines
|
||||
compute_devices_panel.add(cpuCores);
|
||||
}
|
||||
|
||||
// max ram allowed to render
|
||||
OS os = OS.getOS();
|
||||
int all_ram = (int) os.getTotalMemory();
|
||||
ram = new JSlider(0, all_ram);
|
||||
int step = 1000000;
|
||||
double display = (double) all_ram / (double) step;
|
||||
while (display > 10) {
|
||||
step += 1000000;
|
||||
display = (double) all_ram / (double) step;
|
||||
}
|
||||
Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
|
||||
for (int g = 0; g < all_ram; g += step) {
|
||||
labelTable.put(g, new JLabel("" + (g / 1000000)));
|
||||
}
|
||||
ram.setMajorTickSpacing(step);
|
||||
ram.setLabelTable(labelTable);
|
||||
ram.setPaintTicks(true);
|
||||
ram.addChangeListener(new ChangeListener() {
|
||||
@Override public void stateChanged(ChangeEvent e) {
|
||||
ram.setToolTipText(String.format("%,d MiB", (ram.getValue() / 1024)));
|
||||
}
|
||||
});
|
||||
ram.setPaintLabels(true);
|
||||
ram.setValue((int) (config.getMaxAllowedMemory() != -1 ? config.getMaxAllowedMemory() : os.getTotalMemory()));
|
||||
JLabel ramLabel = new JLabel("Memory:");
|
||||
ramLabel.setToolTipText(SwingTooltips.MEMORY.getText());
|
||||
|
||||
compute_devices_constraints.weightx = 1.0 / gpus.size();
|
||||
compute_devices_constraints.gridx = 0;
|
||||
compute_devices_constraints.gridy++;
|
||||
|
||||
gridbag.setConstraints(ramLabel, compute_devices_constraints);
|
||||
compute_devices_panel.add(ramLabel);
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
gridbag.setConstraints(ram, compute_devices_constraints);
|
||||
compute_devices_panel.add(new JLabel(" "), compute_devices_constraints); // Add a space between lines
|
||||
compute_devices_panel.add(ram);
|
||||
|
||||
parent.getContentPanel().add(compute_devices_panel, constraints);
|
||||
|
||||
// priority
|
||||
// ui display low -> high but the actual values are reversed
|
||||
// -19 is highest priority
|
||||
boolean high_priority_support = os.getSupportHighPriority();
|
||||
priority = new JSlider(high_priority_support ? -19 : 0, 19);
|
||||
Hashtable<Integer, JLabel> labelTablePriority = new Hashtable<>();
|
||||
labelTablePriority.put(high_priority_support ? -19 : 0, new JLabel("Low"));
|
||||
labelTablePriority.put(19, new JLabel("High"));
|
||||
priority.setLabelTable(labelTablePriority);
|
||||
|
||||
priority.setMinorTickSpacing(1);
|
||||
priority.setPaintTicks(true);
|
||||
priority.setSnapToTicks(true);
|
||||
priority.addChangeListener(new ChangeListener() {
|
||||
@Override public void stateChanged(ChangeEvent e) {
|
||||
priority.setToolTipText(String.valueOf(priority.getValue()));
|
||||
}
|
||||
});
|
||||
priority.setPaintLabels(true);
|
||||
if (high_priority_support) {
|
||||
// inverse
|
||||
priority.setValue(-1 * config.getPriority());
|
||||
}
|
||||
else {
|
||||
priority.setValue(19 - config.getPriority());
|
||||
}
|
||||
JLabel priorityLabel = new JLabel("Priority");
|
||||
priorityLabel.setToolTipText(SwingTooltips.PRIORITY.getText());
|
||||
|
||||
boolean showPrioritySlider = os.checkNiceAvailability();
|
||||
priority.setVisible(showPrioritySlider);
|
||||
priorityLabel.setVisible(showPrioritySlider);
|
||||
|
||||
compute_devices_constraints.weightx = 1.0 / gpus.size();
|
||||
compute_devices_constraints.gridx = 0;
|
||||
compute_devices_constraints.gridy++;
|
||||
|
||||
gridbag.setConstraints(priorityLabel, compute_devices_constraints);
|
||||
compute_devices_panel.add(priorityLabel);
|
||||
|
||||
compute_devices_constraints.gridx = 1;
|
||||
compute_devices_constraints.weightx = 1.0;
|
||||
|
||||
gridbag.setConstraints(priority, compute_devices_constraints);
|
||||
compute_devices_panel.add(priority);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
parent.getContentPanel().add(compute_devices_panel, constraints);
|
||||
|
||||
// other
|
||||
CollapsibleJPanel advanced_panel = new CollapsibleJPanel(new GridLayout(5, 2), this);
|
||||
advanced_panel.setBorder(BorderFactory.createTitledBorder("Advanced options"));
|
||||
|
||||
JLabel useSysTrayLabel = new JLabel("Minimize to SysTray");
|
||||
useSysTrayLabel.setToolTipText(SwingTooltips.MINIMIZE_TO_SYSTRAY.getText());
|
||||
|
||||
useSysTray = new JCheckBox();
|
||||
useSysTray.setSelected(config.isUseSysTray());
|
||||
advanced_panel.add(useSysTrayLabel);
|
||||
advanced_panel.add(useSysTray);
|
||||
|
||||
JLabel headlessLabel = new JLabel("Block Eevee projects");
|
||||
headlessCheckbox = new JCheckBox();
|
||||
headlessCheckbox.setSelected(config.isHeadless());
|
||||
advanced_panel.add(headlessLabel);
|
||||
advanced_panel.add(headlessCheckbox);
|
||||
|
||||
JLabel proxyLabel = new JLabel("Proxy:");
|
||||
proxyLabel.setToolTipText("http://login:password@host:port\n" + SwingTooltips.PROXY.getText());
|
||||
proxy = new JTextField();
|
||||
proxy.setToolTipText("http://login:password@host:port");
|
||||
proxy.setText(parent.getConfiguration().getProxy());
|
||||
proxy.addKeyListener(new CheckCanStart());
|
||||
|
||||
advanced_panel.add(proxyLabel);
|
||||
advanced_panel.add(proxy);
|
||||
|
||||
JLabel hostnameLabel = new JLabel("Computer name:");
|
||||
hostnameLabel.setToolTipText(SwingTooltips.COMPUTER_NAME.getText());
|
||||
hostname = new JTextField();
|
||||
hostname.setText(parent.getConfiguration().getHostname());
|
||||
|
||||
advanced_panel.add(hostnameLabel);
|
||||
advanced_panel.add(hostname);
|
||||
|
||||
JLabel renderTimeLabel = new JLabel("Max time per frame (in minute):");
|
||||
renderTimeLabel.setToolTipText(SwingTooltips.MAX_TIME_PER_FRAME.getText());
|
||||
int val = 0;
|
||||
if (parent.getConfiguration().getMaxRenderTime() > 0) {
|
||||
val = parent.getConfiguration().getMaxRenderTime() / 60;
|
||||
}
|
||||
renderTime = new JSpinner(new SpinnerNumberModel(val, 0, 1000, 1));
|
||||
|
||||
advanced_panel.add(renderTimeLabel);
|
||||
advanced_panel.add(renderTime);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
parent.getContentPanel().add(advanced_panel, constraints);
|
||||
|
||||
// general settings
|
||||
JPanel general_panel = new JPanel(new GridLayout(1, 2));
|
||||
|
||||
saveFile = new JCheckBox("Save settings", true);
|
||||
general_panel.add(saveFile);
|
||||
|
||||
autoSignIn = new JCheckBox("Auto sign in", config.isAutoSignIn());
|
||||
autoSignIn.addActionListener(new AutoSignInChangeAction());
|
||||
general_panel.add(autoSignIn);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
constraints.gridwidth = 2;
|
||||
parent.getContentPanel().add(general_panel, constraints);
|
||||
|
||||
currentRow++;
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(new JLabel(" "), constraints); // Add a separator between last checkboxes and button
|
||||
|
||||
currentRow++;
|
||||
String buttonText = "Start";
|
||||
if (parent.getClient() != null) {
|
||||
if (parent.getClient().isRunning()) {
|
||||
buttonText = "Save";
|
||||
}
|
||||
}
|
||||
saveButton = new JButton(buttonText);
|
||||
checkDisplaySaveButton();
|
||||
saveButton.addActionListener(new SaveAction());
|
||||
currentRow++;
|
||||
constraints.gridwidth = 2;
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = currentRow;
|
||||
parent.getContentPanel().add(saveButton, constraints);
|
||||
|
||||
// Increase the size of the app Window to ensure it shows all the information with the Advanced Options panel opened.
|
||||
parent.setSize(520,850);
|
||||
|
||||
if (haveAutoStarted == false && config.isAutoSignIn() && checkDisplaySaveButton()) {
|
||||
// auto start
|
||||
haveAutoStarted = true;
|
||||
new SaveAction().actionPerformed(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void resizeWindow() {}
|
||||
|
||||
public boolean checkDisplaySaveButton() {
|
||||
boolean selected = useCPU.isSelected();
|
||||
for (JCheckBoxGPU box : useGPUs) {
|
||||
if (box.isSelected()) {
|
||||
selected = true;
|
||||
}
|
||||
}
|
||||
if (login.getText().isEmpty() || password.getPassword().length == 0 || Proxy.isValidURL(proxy.getText()) == false) {
|
||||
selected = false;
|
||||
}
|
||||
|
||||
saveButton.setEnabled(selected);
|
||||
return selected;
|
||||
}
|
||||
|
||||
private void applyTheme(String theme_) {
|
||||
try {
|
||||
if (theme_.equals("light")) {
|
||||
UIManager.setLookAndFeel(new FlatLightLaf());
|
||||
}
|
||||
else if (theme_.equals("dark")) {
|
||||
UIManager.setLookAndFeel(new FlatDarkLaf());
|
||||
}
|
||||
|
||||
// Apply the new theme
|
||||
FlatLaf.updateUI();
|
||||
}
|
||||
catch (UnsupportedLookAndFeelException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
class ChooseFileAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent arg0) {
|
||||
JOptionPane.showMessageDialog(parent.getContentPane(),
|
||||
"<html>The working directory has to be dedicated directory. <br />Caution, everything not related to SheepIt-Renderfarm will be removed.<br />You should create a directory specifically for it.</html>",
|
||||
"Warning: files will be removed!", JOptionPane.WARNING_MESSAGE);
|
||||
int returnVal = cacheDirChooser.showOpenDialog(parent.getContentPane());
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
File file = cacheDirChooser.getSelectedFile();
|
||||
cacheDir = file;
|
||||
cacheDirText.setText(cacheDir.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CpuChangeAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
checkDisplaySaveButton();
|
||||
}
|
||||
}
|
||||
|
||||
class GpuChangeAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
int counter = 0;
|
||||
for (JCheckBox box : useGPUs) {
|
||||
if (!box.isSelected()) {
|
||||
box.setSelected(false);
|
||||
}
|
||||
else {
|
||||
GPULister gpu;
|
||||
if (useGPUs.get(counter).getGPUDevice().getType().equals("CUDA")) {
|
||||
gpu = new Nvidia();
|
||||
}
|
||||
else if (useGPUs.get(counter).getGPUDevice().getType().equals("OPENCL")) {
|
||||
gpu = new OpenCL();
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate a radio button behavior with check buttons while only 1 GPU
|
||||
// can be selected at a time
|
||||
if (box.equals(e.getSource()) == false) {
|
||||
box.setSelected(false);
|
||||
}
|
||||
|
||||
counter++;
|
||||
}
|
||||
checkDisplaySaveButton();
|
||||
}
|
||||
}
|
||||
|
||||
class AutoSignInChangeAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
if (autoSignIn.isSelected()) {
|
||||
saveFile.setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ApplyThemeAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
applyTheme(themeOptionsGroup.getSelection().getActionCommand());
|
||||
}
|
||||
}
|
||||
|
||||
class SaveAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Configuration config = parent.getConfiguration();
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (themeOptionsGroup.getSelection().getActionCommand() != null)
|
||||
config.setTheme(themeOptionsGroup.getSelection().getActionCommand());
|
||||
|
||||
if (cacheDir != null) {
|
||||
File fromConfig = config.getStorageDir();
|
||||
if (fromConfig != null && fromConfig.getAbsolutePath().equals(cacheDir.getAbsolutePath()) == false) {
|
||||
config.setCacheDir(cacheDir);
|
||||
}
|
||||
else {
|
||||
// do nothing because the directory is the same as before
|
||||
}
|
||||
}
|
||||
|
||||
GPUDevice selected_gpu = null;
|
||||
for (JCheckBoxGPU box : useGPUs) {
|
||||
if (box.isSelected()) {
|
||||
selected_gpu = box.getGPUDevice();
|
||||
}
|
||||
}
|
||||
|
||||
ComputeType method = getComputeType(selected_gpu);
|
||||
config.setComputeMethod(method);
|
||||
|
||||
if (selected_gpu != null) {
|
||||
config.setGPUDevice(selected_gpu);
|
||||
}
|
||||
|
||||
int cpu_cores = -1;
|
||||
if (cpuCores != null) {
|
||||
cpu_cores = cpuCores.getValue();
|
||||
}
|
||||
|
||||
if (cpu_cores > 0) {
|
||||
config.setNbCores(cpu_cores);
|
||||
}
|
||||
|
||||
long max_ram = -1;
|
||||
if (ram != null) {
|
||||
max_ram = ram.getValue();
|
||||
}
|
||||
|
||||
if (max_ram > 0) {
|
||||
config.setMaxAllowedMemory(max_ram);
|
||||
}
|
||||
|
||||
int max_rendertime = -1;
|
||||
if (renderTime != null) {
|
||||
max_rendertime = (Integer) renderTime.getValue() * 60;
|
||||
config.setMaxRenderTime(max_rendertime);
|
||||
}
|
||||
|
||||
// ui display low -> high but the actual values are reversed
|
||||
// -19 is highest priority
|
||||
OS os = OS.getOS();
|
||||
boolean high_priority_support = os.getSupportHighPriority();
|
||||
if (high_priority_support) {
|
||||
// inverse
|
||||
config.setUsePriority(-1 * priority.getValue());
|
||||
}
|
||||
else {
|
||||
config.setUsePriority(19 - priority.getValue());
|
||||
}
|
||||
|
||||
config.setHeadless(headlessCheckbox.isSelected());
|
||||
|
||||
String proxyText = null;
|
||||
if (proxy != null) {
|
||||
try {
|
||||
Proxy.set(proxy.getText());
|
||||
proxyText = proxy.getText();
|
||||
}
|
||||
catch (MalformedURLException e1) {
|
||||
System.err.println("Error: wrong url for proxy");
|
||||
System.err.println(e1);
|
||||
System.exit(2);
|
||||
}
|
||||
}
|
||||
config.setProxy(proxyText);
|
||||
|
||||
parent.setCredentials(login.getText(), new String(password.getPassword()));
|
||||
|
||||
String hostnameText = hostname.getText();
|
||||
if (hostnameText == null || hostnameText.isEmpty()) {
|
||||
hostnameText = parent.getConfiguration().getHostname();
|
||||
}
|
||||
config.setHostname(hostnameText);
|
||||
config.setAutoSignIn(autoSignIn.isSelected());
|
||||
config.setUseSysTray(useSysTray.isSelected());
|
||||
// config.setUIType(GuiSwing.type);
|
||||
|
||||
|
||||
|
||||
if (saveFile.isSelected()) {
|
||||
parent.getSettingsLoader()
|
||||
.setSettings(config.getConfigFilePath(), login.getText(), new String(password.getPassword()), proxyText, hostnameText, method,
|
||||
selected_gpu, cpu_cores, max_ram, max_rendertime, getCachePath(config), autoSignIn.isSelected(), useSysTray.isSelected(),
|
||||
headlessCheckbox.isSelected(), GuiSwing.type, themeOptionsGroup.getSelection().getActionCommand(), config.getPriority());
|
||||
|
||||
// wait for successful authentication (to store the public key)
|
||||
// or do we already have one?
|
||||
if (parent.getClient().getServer().getServerConfig() != null && parent.getClient().getServer().getServerConfig().getPublickey() != null) {
|
||||
parent.getSettingsLoader().saveFile();
|
||||
sessionStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean sysTrayChanged = useSysTray.isSelected() != useSysTrayPrevState;
|
||||
boolean headlessChanged = headlessCheckbox.isSelected() != isHeadlessPrevState;
|
||||
boolean restartRequired = sysTrayChanged || headlessChanged;
|
||||
if (restartRequired) {
|
||||
String warning = "The following changes require a restart to take effect: %s";
|
||||
List<String> changes = new LinkedList<>();
|
||||
|
||||
if (sysTrayChanged) {
|
||||
changes.add("Minimize to SysTray");
|
||||
}
|
||||
|
||||
if (headlessChanged && sessionStarted) { //only show this when the setting gets changed in the middle of a session, not on launch
|
||||
changes.add("Block Eevee Projects");
|
||||
}
|
||||
|
||||
if (changes.size() > 0) {
|
||||
warning = String.format(warning, String.join(", ", changes));
|
||||
JOptionPane.showMessageDialog(null, warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull private ComputeType getComputeType(GPUDevice selected_gpu) {
|
||||
ComputeType method = ComputeType.CPU;
|
||||
if (useCPU.isSelected() && selected_gpu == null) {
|
||||
method = ComputeType.CPU;
|
||||
}
|
||||
else if (useCPU.isSelected() == false && selected_gpu != null) {
|
||||
method = ComputeType.GPU;
|
||||
}
|
||||
else if (useCPU.isSelected() && selected_gpu != null) {
|
||||
method = ComputeType.CPU_GPU;
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
private String getCachePath(Configuration config) {
|
||||
String cachePath = null;
|
||||
if (config.isUserHasSpecifiedACacheDir() && config.getCacheDirForSettings() != null) {
|
||||
cachePath = config.getCacheDirForSettings().getAbsolutePath();
|
||||
}
|
||||
return cachePath;
|
||||
}
|
||||
}
|
||||
|
||||
class JCheckBoxGPU extends JCheckBox {
|
||||
private GPUDevice gpu;
|
||||
|
||||
public JCheckBoxGPU(GPUDevice gpu) {
|
||||
super(gpu.getModel());
|
||||
this.gpu = gpu;
|
||||
}
|
||||
|
||||
public GPUDevice getGPUDevice() {
|
||||
return gpu;
|
||||
}
|
||||
}
|
||||
|
||||
public class CheckCanStart implements KeyListener {
|
||||
|
||||
@Override public void keyPressed(KeyEvent arg0) {
|
||||
}
|
||||
|
||||
@Override public void keyReleased(KeyEvent arg0) {
|
||||
checkDisplaySaveButton();
|
||||
}
|
||||
|
||||
@Override public void keyTyped(KeyEvent arg0) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Laurent CLOUET
|
||||
* Author Laurent CLOUET <laurent.clouet@nopnop.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone.swing.activity;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.Image;
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.Spring;
|
||||
import javax.swing.SpringLayout;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Job;
|
||||
import com.sheepit.client.Log;
|
||||
import com.sheepit.client.Stats;
|
||||
import com.sheepit.client.TransferStats;
|
||||
import com.sheepit.client.Utils;
|
||||
import com.sheepit.client.standalone.GuiSwing;
|
||||
import com.sheepit.client.standalone.GuiSwing.ActivityType;
|
||||
|
||||
import com.sheepit.client.standalone.swing.components.CollapsibleJPanel;
|
||||
|
||||
public class Working implements Activity {
|
||||
private GuiSwing parent;
|
||||
|
||||
private CollapsibleJPanel session_info_panel;
|
||||
private CollapsibleJPanel global_stats_panel;
|
||||
private CollapsibleJPanel last_frame_panel;
|
||||
private JLabel statusContent;
|
||||
private String previousStatus;
|
||||
private JLabel renderedFrameContent;
|
||||
private JLabel remainingFrameContent;
|
||||
private JLabel lastRenderTime;
|
||||
private JLabel lastRender;
|
||||
private JLabel creditEarned;
|
||||
private JButton pauseButton;
|
||||
private JButton exitAfterFrame;
|
||||
private JLabel current_project_name_value;
|
||||
private JLabel current_project_duration_value;
|
||||
private JLabel currrent_project_progression_value;
|
||||
private JLabel current_project_compute_method_value;
|
||||
private JLabel user_info_points_total_value;
|
||||
private JLabel renderable_projects_value;
|
||||
private JLabel waiting_projects_value;
|
||||
private JLabel connected_machines_value;
|
||||
private JLabel user_info_total_rendertime_this_session_value;
|
||||
private JLabel userInfoQueuedUploadsAndSizeValue;
|
||||
private JLabel sessionDownloadsStatsValue;
|
||||
private JLabel sessionUploadsStatsValue;
|
||||
private String currentTheme;
|
||||
private Log log;
|
||||
|
||||
public Working(GuiSwing parent_) {
|
||||
parent = parent_;
|
||||
|
||||
statusContent = new JLabel("Init");
|
||||
renderedFrameContent = new JLabel("0");
|
||||
remainingFrameContent = new JLabel("");
|
||||
creditEarned = new JLabel("");
|
||||
current_project_name_value = new JLabel("");
|
||||
current_project_duration_value = new JLabel("");
|
||||
currrent_project_progression_value = new JLabel("");
|
||||
current_project_compute_method_value = new JLabel("");
|
||||
user_info_points_total_value = new JLabel("");
|
||||
renderable_projects_value = new JLabel("");
|
||||
waiting_projects_value = new JLabel("");
|
||||
connected_machines_value = new JLabel("");
|
||||
user_info_total_rendertime_this_session_value = new JLabel("");
|
||||
lastRenderTime = new JLabel(" "); // Insert a space to ensure the component reserves the space in the screen (for the window size calculation)
|
||||
lastRender = new JLabel("");
|
||||
userInfoQueuedUploadsAndSizeValue = new JLabel("0");
|
||||
sessionDownloadsStatsValue = new JLabel("0KB");
|
||||
sessionUploadsStatsValue = new JLabel("0KB");
|
||||
currentTheme = UIManager.getLookAndFeel().getName(); // Capture the theme on component instantiation
|
||||
previousStatus = "";
|
||||
log = Log.getInstance(parent_.getConfiguration());
|
||||
}
|
||||
|
||||
@Override public void show() {
|
||||
// If the stored theme and the UIManager's theme doesn't match is bc the user has changed it
|
||||
if (!currentTheme.equals(UIManager.getLookAndFeel().getName())) {
|
||||
// And, as the user has changed the theme, then we must recreate all the UI elements with session data
|
||||
// Reason being they are defined as class variables and therefore created once when the object
|
||||
// is created the first time.
|
||||
// As the Java swing engine applies the "look & feel" at creation time, we need to "re-create" the
|
||||
// objects to ensure they have the right theme colors.
|
||||
statusContent = new JLabel(statusContent.getText());
|
||||
renderedFrameContent = new JLabel(renderedFrameContent.getText());
|
||||
remainingFrameContent = new JLabel(remainingFrameContent.getText());
|
||||
creditEarned = new JLabel(creditEarned.getText());
|
||||
current_project_name_value = new JLabel(current_project_name_value.getText());
|
||||
current_project_duration_value = new JLabel(current_project_duration_value.getText());
|
||||
currrent_project_progression_value = new JLabel(currrent_project_progression_value.getText());
|
||||
current_project_compute_method_value = new JLabel(current_project_compute_method_value.getText());
|
||||
user_info_points_total_value = new JLabel(user_info_points_total_value.getText());
|
||||
renderable_projects_value = new JLabel(renderable_projects_value.getText());
|
||||
waiting_projects_value = new JLabel(waiting_projects_value.getText());
|
||||
connected_machines_value = new JLabel(connected_machines_value.getText());
|
||||
user_info_total_rendertime_this_session_value = new JLabel(user_info_total_rendertime_this_session_value.getText());
|
||||
lastRenderTime = new JLabel(lastRenderTime.getText());
|
||||
lastRender = new JLabel(lastRender.getText());
|
||||
userInfoQueuedUploadsAndSizeValue = new JLabel(userInfoQueuedUploadsAndSizeValue.getText());
|
||||
sessionDownloadsStatsValue = new JLabel(sessionDownloadsStatsValue.getText());
|
||||
sessionUploadsStatsValue = new JLabel(sessionUploadsStatsValue.getText());
|
||||
|
||||
// set the new theme as the current one
|
||||
currentTheme = UIManager.getLookAndFeel().getName();
|
||||
}
|
||||
|
||||
// current project
|
||||
JPanel current_project_panel = new JPanel(new GridLayout(5, 2));
|
||||
current_project_panel.setBorder(BorderFactory.createTitledBorder("Project"));
|
||||
|
||||
JLabel current_project_status = new JLabel("Status: ", JLabel.TRAILING);
|
||||
JLabel current_project_name = new JLabel("Name: ", JLabel.TRAILING);
|
||||
JLabel current_project_duration = new JLabel("Rendering for: ", JLabel.TRAILING);
|
||||
JLabel current_project_progression = new JLabel("Remaining: ", JLabel.TRAILING);
|
||||
JLabel current_project_compute_method_label = new JLabel("Compute method: ", JLabel.TRAILING);
|
||||
|
||||
current_project_panel.add(current_project_status);
|
||||
current_project_panel.add(statusContent);
|
||||
|
||||
current_project_panel.add(current_project_name);
|
||||
current_project_panel.add(current_project_name_value);
|
||||
|
||||
current_project_panel.add(current_project_duration);
|
||||
current_project_panel.add(current_project_duration_value);
|
||||
|
||||
current_project_panel.add(current_project_progression);
|
||||
current_project_panel.add(currrent_project_progression_value);
|
||||
|
||||
current_project_panel.add(current_project_compute_method_label);
|
||||
current_project_panel.add(current_project_compute_method_value);
|
||||
|
||||
// user info
|
||||
session_info_panel = new CollapsibleJPanel(new GridLayout(7, 2), this);
|
||||
session_info_panel.setBorder(BorderFactory.createTitledBorder("Session infos"));
|
||||
|
||||
JLabel user_info_credits_this_session = new JLabel("Points earned: ", JLabel.TRAILING);
|
||||
JLabel user_info_total_rendertime_this_session = new JLabel("Duration: ", JLabel.TRAILING);
|
||||
JLabel user_info_pending_uploads_and_size = new JLabel("Queued uploads: ", JLabel.TRAILING);
|
||||
JLabel session_download_stats = new JLabel("Total Downloads: ", JLabel.TRAILING);
|
||||
JLabel session_upload_stats = new JLabel("Total Uploads: ", JLabel.TRAILING);
|
||||
JLabel user_info_rendered_frame_this_session = new JLabel("Rendered frames: ", JLabel.TRAILING);
|
||||
JLabel global_static_renderable_project = new JLabel("Renderable projects: ", JLabel.TRAILING);
|
||||
|
||||
session_info_panel.add(user_info_credits_this_session);
|
||||
session_info_panel.add(creditEarned);
|
||||
|
||||
session_info_panel.add(user_info_rendered_frame_this_session);
|
||||
session_info_panel.add(renderedFrameContent);
|
||||
|
||||
session_info_panel.add(user_info_pending_uploads_and_size);
|
||||
session_info_panel.add(userInfoQueuedUploadsAndSizeValue);
|
||||
|
||||
session_info_panel.add(session_download_stats);
|
||||
session_info_panel.add(sessionDownloadsStatsValue);
|
||||
|
||||
session_info_panel.add(session_upload_stats);
|
||||
session_info_panel.add(sessionUploadsStatsValue);
|
||||
|
||||
session_info_panel.add(global_static_renderable_project);
|
||||
session_info_panel.add(renderable_projects_value);
|
||||
|
||||
session_info_panel.add(user_info_total_rendertime_this_session);
|
||||
session_info_panel.add(user_info_total_rendertime_this_session_value);
|
||||
|
||||
// global stats
|
||||
global_stats_panel = new CollapsibleJPanel(new GridLayout(4, 2), this);
|
||||
global_stats_panel.setBorder(BorderFactory.createTitledBorder("Global stats"));
|
||||
|
||||
JLabel global_stats_machine_connected = new JLabel("Machines connected: ", JLabel.TRAILING);
|
||||
JLabel global_stats_remaining_frame = new JLabel("Remaining frames: ", JLabel.TRAILING);
|
||||
JLabel global_stats_waiting_project = new JLabel("Active projects: ", JLabel.TRAILING);
|
||||
JLabel global_stats_user_points = new JLabel("User's points: ", JLabel.TRAILING);
|
||||
|
||||
global_stats_panel.add(global_stats_waiting_project);
|
||||
global_stats_panel.add(waiting_projects_value);
|
||||
|
||||
global_stats_panel.add(global_stats_machine_connected);
|
||||
global_stats_panel.add(connected_machines_value);
|
||||
|
||||
global_stats_panel.add(global_stats_remaining_frame);
|
||||
global_stats_panel.add(remainingFrameContent);
|
||||
|
||||
global_stats_panel.add(global_stats_user_points);
|
||||
global_stats_panel.add(user_info_points_total_value);
|
||||
|
||||
// last frame
|
||||
last_frame_panel = new CollapsibleJPanel(new GridLayout(2, 2), this);
|
||||
last_frame_panel.setLayout(new BoxLayout(last_frame_panel, BoxLayout.Y_AXIS));
|
||||
last_frame_panel.setBorder(BorderFactory.createTitledBorder("Last uploaded frame"));
|
||||
lastRender.setIcon(new ImageIcon(new BufferedImage(200, 120, BufferedImage.TYPE_INT_ARGB)));
|
||||
lastRender.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
lastRenderTime.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
last_frame_panel.add(lastRenderTime);
|
||||
last_frame_panel.add(lastRender);
|
||||
|
||||
JLabel labelImage = GuiSwing.createLogoWithWatermark();
|
||||
labelImage.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
parent.getContentPanel().add(labelImage);
|
||||
|
||||
JPanel buttonsPanel = new JPanel(new GridLayout(2, 2));
|
||||
|
||||
JButton settingsButton = new JButton("Settings");
|
||||
settingsButton.addActionListener(new SettingsAction());
|
||||
|
||||
pauseButton = new JButton("Pause getting new jobs");
|
||||
Client client = parent.getClient();
|
||||
if (client != null && client.isSuspended()) {
|
||||
pauseButton.setText("Resume getting jobs");
|
||||
}
|
||||
|
||||
pauseButton.addActionListener(new PauseAction());
|
||||
|
||||
JButton blockJob = new JButton("Block this project");
|
||||
blockJob.addActionListener(new blockJobAction());
|
||||
|
||||
exitAfterFrame = new JButton("Exit");
|
||||
exitAfterFrame.addActionListener(new ExitAfterAction());
|
||||
|
||||
buttonsPanel.add(settingsButton);
|
||||
buttonsPanel.add(pauseButton);
|
||||
buttonsPanel.add(blockJob);
|
||||
buttonsPanel.add(exitAfterFrame);
|
||||
|
||||
parent.getContentPanel().setLayout(new GridBagLayout());
|
||||
GridBagConstraints global_constraints = new GridBagConstraints();
|
||||
global_constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||
global_constraints.weightx = 1;
|
||||
global_constraints.gridx = 0;
|
||||
|
||||
parent.getContentPanel().add(new JLabel(" "), global_constraints); // Add a separator between logo and first panel
|
||||
parent.getContentPanel().add(current_project_panel, global_constraints);
|
||||
parent.getContentPanel().add(global_stats_panel, global_constraints);
|
||||
parent.getContentPanel().add(session_info_panel, global_constraints);
|
||||
parent.getContentPanel().add(last_frame_panel, global_constraints);
|
||||
parent.getContentPanel().add(new JLabel(" "), global_constraints); // Add a separator between last panel and buttons
|
||||
parent.getContentPanel().add(buttonsPanel, global_constraints);
|
||||
|
||||
// Set the proper size for the Working (if coming from Settings screen, the window size will be too big for the content!)
|
||||
parent.setSize(520, 820);
|
||||
}
|
||||
|
||||
public void resizeWindow() {
|
||||
parent.revalidate();
|
||||
parent.repaint();
|
||||
|
||||
// Calculate the proper size based on the status of the panels (opened/collapsed)
|
||||
parent.setSize(520, (int) (session_info_panel.getHeight() + global_stats_panel.getHeight() + last_frame_panel.getHeight()) + 400);
|
||||
}
|
||||
|
||||
public void setStatus(String msg_) {
|
||||
setStatus(msg_, false);
|
||||
}
|
||||
|
||||
public void setStatus(String msg_, boolean overwriteSuspendedMsg) {
|
||||
Client client = parent.getClient();
|
||||
|
||||
if (!msg_.equals("")) {
|
||||
this.previousStatus = msg_;
|
||||
}
|
||||
|
||||
if (client != null && client.isSuspended()) {
|
||||
if (overwriteSuspendedMsg) {
|
||||
statusContent.setText("<html>" + msg_ + "</html>");
|
||||
}
|
||||
}
|
||||
else {
|
||||
statusContent.setText("<html>" + msg_ + "</html>");
|
||||
}
|
||||
}
|
||||
|
||||
public void setRenderingProjectName(String msg_) {
|
||||
current_project_name_value.setText("<html>" + (msg_.length() > 26 ? msg_.substring(0, 26) : msg_) + "</html>");
|
||||
}
|
||||
|
||||
public void setRemainingTime(String time_) {
|
||||
currrent_project_progression_value.setText("<html>" + time_ + "</html>");
|
||||
}
|
||||
|
||||
public void setRenderingTime(String time_) {
|
||||
current_project_duration_value.setText("<html>" + time_ + "</html>");
|
||||
}
|
||||
|
||||
public void setComputeMethod(String computeMethod_) {
|
||||
this.current_project_compute_method_value.setText(computeMethod_);
|
||||
}
|
||||
|
||||
public void displayTransferStats(TransferStats downloads, TransferStats uploads) {
|
||||
sessionDownloadsStatsValue.setText(String.format("%s @ %s/s", downloads.getSessionTraffic(), downloads.getAverageSessionSpeed()));
|
||||
sessionUploadsStatsValue.setText(String.format("%s @ %s/s", uploads.getSessionTraffic(), uploads.getAverageSessionSpeed()));
|
||||
updateTime();
|
||||
}
|
||||
|
||||
public void displayStats(Stats stats) {
|
||||
DecimalFormat df = new DecimalFormat("##,##,##,##,##,##,##0");
|
||||
remainingFrameContent.setText(df.format(stats.getRemainingFrame()));
|
||||
creditEarned.setText(df.format(stats.getCreditsEarnedDuringSession()));
|
||||
user_info_points_total_value.setText(df.format(stats.getCreditsEarned()));
|
||||
renderable_projects_value.setText(df.format(stats.getRenderableProject()));
|
||||
waiting_projects_value.setText(df.format(stats.getWaitingProject()));
|
||||
connected_machines_value.setText(df.format(stats.getConnectedMachine()));
|
||||
|
||||
updateTime();
|
||||
}
|
||||
|
||||
public void displayUploadQueueStats(int queueSize, long queueVolume) {
|
||||
userInfoQueuedUploadsAndSizeValue.setText(
|
||||
String.format("%d%s%s", queueSize, (queueSize > 0 ? String.format(" (%.2fMB) ", (queueVolume / 1024.0 / 1024.0)) : ""),
|
||||
(queueSize == this.parent.getConfiguration().getMaxUploadingJob() ? "- Queue full!" : "")));
|
||||
|
||||
// If the user has requested to exit, then we need to update the JButton with the queue size
|
||||
if (this.exitAfterFrame.getText().startsWith("Cancel")) {
|
||||
Client client = parent.getClient();
|
||||
|
||||
if (client != null) {
|
||||
if (client.isRunning()) {
|
||||
queueSize++;
|
||||
}
|
||||
}
|
||||
|
||||
exitAfterFrame.setText(String.format("Cancel exit (%s frame%s to go)", queueSize, (queueSize > 1 ? "s" : "")));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateTime() {
|
||||
if (this.parent.getClient().getStartTime() != 0) {
|
||||
user_info_total_rendertime_this_session_value
|
||||
.setText(Utils.humanDuration(new Date((new Date().getTime() - this.parent.getClient().getStartTime()))));
|
||||
}
|
||||
Job job = this.parent.getClient().getRenderingJob();
|
||||
if (job != null && job.getProcessRender() != null && job.getProcessRender().getStartTime() > 0) {
|
||||
current_project_duration_value
|
||||
.setText("<html>" + Utils.humanDuration(new Date((new Date().getTime() - job.getProcessRender().getStartTime()))) + "</html>");
|
||||
}
|
||||
else {
|
||||
current_project_duration_value.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
public void setRenderedFrame(int n) {
|
||||
renderedFrameContent.setText(String.valueOf(n));
|
||||
showLastRender();
|
||||
}
|
||||
|
||||
public void showLastRender() {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
Job lastJob = client.getPreviousJob();
|
||||
if (lastJob != null) {
|
||||
ImageIcon icon = null;
|
||||
int idInt = Integer.parseInt(lastJob.getId());
|
||||
if (idInt == 1) {
|
||||
icon = new ImageIcon(getClass().getResource("/frame_compute_method.jpg"));
|
||||
}
|
||||
else if (idInt < 20) {
|
||||
icon = new ImageIcon(getClass().getResource("/frame_power_detection.jpg"));
|
||||
}
|
||||
else {
|
||||
try {
|
||||
String path = lastJob.getOutputImagePath();
|
||||
|
||||
BufferedImage img = ImageIO.read(new File(path));
|
||||
float width = img.getWidth();
|
||||
float height = img.getHeight();
|
||||
float factor = 1.0f;
|
||||
if (height > 200) {
|
||||
factor = 200f / height;
|
||||
}
|
||||
if (width * factor > 200) {
|
||||
factor = Math.min(factor, 200f / width);
|
||||
}
|
||||
icon = new ImageIcon(img.getScaledInstance((int) (width * factor), (int) (height * factor), Image.SCALE_FAST));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(String.format("Working::showLastRender() Unable to load/preview rendered frame [%s]. Exception %s",
|
||||
lastJob.getOutputImagePath(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (icon != null) {
|
||||
lastRender.setIcon(icon);
|
||||
// don't use lastJob.getProcessRender().getDuration() due to timezone
|
||||
if (lastJob.getProcessRender().getDuration() > 1) {
|
||||
lastRenderTime.setText("Render time : " + Utils
|
||||
.humanDuration(new Date(lastJob.getProcessRender().getEndTime() - lastJob.getProcessRender().getStartTime())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void alignPanel(Container parent, int rows, int cols, Spring width) {
|
||||
SpringLayout layout;
|
||||
try {
|
||||
layout = (SpringLayout) parent.getLayout();
|
||||
}
|
||||
catch (ClassCastException exc) {
|
||||
System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
|
||||
return;
|
||||
}
|
||||
|
||||
Spring x = Spring.constant(0);
|
||||
for (int c = 0; c < cols; c++) {
|
||||
for (int r = 0; r < rows; r++) {
|
||||
SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols);
|
||||
constraints.setX(x);
|
||||
constraints.setWidth(width);
|
||||
}
|
||||
x = Spring.sum(x, width);
|
||||
}
|
||||
|
||||
Spring y = Spring.constant(0);
|
||||
for (int r = 0; r < rows; r++) {
|
||||
Spring height = Spring.constant(0);
|
||||
for (int c = 0; c < cols; c++) {
|
||||
height = Spring.max(height, getConstraintsForCell(r, c, parent, cols).getHeight());
|
||||
}
|
||||
for (int c = 0; c < cols; c++) {
|
||||
SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols);
|
||||
constraints.setY(y);
|
||||
constraints.setHeight(height);
|
||||
}
|
||||
y = Spring.sum(y, height);
|
||||
}
|
||||
|
||||
SpringLayout.Constraints pCons = layout.getConstraints(parent);
|
||||
pCons.setConstraint(SpringLayout.SOUTH, y);
|
||||
pCons.setConstraint(SpringLayout.EAST, x);
|
||||
|
||||
}
|
||||
|
||||
private Spring getBestWidth(Container parent, int rows, int cols) {
|
||||
Spring x = Spring.constant(0);
|
||||
Spring width = Spring.constant(0);
|
||||
for (int c = 0; c < cols; c++) {
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
width = Spring.max(width, getConstraintsForCell(r, c, parent, cols).getWidth());
|
||||
}
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
private SpringLayout.Constraints getConstraintsForCell(int row, int col, Container parent, int cols) {
|
||||
SpringLayout layout = (SpringLayout) parent.getLayout();
|
||||
Component c = parent.getComponent(row * cols + col);
|
||||
return layout.getConstraints(c);
|
||||
}
|
||||
|
||||
class PauseAction implements ActionListener {
|
||||
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
if (client.isSuspended()) {
|
||||
pauseButton.setText("Pause getting new jobs");
|
||||
client.resume();
|
||||
setStatus(previousStatus);
|
||||
}
|
||||
else {
|
||||
pauseButton.setText("Resume getting jobs");
|
||||
client.suspend();
|
||||
setStatus("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
if (parent != null) {
|
||||
parent.showActivity(ActivityType.SETTINGS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExitAfterAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
if (client.isRunning()) {
|
||||
String[] exitJobOptions = { "Exit after current Jobs", "Exit Immediately", "Do Nothing" };
|
||||
int jobsQueueSize = client.getUploadQueueSize() + (client.isRunning() ? 1 : 0);
|
||||
|
||||
int userDecision = JOptionPane.showOptionDialog(null, String.format(
|
||||
"<html>You have <strong>%d frame%s</strong> being uploaded or rendered. Do you want to finish the jobs or exit now?.\n\n",
|
||||
jobsQueueSize, // Add the current frame to the total count ONLY if the client is running
|
||||
(jobsQueueSize > 1 ? "s" : ""), (jobsQueueSize > 1 ? (jobsQueueSize + " ") : ""), (jobsQueueSize > 1 ? "s" : "")),
|
||||
"Exit Now or Later", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, exitJobOptions,
|
||||
exitJobOptions[2]); // Make the "Do nothing" button the default one to avoid mistakes
|
||||
|
||||
if (userDecision == 0) {
|
||||
exitAfterFrame.setText(String.format("Cancel exit (%s frame%s to go)", jobsQueueSize, (jobsQueueSize > 1 ? "s" : "")));
|
||||
|
||||
client.askForStop();
|
||||
}
|
||||
else if (userDecision == 1) {
|
||||
client.stop();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
exitAfterFrame.setText("Exit");
|
||||
client.cancelStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class blockJobAction implements ActionListener {
|
||||
@Override public void actionPerformed(ActionEvent e) {
|
||||
Client client = parent.getClient();
|
||||
if (client != null) {
|
||||
Job job = client.getRenderingJob();
|
||||
if (job != null) {
|
||||
job.block();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Laurent CLOUET
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
package com.sheepit.client.standalone.swing.components;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.LayoutManager;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.BorderFactory;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.TitledBorder;
|
||||
|
||||
import com.sheepit.client.standalone.swing.activity.Activity;
|
||||
|
||||
public class CollapsibleJPanel extends JPanel {
|
||||
|
||||
private boolean isCompnentsVisible = true;
|
||||
private int originalHeight;
|
||||
private String borderTitle = "";
|
||||
private int COLLAPSED_HEIGHT = 22;
|
||||
private boolean[] originalVisibilty;
|
||||
private Activity parent;
|
||||
|
||||
public CollapsibleJPanel(LayoutManager layoutManager, Activity parent) {
|
||||
setLayout(layoutManager);
|
||||
addMouseListener(new onClickHandler());
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setCollapsed(boolean aFlag) {
|
||||
if (aFlag) {
|
||||
hideComponents();
|
||||
}
|
||||
else {
|
||||
showComponents();
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleCollapsed() {
|
||||
if (isCompnentsVisible) {
|
||||
setCollapsed(true);
|
||||
}
|
||||
else {
|
||||
setCollapsed(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideComponents() {
|
||||
|
||||
Component[] components = getComponents();
|
||||
|
||||
originalVisibilty = new boolean[components.length];
|
||||
|
||||
// Hide all componens on panel
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
originalVisibilty[i] = components[i].isVisible();
|
||||
components[i].setVisible(false);
|
||||
}
|
||||
|
||||
setHeight(COLLAPSED_HEIGHT);
|
||||
|
||||
// Add '+' char to end of border title
|
||||
//setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), borderTitle + " + "));
|
||||
setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), " + " + borderTitle));
|
||||
|
||||
// Update flag
|
||||
isCompnentsVisible = false;
|
||||
}
|
||||
|
||||
private void showComponents() {
|
||||
|
||||
Component[] components = getComponents();
|
||||
|
||||
// Set all components in panel to visible
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
components[i].setVisible(originalVisibilty[i]);
|
||||
}
|
||||
|
||||
setHeight(originalHeight);
|
||||
|
||||
// Add '-' char to end of border title
|
||||
setBorder(BorderFactory.createTitledBorder(" - " + borderTitle));
|
||||
|
||||
// Update flag
|
||||
isCompnentsVisible = true;
|
||||
}
|
||||
|
||||
private void setHeight(int height) {
|
||||
setPreferredSize(new Dimension(getPreferredSize().width, height));
|
||||
setMinimumSize(new Dimension(getMinimumSize().width, height));
|
||||
setMaximumSize(new Dimension(getMaximumSize().width, height));
|
||||
}
|
||||
|
||||
@Override public Component add(Component component) { // Need this to get the original height of panel
|
||||
|
||||
Component returnComponent = super.add(component);
|
||||
|
||||
originalHeight = getPreferredSize().height;
|
||||
|
||||
return returnComponent;
|
||||
}
|
||||
|
||||
@Override public void setBorder(Border border) { // Need this to get the border title
|
||||
|
||||
if (border instanceof TitledBorder && (borderTitle == "")) {
|
||||
borderTitle = ((TitledBorder) border).getTitle();
|
||||
|
||||
((TitledBorder) border).setTitle(" - " + borderTitle);
|
||||
}
|
||||
|
||||
super.setBorder(border);
|
||||
}
|
||||
|
||||
public class onClickHandler implements MouseListener {
|
||||
|
||||
@Override public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override public void mousePressed(MouseEvent e) {
|
||||
if (e.getPoint().y < COLLAPSED_HEIGHT) { // Only if click is on top of panel
|
||||
((CollapsibleJPanel) e.getComponent()).toggleCollapsed();
|
||||
}
|
||||
|
||||
// Recalculate the proper window size
|
||||
parent.resizeWindow();
|
||||
}
|
||||
|
||||
@Override public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Laurent CLOUET
|
||||
* Author Rolf Aretz Lap <rolf.aretz@ottogroup.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone.text;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
import com.sheepit.client.Configuration;
|
||||
import com.sheepit.client.Job;
|
||||
|
||||
public class CLIInputActionHandler implements CLIInputListener {
|
||||
|
||||
@Override public void commandEntered(Client client, String command) {
|
||||
int priorityLength = "priority".length();
|
||||
|
||||
//prevent Null Pointer at next step
|
||||
if (command == null) {
|
||||
return;
|
||||
}
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
if (command.equalsIgnoreCase("block")) {
|
||||
Job job = client.getRenderingJob();
|
||||
if (job != null) {
|
||||
job.block();
|
||||
}
|
||||
}
|
||||
else if (command.equalsIgnoreCase("resume")) {
|
||||
client.resume();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("pause")) {
|
||||
client.suspend();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("stop")) {
|
||||
client.askForStop();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("status")) {
|
||||
displayStatus(client);
|
||||
}
|
||||
else if (command.equalsIgnoreCase("cancel")) {
|
||||
client.cancelStop();
|
||||
}
|
||||
else if (command.equalsIgnoreCase("quit")) {
|
||||
client.stop();
|
||||
System.exit(0);
|
||||
}
|
||||
else if ((command.length() > priorityLength) && (command.substring(0, priorityLength).equalsIgnoreCase("priority"))) {
|
||||
changePriority(client, command.substring(priorityLength));
|
||||
}
|
||||
else {
|
||||
System.out.println("Unknown command: " + command);
|
||||
System.out.println("status: display client status");
|
||||
System.out.println("priority <n>: set the priority for the next renderjob");
|
||||
System.out.println("block: block project");
|
||||
System.out.println("pause: pause client requesting new jobs");
|
||||
System.out.println("resume: resume after client was paused");
|
||||
System.out.println("stop: exit after frame was finished");
|
||||
System.out.println("cancel: cancel exit");
|
||||
System.out.println("quit: exit now");
|
||||
}
|
||||
}
|
||||
|
||||
void changePriority(Client client, String newPriority) {
|
||||
Configuration config = client.getConfiguration();
|
||||
if (config != null) {
|
||||
try {
|
||||
config.setUsePriority(Integer.parseInt(newPriority.trim()));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
System.out.println("Invalid priority: " + newPriority);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void displayStatus(Client client) {
|
||||
if (client.isSuspended()) {
|
||||
System.out.println("Status: paused");
|
||||
}
|
||||
else if (client.isRunning()) {
|
||||
System.out.println("Status: running");
|
||||
}
|
||||
else {
|
||||
System.out.println("Status: will exit after the current frame");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Laurent CLOUET
|
||||
* Author Rolf Aretz Lap <rolf.aretz@ottogroup.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone.text;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
|
||||
public interface CLIInputListener {
|
||||
public void commandEntered(Client client, String command);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Laurent CLOUET
|
||||
* Author Rolf Aretz Lap <rolf.aretz@ottogroup.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package com.sheepit.client.standalone.text;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.sheepit.client.Client;
|
||||
|
||||
public class CLIInputObserver implements Runnable {
|
||||
private BufferedReader in;
|
||||
private Client client;
|
||||
|
||||
public CLIInputObserver(Client client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
private List<CLIInputListener> listeners = new ArrayList<CLIInputListener>();
|
||||
|
||||
public void addListener(CLIInputListener toAdd) {
|
||||
listeners.add(toAdd);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
in = new BufferedReader(new InputStreamReader(System.in));
|
||||
String line = "";
|
||||
|
||||
while ((line != null) && (line.equalsIgnoreCase("quit") == false)) {
|
||||
try {
|
||||
line = in.readLine();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// if the program has been launched into the background (e.g. with nohup), input is not valid
|
||||
client.getLog().info("Unable to read user input, ignoring all further inputs");
|
||||
break;
|
||||
}
|
||||
for (CLIInputListener cliil : listeners)
|
||||
cliil.commandEntered(client, line);
|
||||
}
|
||||
try {
|
||||
in.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
client.getLog().error("FIXME: Unhandled exception while closing InputStreamReader(): " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user