Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a7ea7976f | ||
|
|
ec4347e7d3 |
@@ -0,0 +1,164 @@
|
||||
/* B I N D I N G . H
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
|
||||
/** @addtogroup bu_binding
|
||||
* @brief
|
||||
* Universal input binding schema and action dispatch.
|
||||
*/
|
||||
/** @{ */
|
||||
/** @file bu/binding.h */
|
||||
|
||||
#ifndef BU_BINDING_H
|
||||
#define BU_BINDING_H
|
||||
|
||||
#include "common.h"
|
||||
#include "bu/defines.h"
|
||||
#include "bu/vls.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/* Standardized Attribute Keys (for W3C validation logic) */
|
||||
#define BU_BINDING_ATTR_TYPE "type"
|
||||
#define BU_BINDING_ATTR_KEY "key" /* Values MUST be W3C standard strings */
|
||||
#define BU_BINDING_ATTR_BUTTON "button" /* Values MUST be W3C button integers */
|
||||
#define BU_BINDING_ATTR_MODIFIERS "modifiers"
|
||||
|
||||
/* Standardized Event Types */
|
||||
#define BU_BINDING_EVENT_KEY_PRESS "key-press"
|
||||
#define BU_BINDING_EVENT_KEY_RELEASE "key-release"
|
||||
#define BU_BINDING_EVENT_MOUSE_PRESS "mouse-press"
|
||||
#define BU_BINDING_EVENT_MOUSE_RELEASE "mouse-release"
|
||||
#define BU_BINDING_EVENT_MOUSE_MOTION "mouse-motion"
|
||||
#define BU_BINDING_EVENT_MOUSE_SCROLL "mouse-scroll"
|
||||
|
||||
/**
|
||||
* @brief Action callback signature
|
||||
*
|
||||
* Invoked when an incoming event matches a loaded binding.
|
||||
* The callback receives the full JSON string of the triggering event.
|
||||
*
|
||||
* @param event_json Raw JSON string describing the incoming event
|
||||
* @param registered_data Opaque pointer passed during registration
|
||||
* @param event_data Opaque pointer passed during event processing
|
||||
*/
|
||||
typedef void (*bu_action_func_t)(const char *event_json, void *registered_data, void *event_data);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Load JSON bindings from a file
|
||||
*
|
||||
* Parses a JSON configuration specifying bindings for various contexts.
|
||||
* Performs two-phase validation:
|
||||
* 1. Checks standard fields (like W3C key strings) for correctness.
|
||||
* 2. Invokes context-specific validation hooks (if registered).
|
||||
*
|
||||
* @param json_path Path to the JSON file
|
||||
* @return 0 on success, <0 on error
|
||||
*/
|
||||
BU_EXPORT extern int bu_binding_load(const char *json_path);
|
||||
|
||||
/**
|
||||
* @brief Load JSON bindings from a raw string
|
||||
*
|
||||
* Parses a JSON configuration string specifying bindings for various contexts.
|
||||
* Allows applications to embed overriding bindings natively without external files.
|
||||
*
|
||||
* @param json_string Raw JSON string containing bindings
|
||||
* @return 0 on success, <0 on error
|
||||
*/
|
||||
BU_EXPORT extern int bu_binding_load_str(const char *json_string);
|
||||
|
||||
/**
|
||||
* @brief Register a JSON schema for a specific context
|
||||
*
|
||||
* Defines the allowed vocabulary for a context using a JSON schema file.
|
||||
* The schema file is lazily parsed during bu_binding_load().
|
||||
*
|
||||
* @param context The interaction context (e.g., "dm", "fb")
|
||||
* @param json_path Absolute path to the schema JSON file
|
||||
*/
|
||||
BU_EXPORT extern void bu_binding_register_schema(const char *context, const char *json_path);
|
||||
|
||||
/**
|
||||
* @brief Augment an existing schema with a custom action
|
||||
*
|
||||
* @param context The interaction context (e.g., "dm")
|
||||
* @param action_name The new abstract action string
|
||||
* @param description Human-readable description of the action
|
||||
*/
|
||||
BU_EXPORT extern void bu_binding_schema_add_action(const char *context, const char *action_name, const char *description);
|
||||
|
||||
/**
|
||||
* @brief Remove a custom action from an existing schema
|
||||
*
|
||||
* @param context The interaction context (e.g., "dm")
|
||||
* @param action_name The abstract action string to remove
|
||||
*/
|
||||
BU_EXPORT extern void bu_binding_schema_remove_action(const char *context, const char *action_name);
|
||||
|
||||
/**
|
||||
* @brief Register a C callback for a semantic action
|
||||
*
|
||||
* @param action_name The abstract action string (e.g., "fb_close_window")
|
||||
* @param func The C function to invoke when the action is triggered
|
||||
* @param client_data Opaque pointer passed to the callback
|
||||
*/
|
||||
BU_EXPORT extern void bu_binding_register_action(const char *action_name, bu_action_func_t func, void *client_data);
|
||||
|
||||
/**
|
||||
* @brief Process an incoming native event
|
||||
*
|
||||
* Matches the event JSON against the loaded bindings for the given context.
|
||||
* If a matching action is found and a handler is registered for that action,
|
||||
* the callback is invoked.
|
||||
*
|
||||
* @param context The interaction context (e.g., "view3d", "fb_raw")
|
||||
* @param event_json A JSON string containing the event attributes
|
||||
* @param event_data Opaque pointer passed to the callback representing instance state
|
||||
* @return 1 if an action was triggered and handled, 0 otherwise
|
||||
*/
|
||||
BU_EXPORT extern int bu_binding_process_event(const char *context, const char *event_json, void *event_data);
|
||||
|
||||
/**
|
||||
* @brief Check if a specific action is bound in a given context
|
||||
*
|
||||
* Use this to verify that an application has provided a binding for an essential action.
|
||||
*
|
||||
* @param context The interaction context (e.g., "view3d", "fb_raw")
|
||||
* @param action_name The abstract action string (e.g., "view_mouse_drag_rotate")
|
||||
* @return 1 if bound, 0 if not
|
||||
*/
|
||||
BU_EXPORT extern int bu_binding_action_is_bound(const char *context, const char *action_name);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* BU_BINDING_H */
|
||||
|
||||
/** @} */
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -444,6 +444,9 @@ DM_EXPORT extern int fb_get_interactive(const struct fb *ifp);
|
||||
*/
|
||||
DM_EXPORT extern int fb_next_event(struct fb *ifp, struct fb_event *e);
|
||||
|
||||
/** Dispatch an application-facing event through the framebuffer bindings. */
|
||||
DM_EXPORT extern int fb_process_event_bindings(struct fb *ifp, const struct fb_event *e);
|
||||
|
||||
|
||||
/* Library entry points */
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ set(
|
||||
badmagic.c
|
||||
bitv.c
|
||||
bomb.c
|
||||
binding.cpp
|
||||
booleanize.c
|
||||
bu_init.cpp
|
||||
cache.cpp
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
/* B I N D I N G . C P P
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "bu/binding.h"
|
||||
#include "bu/log.h"
|
||||
#include "bu/malloc.h"
|
||||
#include "bu/str.h"
|
||||
#include "bu/vls.h"
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
/*
|
||||
* Internal data structures to hold the parsed bindings and callbacks
|
||||
*/
|
||||
struct ActionCallback {
|
||||
bu_action_func_t func;
|
||||
void *client_data;
|
||||
};
|
||||
|
||||
struct BindingDef {
|
||||
json match;
|
||||
std::string action;
|
||||
};
|
||||
|
||||
/* Global state for libbu bindings */
|
||||
static std::map<std::string, std::vector<BindingDef>>& get_contexts() {
|
||||
static auto* ptr = new std::map<std::string, std::vector<BindingDef>>();
|
||||
return *ptr;
|
||||
}
|
||||
static std::map<std::string, ActionCallback>& get_action_registry() {
|
||||
static auto* ptr = new std::map<std::string, ActionCallback>();
|
||||
return *ptr;
|
||||
}
|
||||
static std::map<std::string, std::string>& get_schema_paths() {
|
||||
static auto* ptr = new std::map<std::string, std::string>();
|
||||
return *ptr;
|
||||
}
|
||||
static std::map<std::string, std::set<std::string>>& get_schemas() {
|
||||
static auto* ptr = new std::map<std::string, std::set<std::string>>();
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_schema_loaded(const std::string& ctx_name)
|
||||
{
|
||||
if (get_schemas().find(ctx_name) != get_schemas().end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = get_schema_paths().find(ctx_name);
|
||||
if (it == get_schema_paths().end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file(it->second);
|
||||
if (!file.is_open()) {
|
||||
bu_log("ensure_schema_loaded: Failed to open schema %s\n", it->second.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
json j;
|
||||
try {
|
||||
file >> j;
|
||||
} catch (const json::parse_error& e) {
|
||||
bu_log("ensure_schema_loaded: JSON parse error in schema %s: %s\n", it->second.c_str(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> valid_actions;
|
||||
if (j.contains("actions") && j["actions"].is_object()) {
|
||||
for (auto el = j["actions"].begin(); el != j["actions"].end(); ++el) {
|
||||
valid_actions.insert(el.key());
|
||||
}
|
||||
}
|
||||
get_schemas()[ctx_name] = valid_actions;
|
||||
}
|
||||
|
||||
/* W3C Key validation placeholder. A full implementation would check against
|
||||
* an exhaustive list of W3C standard keys (e.g., "Escape", "ArrowUp").
|
||||
* For demonstration, we'll just implement a simple sanity check.
|
||||
*/
|
||||
static int
|
||||
validate_w3c_key(const std::string& key)
|
||||
{
|
||||
/* Example: Reject completely upper-case strings that might be native C macros */
|
||||
if (key.length() > 1 && key == "RETURN") return 0;
|
||||
if (key.length() > 1 && key == "SPACE") return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* W3C Button validation. Values 0, 1, 2, 3, 4 are standard. */
|
||||
static int
|
||||
validate_w3c_button(int button)
|
||||
{
|
||||
return (button >= 0 && button <= 4);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
bu_binding_load(const char *json_path)
|
||||
{
|
||||
if (!json_path) return -1;
|
||||
|
||||
std::ifstream file(json_path);
|
||||
if (!file.is_open()) {
|
||||
bu_log("bu_binding_load: Failed to open %s\n", json_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
return bu_binding_load_str(buffer.str().c_str());
|
||||
}
|
||||
|
||||
int
|
||||
bu_binding_load_str(const char *json_string)
|
||||
{
|
||||
if (!json_string) return -1;
|
||||
|
||||
json j;
|
||||
try {
|
||||
j = json::parse(json_string);
|
||||
} catch (const json::parse_error& e) {
|
||||
bu_log("bu_binding_load_str: JSON parse error: %s\n", e.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!j.contains("contexts") || !j["contexts"].is_object()) {
|
||||
bu_log("bu_binding_load_str: Invalid JSON structure (missing 'contexts' object)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (auto it = j["contexts"].begin(); it != j["contexts"].end(); ++it) {
|
||||
std::string ctx_name = it.key();
|
||||
const auto& ctx_obj = it.value();
|
||||
|
||||
if (!ctx_obj.contains("bindings") || !ctx_obj["bindings"].is_array()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<BindingDef> defs;
|
||||
|
||||
ensure_schema_loaded(ctx_name);
|
||||
bool has_schema = (get_schemas().find(ctx_name) != get_schemas().end());
|
||||
|
||||
for (const auto& binding : ctx_obj["bindings"]) {
|
||||
if (!binding.contains("match") || !binding.contains("action")) {
|
||||
bu_log("bu_binding_load_str: Binding missing 'match' or 'action' in context %s. Skipping.\n", ctx_name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
const json& match = binding["match"];
|
||||
std::string action = binding["action"].get<std::string>();
|
||||
|
||||
/* Phase 1 Validation: Check W3C standard compliance */
|
||||
int is_valid = 1;
|
||||
if (match.contains(BU_BINDING_ATTR_KEY)) {
|
||||
if (!match[BU_BINDING_ATTR_KEY].is_string() || !validate_w3c_key(match[BU_BINDING_ATTR_KEY].get<std::string>())) {
|
||||
bu_log("bu_binding_load_str: Invalid W3C key '%s' in context %s. Binding rejected.\n",
|
||||
match[BU_BINDING_ATTR_KEY].dump().c_str(), ctx_name.c_str());
|
||||
is_valid = 0;
|
||||
}
|
||||
}
|
||||
if (match.contains(BU_BINDING_ATTR_BUTTON)) {
|
||||
if (!match[BU_BINDING_ATTR_BUTTON].is_number_integer() || !validate_w3c_button(match[BU_BINDING_ATTR_BUTTON].get<int>())) {
|
||||
bu_log("bu_binding_load_str: Invalid W3C button '%s' in context %s. Binding rejected.\n",
|
||||
match[BU_BINDING_ATTR_BUTTON].dump().c_str(), ctx_name.c_str());
|
||||
is_valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_valid) continue;
|
||||
|
||||
/* Phase 2 Validation: Schema constraints */
|
||||
if (has_schema) {
|
||||
if (get_schemas()[ctx_name].find(action) == get_schemas()[ctx_name].end()) {
|
||||
bu_log("bu_binding_load_str: Binding for action '%s' rejected by schema for context '%s'.\n",
|
||||
action.c_str(), ctx_name.c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
BindingDef def;
|
||||
def.match = match;
|
||||
def.action = action;
|
||||
get_contexts()[ctx_name].insert(get_contexts()[ctx_name].begin(), def);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
bu_binding_register_schema(const char *context, const char *json_path)
|
||||
{
|
||||
if (!context || !json_path) return;
|
||||
get_schema_paths()[std::string(context)] = std::string(json_path);
|
||||
}
|
||||
|
||||
void
|
||||
bu_binding_schema_add_action(const char *context, const char *action_name, const char *description)
|
||||
{
|
||||
if (!context || !action_name) return;
|
||||
std::string ctx(context);
|
||||
ensure_schema_loaded(ctx);
|
||||
get_schemas()[ctx].insert(std::string(action_name));
|
||||
}
|
||||
|
||||
void
|
||||
bu_binding_schema_remove_action(const char *context, const char *action_name)
|
||||
{
|
||||
if (!context || !action_name) return;
|
||||
std::string ctx(context);
|
||||
ensure_schema_loaded(ctx);
|
||||
get_schemas()[ctx].erase(std::string(action_name));
|
||||
}
|
||||
|
||||
void
|
||||
bu_binding_register_action(const char *action_name, bu_action_func_t func, void *client_data)
|
||||
{
|
||||
if (!action_name || !func) return;
|
||||
|
||||
ActionCallback cb;
|
||||
cb.func = func;
|
||||
cb.client_data = client_data;
|
||||
|
||||
get_action_registry()[std::string(action_name)] = cb;
|
||||
}
|
||||
|
||||
/* Helper to check if the json 'event' contains all the key-value pairs required by 'match' */
|
||||
static int
|
||||
is_match(const json& match, const json& event)
|
||||
{
|
||||
for (auto it = match.begin(); it != match.end(); ++it) {
|
||||
if (!event.contains(it.key())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If it's an array (like modifiers), we need to ensure all elements in 'match' exist in 'event' */
|
||||
if (it.value().is_array()) {
|
||||
if (!event[it.key()].is_array()) return 0;
|
||||
|
||||
for (const auto& req_mod : it.value()) {
|
||||
int found = 0;
|
||||
for (const auto& ev_mod : event[it.key()]) {
|
||||
if (ev_mod == req_mod) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return 0;
|
||||
}
|
||||
}
|
||||
/* Otherwise, simple equality */
|
||||
else if (event[it.key()] != it.value()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
bu_binding_process_event(const char *context, const char *event_json, void *event_data)
|
||||
{
|
||||
if (!context || !event_json) return 0;
|
||||
|
||||
std::string ctx_name(context);
|
||||
auto it = get_contexts().find(ctx_name);
|
||||
if (it == get_contexts().end()) {
|
||||
return 0; /* Context not found */
|
||||
}
|
||||
|
||||
const char *json_str = event_json;
|
||||
|
||||
json event_json_obj;
|
||||
try {
|
||||
event_json_obj = json::parse(json_str);
|
||||
} catch (const json::parse_error& e) {
|
||||
bu_log("bu_binding_process_event: Invalid event JSON: %s\n", e.what());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!event_json_obj.is_object()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const auto& def : it->second) {
|
||||
if (is_match(def.match, event_json_obj)) {
|
||||
/* Match found */
|
||||
auto reg_it = get_action_registry().find(def.action);
|
||||
if (reg_it != get_action_registry().end()) {
|
||||
reg_it->second.func(event_json, reg_it->second.client_data, event_data);
|
||||
return 1;
|
||||
} else {
|
||||
/* We have a binding, but no callback registered for this action */
|
||||
bu_log("Warning: Event mapped to action '%s', but no callback is registered.\n", def.action.c_str());
|
||||
return 1; /* Still mark as handled since it matched a binding */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0; /* Unhandled */
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
bu_binding_action_is_bound(const char *context, const char *action_name)
|
||||
{
|
||||
if (!context || !action_name) return 0;
|
||||
|
||||
std::string ctx(context);
|
||||
std::string act(action_name);
|
||||
|
||||
auto it = get_contexts().find(ctx);
|
||||
if (it == get_contexts().end()) return 0;
|
||||
|
||||
for (const auto& def : it->second) {
|
||||
if (def.action == act) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C++
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -10,6 +10,7 @@ set(
|
||||
test_bomb.c
|
||||
test_badmagic.c
|
||||
test_basename.c
|
||||
test_binding.cpp
|
||||
test_bitv.c
|
||||
test_booleanize.c
|
||||
test_cache.cpp
|
||||
@@ -122,6 +123,7 @@ brlcad_addexec(bu_gethostname test_gethostname.c libbu TEST)
|
||||
brlcad_add_test(NAME bu_argv COMMAND bu_test test_argv)
|
||||
brlcad_add_test(NAME bu_backtrace COMMAND bu_test test_backtrace)
|
||||
brlcad_add_test(NAME bu_bomb COMMAND bu_test test_bomb "$<TARGET_FILE:bu_test>")
|
||||
brlcad_add_test(NAME bu_binding COMMAND bu_test test_binding "${CMAKE_CURRENT_SOURCE_DIR}/test_bindings.json")
|
||||
brlcad_add_test(NAME bu_dir COMMAND bu_test test_dir)
|
||||
brlcad_add_test(NAME bu_list_empty COMMAND bu_test test_list 0)
|
||||
brlcad_add_test(NAME bu_list_single COMMAND bu_test test_list 1)
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/* T E S T _ B I N D I N G . C P P
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bu/binding.h"
|
||||
#include "bu/vls.h"
|
||||
|
||||
int action_triggered = 0;
|
||||
|
||||
void mock_action_handler(const char *event_json, void *registered_data, void *event_data) {
|
||||
action_triggered = 1;
|
||||
printf("Action handler triggered with JSON: %s\n", event_json);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bu_setprogname(argv[0]);
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s <path_to_test_bindings.json>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *json_path = argv[1];
|
||||
|
||||
/* Register schema to restrict allowed actions */
|
||||
bu_binding_schema_add_action("fb", "fb_zoom", "Zoom");
|
||||
bu_binding_schema_add_action("fb", "fb_close_window", "Close Window");
|
||||
|
||||
/* Register hooks before loading JSON */
|
||||
bu_binding_register_action("fb_close_window", mock_action_handler, NULL);
|
||||
|
||||
printf("Loading bindings from %s\n", json_path);
|
||||
if (bu_binding_load(json_path) < 0) {
|
||||
printf("Failed to load bindings.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Test 1: Simulate a matching event */
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"mouse-press\", \"button\": 2}");
|
||||
|
||||
action_triggered = 0;
|
||||
bu_binding_process_event("fb", bu_vls_cstr(&ev_json), NULL);
|
||||
|
||||
if (!action_triggered) {
|
||||
printf("Test 1 Failed: Expected action was not triggered.\n");
|
||||
return -1;
|
||||
}
|
||||
printf("Test 1 Passed: Action triggered successfully.\n");
|
||||
|
||||
/* Test 2: Simulate a non-matching event */
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"mouse-press\", \"button\": 1}");
|
||||
action_triggered = 0;
|
||||
bu_binding_process_event("fb", bu_vls_cstr(&ev_json), NULL);
|
||||
|
||||
if (action_triggered) {
|
||||
printf("Test 2 Failed: Action should not have been triggered.\n");
|
||||
return -1;
|
||||
}
|
||||
printf("Test 2 Passed: Non-matching event correctly ignored.\n");
|
||||
|
||||
/* Test 3: The Escape key binding should have been rejected by the validator */
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"key-press\", \"key\": \"Escape\"}");
|
||||
action_triggered = 0;
|
||||
bu_binding_process_event("fb", bu_vls_cstr(&ev_json), NULL);
|
||||
|
||||
if (action_triggered) {
|
||||
printf("Test 3 Failed: Escape binding should have been filtered by the validator.\n");
|
||||
return -1;
|
||||
}
|
||||
printf("Test 3 Passed: Validator correctly rejected binding at load time.\n");
|
||||
|
||||
bu_vls_free(&ev_json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C++
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"contexts": {
|
||||
"fb": {
|
||||
"bindings": [
|
||||
{
|
||||
"match": {
|
||||
"type": "mouse-press",
|
||||
"button": 2
|
||||
},
|
||||
"action": "fb_close_window"
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"type": "key-press",
|
||||
"key": "Escape"
|
||||
},
|
||||
"action": "fb_invalid_action"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,11 @@ set_property(
|
||||
PROPERTY COMPILE_DEFINITIONS "DM_PLUGIN_SUFFIX=\"${CMAKE_SHARED_LIBRARY_SUFFIX}\""
|
||||
)
|
||||
|
||||
# Files related to key/mouse binding management
|
||||
brlcad_manage_files(dm_bindings.json ${DATA_DIR}/bindings)
|
||||
brlcad_manage_files(dm_schema.json ${DATA_DIR}/bindings)
|
||||
brlcad_manage_files(fb_schema.json ${DATA_DIR}/bindings)
|
||||
|
||||
brlcad_find_package(PNG REQUIRED)
|
||||
find_package(OpenSSL QUIET)
|
||||
|
||||
@@ -165,3 +170,4 @@ cmakefiles(${libdm_ignore_files})
|
||||
# indent-tabs-mode: t
|
||||
# End:
|
||||
# ex: shiftwidth=2 tabstop=8
|
||||
|
||||
|
||||
@@ -2925,6 +2925,7 @@ X24_handle_event(struct fb *ifp, XEvent *event)
|
||||
case ButtonPress:
|
||||
{
|
||||
int button = (int) event->xbutton.button;
|
||||
int w3c_button = 0;
|
||||
|
||||
/* In interactive mode, report the event to the application
|
||||
* rather than consuming it here. */
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"contexts": {
|
||||
"fb": {
|
||||
"description": "Standard interactions for raw framebuffers (rt, fbserv)",
|
||||
"bindings": [
|
||||
{
|
||||
"match": {
|
||||
"type": "mouse-press",
|
||||
"button": 1
|
||||
},
|
||||
"action": "fb_print_pixel_rgb"
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"type": "mouse-press",
|
||||
"button": 2
|
||||
},
|
||||
"action": "fb_close_window"
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"type": "key-press",
|
||||
"key": "Escape"
|
||||
},
|
||||
"action": "fb_close_window"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dm": {
|
||||
"description": "Standard bindings for the Geometry Editor (qged & mged)",
|
||||
"bindings": [
|
||||
{ "match": { "type": "key-press", "key": "A" }, "action": "view_toggle_adc" },
|
||||
{ "match": { "type": "key-press", "key": "M" }, "action": "view_toggle_model_axes" },
|
||||
{ "match": { "type": "key-press", "key": "V" }, "action": "view_toggle_view_axes" },
|
||||
{ "match": { "type": "key-press", "key": "2" }, "action": "view_2" },
|
||||
{ "match": { "type": "key-press", "key": "3" }, "action": "view_3" },
|
||||
{ "match": { "type": "key-press", "key": "4" }, "action": "view_4" },
|
||||
{ "match": { "type": "key-press", "key": "5" }, "action": "view_5" },
|
||||
{ "match": { "type": "key-press", "key": "6" }, "action": "view_6" },
|
||||
{ "match": { "type": "key-press", "key": "7" }, "action": "view_7" },
|
||||
{ "match": { "type": "key-press", "key": "F" }, "action": "view_front" },
|
||||
{ "match": { "type": "key-press", "key": "T" }, "action": "view_top" },
|
||||
{ "match": { "type": "key-press", "key": "B" }, "action": "view_bottom" },
|
||||
{ "match": { "type": "key-press", "key": "L" }, "action": "view_left" },
|
||||
{ "match": { "type": "key-press", "key": "R", "modifiers": ["Shift"] }, "action": "view_rear" },
|
||||
{ "match": { "type": "key-press", "key": "R" }, "action": "view_right" },
|
||||
{ "match": { "type": "mouse-press", "button": 0 }, "action": "view_mouse_press_left" },
|
||||
{ "match": { "type": "mouse-release", "button": 0 }, "action": "view_mouse_release_left" },
|
||||
{ "match": { "type": "mouse-release", "button": 2 }, "action": "view_mouse_release_right" },
|
||||
{ "match": { "type": "mouse-release", "button": 1 }, "action": "view_mouse_release_middle" },
|
||||
{ "match": { "type": "mouse-move", "button": 0, "modifiers": ["Control"] }, "action": "view_mouse_drag_rotate" },
|
||||
{ "match": { "type": "mouse-move", "button": 0, "modifiers": ["Shift"] }, "action": "view_mouse_drag_translate" },
|
||||
{ "match": { "type": "mouse-move", "button": 0, "modifiers": ["Control", "Shift"] }, "action": "view_mouse_drag_scale" },
|
||||
{ "match": { "type": "mouse-move", "button": 0 }, "action": "view_mouse_drag_adjust" },
|
||||
{ "match": { "type": "mouse-wheel" }, "action": "view_mouse_wheel_scale" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,10 +66,22 @@ dm_init_msgs()
|
||||
}
|
||||
|
||||
|
||||
#include "bu/binding.h"
|
||||
|
||||
static void
|
||||
libdm_init(void)
|
||||
{
|
||||
bu_vls_init(&init_msgs);
|
||||
|
||||
const char *dm_schema = bu_dir(NULL, 0, BU_DIR_DATA, "bindings", "dm_schema.json", NULL);
|
||||
if (dm_schema) {
|
||||
bu_binding_register_schema("dm", dm_schema);
|
||||
}
|
||||
|
||||
const char *fb_schema = bu_dir(NULL, 0, BU_DIR_DATA, "bindings", "fb_schema.json", NULL);
|
||||
if (fb_schema) {
|
||||
bu_binding_register_schema("fb", fb_schema);
|
||||
}
|
||||
|
||||
const char *ppath = bu_dir(NULL, 0, BU_DIR_LIBEXEC, "dm", NULL);
|
||||
char **dm_filenames;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include "bu/binding.h"
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
@@ -62,6 +63,13 @@ dm_open(void *ctx, void *interp, const char *type, int argc, const char *argv[])
|
||||
|
||||
const struct dm *d = d_it->second;
|
||||
struct dm *dmp = d->i->dm_open(ctx, interp, argc, argv);
|
||||
|
||||
char *bindings_path = (char *)bu_dir(NULL, 0, BU_DIR_DATA, "bindings", "dm_bindings.json", NULL);
|
||||
if (bindings_path) {
|
||||
bu_binding_load(bindings_path);
|
||||
bu_free(bindings_path, "bindings_path");
|
||||
}
|
||||
|
||||
return dmp;
|
||||
}
|
||||
|
||||
@@ -528,6 +536,12 @@ found_interface:
|
||||
|
||||
return FB_NULL;
|
||||
}
|
||||
char *bindings_path = (char *)bu_dir(NULL, 0, BU_DIR_DATA, "bindings", "dm_bindings.json", NULL);
|
||||
if (bindings_path) {
|
||||
bu_binding_load(bindings_path);
|
||||
bu_free(bindings_path, "bindings_path");
|
||||
}
|
||||
|
||||
return ifp;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"actions": {
|
||||
"view_toggle_adc": "Toggle Angle Distance Cursor",
|
||||
"view_toggle_model_axes": "Toggle Model Axes",
|
||||
"view_toggle_view_axes": "Toggle View Axes",
|
||||
"view_2": "Set View 2",
|
||||
"view_3": "Set View 3",
|
||||
"view_4": "Set View 4",
|
||||
"view_5": "Set View 5",
|
||||
"view_6": "Set View 6",
|
||||
"view_7": "Set View 7",
|
||||
"view_front": "Set Front View",
|
||||
"view_top": "Set Top View",
|
||||
"view_bottom": "Set Bottom View",
|
||||
"view_left": "Set Left View",
|
||||
"view_right": "Set Right View",
|
||||
"view_rear": "Set Rear View",
|
||||
"view_mouse_drag_rotate": "Rotate view via mouse drag",
|
||||
"view_mouse_drag_translate": "Translate view via mouse drag",
|
||||
"view_mouse_drag_scale": "Scale view via mouse drag",
|
||||
"view_mouse_wheel_scale": "Scale view via mouse wheel",
|
||||
"view_mouse_press_left": "Left mouse press action",
|
||||
"view_mouse_press_right": "Right mouse press action"
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "./include/private.h"
|
||||
#include "icv.h"
|
||||
#include "dm.h"
|
||||
#include "bu/binding.h"
|
||||
|
||||
|
||||
struct fb *fb_get(void)
|
||||
@@ -258,6 +259,59 @@ long fb_poll_rate(struct fb *ifp)
|
||||
return ifp->i->if_poll_refresh_rate;
|
||||
}
|
||||
|
||||
static int
|
||||
fb_event_button_to_w3c(int button)
|
||||
{
|
||||
return (button >= 1 && button <= 3) ? button - 1 : button;
|
||||
}
|
||||
|
||||
int
|
||||
fb_process_event_bindings(struct fb *ifp, const struct fb_event *event)
|
||||
{
|
||||
struct bu_vls event_json = BU_VLS_INIT_ZERO;
|
||||
int handled = 0;
|
||||
|
||||
if (!ifp || !event)
|
||||
return 0;
|
||||
|
||||
switch (event->type) {
|
||||
case FB_EVENT_BUTTON_PRESS:
|
||||
bu_vls_sprintf(&event_json,
|
||||
"{\"type\": \"mouse-press\", \"button\": %d, \"x\": %d, \"y\": %d}",
|
||||
fb_event_button_to_w3c(event->button), event->x, event->y);
|
||||
break;
|
||||
case FB_EVENT_BUTTON_RELEASE:
|
||||
bu_vls_sprintf(&event_json,
|
||||
"{\"type\": \"mouse-release\", \"button\": %d, \"x\": %d, \"y\": %d}",
|
||||
fb_event_button_to_w3c(event->button), event->x, event->y);
|
||||
break;
|
||||
case FB_EVENT_MOTION:
|
||||
bu_vls_sprintf(&event_json,
|
||||
"{\"type\": \"mouse-move\", \"x\": %d, \"y\": %d}",
|
||||
event->x, event->y);
|
||||
break;
|
||||
case FB_EVENT_KEY_PRESS:
|
||||
if (event->keycode == 27) {
|
||||
bu_vls_sprintf(&event_json, "{\"type\": \"key-press\", \"key\": \"Escape\"}");
|
||||
} else if (event->keycode >= 32 && event->keycode <= 126) {
|
||||
bu_vls_sprintf(&event_json,
|
||||
"{\"type\": \"key-press\", \"key\": \"%c\"}",
|
||||
event->keycode);
|
||||
}
|
||||
break;
|
||||
case FB_EVENT_CLOSE:
|
||||
bu_vls_sprintf(&event_json, "{\"type\": \"key-press\", \"key\": \"Escape\"}");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bu_vls_strlen(&event_json) > 0)
|
||||
handled = bu_binding_process_event("fb", bu_vls_cstr(&event_json), ifp);
|
||||
bu_vls_free(&event_json);
|
||||
return handled;
|
||||
}
|
||||
|
||||
void fb_set_interactive(struct fb *ifp, int on)
|
||||
{
|
||||
if (!ifp)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"actions": {
|
||||
"fb_print_pixel_rgb": "Print RGB values of pixel under cursor",
|
||||
"fb_close_window": "Close framebuffer window"
|
||||
}
|
||||
}
|
||||
+171
-72
@@ -61,6 +61,10 @@
|
||||
#include "dm.h"
|
||||
#include "./fb_ogl.h"
|
||||
|
||||
#include "bu/binding.h"
|
||||
#include "bu/file.h"
|
||||
#include <X11/keysym.h>
|
||||
|
||||
extern struct fb ogl_interface;
|
||||
|
||||
#define DIRECT_COLOR_VISUAL_ALLOWED 0
|
||||
@@ -774,6 +778,52 @@ ogl_configureWindow(struct fb *ifp, int width, int height)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ogl_action_fb_print_pixel_rgb(const struct bu_vls *event_json, void *registered_data, void *event_data)
|
||||
{
|
||||
struct fb *ifp = (struct fb *)event_data;
|
||||
int x = -1, y = -1;
|
||||
char *p;
|
||||
|
||||
if (!ifp) return;
|
||||
|
||||
if ((p = strstr(bu_vls_cstr(event_json), "\"x\":"))) {
|
||||
sscanf(p + 4, "%d", &x);
|
||||
}
|
||||
if ((p = strstr(bu_vls_cstr(event_json), "\"y\":"))) {
|
||||
sscanf(p + 4, "%d", &y);
|
||||
}
|
||||
|
||||
if (x < 0 || y < 0) {
|
||||
fb_log("No RGB (outside image viewport)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t memidx = (y * WIN(ifp)->mi_pixwidth) * sizeof(struct fb_pixel);
|
||||
struct fb_pixel *oglp = (struct fb_pixel *)&ifp->i->if_mem[memidx];
|
||||
|
||||
fb_log("At image (%d, %d), real RGB=(%3d %3d %3d)\n",
|
||||
x, y, (int)oglp[x].red, (int)oglp[x].green, (int)oglp[x].blue);
|
||||
}
|
||||
|
||||
static void
|
||||
ogl_action_fb_close_window(const struct bu_vls *event_json, void *registered_data, void *event_data)
|
||||
{
|
||||
struct fb *ifp = (struct fb *)event_data;
|
||||
if (ifp) OGL(ifp)->alive = 0;
|
||||
}
|
||||
|
||||
static int bindings_loaded = 0;
|
||||
static void
|
||||
ogl_init_bindings(void)
|
||||
{
|
||||
if (bindings_loaded) return;
|
||||
bindings_loaded = 1;
|
||||
|
||||
|
||||
bu_binding_register_action("fb_print_pixel_rgb", ogl_action_fb_print_pixel_rgb, NULL);
|
||||
bu_binding_register_action("fb_close_window", ogl_action_fb_close_window, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
ogl_do_event(struct fb *ifp)
|
||||
@@ -781,76 +831,122 @@ ogl_do_event(struct fb *ifp)
|
||||
XEvent event;
|
||||
|
||||
while (XCheckWindowEvent(OGL(ifp)->dispp, OGL(ifp)->wind,
|
||||
OGL(ifp)->event_mask, &event)) {
|
||||
switch (event.type) {
|
||||
case Expose:
|
||||
if (!OGL(ifp)->use_ext_ctrl)
|
||||
expose_callback(ifp);
|
||||
break;
|
||||
case ButtonPress:
|
||||
{
|
||||
int button = (int) event.xbutton.button;
|
||||
if (button == Button1) {
|
||||
/* Check for single button mouse remap.
|
||||
* ctrl-1 => 2
|
||||
* meta-1 => 3
|
||||
* cmdkey => 3
|
||||
*/
|
||||
if (event.xbutton.state & ControlMask) {
|
||||
button = Button2;
|
||||
} else if (event.xbutton.state & Mod1Mask) {
|
||||
button = Button3;
|
||||
} else if (event.xbutton.state & Mod2Mask) {
|
||||
button = Button3;
|
||||
}
|
||||
}
|
||||
|
||||
switch (button) {
|
||||
case Button1:
|
||||
break;
|
||||
case Button2:
|
||||
{
|
||||
int x, y;
|
||||
register struct fb_pixel *oglp;
|
||||
|
||||
x = event.xbutton.x;
|
||||
y = ifp->i->if_height - event.xbutton.y - 1;
|
||||
|
||||
if (x < 0 || y < 0) {
|
||||
fb_log("No RGB (outside image viewport)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
size_t memidx = (y * WIN(ifp)->mi_pixwidth) * sizeof(struct fb_pixel);
|
||||
oglp = (struct fb_pixel *)&ifp->i->if_mem[memidx];
|
||||
|
||||
fb_log("At image (%d, %d), real RGB=(%3d %3d %3d)\n",
|
||||
x, y, (int)oglp[x].red, (int)oglp[x].green, (int)oglp[x].blue);
|
||||
|
||||
break;
|
||||
}
|
||||
case Button3:
|
||||
OGL(ifp)->alive = 0;
|
||||
break;
|
||||
default:
|
||||
fb_log("unhandled mouse event\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ConfigureNotify:
|
||||
{
|
||||
XConfigureEvent *conf = (XConfigureEvent *)&event;
|
||||
|
||||
if (conf->width == OGL(ifp)->win_width &&
|
||||
conf->height == OGL(ifp)->win_height)
|
||||
return;
|
||||
|
||||
ogl_configureWindow(ifp, conf->width, conf->height);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
OGL(ifp)->event_mask, &event)) {
|
||||
switch (event.type) {
|
||||
case Expose:
|
||||
if (!OGL(ifp)->use_ext_ctrl)
|
||||
expose_callback(ifp);
|
||||
if (fb_get_interactive(ifp)) {
|
||||
struct fb_event ev = {FB_EVENT_EXPOSE, 0, 0, 0, 0, 0};
|
||||
fb_enqueue_event(ifp, &ev);
|
||||
}
|
||||
break;
|
||||
case ButtonPress:
|
||||
if (fb_get_interactive(ifp)) {
|
||||
struct fb_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = FB_EVENT_BUTTON_PRESS;
|
||||
ev.button = (int)event.xbutton.button;
|
||||
ev.x = event.xbutton.x;
|
||||
ev.y = ifp->i->if_height - event.xbutton.y - 1;
|
||||
ev.state = (int)event.xbutton.state;
|
||||
fb_enqueue_event(ifp, &ev);
|
||||
} else {
|
||||
int button = (int)event.xbutton.button;
|
||||
int w3c_button = 0;
|
||||
if (button == Button1) {
|
||||
if (event.xbutton.state & ControlMask) button = Button2;
|
||||
else if (event.xbutton.state & Mod1Mask) button = Button3;
|
||||
else if (event.xbutton.state & Mod2Mask) button = Button3;
|
||||
}
|
||||
if (button == Button1) w3c_button = 0;
|
||||
else if (button == Button2) w3c_button = 1;
|
||||
else if (button == Button3) w3c_button = 2;
|
||||
else w3c_button = button;
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
bu_vls_sprintf(&ev_json,
|
||||
"{\"type\": \"mouse-press\", \"button\": %d, \"x\": %d, \"y\": %d}",
|
||||
w3c_button, event.xbutton.x,
|
||||
ifp->i->if_height - event.xbutton.y - 1);
|
||||
bu_binding_process_event("fb", bu_vls_cstr(&ev_json), ifp);
|
||||
bu_vls_free(&ev_json);
|
||||
}
|
||||
break;
|
||||
case ButtonRelease:
|
||||
if (fb_get_interactive(ifp)) {
|
||||
struct fb_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = FB_EVENT_BUTTON_RELEASE;
|
||||
ev.button = (int)event.xbutton.button;
|
||||
ev.x = event.xbutton.x;
|
||||
ev.y = ifp->i->if_height - event.xbutton.y - 1;
|
||||
ev.state = (int)event.xbutton.state;
|
||||
fb_enqueue_event(ifp, &ev);
|
||||
}
|
||||
break;
|
||||
case MotionNotify:
|
||||
if (fb_get_interactive(ifp)) {
|
||||
struct fb_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = FB_EVENT_MOTION;
|
||||
ev.x = event.xmotion.x;
|
||||
ev.y = ifp->i->if_height - event.xmotion.y - 1;
|
||||
ev.state = (int)event.xmotion.state;
|
||||
fb_enqueue_event(ifp, &ev);
|
||||
}
|
||||
break;
|
||||
case KeyPress:
|
||||
if (fb_get_interactive(ifp)) {
|
||||
struct fb_event ev;
|
||||
char keybuf[8] = {0};
|
||||
KeySym keysym;
|
||||
int n;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = FB_EVENT_KEY_PRESS;
|
||||
n = XLookupString(&event.xkey, keybuf, sizeof(keybuf), &keysym, NULL);
|
||||
ev.keycode = (n > 0) ? (unsigned char)keybuf[0] : (int)keysym;
|
||||
ev.state = (int)event.xkey.state;
|
||||
fb_enqueue_event(ifp, &ev);
|
||||
} else {
|
||||
KeySym keysym = XLookupKeysym(&event.xkey, 0);
|
||||
char *key_str = XKeysymToString(keysym);
|
||||
if (key_str) {
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
bu_vls_sprintf(&ev_json,
|
||||
"{\"type\": \"key-press\", \"key\": \"%s\"}", key_str);
|
||||
bu_binding_process_event("fb", bu_vls_cstr(&ev_json), ifp);
|
||||
bu_vls_free(&ev_json);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ClientMessage:
|
||||
if (fb_get_interactive(ifp)) {
|
||||
struct fb_event ev = {FB_EVENT_CLOSE, 0, 0, 0, 0, 0};
|
||||
fb_enqueue_event(ifp, &ev);
|
||||
} else {
|
||||
OGL(ifp)->alive = 0;
|
||||
}
|
||||
break;
|
||||
case ConfigureNotify:
|
||||
{
|
||||
XConfigureEvent *conf = (XConfigureEvent *)&event;
|
||||
if (conf->width == OGL(ifp)->win_width &&
|
||||
conf->height == OGL(ifp)->win_height)
|
||||
break;
|
||||
ogl_configureWindow(ifp, conf->width, conf->height);
|
||||
if (fb_get_interactive(ifp)) {
|
||||
struct fb_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = FB_EVENT_RESIZE;
|
||||
ev.x = conf->width;
|
||||
ev.y = conf->height;
|
||||
fb_enqueue_event(ifp, &ev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,9 +1117,10 @@ fb_ogl_open(struct fb *ifp, const char *file, int width, int height)
|
||||
int mode, i;
|
||||
long valuemask;
|
||||
XSetWindowAttributes swa;
|
||||
|
||||
FB_CK_FB(ifp->i);
|
||||
|
||||
ogl_init_bindings();
|
||||
|
||||
/*
|
||||
* First, attempt to determine operating mode for this open,
|
||||
* based upon the "unit number" or flags.
|
||||
@@ -1214,7 +1311,7 @@ fb_ogl_open(struct fb *ifp, const char *file, int width, int height)
|
||||
OGL(ifp)->vip->screen);
|
||||
swa.event_mask = OGL(ifp)->event_mask =
|
||||
ExposureMask | KeyPressMask | KeyReleaseMask |
|
||||
ButtonPressMask | ButtonReleaseMask;
|
||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask;
|
||||
swa.colormap = OGL(ifp)->xcmap;
|
||||
|
||||
#define XCreateWindowDebug(display, parent, x, y, width, height, border_width, depth, class, visual, valuemask, attributes) \
|
||||
@@ -1381,6 +1478,8 @@ ogl_open_existing(struct fb *ifp, int width, int height, struct fb_platform_spec
|
||||
{
|
||||
struct ogl_fb_info *ogl_internal = (struct ogl_fb_info *)fb_p->data;
|
||||
BU_CKMAG(fb_p, FB_OGL_MAGIC, "ogl framebuffer");
|
||||
|
||||
ogl_init_bindings();
|
||||
return open_existing(ifp, ogl_internal->dpy, ogl_internal->win,
|
||||
ogl_internal->cmap, ogl_internal->vip, width, height, ogl_internal->glxc,
|
||||
ogl_internal->double_buffer, ogl_internal->soft_cmap);
|
||||
|
||||
+241
-246
@@ -37,6 +37,12 @@ extern "C" {
|
||||
#include "qtcad/defines.h"
|
||||
#include "bindings.h"
|
||||
|
||||
extern "C" {
|
||||
#include "bu/binding.h"
|
||||
#include "bu/file.h"
|
||||
#include "bu/log.h"
|
||||
}
|
||||
|
||||
typedef void (*bounds_update_t)(struct bview *);
|
||||
static std::unordered_map<struct bview *, bounds_update_t> drag_bounds_updates;
|
||||
static std::unordered_map<struct bview *, long long> drag_update_ts;
|
||||
@@ -68,247 +74,256 @@ restore_drag_bounds_update(struct bview *v, int refresh_bounds)
|
||||
drag_update_ts.erase(v);
|
||||
}
|
||||
|
||||
// TODO - look into QShortcut, see if it might be a better way
|
||||
// to manage this
|
||||
static int bindings_loaded = 0;
|
||||
|
||||
static void qtcad_action_toggle_adc(const char *, void *, void *event_data) {
|
||||
struct bview *v = (struct bview *)event_data;
|
||||
if (v) v->gv_s->gv_adc.draw = !v->gv_s->gv_adc.draw;
|
||||
}
|
||||
static void qtcad_action_toggle_model_axes(const char *, void *, void *event_data) {
|
||||
struct bview *v = (struct bview *)event_data;
|
||||
if (v) v->gv_s->gv_model_axes.draw = !v->gv_s->gv_model_axes.draw;
|
||||
}
|
||||
static void qtcad_action_toggle_view_axes(const char *, void *, void *event_data) {
|
||||
struct bview *v = (struct bview *)event_data;
|
||||
if (v) v->gv_s->gv_view_axes.draw = !v->gv_s->gv_view_axes.draw;
|
||||
}
|
||||
|
||||
static void qtcad_action_view_set(const char *, void *registered_data, void *event_data) {
|
||||
struct bview *v = (struct bview *)event_data;
|
||||
const char *aet_str = (const char *)registered_data;
|
||||
if (v && aet_str) {
|
||||
bn_decode_vect(v->gv_aet, aet_str);
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
}
|
||||
}
|
||||
|
||||
static void qtcad_action_mouse_release(const char *event_json, void *, void *event_data) {
|
||||
struct bview *v = (struct bview *)event_data;
|
||||
if (!v) return;
|
||||
|
||||
int cx = 0, cy = 0, mode = 0, dx = 1, dy = 1;
|
||||
char *p;
|
||||
if ((p = strstr(event_json, "\"cx\":"))) sscanf(p + 5, "%d", &cx);
|
||||
if ((p = strstr(event_json, "\"cy\":"))) sscanf(p + 5, "%d", &cy);
|
||||
if ((p = strstr(event_json, "\"mode\":"))) sscanf(p + 7, "%d", &mode);
|
||||
|
||||
int button = 0;
|
||||
if ((p = strstr(event_json, "\"button\":"))) sscanf(p + 9, "%d", &button);
|
||||
|
||||
unsigned long long view_flags = BV_IDLE;
|
||||
if (button == 0) { // Left
|
||||
if (mode != BV_CENTER) { view_flags = BV_SCALE; dx = 10; dy = 5; }
|
||||
else { view_flags = BV_CENTER; dx = cx; dy = cy; }
|
||||
} else if (button == 2) { // Right
|
||||
if (mode == BV_CENTER) return;
|
||||
view_flags = BV_SCALE; dx = 1; dy = 2;
|
||||
} else if (button == 1) { // Middle
|
||||
view_flags = BV_CENTER; dx = cx; dy = cy;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
point_t keypt = VINIT_ZERO;
|
||||
bv_adjust(v, dx, dy, keypt, 0, view_flags);
|
||||
}
|
||||
|
||||
static void qtcad_action_adjust_view(const char *event_json, void *registered_data, void *event_data) {
|
||||
struct bview *v = (struct bview *)event_data;
|
||||
if (!v) return;
|
||||
|
||||
int dx = 0, dy = 0, mode = 0;
|
||||
int view_flags = (int)(intptr_t)registered_data;
|
||||
char *p;
|
||||
|
||||
if ((p = strstr(event_json, "\"dx\":"))) sscanf(p + 5, "%d", &dx);
|
||||
if ((p = strstr(event_json, "\"dy\":"))) sscanf(p + 5, "%d", &dy);
|
||||
if ((p = strstr(event_json, "\"mode\":"))) sscanf(p + 7, "%d", &mode);
|
||||
|
||||
if (view_flags == 0) { // dynamic fallback
|
||||
view_flags = mode;
|
||||
if (mode == BV_CENTER) view_flags = BV_SCALE;
|
||||
}
|
||||
|
||||
if (view_flags == BV_SCALE) {
|
||||
int mdelta = (abs(dx) > abs(dy)) ? dx : -dy;
|
||||
int f = (int)(2*100*(double)abs(mdelta)/(double)v->gv_height);
|
||||
if (mdelta > 0) { dy = 101 + f; dx = 100; }
|
||||
else { dy = 99 - f; dx = 100; }
|
||||
}
|
||||
|
||||
point_t center;
|
||||
MAT_DELTAS_GET_NEG(center, v->gv_center);
|
||||
|
||||
if (view_flags & (BV_ROT | BV_TRANS | BV_SCALE))
|
||||
suspend_drag_bounds_update(v);
|
||||
|
||||
bv_adjust(v, dx, dy, center, 0, view_flags);
|
||||
}
|
||||
|
||||
static void qtcad_action_wheel_scale(const char *event_json, void *, void *event_data) {
|
||||
struct bview *v = (struct bview *)event_data;
|
||||
if (!v) return;
|
||||
|
||||
int delta = 0;
|
||||
char *p;
|
||||
if ((p = strstr(event_json, "\"delta\":"))) sscanf(p + 8, "%d", &delta);
|
||||
|
||||
int dx = 100 + delta;
|
||||
int dy = 100;
|
||||
point_t origin = VINIT_ZERO;
|
||||
bv_adjust(v, dx, dy, origin, 0, BV_SCALE);
|
||||
}
|
||||
|
||||
extern "C" QCAD_EXPORT int qtcad_load_bindings(const char *json_path) {
|
||||
if (bu_binding_load(json_path) < 0) {
|
||||
bu_log("libqtcad: Failed to load bindings from %s\n", json_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!bu_binding_action_is_bound("dm", "view_toggle_adc") ||
|
||||
!bu_binding_action_is_bound("dm", "view_2")) {
|
||||
bu_log("libqtcad Validation Failed: Provided bindings missing required standard actions.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qtcad_init_bindings(void) {
|
||||
if (bindings_loaded) return;
|
||||
bindings_loaded = 1;
|
||||
|
||||
bu_binding_register_action("view_toggle_adc", qtcad_action_toggle_adc, NULL);
|
||||
bu_binding_register_action("view_toggle_model_axes", qtcad_action_toggle_model_axes, NULL);
|
||||
bu_binding_register_action("view_toggle_view_axes", qtcad_action_toggle_view_axes, NULL);
|
||||
|
||||
bu_binding_register_action("view_2", qtcad_action_view_set, (void*)"35 -25 0");
|
||||
bu_binding_register_action("view_3", qtcad_action_view_set, (void*)"35 25 0");
|
||||
bu_binding_register_action("view_4", qtcad_action_view_set, (void*)"45 45 0");
|
||||
bu_binding_register_action("view_5", qtcad_action_view_set, (void*)"145 25 0");
|
||||
bu_binding_register_action("view_6", qtcad_action_view_set, (void*)"215 25 0");
|
||||
bu_binding_register_action("view_7", qtcad_action_view_set, (void*)"325 25 0");
|
||||
bu_binding_register_action("view_front", qtcad_action_view_set, (void*)"0 0 0");
|
||||
bu_binding_register_action("view_top", qtcad_action_view_set, (void*)"270 90 0");
|
||||
bu_binding_register_action("view_bottom", qtcad_action_view_set, (void*)"270 -90 0");
|
||||
bu_binding_register_action("view_left", qtcad_action_view_set, (void*)"90 0 0");
|
||||
bu_binding_register_action("view_rear", qtcad_action_view_set, (void*)"180 0 0");
|
||||
bu_binding_register_action("view_right", qtcad_action_view_set, (void*)"270 0 0");
|
||||
|
||||
bu_binding_register_action("view_mouse_release_left", qtcad_action_mouse_release, NULL);
|
||||
bu_binding_register_action("view_mouse_release_right", qtcad_action_mouse_release, NULL);
|
||||
bu_binding_register_action("view_mouse_release_middle", qtcad_action_mouse_release, NULL);
|
||||
|
||||
bu_binding_register_action("view_mouse_drag_rotate", qtcad_action_adjust_view, (void*)(intptr_t)BV_ROT);
|
||||
bu_binding_register_action("view_mouse_drag_translate", qtcad_action_adjust_view, (void*)(intptr_t)BV_TRANS);
|
||||
bu_binding_register_action("view_mouse_drag_scale", qtcad_action_adjust_view, (void*)(intptr_t)BV_SCALE);
|
||||
bu_binding_register_action("view_mouse_drag_adjust", qtcad_action_adjust_view, (void*)(intptr_t)0);
|
||||
bu_binding_register_action("view_mouse_wheel_scale", qtcad_action_wheel_scale, NULL);
|
||||
}
|
||||
|
||||
static void append_modifiers(struct bu_vls *vls, QInputEvent *e) {
|
||||
if (!e->modifiers()) return;
|
||||
bu_vls_printf(vls, ", \"modifiers\": [");
|
||||
int added = 0;
|
||||
if (e->modifiers().testFlag(Qt::ShiftModifier)) { bu_vls_printf(vls, "\"Shift\""); added=1; }
|
||||
if (e->modifiers().testFlag(Qt::ControlModifier)) { if(added) bu_vls_printf(vls, ", "); bu_vls_printf(vls, "\"Control\""); added=1; }
|
||||
if (e->modifiers().testFlag(Qt::AltModifier)) { if(added) bu_vls_printf(vls, ", "); bu_vls_printf(vls, "\"Alt\""); }
|
||||
bu_vls_printf(vls, "]");
|
||||
}
|
||||
|
||||
int CADkeyPressEvent(struct bview *v, int UNUSED(x_prev), int UNUSED(y_prev), QKeyEvent *k)
|
||||
{
|
||||
QTCAD_EVENT("keyPress", 1);
|
||||
if (!v)
|
||||
return 0;
|
||||
#if 0
|
||||
QString kstr = QKeySequence(k->key()).toString();
|
||||
bu_log("%s\n", kstr.toStdString().c_str());
|
||||
#endif
|
||||
switch (k->key()) {
|
||||
case 'A':
|
||||
v->gv_s->gv_adc.draw = !v->gv_s->gv_adc.draw;
|
||||
return 1;
|
||||
case 'M':
|
||||
v->gv_s->gv_model_axes.draw = !v->gv_s->gv_model_axes.draw;
|
||||
return 1;
|
||||
case 'V':
|
||||
v->gv_s->gv_view_axes.draw = !v->gv_s->gv_view_axes.draw;
|
||||
return 1;
|
||||
case '2':
|
||||
bn_decode_vect(v->gv_aet, "35 -25 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case '3':
|
||||
bn_decode_vect(v->gv_aet, "35 25 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case '4':
|
||||
bn_decode_vect(v->gv_aet, "45 45 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case '5':
|
||||
bn_decode_vect(v->gv_aet, "145 25 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case '6':
|
||||
bn_decode_vect(v->gv_aet, "215 25 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case '7':
|
||||
bn_decode_vect(v->gv_aet, "325 25 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case 'F':
|
||||
bn_decode_vect(v->gv_aet, "0 0 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case 'T':
|
||||
bn_decode_vect(v->gv_aet, "270 90 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case 'B':
|
||||
bn_decode_vect(v->gv_aet, "270 -90 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case 'L':
|
||||
bn_decode_vect(v->gv_aet, "90 0 0");
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
case 'R':
|
||||
if (k->modifiers().testFlag(Qt::ShiftModifier) == true) {
|
||||
bn_decode_vect(v->gv_aet, "180 0 0");
|
||||
} else {
|
||||
bn_decode_vect(v->gv_aet, "270 0 0");
|
||||
}
|
||||
bv_mat_aet(v);
|
||||
bv_update(v);
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
if (!v) return 0;
|
||||
qtcad_init_bindings();
|
||||
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
char key_str[8] = {0};
|
||||
if (k->key() >= 0x20 && k->key() <= 0x7E) {
|
||||
key_str[0] = (char)k->key();
|
||||
} else {
|
||||
QString kstr = QKeySequence(k->key()).toString();
|
||||
strncpy(key_str, kstr.toStdString().c_str(), 7);
|
||||
}
|
||||
return 0;
|
||||
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"key-press\", \"key\": \"%s\"", key_str);
|
||||
append_modifiers(&ev_json, k);
|
||||
bu_vls_printf(&ev_json, "}");
|
||||
|
||||
int handled = bu_binding_process_event("dm", bu_vls_cstr(&ev_json), v);
|
||||
bu_vls_free(&ev_json);
|
||||
return handled;
|
||||
}
|
||||
|
||||
int CADmousePressEvent(struct bview *v, int UNUSED(x_prev), int UNUSED(y_prev), QMouseEvent *e)
|
||||
{
|
||||
QTCAD_EVENT("mousePress", 1);
|
||||
if (!v) return 0;
|
||||
qtcad_init_bindings();
|
||||
|
||||
if (!v)
|
||||
return 0;
|
||||
int button = -1;
|
||||
if (e->buttons().testFlag(Qt::LeftButton)) button = 0;
|
||||
else if (e->buttons().testFlag(Qt::MiddleButton)) button = 1;
|
||||
else if (e->buttons().testFlag(Qt::RightButton)) button = 2;
|
||||
if (button < 0) return 0;
|
||||
|
||||
// If we're intending the mouse motion to do the work,
|
||||
// then the press has to be a no-op. If we're going
|
||||
// to do configurable key bindings, this will take some
|
||||
// thought - if we want unmodded left button to be a
|
||||
// rotation, and Ctrl+Left to do something else, these
|
||||
// checks are all going to have to be exact-flag-combo-only
|
||||
// actions.
|
||||
if (e->modifiers()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (e->buttons().testFlag(Qt::LeftButton)) {
|
||||
//bu_log("Press Left\n");
|
||||
}
|
||||
if (e->buttons().testFlag(Qt::RightButton)) {
|
||||
//bu_log("Press Right\n");
|
||||
}
|
||||
if (e->buttons().testFlag(Qt::MiddleButton)) {
|
||||
//bu_log("Press Middle\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"mouse-press\", \"button\": %d", button);
|
||||
append_modifiers(&ev_json, e);
|
||||
bu_vls_printf(&ev_json, "}");
|
||||
|
||||
int handled = bu_binding_process_event("dm", bu_vls_cstr(&ev_json), v);
|
||||
bu_vls_free(&ev_json);
|
||||
return handled;
|
||||
}
|
||||
|
||||
int CADmouseReleaseEvent(struct bview *v, double x_press, double y_press, int UNUSED(x_prev), int UNUSED(y_prev), QMouseEvent *e, int mode)
|
||||
{
|
||||
QTCAD_EVENT("mouseRelease", 1);
|
||||
|
||||
if (!v)
|
||||
return 0;
|
||||
|
||||
if (!v) return 0;
|
||||
qtcad_init_bindings();
|
||||
restore_drag_bounds_update(v, 1);
|
||||
|
||||
// If we're intending the mouse motion to do the work,
|
||||
// then the release has to be a no-op. If we're going
|
||||
// to do configurable key bindings, this will take some
|
||||
// thought - if we want unmodded left button to be a
|
||||
// rotation, and Ctrl+Left to do something else, these
|
||||
// checks are all going to have to be exact-flag-combo-only
|
||||
// actions.
|
||||
if (e->modifiers()) {
|
||||
return 0;
|
||||
}
|
||||
if (e->buttons().testFlag(Qt::LeftButton) || e->buttons().testFlag(Qt::RightButton)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double cx, cy;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
cx = (double)e->x();
|
||||
cy = (double)e->y();
|
||||
double cx = e->x();
|
||||
double cy = e->y();
|
||||
#else
|
||||
cx = e->position().x();
|
||||
cy = e->position().y();
|
||||
double cx = e->position().x();
|
||||
double cy = e->position().y();
|
||||
#endif
|
||||
if ((fabs(cx - x_press) > 10) || (fabs(cy - y_press) > 10))
|
||||
return 0;
|
||||
|
||||
int dx = 1;
|
||||
int dy = 1;
|
||||
unsigned long long view_flags = BV_IDLE;
|
||||
if ((fabs(cx - x_press) > 10) || (fabs(cy - y_press) > 10)) return 0;
|
||||
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
//bu_log("Release Left\n");
|
||||
if (mode != BV_CENTER) {
|
||||
view_flags = BV_SCALE;
|
||||
dx = 10;
|
||||
dy = 5;
|
||||
} else {
|
||||
view_flags = BV_CENTER;
|
||||
dx = (int)cx;
|
||||
dy = (int)cy;
|
||||
}
|
||||
}
|
||||
if (e->button() == Qt::RightButton) {
|
||||
//bu_log("Release Right\n");
|
||||
if (mode == BV_CENTER)
|
||||
return 0;
|
||||
view_flags = BV_SCALE;
|
||||
dx = 1;
|
||||
dy = 2;
|
||||
}
|
||||
int button = -1;
|
||||
if (e->button() == Qt::LeftButton) button = 0;
|
||||
else if (e->button() == Qt::MiddleButton) button = 1;
|
||||
else if (e->button() == Qt::RightButton) button = 2;
|
||||
if (button < 0) return 0;
|
||||
|
||||
if (e->button() == Qt::MiddleButton) {
|
||||
//bu_log("Release Center\n");
|
||||
view_flags = BV_CENTER;
|
||||
dx = (int)cx;
|
||||
dy = (int)cy;
|
||||
}
|
||||
|
||||
point_t keypt = VINIT_ZERO;
|
||||
return bv_adjust(v, dx, dy, keypt, 0, view_flags);
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"mouse-release\", \"button\": %d, \"cx\": %d, \"cy\": %d, \"mode\": %d", button, (int)cx, (int)cy, mode);
|
||||
append_modifiers(&ev_json, e);
|
||||
bu_vls_printf(&ev_json, "}");
|
||||
|
||||
int handled = bu_binding_process_event("dm", bu_vls_cstr(&ev_json), v);
|
||||
bu_vls_free(&ev_json);
|
||||
return handled;
|
||||
}
|
||||
|
||||
int CADmouseMoveEvent(struct bview *v, int x_prev, int y_prev, QMouseEvent *e, int mode)
|
||||
{
|
||||
QTCAD_EVENT("mouseMove", 2);
|
||||
if (!v) return 0;
|
||||
qtcad_init_bindings();
|
||||
|
||||
if (!v)
|
||||
return 0;
|
||||
|
||||
unsigned long long view_flags = BV_IDLE;
|
||||
|
||||
if (x_prev == -INT_MAX) {
|
||||
//x_prev = e->x();
|
||||
//y_prev = e->y();
|
||||
return 0;
|
||||
}
|
||||
|
||||
view_flags = mode;
|
||||
if (mode == BV_CENTER)
|
||||
view_flags = BV_SCALE;
|
||||
|
||||
if (e->buttons().testFlag(Qt::LeftButton)) {
|
||||
//bu_log("Left\n");
|
||||
|
||||
if (e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||
//bu_log("Ctrl+Left\n");
|
||||
view_flags = BV_ROT;
|
||||
}
|
||||
|
||||
if (e->modifiers().testFlag(Qt::ShiftModifier)) {
|
||||
//bu_log("Shift+Left\n");
|
||||
view_flags = BV_TRANS;
|
||||
}
|
||||
|
||||
if (e->modifiers().testFlag(Qt::ShiftModifier) && e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||
//bu_log("Ctrl+Shift+Left\n");
|
||||
view_flags = BV_SCALE;
|
||||
}
|
||||
}
|
||||
|
||||
if (e->buttons().testFlag(Qt::MiddleButton)) {
|
||||
//bu_log("Middle\n");
|
||||
}
|
||||
|
||||
if (e->buttons().testFlag(Qt::RightButton)) {
|
||||
//bu_log("Right\n");
|
||||
}
|
||||
|
||||
if (!e->buttons().testFlag(Qt::LeftButton))
|
||||
return 0;
|
||||
if (x_prev == -INT_MAX) return 0;
|
||||
if (!e->buttons().testFlag(Qt::LeftButton)) return 0;
|
||||
|
||||
long long now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
std::unordered_map<struct bview *, long long>::iterator ts_it = drag_update_ts.find(v);
|
||||
if (ts_it != drag_update_ts.end() && (now_ms - ts_it->second) < drag_update_interval_ms)
|
||||
return -1;
|
||||
if (ts_it != drag_update_ts.end() && (now_ms - ts_it->second) < drag_update_interval_ms) return -1;
|
||||
drag_update_ts[v] = now_ms;
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
@@ -319,53 +334,33 @@ int CADmouseMoveEvent(struct bview *v, int x_prev, int y_prev, QMouseEvent *e, i
|
||||
int dy = e->position().y() - y_prev;
|
||||
#endif
|
||||
|
||||
if (view_flags == BV_SCALE) {
|
||||
// Build in some sensitivity to how much the mouse moved when doing
|
||||
// a motion based scale
|
||||
int mdelta = (abs(dx) > abs(dy)) ? dx : -dy;
|
||||
int f = (int)(2*100*(double)abs(mdelta)/(double)v->gv_height);
|
||||
|
||||
if (mdelta > 0) {
|
||||
dy = 101 + f;
|
||||
dx = 100;
|
||||
} else {
|
||||
dy = 99 - f;
|
||||
dx = 100;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO - the key point and the mode/flags are all hardcoded
|
||||
// right now, but eventually for shift grips they will need to
|
||||
// respond to the various mod keys. The intent is to set flags
|
||||
// based on which mod keys are set to allow bv_adjust to
|
||||
// do the correct math.
|
||||
point_t center;
|
||||
MAT_DELTAS_GET_NEG(center, v->gv_center);
|
||||
|
||||
if (view_flags & (BV_ROT | BV_TRANS | BV_SCALE))
|
||||
suspend_drag_bounds_update(v);
|
||||
|
||||
return bv_adjust(v, dx, dy, center, 0, view_flags);
|
||||
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"mouse-move\", \"button\": 0, \"dx\": %d, \"dy\": %d, \"mode\": %d", dx, dy, mode);
|
||||
append_modifiers(&ev_json, e);
|
||||
bu_vls_printf(&ev_json, "}");
|
||||
|
||||
int handled = bu_binding_process_event("dm", bu_vls_cstr(&ev_json), v);
|
||||
bu_vls_free(&ev_json);
|
||||
return handled;
|
||||
}
|
||||
|
||||
int CADwheelEvent(struct bview *v, QWheelEvent *e)
|
||||
{
|
||||
QTCAD_EVENT("mouseWheel", 1);
|
||||
|
||||
if (!v)
|
||||
return 0;
|
||||
if (!v) return 0;
|
||||
qtcad_init_bindings();
|
||||
|
||||
QPoint delta = e->angleDelta();
|
||||
int mdelta = -1* delta.y() / 8;
|
||||
|
||||
int dx = 100 + mdelta;
|
||||
int dy = 100;
|
||||
|
||||
point_t origin = VINIT_ZERO;
|
||||
return bv_adjust(v, dx, dy, origin, 0, BV_SCALE);
|
||||
int mdelta = -1 * delta.y() / 8;
|
||||
|
||||
struct bu_vls ev_json = BU_VLS_INIT_ZERO;
|
||||
bu_vls_sprintf(&ev_json, "{\"type\": \"mouse-wheel\", \"delta\": %d", mdelta);
|
||||
append_modifiers(&ev_json, e);
|
||||
bu_vls_printf(&ev_json, "}");
|
||||
|
||||
int handled = bu_binding_process_event("dm", bu_vls_cstr(&ev_json), v);
|
||||
bu_vls_free(&ev_json);
|
||||
return handled;
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
|
||||
+180
-234
@@ -49,6 +49,7 @@
|
||||
#include "bu/path.h"
|
||||
#include "bu/process.h"
|
||||
#include "bu/units.h"
|
||||
#include "bu/binding.h"
|
||||
#include "vmath.h"
|
||||
#include "rt/db4.h"
|
||||
#include "rt/geom.h"
|
||||
@@ -2824,6 +2825,148 @@ to_data_vZ(struct ged *gedp,
|
||||
}
|
||||
|
||||
|
||||
static void tclcad_action_eval_aet(const struct bu_vls *ev, void *reg, void *event_data) {
|
||||
struct bview *gdvp = (struct bview *)event_data;
|
||||
const char *aet_args = (const char *)reg;
|
||||
struct bu_vls cmd = BU_VLS_INIT_ZERO;
|
||||
bu_vls_printf(&cmd, "%s aet %s %s", bu_vls_cstr(¤t_top->to_gedp->go_name), bu_vls_cstr(&gdvp->gv_name), aet_args);
|
||||
Tcl_Eval(current_top->to_interp, bu_vls_cstr(&cmd));
|
||||
bu_vls_free(&cmd);
|
||||
}
|
||||
|
||||
static void tclcad_action_mouse_drag(const struct bu_vls *ev, void *reg, void *event_data) {
|
||||
struct bview *gdvp = (struct bview *)event_data;
|
||||
const char *cmd_name = (const char *)reg;
|
||||
int cx = gdvp->gv_prevMouseX;
|
||||
int cy = gdvp->gv_prevMouseY;
|
||||
char *p;
|
||||
if ((p = strstr(bu_vls_cstr(ev), "\"cx\":"))) sscanf(p + 5, "%d", &cx);
|
||||
if ((p = strstr(bu_vls_cstr(ev), "\"cy\":"))) sscanf(p + 5, "%d", &cy);
|
||||
|
||||
struct bu_vls cmd = BU_VLS_INIT_ZERO;
|
||||
bu_vls_printf(&cmd, "%s %s %s %d %d", bu_vls_cstr(¤t_top->to_gedp->go_name), cmd_name, bu_vls_cstr(&gdvp->gv_name), cx, cy);
|
||||
Tcl_Eval(current_top->to_interp, bu_vls_cstr(&cmd));
|
||||
bu_vls_free(&cmd);
|
||||
}
|
||||
|
||||
static void tclcad_action_wheel_scale(const struct bu_vls *ev, void *reg, void *event_data) {
|
||||
struct bview *gdvp = (struct bview *)event_data;
|
||||
int delta = 0;
|
||||
char *p;
|
||||
if ((p = strstr(bu_vls_cstr(ev), "\"delta\":"))) sscanf(p + 8, "%d", &delta);
|
||||
const char *sf = (delta > 0) ? "1.1" : "0.9";
|
||||
struct bu_vls cmd = BU_VLS_INIT_ZERO;
|
||||
bu_vls_printf(&cmd, "%s zoom %s %s", bu_vls_cstr(¤t_top->to_gedp->go_name), bu_vls_cstr(&gdvp->gv_name), sf);
|
||||
Tcl_Eval(current_top->to_interp, bu_vls_cstr(&cmd));
|
||||
bu_vls_free(&cmd);
|
||||
}
|
||||
|
||||
static void tclcad_action_mouse_click(const struct bu_vls *ev, void *reg, void *event_data) {
|
||||
struct bview *gdvp = (struct bview *)event_data;
|
||||
const char *cmd_format = (const char *)reg;
|
||||
int cx = gdvp->gv_prevMouseX;
|
||||
int cy = gdvp->gv_prevMouseY;
|
||||
char *p;
|
||||
if ((p = strstr(bu_vls_cstr(ev), "\"cx\":"))) sscanf(p + 5, "%d", &cx);
|
||||
if ((p = strstr(bu_vls_cstr(ev), "\"cy\":"))) sscanf(p + 5, "%d", &cy);
|
||||
|
||||
struct bu_vls cmd = BU_VLS_INIT_ZERO;
|
||||
bu_vls_printf(&cmd, cmd_format, bu_vls_cstr(¤t_top->to_gedp->go_name), bu_vls_cstr(&gdvp->gv_name), cx, cy);
|
||||
Tcl_Eval(current_top->to_interp, bu_vls_cstr(&cmd));
|
||||
bu_vls_free(&cmd);
|
||||
}
|
||||
|
||||
static int bindings_loaded_mged = 0;
|
||||
|
||||
static int tclcad_load_bindings_cmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[]) {
|
||||
if (argc < 2) {
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " <json_path>\"", NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
char *path = (char *)bu_dir(NULL, 0, BU_DIR_DATA, argv[1], NULL);
|
||||
if (!path) {
|
||||
Tcl_AppendResult(interp, "Failed to resolve path: ", argv[1], NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if (bu_binding_load(path) < 0) {
|
||||
bu_free(path, "path");
|
||||
Tcl_AppendResult(interp, "Failed to load bindings from: ", argv[1], NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
bu_free(path, "path");
|
||||
|
||||
/* Validate that application provided required bindings */
|
||||
if (!bu_binding_action_is_bound("dm", "view_mouse_drag_rotate") ||
|
||||
!bu_binding_action_is_bound("dm", "view_mouse_drag_translate") ||
|
||||
!bu_binding_action_is_bound("dm", "view_mouse_drag_scale")) {
|
||||
Tcl_AppendResult(interp, "Validation Failed: The provided bindings do not map inputs for view_mouse_drag_rotate/translate/scale. These are required standard actions.", NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int tclcad_dispatch_event_cmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[]) {
|
||||
if (argc < 8) return TCL_OK;
|
||||
struct tclcad_obj *to = (struct tclcad_obj *)clientData;
|
||||
struct bview *gdvp = bv_set_find_view(&to->to_gedp->ged_views, argv[2]);
|
||||
if (!gdvp) return TCL_OK;
|
||||
|
||||
int x = atoi(argv[5]);
|
||||
int y = atoi(argv[6]);
|
||||
int state = atoi(argv[7]);
|
||||
|
||||
struct bu_vls ev = BU_VLS_INIT_ZERO;
|
||||
bu_vls_printf(&ev, "{\"type\": \"%s\"", argv[3]);
|
||||
|
||||
if (!strcmp(argv[3], "key-press")) {
|
||||
char key_str[16] = {0};
|
||||
bu_strlcpy(key_str, argv[4], sizeof(key_str));
|
||||
if (strlen(key_str) == 1 && key_str[0] >= 'a' && key_str[0] <= 'z') {
|
||||
key_str[0] = key_str[0] - 'a' + 'A';
|
||||
}
|
||||
bu_vls_printf(&ev, ", \"key\": \"%s\"", key_str);
|
||||
} else if (!strcmp(argv[3], "mouse-wheel")) {
|
||||
int delta = (argc > 8) ? atoi(argv[8]) : 0;
|
||||
int mdelta = -1 * delta / 120;
|
||||
bu_vls_printf(&ev, ", \"delta\": %d", mdelta);
|
||||
} else {
|
||||
int btn = atoi(argv[4]) - 1;
|
||||
if (!strcmp(argv[3], "mouse-move")) {
|
||||
if (state & 0x100) btn = 0;
|
||||
else if (state & 0x200) btn = 1;
|
||||
else if (state & 0x400) btn = 2;
|
||||
else btn = -1;
|
||||
|
||||
int dx = x - gdvp->gv_prevMouseX;
|
||||
int dy = y - gdvp->gv_prevMouseY;
|
||||
bu_vls_printf(&ev, ", \"dx\": %d, \"dy\": %d, \"mode\": 0", dx, dy);
|
||||
}
|
||||
if (btn >= 0) bu_vls_printf(&ev, ", \"button\": %d", btn);
|
||||
bu_vls_printf(&ev, ", \"cx\": %d, \"cy\": %d", x, y);
|
||||
}
|
||||
|
||||
if (state & (1 | 4 | 8)) {
|
||||
bu_vls_printf(&ev, ", \"modifiers\": [");
|
||||
int added = 0;
|
||||
if (state & 1) { bu_vls_printf(&ev, "\"Shift\""); added=1; }
|
||||
if (state & 4) { if(added) bu_vls_printf(&ev, ", "); bu_vls_printf(&ev, "\"Control\""); added=1; }
|
||||
if (state & 8) { if(added) bu_vls_printf(&ev, ", "); bu_vls_printf(&ev, "\"Alt\""); }
|
||||
bu_vls_printf(&ev, "]");
|
||||
}
|
||||
bu_vls_printf(&ev, "}");
|
||||
|
||||
bu_binding_process_event("dm", bu_vls_cstr(&ev), gdvp);
|
||||
|
||||
gdvp->gv_prevMouseX = x;
|
||||
gdvp->gv_prevMouseY = y;
|
||||
|
||||
bu_vls_free(&ev);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
to_init_default_bindings(struct bview *gdvp)
|
||||
{
|
||||
@@ -2832,6 +2975,35 @@ to_init_default_bindings(struct bview *gdvp)
|
||||
if (dm_get_pathname((struct dm *)gdvp->dmp)) {
|
||||
struct bu_vls *pathvls = dm_get_pathname((struct dm *)gdvp->dmp);
|
||||
if (pathvls) {
|
||||
if (!bindings_loaded_mged) {
|
||||
bindings_loaded_mged = 1;
|
||||
|
||||
|
||||
bu_binding_register_action("view_2", tclcad_action_eval_aet, (void*)"35 -25 0");
|
||||
bu_binding_register_action("view_3", tclcad_action_eval_aet, (void*)"35 25 0");
|
||||
bu_binding_register_action("view_4", tclcad_action_eval_aet, (void*)"45 45 0");
|
||||
bu_binding_register_action("view_5", tclcad_action_eval_aet, (void*)"145 25 0");
|
||||
bu_binding_register_action("view_6", tclcad_action_eval_aet, (void*)"215 25 0");
|
||||
bu_binding_register_action("view_7", tclcad_action_eval_aet, (void*)"325 25 0");
|
||||
bu_binding_register_action("view_front", tclcad_action_eval_aet, (void*)"0 0 0");
|
||||
bu_binding_register_action("view_top", tclcad_action_eval_aet, (void*)"270 90 0");
|
||||
bu_binding_register_action("view_bottom", tclcad_action_eval_aet, (void*)"270 -90 0");
|
||||
bu_binding_register_action("view_left", tclcad_action_eval_aet, (void*)"90 0 0");
|
||||
bu_binding_register_action("view_rear", tclcad_action_eval_aet, (void*)"180 0 0");
|
||||
bu_binding_register_action("view_right", tclcad_action_eval_aet, (void*)"270 0 0");
|
||||
|
||||
bu_binding_register_action("view_mouse_drag_rotate", tclcad_action_mouse_drag, (void*)"mouse_rot");
|
||||
bu_binding_register_action("view_mouse_drag_translate", tclcad_action_mouse_drag, (void*)"mouse_trans");
|
||||
bu_binding_register_action("view_mouse_drag_scale", tclcad_action_mouse_drag, (void*)"mouse_scale");
|
||||
bu_binding_register_action("view_mouse_wheel_scale", tclcad_action_wheel_scale, NULL);
|
||||
|
||||
bu_binding_register_action("view_mouse_press_left", tclcad_action_mouse_click, (void*)"%s zoom %s 0.5");
|
||||
bu_binding_register_action("view_mouse_press_right", tclcad_action_mouse_click, (void*)"%s zoom %s 2.0");
|
||||
|
||||
Tcl_CreateCommand(current_top->to_interp, "tclcad_dispatch_event", tclcad_dispatch_event_cmd, (ClientData)current_top, (Tcl_CmdDeleteProc *)NULL);
|
||||
Tcl_CreateCommand(current_top->to_interp, "tclcad_load_bindings", tclcad_load_bindings_cmd, (ClientData)current_top, (Tcl_CmdDeleteProc *)NULL);
|
||||
}
|
||||
|
||||
bu_vls_printf(&bindings, "bind %s <Configure> {%s configure %s; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
@@ -2848,240 +3020,14 @@ to_init_default_bindings(struct bview *gdvp)
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Mouse Bindings */
|
||||
bu_vls_printf(&bindings, "bind %s <2> {%s vslew %s %%x %%y; focus %s; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name),
|
||||
bu_vls_addr(pathvls));
|
||||
bu_vls_printf(&bindings, "bind %s <1> {%s zoom %s 0.5; focus %s; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name),
|
||||
bu_vls_addr(pathvls));
|
||||
bu_vls_printf(&bindings, "bind %s <3> {%s zoom %s 2.0; focus %s; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name),
|
||||
bu_vls_addr(pathvls));
|
||||
bu_vls_printf(&bindings, "bind %s <4> {%s zoom %s 1.1; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <5> {%s zoom %s 0.9; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <MouseWheel> {if {%%D < 0} {%s zoom %s 0.9} else {%s zoom %s 1.1}; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Idle Mode */
|
||||
bu_vls_printf(&bindings, "bind %s <ButtonRelease> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <KeyRelease-Control_L> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <KeyRelease-Control_R> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <KeyRelease-Shift_L> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <KeyRelease-Shift_R> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <KeyRelease-Alt_L> {%s idle_mode %s; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <KeyRelease-Alt_R> {%s idle_mode %s; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Rotate Mode */
|
||||
bu_vls_printf(&bindings, "bind %s <Control-ButtonRelease-1> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-ButtonPress-1> {%s rotate_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-ButtonPress-2> {%s rotate_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-ButtonPress-3> {%s rotate_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Translate Mode */
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-ButtonRelease-1> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-ButtonPress-1> {%s translate_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-ButtonPress-2> {%s translate_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-ButtonPress-3> {%s translate_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Scale Mode */
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Shift-ButtonRelease-1> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Shift-ButtonPress-1> {%s scale_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Shift-ButtonPress-2> {%s scale_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Shift-ButtonPress-3> {%s scale_mode %s %%x %%y}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Constrained Rotate Mode */
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Lock-ButtonRelease-1> {%s idle_mode %s}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Lock-ButtonPress-1> {%s constrain_rmode %s x %%x %%y; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Lock-ButtonPress-2> {%s constrain_rmode %s y %%x %%y; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Control-Lock-ButtonPress-3> {%s constrain_rmode %s z %%x %%y; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Constrained Translate Mode */
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-Lock-ButtonRelease-1> {%s idle_mode %s; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-Lock-ButtonPress-1> {%s constrain_tmode %s x %%x %%y; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-Lock-ButtonPress-2> {%s constrain_tmode %s y %%x %%y; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Shift-Lock-ButtonPress-3> {%s constrain_tmode %s z %%x %%y; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
/* Key Bindings */
|
||||
bu_vls_printf(&bindings, "bind %s 3 {%s aet %s 35 25; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s 4 {%s aet %s 45 45; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s f {%s aet %s 0 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s F {%s aet %s 0 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s R {%s aet %s 180 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s r {%s aet %s 270 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s l {%s aet %s 90 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s L {%s aet %s 90 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s t {%s aet %s 270 90; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s T {%s aet %s 270 90; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s b {%s aet %s 270 -90; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s B {%s aet %s 270 -90; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s + {%s zoom %s 2.0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s = {%s zoom %s 2.0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s _ {%s zoom %s 0.5; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s - {%s zoom %s 0.5; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Key-Left> {%s rot %s -v 0 1 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Key-Right> {%s rot %s -v 0 -1 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Key-Up> {%s rot %s -v 1 0 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Key-Down> {%s rot %s -v -1 0 0; break}; ",
|
||||
bu_vls_addr(pathvls),
|
||||
bu_vls_addr(¤t_top->to_gedp->go_name),
|
||||
bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
Tcl_Eval(current_top->to_interp, bu_vls_addr(&bindings));
|
||||
/* Dispatch all events to bu_binding */
|
||||
bu_vls_printf(&bindings, "bind %s <Any-KeyPress> {tclcad_dispatch_event %%W %s %s key-press %%K %%x %%y %%s; break}; ", bu_vls_addr(pathvls), bu_vls_addr(¤t_top->to_gedp->go_name), bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Any-ButtonPress> {tclcad_dispatch_event %%W %s %s mouse-press %%b %%x %%y %%s; break}; ", bu_vls_addr(pathvls), bu_vls_addr(¤t_top->to_gedp->go_name), bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Any-ButtonRelease> {tclcad_dispatch_event %%W %s %s mouse-release %%b %%x %%y %%s; break}; ", bu_vls_addr(pathvls), bu_vls_addr(¤t_top->to_gedp->go_name), bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <Any-Motion> {tclcad_dispatch_event %%W %s %s mouse-move 0 %%x %%y %%s; break}; ", bu_vls_addr(pathvls), bu_vls_addr(¤t_top->to_gedp->go_name), bu_vls_addr(&gdvp->gv_name));
|
||||
bu_vls_printf(&bindings, "bind %s <MouseWheel> {tclcad_dispatch_event %%W %s %s mouse-wheel 0 %%x %%y %%s %%D; break}; ", bu_vls_addr(pathvls), bu_vls_addr(¤t_top->to_gedp->go_name), bu_vls_addr(&gdvp->gv_name));
|
||||
|
||||
Tcl_Eval(current_top->to_interp, bu_vls_cstr(&bindings));
|
||||
}
|
||||
}
|
||||
bu_vls_free(&bindings);
|
||||
|
||||
@@ -49,12 +49,8 @@ proc mged_bind_dm { w } {
|
||||
|
||||
bind $w <Enter> [list mged_activate_dm $w]
|
||||
|
||||
#default mouse bindings
|
||||
default_mouse_bindings $w
|
||||
# Load standardized JSON bindings for mged
|
||||
|
||||
#default key bindings
|
||||
set forwarding_key($w) 0
|
||||
default_key_bindings $w
|
||||
}
|
||||
|
||||
proc print_return_val str {
|
||||
@@ -64,129 +60,6 @@ proc print_return_val str {
|
||||
}
|
||||
}
|
||||
|
||||
if ![info exists mged_default(dm_key_bindings)] {
|
||||
set mged_default(dm_key_bindings) "\tKey Sequence\t\tBehavior
|
||||
\ta\t\t\ttoggle angle distance cursor (ADC)
|
||||
\te\t\t\ttoggle edit axes
|
||||
\tm\t\t\ttoggle model axes
|
||||
\tv\t\t\ttoggle view axes
|
||||
\ti\t\t\tadvance illumination pointer forward
|
||||
\tI\t\t\tadvance illumination pointer backward
|
||||
\tp\t\t\tsimulate mouse press (i.e. to pick a solid)
|
||||
\t3\t\t\tview - ae 35 25
|
||||
\t4\t\t\tview - ae 45 45
|
||||
\tf\t\t\tfront view
|
||||
\tt\t\t\ttop view
|
||||
\tb\t\t\tbottom view
|
||||
\tl\t\t\tleft view
|
||||
\tr\t\t\tright view
|
||||
\tR\t\t\trear view
|
||||
\ts\t\t\tenter solid illumination state
|
||||
\to\t\t\tenter object illumination state
|
||||
\tq\t\t\treject edit
|
||||
\tu\t\t\tzero knobs and sliders
|
||||
\t<F1>\t\t\ttoggle depthcue
|
||||
\t<F2>\t\t\ttoggle zclip
|
||||
\t<F3>\t\t\ttoggle perspective
|
||||
\t<F4>\t\t\ttoggle zbuffer
|
||||
\t<F5>\t\t\ttoggle lighting
|
||||
\t<F6>\t\t\ttoggle perspective angle
|
||||
\t<F7>\t\t\ttoggle faceplate
|
||||
\t<F8>\t\t\ttoggle faceplate GUI
|
||||
\t<F9>\t\t\ttoggle keystroke forwarding
|
||||
\t<F12>\t\t\tzero knobs
|
||||
\t<Left>\t\t\tabsolute rotate about y axis
|
||||
\t<Right>\t\t\tabsolute rotate about y axis
|
||||
\t<Down>\t\t\tabsolute rotate about x axis
|
||||
\t<Up>\t\t\tabsolute rotate about x axis
|
||||
\t<Shift-Left>\t\tabsolute translate in X direction
|
||||
\t<Shift-Right>\t\tabsolute translate in X direction
|
||||
\t<Shift-Down>\t\tabsolute translate in Z direction
|
||||
\t<Shift-Up>\t\tabsolute translate in Z direction
|
||||
\t<Control-Shift-Left>\tabsolute rotate about z axis
|
||||
\t<Control-Shift-Right>\tabsolute rotate about z axis
|
||||
\t<Control-Shift-Down>\tabsolute translate in Y direction
|
||||
\t<Control-Shift-Up>\tabsolute translate in Y direction
|
||||
\t<Control-n>\t\tgoto next view
|
||||
\t<Control-p>\t\tgoto previous view
|
||||
\t<Control-t>\t\ttoggle between the current view and the last view"
|
||||
}
|
||||
|
||||
proc default_key_bindings { w } {
|
||||
# common view shortcuts
|
||||
bind $w 2 "winset $w; ae 35 -25; break"
|
||||
bind $w 3 "winset $w; press 35,25; break"
|
||||
bind $w 4 "winset $w; press 45,45; break"
|
||||
bind $w 5 "winset $w; ae 145 25; break"
|
||||
bind $w 6 "winset $w; ae 215 25; break"
|
||||
bind $w 7 "winset $w; ae 325 25; break"
|
||||
|
||||
# default key shortcuts
|
||||
bind $w a "winset $w; adc; break"
|
||||
bind $w b "winset $w; press bottom; break"
|
||||
bind $w c "open_cmd_win id_0"
|
||||
bind $w e "winset $w; rset ax edit_draw !; update_gui $w edit_draw \[rset ax edit_draw\]; break"
|
||||
bind $w f "winset $w; press front; break"
|
||||
bind $w i "winset $w; aip f; break"
|
||||
bind $w l "winset $w; press left; break"
|
||||
bind $w m "winset $w; rset ax model_draw !; update_gui $w model_draw \[rset ax model_draw\]; break"
|
||||
bind $w o "winset $w; press oill; break"
|
||||
bind $w p "winset $w; M 1 0 0; break"
|
||||
bind $w q "winset $w; press reject; break"
|
||||
bind $w r "winset $w; press right; break"
|
||||
bind $w s "winset $w; press sill; break"
|
||||
bind $w t "winset $w; press top; break"
|
||||
bind $w u "winset $w; svb; break"
|
||||
bind $w v "winset $w; rset ax view_draw !; update_gui $w view_draw \[rset ax view_draw\]; break"
|
||||
|
||||
# default shift+key shortcuts
|
||||
bind $w A "winset $w; press accept; break"
|
||||
bind $w I "winset $w; aip b; break"
|
||||
bind $w N "winset $w; puts \[nirt -b\]; break"
|
||||
bind $w P "winset $w; catch {sed_apply}; break"
|
||||
bind $w R "winset $w; press rear; break"
|
||||
bind $w S "winset $w; catch {sed_reset}; break"
|
||||
|
||||
# default control+key shortcuts
|
||||
bind $w <Control-n> "winset $w; _mged_view_ring next; break"
|
||||
bind $w <Control-p> "winset $w; _mged_view_ring prev; break"
|
||||
bind $w <Control-t> "winset $w; _mged_view_ring toggle; break"
|
||||
|
||||
# shift grips navigation
|
||||
bind $w <Control-Shift-Down> "winset $w; knob -i aY \$mged_default(tran_factor); break"
|
||||
bind $w <Control-Shift-Left> "winset $w; knob -i az \$mged_default(rot_factor); break"
|
||||
bind $w <Control-Shift-Right> "winset $w; knob -i az -\$mged_default(rot_factor); break"
|
||||
bind $w <Control-Shift-Up> "winset $w; knob -i aY -\$mged_default(tran_factor); break"
|
||||
bind $w <Down> "winset $w; knob -i ax \$mged_default(rot_factor); break"
|
||||
bind $w <Left> "winset $w; knob -i ay -\$mged_default(rot_factor); break"
|
||||
bind $w <Right> "winset $w; knob -i ay \$mged_default(rot_factor); break"
|
||||
bind $w <Up> "winset $w; knob -i ax -\$mged_default(rot_factor); break"
|
||||
bind $w <Shift-Down> "winset $w; knob -i aZ -\$mged_default(tran_factor); break"
|
||||
bind $w <Shift-Left> "winset $w; knob -i aX \$mged_default(tran_factor); break"
|
||||
bind $w <Shift-Right> "winset $w; knob -i aX -\$mged_default(tran_factor); break"
|
||||
bind $w <Shift-Up> "winset $w; knob -i aZ \$mged_default(tran_factor); break"
|
||||
|
||||
# function keys (settings)
|
||||
bind $w <F1> "winset $w; dm set depthcue !; update_gui $w depthcue \[dm set depthcue\]; break"
|
||||
bind $w <F2> "winset $w; dm set zclip !; update_gui $w zclip \[dm set zclip\]; break"
|
||||
bind $w <F3> "winset $w; set perspective_mode !; update_gui $w perspective_mode \$perspective_mode; break"
|
||||
bind $w <F4> "winset $w; dm set zbuffer !; update_gui $w zbuffer \[dm set zbuffer\]; break"
|
||||
bind $w <F5> "winset $w; dm set lighting !; update_gui $w lighting \[dm set lighting\]; break"
|
||||
bind $w <F6> "winset $w; set toggle_perspective !; break"
|
||||
bind $w <F7> "winset $w; set faceplate !; update_gui $w faceplate \$faceplate; break"
|
||||
bind $w <F8> "winset $w; set orig_gui !; update_gui $w orig_gui \$orig_gui; break"
|
||||
bind $w <F9> "toggle_forward_key_bindings $w; update_gui $w forward_keys \$forwarding_key($w); break"
|
||||
bind $w <F12> "winset $w; knob zero; break"
|
||||
|
||||
# interrupt
|
||||
bind $w <Escape> "winset $w; reset_everything $w; break"
|
||||
|
||||
# Throw away other key events
|
||||
bind $w <KeyPress> {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
proc reset_everything { w } {
|
||||
global mged_gui
|
||||
|
||||
@@ -202,274 +75,6 @@ proc reset_everything { w } {
|
||||
set_mouse_behavior $id
|
||||
}
|
||||
|
||||
proc set_forward_keys { w val } {
|
||||
global forwarding_key
|
||||
|
||||
set forwarding_key($w) $val
|
||||
if {$forwarding_key($w)} {
|
||||
forward_key_bindings $w
|
||||
} else {
|
||||
default_key_bindings $w
|
||||
}
|
||||
}
|
||||
|
||||
proc toggle_forward_key_bindings { w } {
|
||||
global forwarding_key
|
||||
|
||||
if {$forwarding_key($w)} {
|
||||
default_key_bindings $w
|
||||
set forwarding_key($w) 0
|
||||
} else {
|
||||
forward_key_bindings $w
|
||||
set forwarding_key($w) 1
|
||||
}
|
||||
}
|
||||
|
||||
proc forward_key_bindings { w } {
|
||||
set id [get_player_id_dm $w]
|
||||
|
||||
if {$id == "mged"} {
|
||||
return
|
||||
}
|
||||
|
||||
# First, unset the default key bindings
|
||||
bind $w a {}
|
||||
bind $w e {}
|
||||
bind $w m {}
|
||||
bind $w v {}
|
||||
bind $w i {}
|
||||
bind $w I {}
|
||||
bind $w p {}
|
||||
bind $w 2 {}
|
||||
bind $w 3 {}
|
||||
bind $w 4 {}
|
||||
bind $w 5 {}
|
||||
bind $w 6 {}
|
||||
bind $w 7 {}
|
||||
bind $w f {}
|
||||
bind $w t {}
|
||||
bind $w b {}
|
||||
bind $w l {}
|
||||
bind $w N {}
|
||||
bind $w r {}
|
||||
bind $w R {}
|
||||
bind $w s {}
|
||||
bind $w o {}
|
||||
bind $w q {}
|
||||
bind $w u {}
|
||||
bind $w c {}
|
||||
bind $w A {}
|
||||
bind $w P {}
|
||||
bind $w S {}
|
||||
bind $w <F1> {}
|
||||
bind $w <F2> {}
|
||||
bind $w <F3> {}
|
||||
bind $w <F4> {}
|
||||
bind $w <F5> {}
|
||||
bind $w <F6> {}
|
||||
bind $w <F7> {}
|
||||
bind $w <F8> {}
|
||||
bind $w <F12> {}
|
||||
|
||||
bind $w <Left> {}
|
||||
bind $w <Right> {}
|
||||
bind $w <Down> {}
|
||||
bind $w <Up> {}
|
||||
bind $w <Shift-Left> {}
|
||||
bind $w <Shift-Right> {}
|
||||
bind $w <Shift-Down> {}
|
||||
bind $w <Shift-Up> {}
|
||||
bind $w <Control-Shift-Left> {}
|
||||
bind $w <Control-Shift-Right> {}
|
||||
bind $w <Control-Shift-Down> {}
|
||||
bind $w <Control-Shift-Up> {}
|
||||
|
||||
bind $w <Control-n> {}
|
||||
bind $w <Control-p> {}
|
||||
bind $w <Control-t> {}
|
||||
|
||||
bind $w <Escape> {}
|
||||
|
||||
# The focus commands in the binding below are necessary to insure
|
||||
# that .$id.t gets the event.
|
||||
bind $w <KeyPress> "\
|
||||
focus .$id.t;\
|
||||
set mged_gui(.$id.t,insert_char_flag) 1;\
|
||||
event generate .$id.t <KeyPress> -state %s -keysym %K;\
|
||||
set mged_gui(.$id.t,insert_char_flag) 0;\
|
||||
focus %W;\
|
||||
break"
|
||||
}
|
||||
|
||||
proc default_mouse_bindings { w } {
|
||||
global no_focus_hack
|
||||
global transform
|
||||
global tcl_platform
|
||||
|
||||
# default button bindings
|
||||
if {![regexp {^[0-9]+} $::tcl_platform(osVersion) osMajorVersion]} {
|
||||
set osMajorVersion 0
|
||||
}
|
||||
|
||||
if {[info exists no_focus_hack] && $no_focus_hack} {
|
||||
bind $w <1> "winset $w; zoom 0.5; break"
|
||||
bind $w <2> "winset $w; set tmpstr \[dm m %x %y\]; print_return_val \$tmpstr; break"
|
||||
bind $w <3> "winset $w; zoom 2.0; break"
|
||||
|
||||
# Mouse wheel fun with Tcl/Tk - see https://wiki.tcl-lang.org/page/mousewheel
|
||||
bind $w <MouseWheel> "winset $w; focus $w; if {%D < 0} {zoom 0.9} else {zoom 1.1}"
|
||||
bind $w <Button-4> {event generate [focus] <MouseWheel> -delta 120}
|
||||
bind $w <Button-5> {event generate [focus] <MouseWheel> -delta -120}
|
||||
|
||||
if {$::tcl_platform(os) == "Darwin"} {
|
||||
# Mac option-key emulates mouse 2 (doesn't work?)
|
||||
bind $w <Option-ButtonPress-1> "winset $w; set tmpstr \[dm m %x %y\]; print_return_val \$tmpstr; break"
|
||||
# Mac command-key emulates mouse 3
|
||||
bind $w <Mod2-ButtonPress-1> "winset $w; zoom 2.0; break"
|
||||
}
|
||||
} else {
|
||||
bind $w <1> "winset $w; focus $w; zoom 0.5; break"
|
||||
bind $w <2> "winset $w; focus $w; set tmpstr \[dm m %x %y\]; print_return_val \$tmpstr; break"
|
||||
bind $w <3> "winset $w; focus $w; zoom 2.0; break"
|
||||
|
||||
# Mouse wheel fun with Tcl/Tk - see https://wiki.tcl-lang.org/page/mousewheel
|
||||
bind $w <MouseWheel> "winset $w; focus $w; if {%D < 0} {zoom 0.9} else {zoom 1.1}; break"
|
||||
bind $w <Button-4> {event generate [focus] <MouseWheel> -delta 120}
|
||||
bind $w <Button-5> {event generate [focus] <MouseWheel> -delta -120}
|
||||
|
||||
if {$::tcl_platform(os) == "Darwin"} {
|
||||
# Mac command-key emulates mouse 2 (doesn't work?)
|
||||
bind $w <Option-ButtonPress-1> "winset $w; focus $w; set tmpstr \[dm m %x %y\]; print_return_val \$tmpstr; break"
|
||||
# Mac command-key emulates mouse 3
|
||||
bind $w <Mod2-ButtonPress-1> "winset $w; focus $w; zoom 2.0; break"
|
||||
}
|
||||
}
|
||||
|
||||
bind $w <ButtonRelease> "winset $w; dm idle; break"
|
||||
bind $w <Motion> "winset $w; if {\[dm type\] == \"tkswrast\"} {dm motion %x %y}; break"
|
||||
bind $w <KeyRelease-Control_L> "winset $w; dm idle; break"
|
||||
bind $w <KeyRelease-Control_R> "winset $w; dm idle; break"
|
||||
bind $w <KeyRelease-Shift_L> "winset $w; dm idle; break"
|
||||
bind $w <KeyRelease-Shift_R> "winset $w; dm idle; break"
|
||||
bind $w <KeyRelease-Alt_L> "winset $w; dm idle; break"
|
||||
bind $w <KeyRelease-Alt_R> "winset $w; dm idle; break"
|
||||
|
||||
if ![catch {adc draw} result] {
|
||||
set adcflag $result
|
||||
} else {
|
||||
set adcflag 0
|
||||
}
|
||||
|
||||
if {$adcflag == "1" && $transform == "a"} {
|
||||
bind $w <Shift-ButtonPress-1> "winset $w; dm adc t %x %y; \
|
||||
shift_grip_hints $w \"Translate ADC\"; break"
|
||||
bind $w <Shift-ButtonPress-2> "winset $w; dm adc t %x %y; \
|
||||
shift_grip_hints $w \"Translate ADC\"; break"
|
||||
bind $w <Shift-ButtonPress-3> "winset $w; dm adc d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
|
||||
bind $w <Control-ButtonPress-1> "winset $w; dm adc 1 %x %y; \
|
||||
shift_grip_hints $w \"Rotate Angle 1\"; break"
|
||||
bind $w <Control-ButtonPress-2> "winset $w; dm adc 2 %x %y; \
|
||||
shift_grip_hints $w \"Rotate Angle 2\"; break"
|
||||
bind $w <Control-ButtonPress-3> "winset $w; dm adc d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
|
||||
bind $w <Shift-Control-ButtonPress-1> "winset $w; dm adc d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
bind $w <Shift-Control-ButtonPress-2> "winset $w; dm adc d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
bind $w <Shift-Control-ButtonPress-3> "winset $w; dm adc d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
|
||||
#constrained adc defaults
|
||||
bind $w <Alt-Shift-ButtonPress-1> "winset $w; dm con a x %x %y; \
|
||||
shift_grip_hints $w \"X Translate ADC\"; break"
|
||||
bind $w <Alt-Shift-ButtonPress-2> "winset $w; dm con a y %x %y; \
|
||||
shift_grip_hints $w \"Y Translate ADC\"; break"
|
||||
bind $w <Alt-Shift-ButtonPress-3> "winset $w; dm con a d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
|
||||
bind $w <Alt-Control-ButtonPress-1> "winset $w; dm con a 1 %x %y; \
|
||||
shift_grip_hints $w \"Rotate Angle 1\"; break"
|
||||
bind $w <Alt-Control-ButtonPress-2> "winset $w; dm con a 2 %x %y; \
|
||||
shift_grip_hints $w \"Rotate Angle 2\"; break"
|
||||
bind $w <Alt-Control-ButtonPress-3> "winset $w; dm con a d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
|
||||
bind $w <Alt-Shift-Control-ButtonPress-1> "winset $w; dm con a d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
bind $w <Alt-Shift-Control-ButtonPress-2> "winset $w; dm con a d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
bind $w <Alt-Shift-Control-ButtonPress-3> "winset $w; dm con a d %x %y; \
|
||||
shift_grip_hints $w \"Translate Tick Distance\"; break"
|
||||
} else {
|
||||
bind $w <Shift-ButtonPress-1> "winset $w; dm am t %x %y; \
|
||||
shift_grip_hints $w Translate; break"
|
||||
bind $w <Shift-ButtonPress-2> "winset $w; dm am t %x %y; \
|
||||
shift_grip_hints $w Translate; break"
|
||||
bind $w <Shift-ButtonPress-3> "winset $w; dm am t %x %y; \
|
||||
shift_grip_hints $w Translate; break"
|
||||
|
||||
bind $w <Control-ButtonPress-1> "winset $w; dm am r %x %y; \
|
||||
shift_grip_hints $w Rotate; break"
|
||||
bind $w <Control-ButtonPress-2> "winset $w; dm am r %x %y; \
|
||||
shift_grip_hints $w Rotate; break"
|
||||
bind $w <Control-ButtonPress-3> "winset $w; dm am r %x %y; \
|
||||
shift_grip_hints $w Rotate; break"
|
||||
|
||||
bind $w <Shift-Control-ButtonPress-1> "winset $w; dm am s %x %y; \
|
||||
shift_grip_hints $w Scale/Zoom; break"
|
||||
bind $w <Shift-Control-ButtonPress-2> "winset $w; dm am s %x %y; \
|
||||
shift_grip_hints $w Scale/Zoom; break"
|
||||
bind $w <Shift-Control-ButtonPress-3> "winset $w; dm am s %x %y; \
|
||||
shift_grip_hints $w Scale/Zoom; break"
|
||||
|
||||
#constrained defaults
|
||||
bind $w <Alt-Shift-ButtonPress-1> "winset $w; dm con t x %x %y; \
|
||||
shift_grip_hints $w \"X Translation\"; break"
|
||||
bind $w <Alt-Shift-ButtonPress-2> "winset $w; dm con t y %x %y; \
|
||||
shift_grip_hints $w \"Y Translation\"; break"
|
||||
bind $w <Alt-Shift-ButtonPress-3> "winset $w; dm con t z %x %y; \
|
||||
shift_grip_hints $w \"Z Translation\"; break"
|
||||
|
||||
bind $w <Alt-Control-ButtonPress-1> "winset $w; dm con r x %x %y; \
|
||||
shift_grip_hints $w \"X Rotation\"; break"
|
||||
bind $w <Alt-Control-ButtonPress-2> "winset $w; dm con r y %x %y; \
|
||||
shift_grip_hints $w \"Y Rotation\"; break"
|
||||
bind $w <Alt-Control-ButtonPress-3> "winset $w; dm con r z %x %y; \
|
||||
shift_grip_hints $w \"Z Rotation\"; break"
|
||||
|
||||
bind $w <Alt-Shift-Control-ButtonPress-1> "winset $w; dm con s x %x %y; \
|
||||
scale_shift_grip_hints $w X; break"
|
||||
bind $w <Alt-Shift-Control-ButtonPress-2> "winset $w; dm con s y %x %y; \
|
||||
scale_shift_grip_hints $w Y; break"
|
||||
bind $w <Alt-Shift-Control-ButtonPress-3> "winset $w; dm con s z %x %y; \
|
||||
scale_shift_grip_hints $w Z; break"
|
||||
}
|
||||
}
|
||||
|
||||
proc shift_grip_hints { w hint } {
|
||||
global mged_display
|
||||
global mged_gui
|
||||
global win_to_id
|
||||
|
||||
if ![info exists win_to_id($w)] {
|
||||
return
|
||||
}
|
||||
|
||||
set id $win_to_id($w)
|
||||
set mged_gui($id,illum_label) $hint
|
||||
}
|
||||
|
||||
proc scale_shift_grip_hints { w axis } {
|
||||
if {[status state] == "OBJ EDIT"} {
|
||||
shift_grip_hints $w "$axis Scale"
|
||||
} else {
|
||||
shift_grip_hints $w "Scale/Zoom"
|
||||
}
|
||||
}
|
||||
|
||||
proc update_gui { w vname val } {
|
||||
global mged_players
|
||||
global mged_gui
|
||||
@@ -482,6 +87,14 @@ proc update_gui { w vname val } {
|
||||
}
|
||||
}
|
||||
|
||||
# Dummy stubs to prevent legacy scripts from failing if they try to invoke them
|
||||
proc default_key_bindings { w } {}
|
||||
proc default_mouse_bindings { w } {}
|
||||
proc forward_key_bindings { w } {}
|
||||
proc toggle_forward_key_bindings { w } {}
|
||||
proc set_forward_keys { w val } {}
|
||||
proc shift_grip_hints { w hint } {}
|
||||
proc scale_shift_grip_hints { w axis } {}
|
||||
|
||||
# Local Variables:
|
||||
# mode: Tcl
|
||||
|
||||
Reference in New Issue
Block a user