summaryrefslogtreecommitdiffstats
path: root/labb8/lib/StanfordCPPLib/platform.cpp
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2020-12-03 17:11:43 +0100
committerGustav Sörnäs <gustav@sornas.net>2020-12-08 10:21:07 +0100
commit0c39051ba80f04b1177833a006f2d442a7170b56 (patch)
tree9e657946a061b5b305f9cf75634db7b37e979eb3 /labb8/lib/StanfordCPPLib/platform.cpp
parent7b7f6808a7b2db2ed21103767434c1445f7815c2 (diff)
downloadtddd86-0c39051ba80f04b1177833a006f2d442a7170b56.tar.gz
add initial files l8
Diffstat (limited to 'labb8/lib/StanfordCPPLib/platform.cpp')
-rwxr-xr-xlabb8/lib/StanfordCPPLib/platform.cpp1417
1 files changed, 1417 insertions, 0 deletions
diff --git a/labb8/lib/StanfordCPPLib/platform.cpp b/labb8/lib/StanfordCPPLib/platform.cpp
new file mode 100755
index 0000000..d24140e
--- /dev/null
+++ b/labb8/lib/StanfordCPPLib/platform.cpp
@@ -0,0 +1,1417 @@
+/*
+ * File: platform.cpp
+ * ------------------
+ * This file implements the platform interface by passing commands to
+ * a Java back end that manages the display.
+ */
+
+#ifdef _WIN32
+# include <windows.h>
+# include <tchar.h>
+# undef MOUSE_EVENT
+# undef KEY_EVENT
+# undef MOUSE_MOVED
+# undef HELP_KEY
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <dirent.h>
+# include <errno.h>
+# include <pwd.h>
+# include <unistd.h>
+static bool tracePipe;
+static int pin;
+static int pout;
+#endif
+
+#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include "error.h"
+#include "filelib.h"
+#include "gevents.h"
+#include "gtimer.h"
+#include "gtypes.h"
+#include "hashmap.h"
+#include "queue.h"
+#include "platform.h"
+#include "stack.h"
+#include "strlib.h"
+#include "tokenscanner.h"
+#include "vector.h"
+
+using namespace std;
+
+static string getLineConsole();
+static void putConsole(const string & str);
+static void endLineConsole();
+static void echoConsole(const string & str);
+static void fileLogConsole(const string & str);
+static int scanInt(TokenScanner & scanner);
+static double scanDouble(TokenScanner & scanner);
+static GDimension scanDimension(const string & str);
+static GRectangle scanRectangle(const string & str);
+
+class ConsoleStreambuf : public streambuf {
+
+private:
+
+/* Constants */
+
+ static const int BUFFER_SIZE = 1024;
+
+/* Instance variables */
+
+ char inBuffer[BUFFER_SIZE];
+ char outBuffer[BUFFER_SIZE];
+
+public:
+
+ ConsoleStreambuf() {
+ setg(inBuffer, inBuffer, inBuffer);
+ setp(outBuffer, outBuffer + BUFFER_SIZE);
+ }
+
+ ~ConsoleStreambuf() {
+ /* Empty */
+ }
+
+ virtual int underflow() {
+ // Allow long strings at some point
+ string line = getLineConsole();
+ int n = line.length();
+ if (n + 1 >= BUFFER_SIZE) error("String too long");
+ for (int i = 0; i < n; i++) {
+ inBuffer[i] = line[i];
+ }
+ inBuffer[n++] = '\n';
+ inBuffer[n] = '\0';
+ setg(inBuffer, inBuffer, inBuffer + n);
+ return inBuffer[0];
+ }
+
+ virtual int overflow(int ch = EOF) {
+ string line = "";
+ for (char *cp = pbase(); cp < pptr(); cp++) {
+ if (*cp == '\n') {
+ putConsole(line);
+ endLineConsole();
+ line = "";
+ } else {
+ line += *cp;
+ }
+ }
+ if (line != "") {
+ putConsole(line);
+ }
+ setp(outBuffer, outBuffer + BUFFER_SIZE);
+ if (ch != EOF) {
+ outBuffer[0] = ch;
+ pbump(1);
+ }
+ return ch != EOF;
+ }
+
+ virtual int sync() {
+ return overflow();
+ }
+};
+
+/* Private data */
+
+static Queue<GEvent> eventQueue;
+static HashMap<string,GTimerData *> timerTable;
+static HashMap<string,GWindowData *> windowTable;
+static HashMap<string,GObject *> sourceTable;
+static HashMap<string,string> optionTable;
+static string programName;
+static ofstream logfile;
+static streambuf* cin_old_buf;
+static streambuf* cout_old_buf;
+static ConsoleStreambuf* cinout_new_buf;
+
+
+#ifdef _WIN32
+static HANDLE rdFromJBE = NULL;
+static HANDLE wrFromJBE = NULL;
+static HANDLE rdToJBE = NULL;
+static HANDLE wrToJBE = NULL;
+#endif
+
+/* Prototypes */
+
+static void initPipe();
+static void putPipe(string line);
+static string getPipe();
+static string getResult();
+static void getStatus();
+static GEvent parseEvent(string line);
+static GEvent parseMouseEvent(TokenScanner & scanner, EventType type);
+static GEvent parseKeyEvent(TokenScanner & scanner, EventType type);
+static GEvent parseTimerEvent(TokenScanner & scanner, EventType type);
+static GEvent parseWindowEvent(TokenScanner & scanner, EventType type);
+static GEvent parseActionEvent(TokenScanner & scanner, EventType type);
+
+/* Implementation of the Platform class */
+
+Platform::Platform() {
+ /* Empty */
+}
+
+Platform::~Platform() {
+ /* Empty */
+}
+
+/* Unix implementations of filelib.h primitives */
+
+#ifndef _WIN32
+
+bool Platform::fileExists(string filename) {
+ struct stat fileInfo;
+ return stat(filename.c_str(), &fileInfo) == 0;
+}
+
+bool Platform::isFile(string filename) {
+ struct stat fileInfo;
+ if (stat(filename.c_str(), &fileInfo) != 0) return false;
+ return S_ISREG(fileInfo.st_mode) != 0;
+}
+
+bool Platform::isSymbolicLink(string filename) {
+ struct stat fileInfo;
+ if (stat(filename.c_str(), &fileInfo) != 0) return false;
+ return S_ISLNK(fileInfo.st_mode) != 0;
+}
+
+bool Platform::isDirectory(string filename) {
+ struct stat fileInfo;
+ if (stat(filename.c_str(), &fileInfo) != 0) return false;
+ return S_ISDIR(fileInfo.st_mode) != 0;
+}
+
+void Platform::setCurrentDirectory(string path) {
+ if (chdir(path.c_str()) == 0) {
+ string msg = "setCurrentDirectory: ";
+ string err = string(strerror(errno));
+ error(msg + err);
+ }
+}
+
+string Platform::getCurrentDirectory() {
+ char *cwd = getcwd(NULL, 0);
+ if (cwd == NULL) {
+ string msg = "getCurrentDirectory: ";
+ string err = string(strerror(errno));
+ error(msg + err);
+ }
+ string result = string(cwd);
+ free(cwd);
+ return result;
+}
+
+void Platform::createDirectory(string path) {
+ if (endsWith(path, "/")) {
+ path = path.substr(0, path.length() - 2);
+ }
+ if (mkdir(path.c_str(), 0777) != 0) {
+ if (errno == EEXIST && isDirectory(path)) return;
+ string msg = "createDirectory: ";
+ string err = string(strerror(errno));
+ error(msg + err);
+ }
+}
+
+string Platform::getDirectoryPathSeparator() {
+ return "/";
+}
+
+string Platform::getSearchPathSeparator() {
+ return ":";
+}
+
+string Platform::expandPathname(string filename) {
+ if (filename == "") return "";
+ int len = filename.length();
+ if (filename[0] == '~') {
+ int spos = 1;
+ while (spos < len && filename[spos] != '\\' && filename[spos] != '/') {
+ spos++;
+ }
+ char *homedir = NULL;
+ if (spos == 1) {
+ homedir = getenv("HOME");
+ if (homedir == NULL) homedir = getpwuid(getuid())->pw_dir;
+ } else {
+ struct passwd *pw = getpwnam(filename.substr(1, spos - 1).c_str());
+ if (pw == NULL) error("expandPathname: No such user");
+ homedir = pw->pw_dir;
+ }
+ filename = string(homedir) + filename.substr(spos);
+ len = filename.length();
+ }
+ for (int i = 0; i < len; i++) {
+ if (filename[i] == '\\') filename[i] = '/';
+ }
+ return filename;
+}
+
+void Platform::listDirectory(string path, vector<string> & list) {
+ if (path == "") path = ".";
+ DIR *dir = opendir(path.c_str());
+ if (dir == NULL) error(string("listDirectory: Can't open ") + path);
+ list.clear();
+ while (true) {
+ struct dirent *ep = readdir(dir);
+ if (ep == NULL) break;
+ string name = string(ep->d_name);
+ if (name != "." && name != "..") list.push_back(name);
+ }
+ closedir(dir);
+ sort(list.begin(), list.end());
+}
+
+#else
+
+bool Platform::fileExists(string filename) {
+ return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
+}
+
+bool Platform::isFile(string filename) {
+ DWORD attr = GetFileAttributesA(filename.c_str());
+ return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_NORMAL);
+}
+
+bool Platform::isSymbolicLink(string filename) {
+ DWORD attr = GetFileAttributesA(filename.c_str());
+ return attr != INVALID_FILE_ATTRIBUTES
+ && (attr & FILE_ATTRIBUTE_REPARSE_POINT);
+}
+
+bool Platform::isDirectory(string filename) {
+ DWORD attr = GetFileAttributesA(filename.c_str());
+ return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY);
+}
+
+void Platform::setCurrentDirectory(string path) {
+ if (!isDirectory(path) || !SetCurrentDirectoryA(path.c_str())) {
+ error("setCurrentDirectory: Can't change to " + path);
+ }
+}
+
+string Platform::getCurrentDirectory() {
+ char path[MAX_PATH + 1];
+ int n = GetCurrentDirectoryA(MAX_PATH + 1, path);
+ return string(path, n);
+}
+
+void Platform::createDirectory(string path) {
+ if (!CreateDirectoryA(path.c_str(), NULL)) {
+ error("createDirectory: Can't create " + path);
+ }
+}
+
+string Platform::getDirectoryPathSeparator() {
+ return "\\";
+}
+
+string Platform::getSearchPathSeparator() {
+ return ";";
+}
+
+string Platform::expandPathname(string filename) {
+ if (filename == "") return "";
+ int len = filename.length();
+ for (int i = 0; i < len; i++) {
+ if (filename[i] == '/') filename[i] = '\\';
+ }
+ return filename;
+}
+
+void Platform::listDirectory(string path, vector<string> & list) {
+ if (path == "") path = ".";
+ string pattern = path + "\\*.*";
+ WIN32_FIND_DATAA fd;
+ HANDLE h = FindFirstFileA(pattern.c_str(), &fd);
+ if (h == INVALID_HANDLE_VALUE) {
+ error("listDirectory: Can't list directory");
+ }
+ list.clear();
+ while (true) {
+ string name = string(fd.cFileName);
+ if (name != "." && name != "..") list.push_back(name);
+ if (!FindNextFileA(h, &fd)) break;
+ }
+ FindClose(h);
+ sort(list.begin(), list.end());
+}
+
+#endif
+
+string Platform::openFileDialog(string title, string mode, string path) {
+ ostringstream os;
+ os << "File.openFileDialog(";
+ writeQuotedString(os, title);
+ os << ", \"" << mode << "\", \"" << path << "\")";
+ putPipe(os.str());
+ return getResult();
+}
+
+void Platform::createGWindow(const GWindow & gw, double width, double height,
+ GObject *topCompound) {
+ ostringstream os;
+ os << gw.gwd;
+ string id = os.str();
+ windowTable.put(id, gw.gwd);
+ os.str("");
+ os << "GWindow.create(\"" << id << "\", " << width << ", " << height
+ << ", \"" << topCompound << "\")";
+ putPipe(os.str());
+ getStatus();
+}
+
+void Platform::deleteGWindow(const GWindow & gw) {
+ ostringstream os;
+ os << gw.gwd;
+ string id = os.str();
+ windowTable.remove(id);
+ os.str("");
+ os << "GWindow.delete(\"" << gw.gwd << "\")";
+ putPipe(os.str());
+}
+
+void Platform::close(const GWindow & gw) {
+ ostringstream os;
+ os << "GWindow.close(\"" << gw.gwd << "\")";
+ putPipe(os.str());
+}
+
+void Platform::requestFocus(const GWindow & gw) {
+ ostringstream os;
+ os << "GWindow.requestFocus(\"" << gw.gwd << "\")";
+ putPipe(os.str());
+}
+
+void Platform::clear(const GWindow & gw) {
+ ostringstream os;
+ os << "GWindow.clear(\"" << gw.gwd << "\")";
+ putPipe(os.str());
+}
+
+void Platform::repaint(const GWindow & gw) {
+ ostringstream os;
+ os << "GWindow.repaint(\"" << gw.gwd << "\")";
+ putPipe(os.str());
+}
+
+void Platform::setVisible(const GWindow&, bool) {
+// ostringstream os;
+// os << boolalpha << "GWindow.setVisible(\"" << gw.gwd << "\", "
+// << flag << ")";
+// putPipe(os.str());
+}
+
+void Platform::setWindowTitle(const GWindow & gw, string title) {
+ ostringstream os;
+ os << "GWindow.setTitle(\"" << gw.gwd << "\", ";
+ writeQuotedString(os, title);
+ os << ")";
+ putPipe(os.str());
+}
+
+double Platform::getScreenWidth() {
+ putPipe("GWindow.getScreenWidth()");
+ return stringToReal(getResult());
+}
+
+double Platform::getScreenHeight() {
+ putPipe("GWindow.getScreenHeight()");
+ return stringToReal(getResult());
+}
+
+void Platform::pause(double milliseconds) {
+ ostringstream os;
+ os << "GTimer.pause(" << milliseconds << ")";
+ putPipe(os.str());
+ getStatus();
+}
+
+void Platform::createTimer(const GTimer & timer, double delay) {
+ ostringstream os;
+ os << timer.gtd;
+ string id = os.str();
+ timerTable.put(id, timer.gtd);
+ os.str("");
+ os << "GTimer.create(\"" << id << "\", " << delay << ")";
+ putPipe(os.str());
+}
+
+void Platform::deleteTimer(const GTimer & timer) {
+ ostringstream os;
+ os << timer.gtd;
+ string id = os.str();
+ timerTable.remove(id);
+ os.str("");
+ os << "GTimer.deleteTimer(\"" << id << "\")";
+ putPipe(os.str());
+}
+
+void Platform::startTimer(const GTimer & timer) {
+ ostringstream os;
+ os << "GTimer.startTimer(\"" << timer.gtd << "\")";
+ putPipe(os.str());
+}
+
+void Platform::stopTimer(const GTimer & timer) {
+ ostringstream os;
+ os << "GTimer.stopTimer(\"" << timer.gtd << "\")";
+ putPipe(os.str());
+}
+
+void Platform::createSound(Sound *sound, string filename) {
+ ostringstream os;
+ os << "Sound.create(\"" << sound << "\", ";
+ writeQuotedString(os, filename);
+ os << ")";
+ putPipe(os.str());
+ getStatus();
+}
+
+void Platform::deleteSound(Sound *sound) {
+ ostringstream os;
+ os << "Sound.delete(\"" << sound << "\")";
+ putPipe(os.str());
+}
+
+void Platform::playSound(Sound *sound) {
+ ostringstream os;
+ os << "Sound.play(\"" << sound << "\")";
+ putPipe(os.str());
+}
+
+void Platform::deleteGObject(GObject *gobj) {
+ ostringstream os;
+ os << "GObject.delete(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::add(GObject *compound, GObject *gobj) {
+ ostringstream os;
+ os << "GCompound.add(\"" << compound << "\", \"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::remove(GObject *gobj) {
+ ostringstream os;
+ os << "GObject.remove(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::setRegionAlignment(const GWindow & gw, string region,
+ string align) {
+ ostringstream os;
+ os << "GWindow.setRegionAlignment(\"" << gw.gwd << "\", \"" << region
+ << "\", \"" << align << "\")";
+ putPipe(os.str());
+}
+
+void Platform::addToRegion(const GWindow & gw, GObject *gobj, string region) {
+ ostringstream os;
+ os << "GWindow.addToRegion(\"" << gw.gwd << "\", \"" << gobj << "\", \""
+ << region << "\")";
+ putPipe(os.str());
+}
+
+void Platform::removeFromRegion(const GWindow & gw, GObject *gobj,
+ string region) {
+ ostringstream os;
+ os << "GWindow.removeFromRegion(\"" << gw.gwd << "\", \""
+ << gobj << "\", \"" << region << "\")";
+ putPipe(os.str());
+}
+
+void Platform::sendForward(GObject *gobj) {
+ ostringstream os;
+ os << "GObject.sendForward(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::sendToFront(GObject *gobj) {
+ ostringstream os;
+ os << "GObject.sendToFront(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::sendBackward(GObject *gobj) {
+ ostringstream os;
+ os << "GObject.sendBackward(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::sendToBack(GObject *gobj) {
+ ostringstream os;
+ os << "GObject.sendToBack(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::setVisible(GObject *gobj, bool flag) {
+ ostringstream os;
+ os << boolalpha << "GObject.setVisible(\"" << gobj << "\", " << flag << ")";
+ putPipe(os.str());
+}
+
+void Platform::setColor(GObject *gobj, string color) {
+ ostringstream os;
+ os << "GObject.setColor(\"" << gobj << "\", \"" << color << "\")";
+ putPipe(os.str());
+}
+
+void Platform::scale(GObject *gobj, double sx, double sy) {
+ ostringstream os;
+ os << "GObject.scale(\"" << gobj << "\", " << sx << ", " << sy << ")";
+ putPipe(os.str());
+}
+
+void Platform::rotate(GObject *gobj, double theta) {
+ ostringstream os;
+ os << "GObject.rotate(\"" << gobj << "\", " << theta << ")";
+ putPipe(os.str());
+}
+
+// Move this computation into gobjects.cpp
+
+bool Platform::contains(const GObject *gobj, double x, double y) {
+ ostringstream os;
+ os << "GObject.contains(\"" << gobj << "\", " << x << ", " << y << ")";
+ putPipe(os.str());
+ return getResult() == "true";
+}
+
+// Move this computation into gobjects.cpp
+
+GRectangle Platform::getBounds(const GObject *gobj) {
+ ostringstream os;
+ os << "GObject.getBounds(\"" << gobj << "\")";
+ putPipe(os.str());
+ string result = getResult();
+ if (!startsWith(result, "GRectangle(")) error(result);
+ return scanRectangle(result);
+}
+
+void Platform::setLineWidth(GObject *gobj, double lineWidth) {
+ ostringstream os;
+ os << "GObject.setLineWidth(\"" << gobj << "\", " << lineWidth << ")";
+ putPipe(os.str());
+}
+
+void Platform::setLocation(GObject *gobj, double x, double y) {
+ ostringstream os;
+ os << "GObject.setLocation(\"" << gobj << "\", " << x << ", " << y << ")";
+ putPipe(os.str());
+}
+
+void Platform::setSize(GObject *gobj, double width, double height) {
+ ostringstream os;
+ os << "GObject.setSize(\"" << gobj << "\", " << width << ", "
+ << height << ")";
+ putPipe(os.str());
+}
+
+void Platform::setFrameRectangle(GObject *gobj, double x, double y,
+ double width, double height) {
+ ostringstream os;
+ os << "GArc.setFrameRectangle(\"" << gobj << "\", "
+ << x << ", " << y << ", "
+ << width << ", " << height << ")";
+ putPipe(os.str());
+}
+
+void Platform::draw(const GWindow & gw, const GObject *gobj) {
+ ostringstream os;
+ os << "GWindow.draw(\"" << gw.gwd << "\", \"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::setFilled(GObject *gobj, bool flag) {
+ ostringstream os;
+ os << boolalpha << "GObject.setFilled(\"" << gobj << "\", " << flag << ")";
+ putPipe(os.str());
+}
+
+void Platform::setFillColor(GObject *gobj, string color) {
+ ostringstream os;
+ os << "GObject.setFillColor(\"" << gobj << "\", \"" << color << "\")";
+ putPipe(os.str());
+}
+
+void Platform::createGRect(GObject *gobj, double width, double height) {
+ ostringstream os;
+ os << "GRect.create(\"" << gobj << "\", " << width << ", "
+ << height << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGRoundRect(GObject *gobj, double width, double height,
+ double corner) {
+ ostringstream os;
+ os << "GRoundRect.create(\"" << gobj << "\", " << width << ", " << height
+ << ", " << corner << ")";
+ putPipe(os.str());
+}
+
+void Platform::createG3DRect(GObject *gobj, double width, double height,
+ bool raised) {
+ ostringstream os;
+ os << boolalpha << "G3DRect.create(\"" << gobj << "\", "
+ << width << ", " << height << ", " << raised << ")";
+ putPipe(os.str());
+}
+
+void Platform::setRaised(GObject *gobj, bool raised) {
+ ostringstream os;
+ os << boolalpha << "G3DRect.setRaised(\"" << gobj << "\", "
+ << raised << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGLabel(GObject *gobj, string label) {
+ ostringstream os;
+ // *** BUGBUG: must escape quotation marks in label string (Marty)
+ os << "GLabel.create(\"" << gobj << "\", ";
+ writeQuotedString(os, label);
+ os << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGLine(GObject *gobj, double x1, double y1,
+ double x2, double y2) {
+ ostringstream os;
+ os << "GLine.create(\"" << gobj << "\", " << x1 << ", " << y1
+ << ", " << x2 << ", " << y2 << ")";
+ putPipe(os.str());
+}
+
+void Platform::setStartPoint(GObject *gobj, double x, double y) {
+ ostringstream os;
+ os << "GLine.setStartPoint(\"" << gobj << "\", " << x << ", " << y << ")";
+ putPipe(os.str());
+}
+
+void Platform::setEndPoint(GObject *gobj, double x, double y) {
+ ostringstream os;
+ os << "GLine.setEndPoint(\"" << gobj << "\", " << x << ", " << y << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGArc(GObject *gobj, double width, double height,
+ double start, double sweep) {
+ ostringstream os;
+ os << "GArc.create(\"" << gobj << "\", " << width << ", " << height
+ << ", " << start << ", " << sweep << ")";
+ putPipe(os.str());
+}
+
+void Platform::setStartAngle(GObject *gobj, double angle) {
+ ostringstream os;
+ os << "GArc.setStartAngle(\"" << gobj << "\", " << angle << ")";
+ putPipe(os.str());
+}
+
+void Platform::setSweepAngle(GObject *gobj, double angle) {
+ ostringstream os;
+ os << "GArc.setSweepAngle(\"" << gobj << "\", " << angle << ")";
+ putPipe(os.str());
+}
+
+GDimension Platform::createGImage(GObject *gobj, string filename) {
+ ostringstream os;
+ os << "GImage.create(\"" << gobj << "\", \"" << filename << "\")";
+ putPipe(os.str());
+ string result = getResult();
+ if (!startsWith(result, "GDimension(")) error(result);
+ return scanDimension(result);
+}
+
+void Platform::createGPolygon(GObject *gobj) {
+ ostringstream os;
+ os << "GPolygon.create(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::addVertex(GObject *gobj, double x, double y) {
+ ostringstream os;
+ os << "GPolygon.addVertex(\"" << gobj << "\", " << x << ", " << y << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGOval(GObject *gobj, double width, double height) {
+ ostringstream os;
+ os << "GOval.create(\"" << gobj << "\", " << width << ", "
+ << height << ")";
+ putPipe(os.str());
+}
+
+void Platform::setActionCommand(GObject *gobj, string cmd) {
+ ostringstream os;
+ os << "GInteractor.setActionCommand(\"" << gobj << "\", ";
+ writeQuotedString(os, cmd);
+ os << ")";
+ putPipe(os.str());
+}
+
+GDimension Platform::getSize(GObject *gobj) {
+ ostringstream os;
+ os << "GInteractor.getSize(\"" << gobj << "\")";
+ putPipe(os.str());
+ return scanDimension(getResult());
+}
+
+void Platform::createGButton(GObject *gobj, string label) {
+ ostringstream os;
+ os << gobj;
+ sourceTable.put(os.str(), gobj);
+ os.str("");
+ os << "GButton.create(\"" << gobj << "\", ";
+ writeQuotedString(os, label);
+ os << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGCheckBox(GObject *gobj, string label) {
+ ostringstream os;
+ os << gobj;
+ sourceTable.put(os.str(), gobj);
+ os.str("");
+ os << "GCheckBox.create(\"" << gobj << "\", ";
+ writeQuotedString(os, label);
+ os << ")";
+ putPipe(os.str());
+}
+
+bool Platform::isSelected(GObject *gobj) {
+ ostringstream os;
+ os << "GCheckBox.isSelected(\"" << gobj << "\")";
+ putPipe(os.str());
+ return getResult() == "true";
+}
+
+void Platform::setSelected(GObject *gobj, bool state) {
+ ostringstream os;
+ os << boolalpha << "GCheckBox.setSelected(\"" << gobj << "\", "
+ << state << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGSlider(GObject *gobj, int min, int max, int value) {
+ ostringstream os;
+ os << gobj;
+ sourceTable.put(os.str(), gobj);
+ os.str("");
+ os << "GSlider.create(\"" << gobj << "\", " << min << ", " << max
+ << ", " << value << ")";
+ putPipe(os.str());
+}
+
+int Platform::getValue(GObject *gobj) {
+ ostringstream os;
+ os << "GSlider.getValue(\"" << gobj << "\")";
+ putPipe(os.str());
+ return stringToInteger(getResult());
+}
+
+void Platform::setValue(GObject *gobj, int value) {
+ ostringstream os;
+ os << "GSlider.setValue(\"" << gobj << "\", " << value << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGTextField(GObject *gobj, int nChars) {
+ ostringstream os;
+ os << gobj;
+ sourceTable.put(os.str(), gobj);
+ os.str("");
+ os << "GTextField.create(\"" << gobj << "\", " << nChars << ")";
+ putPipe(os.str());
+}
+
+string Platform::getText(GObject *gobj) {
+ ostringstream os;
+ os << "GTextField.getText(\"" << gobj << "\")";
+ putPipe(os.str());
+ return getResult();
+}
+
+void Platform::setText(GObject *gobj, string str) {
+ ostringstream os;
+ os << "GTextField.setText(\"" << gobj << "\", ";
+ writeQuotedString(os, str);
+ os << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGChooser(GObject *gobj) {
+ ostringstream os;
+ os << gobj;
+ sourceTable.put(os.str(), gobj);
+ os.str("");
+ os << "GChooser.create(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::addItem(GObject *gobj, string item) {
+ ostringstream os;
+ os << "GChooser.addItem(\"" << gobj << "\", ";
+ writeQuotedString(os, item);
+ os << ")";
+ putPipe(os.str());
+}
+
+string Platform::getSelectedItem(GObject *gobj) {
+ ostringstream os;
+ os << "GChooser.getSelectedItem(\"" << gobj << "\")";
+ putPipe(os.str());
+ return getResult();
+}
+
+void Platform::setSelectedItem(GObject *gobj, string item) {
+ ostringstream os;
+ os << "GChooser.setSelectedItem(\"" << gobj << "\", ";
+ writeQuotedString(os, item);
+ os << ")";
+ putPipe(os.str());
+}
+
+void Platform::createGCompound(GObject *gobj) {
+ ostringstream os;
+ os << "GCompound.create(\"" << gobj << "\")";
+ putPipe(os.str());
+}
+
+void Platform::setFont(GObject *gobj, string font) {
+ ostringstream os;
+ os << "GLabel.setFont(\"" << gobj << "\", \"" << font << "\")";
+ putPipe(os.str());
+}
+
+void Platform::setLabel(GObject *gobj, string str) {
+ ostringstream os;
+ os << "GLabel.setLabel(\"" << gobj << "\", ";
+ writeQuotedString(os, str);
+ os << ")";
+ putPipe(os.str());
+}
+
+double Platform::getFontAscent(const GObject *gobj) {
+ ostringstream os;
+ os << "GLabel.getFontAscent(\"" << gobj << "\")";
+ putPipe(os.str());
+ return stringToReal(getResult());
+}
+
+double Platform::getFontDescent(const GObject *gobj) {
+ ostringstream os;
+ os << "GLabel.getFontDescent(\"" << gobj << "\")";
+ putPipe(os.str());
+ return stringToReal(getResult());
+}
+
+GDimension Platform::getGLabelSize(const GObject *gobj) {
+ ostringstream os;
+ os << "GLabel.getGLabelSize(\"" << gobj << "\")";
+ putPipe(os.str());
+ return scanDimension(getResult());
+}
+
+GEvent Platform::getNextEvent(int mask) {
+ if (eventQueue.isEmpty()) {
+ putPipe("GEvent.getNextEvent(" + integerToString(mask) + ")");
+ getResult();
+ if (eventQueue.isEmpty()) return GEvent();
+ }
+ return eventQueue.dequeue();
+}
+
+GEvent Platform::waitForEvent(int mask) {
+ while (eventQueue.isEmpty()) {
+ putPipe("GEvent.waitForEvent(" + integerToString(mask) + ")");
+ getResult();
+ }
+ return eventQueue.dequeue();
+}
+
+void Platform::exitGraphics() {
+ putPipe("GWindow.exitGraphics()");
+ exit(0);
+}
+
+Platform *getPlatform() {
+ static Platform gp;
+ return &gp;
+}
+
+#ifdef _WIN32
+
+/* Windows implementation of interface to Java back end */
+
+int startupMain(int argc, char **argv) {
+ extern int Main(int argc, char **argv);
+ programName = getRoot(getTail(argv[0]));
+ initPipe();
+#ifndef PLAIN_TEXT_CONSOLE
+ cin_old_buf = cin.rdbuf();
+ cout_old_buf = cout.rdbuf();
+ cinout_new_buf = new ConsoleStreambuf();
+ cin.rdbuf(cinout_new_buf);
+ cout.rdbuf(cinout_new_buf);
+ ShowWindow(GetConsoleWindow(), SW_HIDE);
+#endif
+ return Main(argc, argv);
+}
+
+static void initPipe() {
+ SECURITY_ATTRIBUTES attr;
+ attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ attr.bInheritHandle = true;
+ attr.lpSecurityDescriptor = NULL;
+ if (!CreatePipe(&rdFromJBE, &wrFromJBE, &attr, 0)) {
+ error("Can't create fromJBE");
+ }
+ if (!SetHandleInformation(rdFromJBE, HANDLE_FLAG_INHERIT, 0)) {
+ error("SetHandleInformation failed for fromJBE");
+ }
+ if (!CreatePipe(&rdToJBE, &wrToJBE, &attr, 0)) {
+ error("Can't create toJBE");
+ }
+ if (!SetHandleInformation(wrToJBE, HANDLE_FLAG_INHERIT, 0)) {
+ error("SetHandleInformation failed for toJBE");
+ }
+ string cmd = "java -jar spl.jar " + programName;
+ int n = cmd.length();
+ char *cmdLine = new char[n + 1];
+ for (int i = 0; i < n; i++) {
+ cmdLine[i] = cmd[i];
+ }
+ cmdLine[n] = '\0';
+ PROCESS_INFORMATION pInfo;
+ memset(&pInfo, 0, sizeof(PROCESS_INFORMATION));
+ STARTUPINFOA sInfo;
+ memset(&sInfo, 0, sizeof(STARTUPINFOA));
+ sInfo.cb = sizeof(STARTUPINFOA);
+ sInfo.dwFlags = STARTF_USESTDHANDLES;
+ sInfo.hStdInput = rdToJBE;
+ sInfo.hStdOutput = wrFromJBE;
+ sInfo.hStdError = wrFromJBE;
+ int ok = CreateProcessA(NULL, cmdLine, NULL, NULL, true, CREATE_NO_WINDOW,
+ NULL, NULL, &sInfo, &pInfo);
+ if (!ok) {
+ DWORD err = GetLastError();
+ cerr << endl;
+ cerr << "ERROR: Stanford C++ library was unable to connect" << endl;
+ cerr << " to its Java back-end to launch 'spl.jar'." << endl;
+ cerr << " Please check your Java installation and make sure" << endl;
+ cerr << " that spl.jar is properly attached to your project." << endl;
+ cerr << endl;
+ cerr << err << endl;
+ } else {
+ CloseHandle(pInfo.hProcess);
+ CloseHandle(pInfo.hThread);
+ }
+}
+
+static void putPipe(string line) {
+ DWORD nch;
+ WriteFile(wrToJBE, line.c_str(), line.length(), &nch, NULL);
+ WriteFile(wrToJBE, "\n", 1, &nch, NULL);
+ FlushFileBuffers(wrToJBE);
+}
+
+static string getPipe() {
+ string line = "";
+ DWORD nch;
+ while (true) {
+ char ch;
+ ReadFile(rdFromJBE, &ch, 1, &nch, NULL);
+ if (ch == '\n' || ch == '\r') break;
+ line += ch;
+ }
+
+ // cerr << "getPipe(): \"" << line << "\"" << endl;
+ return line;
+}
+
+#else
+
+/* Linux/Mac implementation of interface to Java back end */
+
+static void scanOptions() {
+ char *home = getenv("HOME");
+ if (home != NULL) {
+ string filename = string() + home + "/.spl";
+ ifstream infile(filename.c_str());
+ if (!infile.fail()) {
+ string line;
+ while (getline(infile, line)) {
+ size_t equals = line.find('=');
+ if (equals != string::npos) {
+ string key = line.substr(0, equals);
+ string value = line.substr(equals + 1);
+ optionTable.put(key, value);
+ }
+ }
+ infile.close();
+ }
+ }
+}
+
+static string getOption(string key) {
+ char *str = getenv(key.c_str());
+ if (str != NULL) return string(str);
+ return optionTable.get(key);
+}
+
+int startupMain(int argc, char **argv) {
+ extern int Main(int argc, char **argv);
+ string arg0 = argv[0];
+ programName = getRoot(getTail(arg0));
+ size_t ax = arg0.find(".app/Contents/");
+ if (ax != string::npos) {
+ while (ax > 0 && arg0[ax] != '/') {
+ ax--;
+ }
+ if (ax > 0) {
+ string cwd = arg0.substr(0, ax);
+ chdir(cwd.c_str());
+ }
+ }
+ char *noConsoleFlag = getenv("NOCONSOLE");
+ if (noConsoleFlag != NULL && startsWith(string(noConsoleFlag), "t")) {
+ return Main(argc, argv);
+ }
+ scanOptions();
+ initPipe();
+#ifndef PLAIN_TEXT_CONSOLE
+ cin_old_buf = cin.rdbuf();
+ cout_old_buf = cout.rdbuf();
+ cinout_new_buf = new ConsoleStreambuf();
+ cin.rdbuf(cinout_new_buf);
+ cout.rdbuf(cinout_new_buf);
+ string font = getOption("CPPFONT");
+ if (font != "") setConsoleFont(font);
+#endif
+ return Main(argc, argv);
+}
+
+static void initPipe() {
+ char *trace = getenv("JBETRACE");
+ logfile.open("/dev/tty");
+ tracePipe = trace != NULL && startsWith(toLowerCase(trace), "t");
+ int toJBE[2], fromJBE[2];
+ pipe(toJBE);
+ pipe(fromJBE);
+ int child = fork();
+ if (child == 0) {
+ dup2(toJBE[0], 0);
+ close(toJBE[0]);
+ close(toJBE[1]);
+ dup2(fromJBE[1], 1);
+ close(fromJBE[0]);
+ close(fromJBE[1]);
+#ifdef __APPLE__
+ string option = "-Xdock:name=" + programName;
+ execlp("java", "java", option.c_str(), "-jar", "spl.jar",
+ programName.c_str(), NULL);
+#else
+ execlp("java", "java", "-jar", "spl.jar", programName.c_str(), NULL);
+#endif
+ cerr << endl;
+ cerr << "ERROR: Stanford C++ library was unable to connect" << endl;
+ cerr << " to its Java back-end to launch 'spl.jar'." << endl;
+ cerr << " Please check your Java installation and make sure" << endl;
+ cerr << " that spl.jar is properly attached to your project." << endl;
+ cerr << endl;
+ throw new ErrorException("Could not exec spl.jar");
+ } else {
+ pin = fromJBE[0];
+ pout = toJBE[1];
+ close(fromJBE[1]);
+ close(toJBE[0]);
+ }
+}
+
+static void putPipe(string line) {
+ write(pout, line.c_str(), line.length());
+ write(pout, "\n", 1);
+ if (tracePipe) logfile << "-> " << line << endl;
+}
+
+static string getPipe() {
+ string line = "";
+ while (true) {
+ char ch;
+ read(pin, &ch, 1);
+ if (ch == '\n') break;
+ line += ch;
+ }
+ if (tracePipe) logfile << "<- " << line << endl;
+ return line;
+}
+
+#endif
+
+static string getResult() {
+ while (true) {
+ string line = getPipe();
+ bool result = startsWith(line, "result:");
+ bool event = startsWith(line, "event:");
+ bool hasACMException = line.find("acm.util.ErrorException") != string::npos;
+ bool hasException = line.find("xception") != string::npos;
+
+ // *** added by Marty: if there is a back-end error, display it
+ if (((result || event) && hasACMException) || (!result && !event && hasException)) {
+ cerr << "ERROR emitted from Stanford Java back-end process:"
+ << endl << line << endl;
+ throw new ErrorException(line.substr(result ? 7 : 6));
+ } else if (startsWith(line, "result:")) {
+ return line.substr(7);
+ } else if (startsWith(line, "event:")) {
+ eventQueue.enqueue(parseEvent(line.substr(6)));
+ } else {
+ if (line.find("\tat") == 0) {
+ // part of a Java exception stack trace, so echo it
+ cerr << line << endl;
+ }
+ }
+ }
+}
+
+static void getStatus() {
+ string result = getResult();
+ if (result != "ok") error(result);
+}
+
+static GEvent parseEvent(string line) {
+ TokenScanner scanner(line);
+ scanner.ignoreWhitespace();
+ scanner.scanNumbers();
+ scanner.scanStrings();
+ string name = scanner.nextToken();
+ if (name == "mousePressed") {
+ return parseMouseEvent(scanner, MOUSE_PRESSED);
+ } else if (name == "mouseReleased") {
+ return parseMouseEvent(scanner, MOUSE_RELEASED);
+ } else if (name == "mouseClicked") {
+ return parseMouseEvent(scanner, MOUSE_CLICKED);
+ } else if (name == "mouseMoved") {
+ return parseMouseEvent(scanner, MOUSE_MOVED);
+ } else if (name == "mouseDragged") {
+ return parseMouseEvent(scanner, MOUSE_DRAGGED);
+ } else if (name == "keyPressed") {
+ return parseKeyEvent(scanner, KEY_PRESSED);
+ } else if (name == "keyReleased") {
+ return parseKeyEvent(scanner, KEY_RELEASED);
+ } else if (name == "keyTyped") {
+ return parseKeyEvent(scanner, KEY_TYPED);
+ } else if (name == "actionPerformed") {
+ return parseActionEvent(scanner, ACTION_PERFORMED);
+ } else if (name == "timerTicked") {
+ return parseTimerEvent(scanner, TIMER_TICKED);
+ } else if (name == "windowClosed") {
+ GWindowEvent e = parseWindowEvent(scanner, WINDOW_CLOSED);
+ e.getGWindow().setVisible(false);
+ e.getGWindow().close();
+ return e;
+ } else if (name == "windowResized") {
+ return parseWindowEvent(scanner, WINDOW_RESIZED);
+ } else if (name == "lastWindowClosed") {
+ exit(0);
+ } else if (name == "lastWindowGWindow_closed") {
+ exit(0);
+ } else {
+ /* Ignore for now */
+ }
+ return GEvent();
+}
+
+static GEvent parseMouseEvent(TokenScanner & scanner, EventType type) {
+ scanner.verifyToken("(");
+ string id = scanner.getStringValue(scanner.nextToken());
+ scanner.verifyToken(",");
+ double time = scanDouble(scanner);
+ scanner.verifyToken(",");
+ int modifiers = scanInt(scanner);
+ scanner.verifyToken(",");
+ double x = scanDouble(scanner);
+ scanner.verifyToken(",");
+ double y = scanDouble(scanner);
+ scanner.verifyToken(")");
+ GMouseEvent e(type, GWindow(windowTable.get(id)), x, y);
+ e.setEventTime(time);
+ e.setModifiers(modifiers);
+ return e;
+}
+
+static GEvent parseKeyEvent(TokenScanner & scanner, EventType type) {
+ scanner.verifyToken("(");
+ string id = scanner.getStringValue(scanner.nextToken());
+ scanner.verifyToken(",");
+ double time = scanDouble(scanner);
+ scanner.verifyToken(",");
+ int modifiers = scanInt(scanner);
+ scanner.verifyToken(",");
+ int keyChar = scanInt(scanner);
+ scanner.verifyToken(",");
+ int keyCode = scanInt(scanner);
+ scanner.verifyToken(")");
+ GKeyEvent e(type, GWindow(windowTable.get(id)), char(keyChar), keyCode);
+ e.setEventTime(time);
+ e.setModifiers(modifiers);
+ return e;
+}
+
+static GEvent parseTimerEvent(TokenScanner & scanner, EventType type) {
+ scanner.verifyToken("(");
+ string id = scanner.getStringValue(scanner.nextToken());
+ scanner.verifyToken(",");
+ double time = scanDouble(scanner);
+ scanner.verifyToken(")");
+ GTimerEvent e(type, GTimer(timerTable.get(id)));
+ e.setEventTime(time);
+ return e;
+}
+
+static GEvent parseWindowEvent(TokenScanner & scanner, EventType type) {
+ scanner.verifyToken("(");
+ string id = scanner.getStringValue(scanner.nextToken());
+ scanner.verifyToken(",");
+ double time = scanDouble(scanner);
+ scanner.verifyToken(")");
+ GWindowEvent e(type, GWindow(windowTable.get(id)));
+ e.setEventTime(time);
+ return e;
+}
+
+static GEvent parseActionEvent(TokenScanner & scanner, EventType type) {
+ scanner.verifyToken("(");
+ string id = scanner.getStringValue(scanner.nextToken());
+ scanner.verifyToken(",");
+ string action = scanner.getStringValue(scanner.nextToken());
+ scanner.verifyToken(",");
+ double time = scanDouble(scanner);
+ scanner.verifyToken(")");
+ GActionEvent e(type, sourceTable.get(id), action);
+ e.setEventTime(time);
+ return e;
+}
+
+/* Console code */
+
+void Platform::clearConsole() {
+ putPipe("JBEConsole.clear()");
+}
+
+void Platform::setConsoleFont(const string & font) {
+ ostringstream os;
+ os << "JBEConsole.setFont(\"" << font << "\")";
+ putPipe(os.str());
+}
+
+void Platform::setConsoleSize(double width, double height) {
+ ostringstream os;
+ os << "JBEConsole.setSize(" << width << ", " << height << ")";
+ putPipe(os.str());
+}
+
+static string getLineConsole() {
+ putPipe("JBEConsole.getLine()");
+ string result = getResult();
+ echoConsole(result + "\n"); // wrong for multiple inputs on one line
+ return result;
+}
+
+static void putConsole(const string & str) {
+ ostringstream os;
+ os << "JBEConsole.print(";
+ writeQuotedString(os, str);
+ os << ")";
+ putPipe(os.str());
+ echoConsole(str);
+}
+
+static void echoConsole(const string & str) {
+ if (getConsoleEcho()) {
+ // write to the standard cout console for output copy/pasting
+ cin.rdbuf(cin_old_buf);
+ cout.rdbuf(cout_old_buf);
+ cout << str;
+ cout.flush();
+ cin.rdbuf(cinout_new_buf);
+ cout.rdbuf(cinout_new_buf);
+ }
+ fileLogConsole(str);
+}
+
+static void fileLogConsole(const string & str) {
+ string consoleLogFile = getConsoleLogFile();
+ if (consoleLogFile.length() > 0) {
+ // A bit inefficient; opens/writes/closes the file on each print
+ // statement. But this enables fine-grained control and changing
+ // the log file in mid-execution with minimal code base change
+ ofstream outfile;
+ outfile.open(consoleLogFile.c_str(), fstream::out | fstream::app);
+ outfile << str;
+ outfile.flush();
+ outfile.close();
+ }
+}
+
+static void endLineConsole() {
+ putPipe("JBEConsole.println()");
+ echoConsole("\n");
+}
+
+static int scanInt(TokenScanner & scanner) {
+ string token = scanner.nextToken();
+ if (token == "-") token += scanner.nextToken();
+ return stringToInteger(token);
+}
+
+static double scanDouble(TokenScanner & scanner) {
+ string token = scanner.nextToken();
+ if (token == "-") token += scanner.nextToken();
+ return stringToReal(token);
+}
+
+static GDimension scanDimension(const string & str) {
+ TokenScanner scanner(str);
+ scanner.scanNumbers();
+ scanner.ignoreWhitespace();
+ scanner.verifyToken("GDimension");
+ scanner.verifyToken("(");
+ double width = scanDouble(scanner);
+ scanner.verifyToken(",");
+ double height = scanDouble(scanner);
+ scanner.verifyToken(")");
+ return GDimension(width, height);
+}
+
+static GRectangle scanRectangle(const string & str) {
+ TokenScanner scanner(str);
+ scanner.scanNumbers();
+ scanner.ignoreWhitespace();
+ scanner.verifyToken("GRectangle");
+ scanner.verifyToken("(");
+ double x = scanDouble(scanner);
+ scanner.verifyToken(",");
+ double y = scanDouble(scanner);
+ scanner.verifyToken(",");
+ double width = scanDouble(scanner);
+ scanner.verifyToken(",");
+ double height = scanDouble(scanner);
+ scanner.verifyToken(")");
+ return GRectangle(x, y, width, height);
+}