Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/UserInput/keycommands.cpp')
-rw-r--r--Source/UserInput/keycommands.cpp406
1 files changed, 406 insertions, 0 deletions
diff --git a/Source/UserInput/keycommands.cpp b/Source/UserInput/keycommands.cpp
new file mode 100644
index 00000000..9a66ca82
--- /dev/null
+++ b/Source/UserInput/keycommands.cpp
@@ -0,0 +1,406 @@
+//-----------------------------------------------------------------------------
+// Name: keycommands.cpp
+// Developer: Wolfire Games LLC
+// Description:
+// License: Read below
+//-----------------------------------------------------------------------------
+//
+// Copyright 2022 Wolfire Games LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-----------------------------------------------------------------------------
+#include "keycommands.h"
+
+#include <Internal/error.h>
+#include <Internal/checksum.h>
+#include <Internal/common.h>
+
+#include <UserInput/keyboard.h>
+#include <UserInput/keyTranslator.h>
+
+#include <Logging/logdata.h>
+
+#include <SDL.h>
+
+#include <algorithm>
+
+using namespace KeyCommand;
+
+// Array of key commands, one for each label
+static Command commands[kNumLabels];
+static Command *commands_sorted_by_length[kNumLabels];
+typedef std::pair<unsigned long, int> HashInt;
+typedef std::vector<HashInt> HashIntVector;
+HashIntVector hash_to_command;
+
+struct HashIntCompare {
+ bool operator()(const HashInt& a, const HashInt& b){
+ return a.first < b.first;
+ }
+ bool operator()(const HashInt& a, unsigned long b){
+ return a.first < b;
+ }
+};
+
+Command::Command(): num_key_events(0), state(kUp)
+{
+ display_text[0] = '\0';
+}
+
+bool KeyCommand::CheckPressed( const Keyboard& keyboard, int label, const uint32_t kimf ) {
+ if( kimf & keyboard.GetModes() )
+ {
+ if(commands[label].num_key_events == 0){
+ return false;
+ } else {
+ return commands[label].state == kPressed;
+ }
+ }
+ else
+ {
+ return false;
+ }
+}
+
+#ifdef PLATFORM_MACOSX
+ const int command_key = Keyboard::GUI;
+ const SDL_Scancode command_SDL_SCANCODE_LEFT_key = SDL_SCANCODE_LGUI;
+ const SDL_Scancode command_SDL_SCANCODE_RIGHT_key = SDL_SCANCODE_RGUI;
+#else
+ const int command_key = Keyboard::CTRL;
+ const SDL_Scancode command_SDL_SCANCODE_LEFT_key = SDL_SCANCODE_LCTRL;
+ const SDL_Scancode command_SDL_SCANCODE_RIGHT_key = SDL_SCANCODE_RCTRL;
+#endif
+
+bool KeyCommand::CheckDown( const Keyboard& keyboard, int label, const uint32_t kimf ) {
+ if( kimf & keyboard.GetModes() )
+ {
+ if(commands[label].num_key_events == 0){
+ return false;
+ } else {
+ const Command &cmd = commands[label];
+ for(int i=0, len=cmd.num_key_events; i<len; ++i){
+ const KeyEvent &key_event = cmd.key_events[i];
+ bool key_valid = false;
+ switch(key_event.type_){
+ case kPressed:
+ case kDown:
+ if( key_event.sc_ == key_event.sc2_ ) {
+ key_valid = keyboard.isScancodeDown(key_event.sc_, KIMF_ANY);
+ } else {
+ key_valid = keyboard.isScancodeDown(key_event.sc_, KIMF_ANY) || keyboard.isScancodeDown(key_event.sc2_, KIMF_ANY);
+ }
+ break;
+ case kUp:
+ if( key_event.sc_ == key_event.sc2_ ) {
+ key_valid = !keyboard.isScancodeDown(key_event.sc_, KIMF_ANY);
+ } else {
+ key_valid = !keyboard.isScancodeDown(key_event.sc_, KIMF_ANY) && !keyboard.isScancodeDown(key_event.sc2_, KIMF_ANY);
+ }
+ break;
+ }
+ if(!key_valid){
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+}
+
+const char* KeyCommand::GetDisplayText(int label) {
+ return commands[label].display_text;
+}
+
+bool CompareSubstr(const char* str, const char* substr, int substr_len){
+ int i=0;
+ while(str[i] != '\0' && substr[i] != '\0' && i < substr_len){
+ if(str[i] != substr[i]){
+ return false;
+ }
+ ++i;
+ }
+ return str[i] == '\0' && i == substr_len;
+}
+
+void KeyCommand::BindString(const char* bind_str) {
+ KeyEvent events[kMaxKeyEvents];
+ enum ParseStage {kInputs, kCommand};
+ ParseStage parse_stage = kInputs;
+ int num_events = 0;
+ int i = 0;
+ int start_token = 0;
+ std::string str;
+ std::string display;
+ str.reserve(255);
+ display.reserve(255);
+ while(true){
+ char c = bind_str[i];
+ if(parse_stage == kInputs){
+ if(c == '+' || c == ':'){
+ if(c == ':'){
+ parse_stage = kCommand;
+ }
+ if(num_events >= kMaxKeyEvents){
+ FatalError("Error", "Too many key events in %s", bind_str);
+ }
+ // ignore white space at start or end
+ while(bind_str[start_token] == ' ' || bind_str[start_token] == '\t'){
+ ++start_token;
+ }
+ int end_token = i-1;
+ while(bind_str[end_token] == ' ' || bind_str[end_token] == '\t'){
+ --end_token;
+ }
+ // Read token and attempt to parse key name
+ int token_len = end_token-start_token+1;
+ if(CompareSubstr("cmd", &bind_str[start_token], token_len)){
+#ifdef __MACOSX__
+ display += "Cmd";
+#else
+ display += "Ctrl";
+#endif
+ events[num_events].sc_ = command_SDL_SCANCODE_LEFT_key;
+ events[num_events].sc2_ = command_SDL_SCANCODE_RIGHT_key;
+ events[num_events].type_ = kDown;
+ ++num_events;
+ } else if(CompareSubstr("shift", &bind_str[start_token], token_len)){
+ display += "Shift";
+ events[num_events].sc_ = SDL_SCANCODE_LSHIFT;
+ events[num_events].sc2_ = SDL_SCANCODE_RSHIFT;
+ events[num_events].type_ = kDown;
+ ++num_events;
+ } else if(CompareSubstr("alt", &bind_str[start_token], token_len)){
+#ifdef __MACOSX__
+ display += "Option";
+#else
+ display += "Alt";
+#endif
+ events[num_events].sc_ = SDL_SCANCODE_LALT;
+ events[num_events].sc2_ = SDL_SCANCODE_RALT;
+ events[num_events].type_ = kDown;
+ ++num_events;
+ } else if(CompareSubstr("ctrl", &bind_str[start_token], token_len)){
+ display += "Ctrl";
+ events[num_events].sc_ = SDL_SCANCODE_LCTRL;
+ events[num_events].sc2_ = SDL_SCANCODE_RCTRL;
+ events[num_events].type_ = kDown;
+ ++num_events;
+ } else {
+ str.clear();
+ for(int j=start_token; j<=end_token; ++j){
+ str += bind_str[j];
+ }
+ if(str.length() == 1 && str[0] >= 'a' && str[0] <= 'z'){
+ display += (str[0] + ('A' - 'a'));
+ } else {
+ display += str;
+ }
+ SDL_Scancode key = StringToSDLScancode(str);
+ if(key == SDL_SCANCODE_SYSREQ){
+ FatalError("Error", "Unknown key: %s", str.c_str());
+ } else {
+ events[num_events].sc_ = key;
+ events[num_events].sc2_ = key;
+ events[num_events].type_ = kPressed;
+ ++num_events;
+ }
+ }
+ if(c == '+'){
+ display = display + "+";
+ }
+ start_token = i+1;
+ }
+ } else if(parse_stage == kCommand) {
+ if(c == '\0'){
+ while(bind_str[start_token] == ' ' || bind_str[start_token] == '\t'){
+ ++start_token;
+ }
+ int end_token = i-1;
+ while(bind_str[end_token] == ' ' || bind_str[end_token] == '\t'){
+ --end_token;
+ }
+ str.clear();
+ for(int j=start_token; j<=end_token; ++j){
+ str += bind_str[j];
+ }
+ int id = -1;
+ unsigned long hash = djb2_string_hash(str.c_str());
+ HashIntVector::iterator iter = std::lower_bound(hash_to_command.begin(), hash_to_command.end(), hash, HashIntCompare());
+ if(iter->first != hash) {
+ LOGW << "Could not find " << str << " in hash map" << std::endl;
+ } else {
+ id = iter->second;
+ Command& kc = commands[id];
+ kc.num_key_events = num_events;
+ for(int j=0; j<num_events; ++j){
+ kc.key_events[j] = events[j];
+ }
+ FormatString(kc.display_text, KeyCommand::kDisplayTextBufSize, "%s", display.c_str());
+ }
+ }
+ }
+ if(c == '\0'){
+ break;
+ }
+ ++i;
+ }
+}
+
+void AddPairToHash(const char* cstr, int id){
+ hash_to_command.resize(hash_to_command.size()+1);
+ HashInt &new_pair = hash_to_command.back();
+ new_pair.first = djb2_string_hash(cstr);
+ new_pair.second = id;
+}
+
+int CommandLengthCompare (const void* a, const void* b){
+ return (*((Command**)b))->num_key_events - (*((Command**)a))->num_key_events;
+}
+
+void KeyCommand::FinalizeBindings() {
+ // Prepare list of commands sorted by length (so they can be evaluated from most-specific to least)
+ for(int i=0; i<kNumLabels; ++i){
+ commands_sorted_by_length[i] = &commands[i];
+ }
+ qsort(commands_sorted_by_length, kNumLabels, sizeof(commands_sorted_by_length[0]), CommandLengthCompare);
+}
+
+void KeyCommand::Initialize() {
+ // Create structure to map string hashes to command labels
+ hash_to_command.reserve(kNumLabels);
+ AddPairToHash("toggle_level_load_stress", kToggleLevelLoadStress);
+ AddPairToHash("pause",kPause);
+ AddPairToHash("refresh",kRefresh);
+ AddPairToHash("quit",kQuit);
+ AddPairToHash("back",kBack);
+ AddPairToHash("clone_transform",kCloneTransform);
+ AddPairToHash("snap_transform",kSnapTransform);
+ AddPairToHash("force_rotate",kForceRotate);
+ AddPairToHash("force_scale",kForceScale);
+ AddPairToHash("force_translate",kForceTranslate);
+ AddPairToHash("normal_transform",kNormalTransform);
+ AddPairToHash("edit_script_params",kEditScriptParams);
+ AddPairToHash("single_selected", kSingleSelected);
+ AddPairToHash("edit_color",kEditColor);
+ AddPairToHash("search_scenegraph",kSearchScenegraph);
+ AddPairToHash("scenegraph",kScenegraph);
+ AddPairToHash("toggle_player",kTogglePlayer);
+ AddPairToHash("toggle_decal_editing",kToggleDecalEditing);
+ AddPairToHash("toggle_object_editing",kToggleObjectEditing);
+ AddPairToHash("toggle_hotspot_editing",kToggleHotspotEditing);
+ AddPairToHash("open_spawner",kOpenSpawner);
+ AddPairToHash("save_selected_items",kSaveSelectedItems);
+ AddPairToHash("save_level",kSaveLevel);
+ AddPairToHash("save_level_as",kSaveLevelAs);
+ AddPairToHash("undo",kUndo);
+ AddPairToHash("redo",kRedo);
+ AddPairToHash("cut",kCut);
+ AddPairToHash("paste",kPaste);
+ AddPairToHash("copy",kCopy);
+ AddPairToHash("enable_imposter",kEnableImposter);
+ AddPairToHash("disable_imposter",kDisableImposter);
+ AddPairToHash("connect",kConnect);
+ AddPairToHash("disconnect",kDisconnect);
+ AddPairToHash("group",kGroup);
+ AddPairToHash("ungroup",kUngroup);
+ AddPairToHash("box_select",kBoxSelect);
+ AddPairToHash("deselect_all",kDeselectAll);
+ AddPairToHash("select_all",kSelectAll);
+ AddPairToHash("select_similar",kSelectSimilar);
+ AddPairToHash("add_to_selection",kAddToSelection);
+ AddPairToHash("bake_gi",kBakeGI);
+ AddPairToHash("test1",kTest1);
+ AddPairToHash("print_objects", kPrintObjects);
+ AddPairToHash("kill_selected", kKillSelected);
+ AddPairToHash("new_level", kNewLevel);
+ AddPairToHash("open_level", kOpenLevel);
+ AddPairToHash("open_custom_editor", kOpenCustomEditor);
+ AddPairToHash("frame_selected", kFrameSelected);
+ AddPairToHash("frame_selected_force", kFrameSelectedForce);
+ AddPairToHash("load_item_search", kLoadItemSearch);
+ AddPairToHash("make_selected_character_saved_corpse", kMakeSelectedCharacterSavedCorpse);
+ AddPairToHash("revive_selected_character_and_unsave_corpse", kReviveSelectedCharacterAndUnsaveCorpse);
+
+ std::sort(hash_to_command.begin(), hash_to_command.end(), HashIntCompare());
+
+ // Make sure there are no hash collisions
+ for(int i=0, len=hash_to_command.size()-1; i<len; ++i) {
+ if(hash_to_command[i].first == hash_to_command[i+1].first) {
+ FatalError("Error", "Hash collision in KeyCommand hash map");
+ }
+ }
+ for(int i=0; i<kNumLabels; ++i){
+ commands[i].num_key_events = 0;
+ }
+}
+
+void KeyCommand::HandleKeyDownEvent( const Keyboard &keyboard, SDL_Keysym key_id ) {
+ // Check for key_pressed events from longest command to shortest
+ int cmd_length = -1;
+ SDL_Scancode sc = key_id.scancode;
+ for(int i=0; i<kNumLabels; ++i){
+ Command &cmd = *(commands_sorted_by_length[i]);
+ if(cmd.num_key_events < cmd_length) {
+ return;
+ }
+ bool cmd_valid = true;
+ for(int j=0; j<cmd.num_key_events; ++j){
+ const KeyEvent &key_event = cmd.key_events[j];
+ bool key_valid = false;
+
+ switch(key_event.type_){
+ case kPressed:
+ if( key_event.sc_ == key_event.sc2_ ) {
+ key_valid = (sc == key_event.sc_);
+ } else {
+ key_valid = (sc == key_event.sc_) || (sc == key_event.sc2_);
+ }
+ break;
+ case kDown:
+ if( key_event.sc_ == key_event.sc2_ ) {
+ key_valid = keyboard.isScancodeDown(key_event.sc_,KIMF_ANY);
+ } else {
+ key_valid = keyboard.isScancodeDown(key_event.sc_,KIMF_ANY) || keyboard.isScancodeDown(key_event.sc2_,KIMF_ANY);
+ }
+ break;
+ case kUp:
+ if( key_event.sc_ == key_event.sc2_ ) {
+ key_valid = !keyboard.isScancodeDown(key_event.sc_,KIMF_ANY);
+ } else {
+ key_valid = !keyboard.isScancodeDown(key_event.sc_,KIMF_ANY) && !keyboard.isScancodeDown(key_event.sc2_,KIMF_ANY);
+ }
+ break;
+ }
+ if(!key_valid){
+ cmd_valid = false;
+ break;
+ }
+ }
+ if(cmd_valid){
+ cmd_length = cmd.num_key_events;
+ cmd.state = kPressed;
+ }
+ }
+}
+
+void KeyCommand::ClearKeyPresses() {
+ for(int i=0; i<kNumLabels; ++i){
+ commands[i].state = kUp;
+ }
+}