KoMo2  1.0.0
A modern ARM emulator GUI.
main.cpp
Go to the documentation of this file.
1 
22 #include <glib.h>
23 #include <glibmm/optioncontext.h>
24 #include <gtkmm/application.h>
25 #include <libgen.h>
26 #include <sys/signal.h>
27 #include <unistd.h>
28 #include <array>
29 #include <fstream>
30 #include <iostream>
31 #include <regex>
32 #include <sstream>
33 #include <utility>
34 #include "../../libs/rapidjson/document.h"
35 #include "models/KoMo2Model.h"
36 #include "views/MainWindowView.h"
37 
38 // ! Forward function declarations
39 void initJimulator(const std::string argv0);
40 void initCompilerPipes(KoMo2Model* const mainModel);
41 const std::string getAbsolutePathToRootDirectory(const char* const arg);
42 const int initialiseCommandLine(
43  const Glib::RefPtr<Gio::ApplicationCommandLine>&,
44  const Glib::RefPtr<Gtk::Application>& app);
45 const bool receivedCompilerOutput(GIOChannel* source,
46  GIOCondition condition,
47  gpointer data);
48 void readProgramVariables(const std::string argv0);
49 
53 int emulator_PID = -1;
54 
55 // Communication pipes
59 // Defined as extern in jimulatorInterface.h
62 
66 std::string version = "1.0.0";
67 
71 std::string manual = "https://github.com/LawrenceWarren/KoMo2#user-manual";
72 
76 std::string help = "Please view the user manual (" + manual + ") for help.";
77 
81 int refresh = 200;
82 
86 void* model;
87 
94 int main(int argc, char* argv[]) {
95  auto argv0 = getAbsolutePathToRootDirectory(argv[0]);
96  auto app = Gtk::Application::create(argc, argv, "uon.cs.KoMo2",
97  Gio::APPLICATION_HANDLES_COMMAND_LINE);
98 
99  readProgramVariables(argv0);
100 
101  // Setup communication to Jimulator child process
102  initJimulator(argv0);
103 
104  // Setup command line argument recognition
105  app->signal_command_line().connect(
106  sigc::bind(sigc::ptr_fun(initialiseCommandLine), app), false);
107 
108  // Setup model & view
109  MainWindowView koMo2Window(400, 400);
110  KoMo2Model mainModel(&koMo2Window, argv0, manual, refresh);
111 
112  model = &mainModel;
113 
114  // Setup communication methods to compile child process
115  initCompilerPipes(&mainModel);
116 
117  // Run
118  auto exit = app->run(koMo2Window);
119  kill(emulator_PID, SIGKILL); // Kill Jimulator if main window is killed
120  return exit;
121 }
122 
128 void initCompilerPipes(KoMo2Model* mainModel) {
129  // Allows for printing compile output in the KoMo2 terminal
130  if (pipe(compilerCommunication)) {
131  std::cout << "A pipe error ocurred." << std::endl;
132  exit(1);
133  }
134 
135  // Sets up the function `receivedCompilerOutput` to fire whenever a change to
136  // the `compilerCommunication` pipe occurs. This in turn calls a function
137  // in the mainModel object.
138  auto fd = g_io_channel_unix_new(compilerCommunication[0]);
139  auto f = (GIOFunc)receivedCompilerOutput;
140  g_io_add_watch(fd, G_IO_IN, f, mainModel);
141 }
142 
152 void initJimulator(std::string argv0) {
153  // sets up the pipes to allow communication between Jimulator and
154  // KoMo2 processes.
156  std::cout << "A pipe error ocurred." << std::endl;
157  exit(1);
158  }
159 
162 
163  // Stores the emulator_PID for later.
164  emulator_PID = fork();
165 
166  // Jimulator process.
167  if (emulator_PID == 0) {
168  // Closes Jimulator stdout - Jimulator can write to this pipe using printf
169  close(1);
170  dup2(communicationFromJimulator[1], 1);
171 
172  // Closes Jimulator stdin - Jimulator can write to this pipe using scanf
173  close(0);
174  dup2(communicationToJimulator[0], 0);
175 
176  auto jimulatorPath = argv0.append("/bin/jimulator").c_str();
177  execlp(jimulatorPath, "", (char*)0);
178  // should never get here
179  _exit(1);
180  }
181 }
182 
188 const std::string getAbsolutePathToRootDirectory(const char* const arg) {
189  // Gets a path to this executable
190  auto buf = realpath(arg, NULL);
191  const std::string argv0(buf);
192  free(buf);
193 
194  return argv0.substr(0, argv0.size() - 7);
195 }
196 
201 void readProgramVariables(const std::string argv0) {
202  // Reading the file into a large string
203  std::string s;
204  std::ifstream variablesFile(argv0 + "/variables.json");
205  std::stringstream ss;
206  bool first = true;
207 
208  if (variablesFile.is_open()) {
209  while (getline(variablesFile, s)) {
210  if (first) {
211  first = false;
212  } else {
213  ss << '\n';
214  }
215 
216  ss << s;
217  }
218  variablesFile.close();
219  }
220 
221  // JSON parsing the string
222  rapidjson::Document d;
223  d.Parse(ss.str().c_str());
224 
225  if (not d.IsObject()) {
226  std::cout << "Can't read variables.json, using defaults." << std::endl;
227  return;
228  }
229 
230  if (d.HasMember("version")) {
231  version = std::string(d["version"].GetString());
232  }
233 
234  if (d.HasMember("manual")) {
235  manual = std::string(d["manual"].GetString());
236  }
237 
238  if (d.HasMember("help")) {
239  help = std::string(d["help"].GetString());
240  } else {
241  help = "Please view the user manual (" + manual + ") for help.";
242  }
243 
244  if (d.HasMember("refresh")) {
245  refresh = d["refresh"].GetInt();
246  }
247 }
248 
256 const int handleCommandLine(const bool isVersion,
257  const bool isHelp,
258  const bool isMnemonics) {
259  if (isMnemonics) {
260  static_cast<KoMo2Model*>(model)->getDisassemblyModel()->setEnglishMnemonic(
261  true);
262  }
263 
264  if (isVersion) {
265  std::cout << version << std::endl;
266  return 1;
267  }
268 
269  if (isHelp) {
270  std::cout << help << std::endl;
271  return 1;
272  }
273 
274  return 0;
275 }
276 
288  const Glib::RefPtr<Gio::ApplicationCommandLine>& cmd,
289  const Glib::RefPtr<Gtk::Application>& app) {
290  Glib::OptionGroup group("options", "main options");
291  bool isVersion = false, isHelp = false,
292  isMnemonics = false; // Booleans for options being toggled
293  Glib::OptionEntry version, help, mnemonics; // Option objects
294 
295  // Setting the version `-v` option
296  version.set_long_name("version");
297  version.set_short_name('v');
298  version.set_description("Display version information.");
299  group.add_entry(version, isVersion);
300 
301  // Setting the help `-h` option
302  help.set_long_name("help");
303  help.set_short_name('h');
304  help.set_description("Display a help message.");
305  group.add_entry(help, isHelp);
306 
307  mnemonics.set_long_name("english");
308  mnemonics.set_short_name('e');
309  mnemonics.set_description(
310  "Play ARM mnemonics in English if using a screenreader.");
311  group.add_entry(mnemonics, isMnemonics);
312 
313  Glib::OptionContext context;
314  context.add_group(group);
315 
316  // Parses command line arguments.
317  Glib::OptionGroup gtkgroup(gtk_get_option_group(true));
318  context.add_group(gtkgroup);
319  int argc;
320  char** argv = cmd->get_arguments(argc);
321  context.parse(argc, argv);
322 
323  int returnVal = handleCommandLine(isVersion, isHelp, isMnemonics);
324 
325  // If command line arguments were valid, activate app, else not.
326  if (not returnVal) {
327  app->activate();
328  }
329 
330  return returnVal;
331 }
332 
349 const bool receivedCompilerOutput(GIOChannel* source,
350  GIOCondition condition,
351  gpointer data) {
352  // data is always a pointer to the main window
353  auto p = static_cast<KoMo2Model*>(data);
354  char buff[128]; // An arbitrary size
355 
356  // The amount of bytes that were read
357  int x = read(compilerCommunication[0], buff, 128);
358 
359  // There was a pipe reading failure.
360  if (x < 0) {
361  std::cout << "Error reading from compiler communcation pipe." << std::endl;
362  return false;
363  }
364  // There is no more data to be read.
365  else if (x == 0) {
366  return false;
367  }
368 
369  buff[x] = '\0';
370  p->getTerminalModel()->appendTextToTextView(buff);
371  return true;
372 }
int communicationFromJimulator[2]
The pipe which will be used by KoMo2 to read from Jimulator (i.e. Jimulator will write to it...
Definition: main.cpp:56
int writeToJimulator
Stores the file descriptor used for writing to Jimulator.
Definition: main.cpp:60
std::string manual
Manual information read from variables.json is stored here.
Definition: main.cpp:71
void initCompilerPipes(KoMo2Model *const mainModel)
Initialises the communication pipes between the compiler process & the main KoMo2 GUI...
Definition: main.cpp:128
int readFromJimulator
Stores the file descriptor used for reading from Jimulator.
Definition: main.cpp:61
void readProgramVariables(const std::string argv0)
Reads the program variables from the "variables.json" file and populates global variables with the va...
Definition: main.cpp:201
int communicationToJimulator[2]
The pipe which will be used by KoMo2 to write to Jimulator (i.e. Jimulator will read from it...
Definition: main.cpp:57
const int initialiseCommandLine(const Glib::RefPtr< Gio::ApplicationCommandLine > &, const Glib::RefPtr< Gtk::Application > &app)
Sets up and parses custom command line arguments. To add more command line arguments, create a new boolean and a new Glib::OptionEntry object, and then add a longname, shortname, and description, then add it to the group. Then pass the boolean into the handleCommandLine function.
Definition: main.cpp:287
const bool receivedCompilerOutput(GIOChannel *source, GIOCondition condition, gpointer data)
This callback function is called when the compiler communication pipe receives a change in state (mea...
Definition: main.cpp:349
void initJimulator(const std::string argv0)
Initialises Jimulator in a separate child process.
Definition: main.cpp:152
std::string help
Help information read from variables.json is stored here.
Definition: main.cpp:76
The class definition of the main window of the program. This main window is the mater view of the pro...
int emulator_PID
This variable will store the PID of the Jimulator process.
Definition: main.cpp:53
sourceFile source
The source file that is currently loaded into Jimulator.
A file containing the definition of the KoMo2Model class.
int compilerCommunication[2]
The pipe that handles communication between the compiler process and KoMo2.
Definition: main.cpp:58
int refresh
Refresh rate information read from variables.json is stored here.
Definition: main.cpp:81
The logical model of the entire application. All other models should be member variables of this mode...
Definition: KoMo2Model.h:34
std::string version
Version information read from variables.json is stored here.
Definition: main.cpp:66
const int handleCommandLine(const bool isVersion, const bool isHelp, const bool isMnemonics)
Handles if a command line flag is set.
Definition: main.cpp:256
void * model
Used as a reference to the master KoMo2Model.
Definition: main.cpp:86
A class declaration for the MainWindowView class, which inherits from GTK::Window.
const std::string getAbsolutePathToRootDirectory(const char *const arg)
get the absolute path to the directory of the binary.
Definition: main.cpp:188
int main(int argc, char *argv[])
The program entry point.
Definition: main.cpp:94