2015-06-15 20:58:08 +00:00
|
|
|
//: 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;
|
|
|
|
}
|
2015-06-17 08:22:25 +00:00
|
|
|
|
|
|
|
//: Warn if a scenario uses both 'keyboard' and 'events'.
|
|
|
|
|
|
|
|
:(scenario recipes_should_not_use_events_alongside_keyboard)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
scenario recipes-should-not-use-events-alongside-keyboard [
|
|
|
|
assume-keyboard [abc]
|
|
|
|
assume-events []
|
|
|
|
]
|
|
|
|
+warn: can't use 'keyboard' and 'events' in the same program/scenario
|
|
|
|
|
|
|
|
:(before "End Globals")
|
|
|
|
bool Keyboard_used = false;
|
|
|
|
bool Events_used = false;
|
|
|
|
:(before "End Setup")
|
|
|
|
Keyboard_used = Events_used = false;
|
|
|
|
:(after "case ASSUME_EVENTS:")
|
|
|
|
//? cerr << "events!\n"; //? 1
|
|
|
|
Events_used = true;
|
|
|
|
:(before "Running One Instruction")
|
|
|
|
//? cerr << current_instruction().to_string() << '\n'; //? 1
|
|
|
|
for (long long int i = 0; i < SIZE(current_instruction().ingredients); ++i) {
|
|
|
|
if (current_instruction().ingredients.at(i).value == KEYBOARD) Keyboard_used = true;
|
|
|
|
if (current_instruction().ingredients.at(i).value == EVENTS) Events_used = true;
|
|
|
|
}
|
|
|
|
for (long long int i = 0; i < SIZE(current_instruction().products); ++i) {
|
|
|
|
if (current_instruction().products.at(i).value == KEYBOARD) Keyboard_used = true;
|
|
|
|
if (current_instruction().products.at(i).value == EVENTS) Events_used = true;
|
|
|
|
}
|
|
|
|
:(before "End of Instruction") // might miss some early returns like 'reply'
|
|
|
|
//? cerr << Keyboard_used << Events_used << '\n'; //? 1
|
|
|
|
if (Keyboard_used && Events_used)
|
|
|
|
raise << "can't use 'keyboard' and 'events' in the same program/scenario\n" << die();
|
|
|
|
|
|
|
|
:(scenario recipes_should_not_use_events_alongside_keyboard_including_nested_run)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
scenario recipes-should-not-use-events-alongside-keyboard-including-nested-run [
|
|
|
|
assume-events [
|
|
|
|
type [abc]
|
|
|
|
]
|
|
|
|
run [
|
|
|
|
keyboard:location <- copy 0:literal # unsafe
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+warn: can't use 'keyboard' and 'events' in the same program/scenario
|