Compare commits

...
2 Commits
19 changed files with 1441 additions and 948 deletions
+164
View File
@@ -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
*/
+3
View File
@@ -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 */
+1
View File
@@ -41,6 +41,7 @@ set(
badmagic.c
bitv.c
bomb.c
binding.cpp
booleanize.c
bu_init.cpp
cache.cpp
+362
View File
@@ -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
*/
+2
View File
@@ -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)
+109
View File
@@ -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
*/
+22
View File
@@ -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"
}
]
}
}
}
+6
View File
@@ -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
+1
View File
@@ -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. */
+59
View File
@@ -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" }
]
}
}
}
+12
View File
@@ -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;
+14
View File
@@ -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;
}
+25
View File
@@ -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"
}
}
+54
View File
@@ -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)
+6
View File
@@ -0,0 +1,6 @@
{
"actions": {
"fb_print_pixel_rgb": "Print RGB values of pixel under cursor",
"fb_close_window": "Close framebuffer window"
}
}
+171 -72
View File
@@ -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
View File
@@ -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
View File
@@ -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(&current_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(&current_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(&current_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(&current_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(&current_top->to_gedp->go_name),
@@ -2848,240 +3020,14 @@ to_init_default_bindings(struct bview *gdvp)
bu_vls_addr(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_top->to_gedp->go_name),
bu_vls_addr(&gdvp->gv_name),
bu_vls_addr(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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);
+9 -396
View File
@@ -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