about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-06-15 13:58:08 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-06-15 13:58:08 -0700
commita5840f21020f6d568f82f84afe9fe99047a49f4a (patch)
tree72f8b28e8166268ee38174c2c7160804956297ad
parent7c0d16a79fba21e67d3b46f12f29042f84f997ba (diff)
downloadmu-a5840f21020f6d568f82f84afe9fe99047a49f4a.tar.gz
1566 - fake mouse clicks in scenarios
-rw-r--r--011load.cc3
-rw-r--r--041name.cc1
-rw-r--r--050scenario.cc2
-rw-r--r--078mouse.mu43
-rw-r--r--079scenario_events.cc149
5 files changed, 197 insertions, 1 deletions
diff --git a/011load.cc b/011load.cc
index 7d9c3047..158e4b10 100644
--- a/011load.cc
+++ b/011load.cc
@@ -17,6 +17,7 @@ vector<recipe_number> load(string form) {
 }
 
 vector<recipe_number> load(istream& in) {
+  in >> std::noskipws;
   vector<recipe_number> result;
   while (!in.eof()) {
 //?     cerr << "===\n"; //? 1
@@ -69,6 +70,7 @@ recipe slurp_recipe(istream& in) {
 }
 
 bool next_instruction(istream& in, instruction* curr) {
+  in >> std::noskipws;
   curr->clear();
   if (in.eof()) return false;
 //?   show_rest_of_stream(in); //? 1
@@ -82,6 +84,7 @@ bool next_instruction(istream& in, instruction* curr) {
     skip_whitespace(in);  if (in.eof()) return false;
 //?     show_rest_of_stream(in); //? 1
     string word = next_word(in);  if (in.eof()) return false;
+//?     cerr << "AAA: " << word << '\n'; //? 1
     words.push_back(word);
     skip_whitespace(in);  if (in.eof()) return false;
   }
diff --git a/041name.cc b/041name.cc
index dbbf8733..c3bd49fa 100644
--- a/041name.cc
+++ b/041name.cc
@@ -76,6 +76,7 @@ void check_metadata(map<string, vector<type_number> >& metadata, const reagent&
 }
 
 bool disqualified(/*mutable*/ reagent& x) {
+//?   cerr << x.to_string() << '\n'; //? 1
   if (x.types.empty())
     raise << "missing type in " << x.to_string() << '\n';
   assert(!x.types.empty());
diff --git a/050scenario.cc b/050scenario.cc
index ae35b8dd..1eacf58f 100644
--- a/050scenario.cc
+++ b/050scenario.cc
@@ -134,7 +134,7 @@ void run_mu_scenario(const scenario& s) {
     Trace_stream = new trace_stream;
     setup();
   }
-//?   cerr << '^' << s.to_run << "$\n"; //? 2
+//?   cerr << '^' << s.to_run << "$\n"; //? 3
   run("recipe "+s.name+" [ " + s.to_run + " ]");
   if (not_already_inside_test && Trace_stream) {
     teardown();
diff --git a/078mouse.mu b/078mouse.mu
new file mode 100644
index 00000000..2590131f
--- /dev/null
+++ b/078mouse.mu
@@ -0,0 +1,43 @@
+exclusive-container event [
+  # update the ASSUME_EVENTS handler if you add more variants
+  keyboard:keyboard-event
+  mouse:mouse-event
+]
+
+container keyboard-event [
+  key:character
+]
+
+container mouse-event [
+  type:character
+  row:number
+  column:number
+]
+
+container events [
+  index:number
+  data:address:array:event  
+]
+
+recipe read-event [
+  default-space:address:array:location <- new location:type, 30:literal
+  x:address:events <- next-ingredient
+  {
+    break-unless x:address:events
+    idx:address:number <- get-address x:address:events/deref, index:offset
+    buf:address:array:event <- get x:address:events/deref, data:offset
+    {
+      max:number <- length buf:address:array:event/deref
+      done?:boolean <- greater-or-equal idx:address:number/deref, max:number
+      break-unless done?:boolean
+      dummy:address:event <- new event:type
+      reply dummy:address:event/deref, x:address:events/same-as-ingredient:0
+    }
+    result:event <- index buf:address:array:event/deref, idx:address:number/deref
+    idx:address:number/deref <- add idx:address:number/deref, 1:literal
+    reply result:event, x:address:events/same-as-ingredient:0
+  }
+  # real event source
+  result:event <- read-keyboard-or-mouse-event
+  reply result:event
+]
diff --git a/079scenario_events.cc b/079scenario_events.cc
new file mode 100644
index 00000000..72a3b8cf
--- /dev/null
+++ b/079scenario_events.cc
@@ -0,0 +1,149 @@
+//: For testing both keyboard and mouse, use 'assume-events' rather than
+//: 'assume-keyboard'.
+//:
+//: This layer is tightly coupled with the definition of the 'event' type.
+
+:(scenarios run_mu_scenario)
+:(scenario events_in_scenario)
+scenario events-in-scenario [
+  assume-events [
+    type [abc]
+    left-click 0, 1
+    type [d]
+  ]
+  run [
+    # 3 keyboard events; each event occupies 4 locations
+#?     $start-tracing
+    1:event <- read-event events:address
+    5:event <- read-event events:address
+    9:event <- read-event events:address
+    # mouse click
+    13:event <- read-event events:address
+    # final keyboard event
+    17:event <- read-event events:address
+  ]
+  memory-should-contain [
+    1 <- 0  # type 'keyboard'
+    2 <- 97  # 'a'
+    3 <- 0  # unused
+    4 <- 0  # unused
+    5 <- 0  # type 'keyboard'
+    6 <- 98  # 'b'
+    7 <- 0  # unused
+    8 <- 0  # unused
+    9 <- 0  # type 'keyboard'
+    10 <- 99  # 'c'
+    11 <- 0  # unused
+    12 <- 0  # unused
+    13 <- 1  # type 'mouse'
+    14 <- 65513  # mouse click
+    15 <- 0  # row
+    16 <- 1  # column
+    17 <- 0  # type 'keyboard'
+    18 <- 100  # 'd'
+    19 <- 0  # unused
+    20 <- 0  # unused
+    21 <- 0
+  ]
+]
+
+// 'events' is a special variable like 'keyboard' and 'screen'
+:(before "End Scenario Globals")
+const long long int EVENTS = Next_predefined_global_for_scenarios++;
+:(before "End Predefined Scenario Locals In Run")
+Name[tmp_recipe.at(0)]["events"] = EVENTS;
+:(before "End is_special_name Cases")
+if (s == "events") return true;
+
+//: Unlike assume-keyboard, assume-events is easiest to implement as just a
+//: primitive recipe.
+:(before "End Primitive Recipe Declarations")
+ASSUME_EVENTS,
+:(before "End Primitive Recipe Numbers")
+Recipe_number["assume-events"] = ASSUME_EVENTS;
+:(before "End Primitive Recipe Implementations")
+case ASSUME_EVENTS: {
+//?   cerr << "aaa: " << current_instruction().ingredients.at(0).name << '\n'; //? 1
+  // create a temporary recipe just for parsing; it won't contain valid instructions
+  istringstream in("[" + current_instruction().ingredients.at(0).name + "]");
+  recipe r = slurp_recipe(in);
+  long long int num_events = count_events(r);
+//?   cerr << "fff: " << num_events << '\n'; //? 1
+  // initialize the events
+  long long int size = num_events*size_of_event() + /*space for length*/1;
+  ensure_space(size);
+  long long int event_data_address = Current_routine->alloc;
+  Memory[event_data_address] = num_events;
+  ++Current_routine->alloc;
+  for (long long int i = 0; i < SIZE(r.steps); ++i) {
+    const instruction& curr = r.steps.at(i);
+    if (curr.name == "left-click") {
+      Memory[Current_routine->alloc] = /*tag for 'mouse-event' variant of 'event' exclusive-container*/1;
+      Memory[Current_routine->alloc+1+/*offset of 'type' in 'mouse-event'*/0] = TB_KEY_MOUSE_LEFT;
+      Memory[Current_routine->alloc+1+/*offset of 'row' in 'mouse-event'*/1] = to_integer(curr.ingredients.at(0).name);
+      Memory[Current_routine->alloc+1+/*offset of 'column' in 'mouse-event'*/2] = to_integer(curr.ingredients.at(1).name);
+//?       cerr << "AA left click: " << Memory[Current_routine->alloc+2] << ' ' << Memory[Current_routine->alloc+3] << '\n'; //? 1
+      Current_routine->alloc += size_of_event();
+    }
+    // End Event Handlers
+    else {
+      // keyboard input
+      assert(curr.name == "type");
+      const string& contents = curr.ingredients.at(0).name;
+      const char* raw_contents = contents.c_str();
+      long long int num_keyboard_events = unicode_length(contents);
+      long long int curr = 0;
+      for (long long int i = 0; i < num_keyboard_events; ++i) {
+        Memory[Current_routine->alloc] = /*tag for 'keyboard-event' variant of 'event' exclusive-container*/0;
+        uint32_t curr_character;
+        assert(curr < SIZE(contents));
+        tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]);
+//?         cerr << "AA keyboard: " << curr_character << '\n'; //? 1
+        Memory[Current_routine->alloc+/*skip exclusive container tag*/1] = curr_character;
+        curr += tb_utf8_char_length(raw_contents[curr]);
+        Current_routine->alloc += size_of_event();
+      }
+    }
+  }
+  assert(Current_routine->alloc == event_data_address+size);
+  // wrap the array of events in an event object
+  ensure_space(size_of_events());
+  Memory[EVENTS] = Current_routine->alloc;
+  Current_routine->alloc += size_of_events();
+  Memory[Memory[EVENTS]+/*offset of 'data' in container 'events'*/1] = event_data_address;
+  break;
+}
+
+:(code)
+long long int count_events(const recipe& r) {
+  long long int result = 0;
+  for (long long int i = 0; i < SIZE(r.steps); ++i) {
+    const instruction& curr = r.steps.at(i);
+//?     cerr << curr.name << '\n'; //? 1
+    if (curr.name == "type")
+      result += unicode_length(curr.ingredients.at(0).name);
+    else
+      result++;
+  }
+  return result;
+}
+
+long long int size_of_event() {
+  // memoize result if already computed
+  static long long int result = 0;
+  if (result) return result;
+  vector<type_number> type;
+  type.push_back(Type_number["event"]);
+  result = size_of(type);
+  return result;
+}
+
+long long int size_of_events() {
+  // memoize result if already computed
+  static long long int result = 0;
+  if (result) return result;
+  vector<type_number> type;
+  type.push_back(Type_number["events"]);
+  result = size_of(type);
+  return result;
+}