From be2b059224841fae64b916b5ee308924fd3f7bf3 Mon Sep 17 00:00:00 2001 From: Fredrik Modeen Date: Fri, 30 Oct 2009 21:35:17 +0000 Subject: [PATCH] So here it goes.. I hope I have fixed all parts that don't follow our guidelines. (that python script was good start) This is the filter.. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33847 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/input_server/filters/Jamfile | 1 + .../shortcut_catcher/BitFieldTesters.cpp | 305 +++ .../shortcut_catcher/BitFieldTesters.h | 121 ++ .../shortcut_catcher/CommandActuators.cpp | 1713 +++++++++++++++++ .../shortcut_catcher/CommandActuators.h | 369 ++++ .../shortcut_catcher/CommandExecutor.cpp | 90 + .../shortcut_catcher/CommandExecutor.h | 31 + .../filters/shortcut_catcher/Jamfile | 17 + .../shortcut_catcher/KeyCommandMap.cpp | 312 +++ .../filters/shortcut_catcher/KeyCommandMap.h | 58 + .../filters/shortcut_catcher/KeyInfos.cpp | 186 ++ .../filters/shortcut_catcher/KeyInfos.h | 33 + .../shortcut_catcher/ParseCommandLine.cpp | 321 +++ .../shortcut_catcher/ParseCommandLine.h | 47 + .../ShortcutsFilterConstants.h | 24 + .../ShortcutsServerFilter.cpp | 87 + .../shortcut_catcher/ShortcutsServerFilter.h | 53 + 17 files changed, 3768 insertions(+) create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.cpp create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.h create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.cpp create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.h create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.cpp create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.h create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/Jamfile create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.cpp create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.h create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.cpp create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.h create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.cpp create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.h create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/ShortcutsFilterConstants.h create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.cpp create mode 100644 src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.h diff --git a/src/add-ons/input_server/filters/Jamfile b/src/add-ons/input_server/filters/Jamfile index 62949cc4bc..d8da512e27 100644 --- a/src/add-ons/input_server/filters/Jamfile +++ b/src/add-ons/input_server/filters/Jamfile @@ -2,3 +2,4 @@ SubDir HAIKU_TOP src add-ons input_server filters ; SubInclude HAIKU_TOP src add-ons input_server filters screen_saver ; SubInclude HAIKU_TOP src add-ons input_server filters vmware_mouse ; +SubInclude HAIKU_TOP src add-ons input_server filters shortcut_catcher ; diff --git a/src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.cpp b/src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.cpp new file mode 100644 index 0000000000..976ee71c81 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.cpp @@ -0,0 +1,305 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#include "BitFieldTesters.h" + + +#include + +BitFieldTester::BitFieldTester() +{ + // empty +} + + +BitFieldTester::BitFieldTester(BMessage* from) + : + BArchivable(from) +{ + // empty +} + + +status_t +BitFieldTester::Archive(BMessage* into, bool deep) const +{ + return BArchivable::Archive(into, deep); +} + + +// ---------------- ConstantFieldTester starts ------------------------------- +ConstantFieldTester::ConstantFieldTester(bool result) + : + fResult(result) +{ + // empty +} + + +ConstantFieldTester::ConstantFieldTester(BMessage* from) + : + BitFieldTester(from) +{ + if (from->FindBool("ctRes", &fResult) != B_NO_ERROR) + printf("ConstantFieldTester: Error, no ctRes!\n"); +} + + +status_t +ConstantFieldTester::Archive(BMessage* into, bool deep) const +{ + status_t ret = BitFieldTester::Archive(into, deep); + into->AddBool("ctRes", fResult); + return ret; +} + + +BArchivable* +ConstantFieldTester::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "ConstantFieldTester")) + return new ConstantFieldTester(from); + else + return NULL; +} + + +bool +ConstantFieldTester::IsMatching(uint32 field) +{ + return fResult; +} + + +// ---------------- HasBitsFieldTester starts ------------------------------- +HasBitsFieldTester::HasBitsFieldTester(uint32 requiredBits, + uint32 forbiddenBits) + : + fRequiredBits(requiredBits), + fForbiddenBits(forbiddenBits) +{ + // empty +} + + +HasBitsFieldTester::HasBitsFieldTester(BMessage* from) + : + BitFieldTester(from) +{ + if (from->FindInt32("rqBits", (int32*) &fRequiredBits) != B_NO_ERROR) + printf("HasBitsFieldTester: Error, no rqBits!\n"); + + if (from->FindInt32("fbBits", (int32*) &fForbiddenBits) != B_NO_ERROR) + printf("HasBitsFieldTester: Error, no fbBits!\n"); +} + + +status_t +HasBitsFieldTester::Archive(BMessage* into, bool deep) const +{ + status_t ret = BitFieldTester::Archive(into, deep); + into->AddInt32("rqBits", fRequiredBits); + into->AddInt32("fbBits", fForbiddenBits); + return ret; +} + + +BArchivable* +HasBitsFieldTester::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "HasBitsFieldTester")) + return new HasBitsFieldTester(from); + else + return NULL; +} + + +bool +HasBitsFieldTester::IsMatching(uint32 field) +{ + return ((((fRequiredBits & (~field)) == 0)) + && ((fForbiddenBits & (~field)) == fForbiddenBits)); +} + + +// ---------------- NotFieldTester starts ------------------------------- +NotFieldTester::NotFieldTester(BitFieldTester* slave) + : + fSlave(slave) +{ + // empty +} + + +NotFieldTester::~NotFieldTester() +{ + delete fSlave; +} + + +NotFieldTester::NotFieldTester(BMessage* from) + : + BitFieldTester(from), + fSlave(NULL) +{ + BMessage slaveMsg; + if (from->FindMessage("nSlave", &slaveMsg) == B_NO_ERROR) { + BArchivable* slaveObj = instantiate_object(&slaveMsg); + if (slaveObj) { + fSlave = dynamic_cast(slaveObj); + if (fSlave == NULL) { + printf("NotFieldTester: + Error casting slaveObj to BitFieldTester!\n"); + delete slaveObj; + } + } else + printf("NotFieldTester: instantiate_object returned NULL!\n"); + } else + printf("NotFieldTester: Couldn't unarchive NotFieldTester slave!\n"); +} + + +status_t +NotFieldTester::Archive(BMessage* into, bool deep) const +{ + if (fSlave == NULL) + return B_ERROR; + + status_t ret = BitFieldTester::Archive(into, deep); + + if (ret == B_NO_ERROR) { + BMessage msg; + ret = fSlave->Archive(&msg, deep); + into->AddMessage("nSlave", &msg); + } + + return ret; +} + + +BArchivable* +NotFieldTester::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "NotFieldTester")) + return new NotFieldTester(from); + else + return NULL; +} + + +bool +NotFieldTester::IsMatching(uint32 field) +{ + return fSlave ? (!fSlave->IsMatching(field)) : false; +} + + +// ---------------- MinMatchFieldTester starts ------------------------------- +MinMatchFieldTester::MinMatchFieldTester(int minNum, bool deleteSlaves) + : + fMinNum(minNum), + fDeleteSlaves(deleteSlaves) // fDeleteSlaves state not archived! +{ + // empty +} + + +MinMatchFieldTester::~MinMatchFieldTester() +{ + if (fDeleteSlaves) { + int nr = fSlaves.CountItems(); + for (int i = 0; i < nr; i++) + delete ((BitFieldTester*) fSlaves.ItemAt(i)); + } +} + + +MinMatchFieldTester::MinMatchFieldTester(BMessage* from) + : + BitFieldTester(from), + fDeleteSlaves(true) +{ + int i = 0; + BMessage slaveMsg; + while (from->FindMessage("mSlave", i++, &slaveMsg) == B_NO_ERROR) { + BArchivable* slaveObj = instantiate_object(&slaveMsg); + if (slaveObj) { + BitFieldTester* nextSlave = dynamic_cast(slaveObj); + if (nextSlave) + fSlaves.AddItem(nextSlave); + else { + printf("MinMatchFieldTester: + Error casting slaveObj to BitFieldTester!\n"); + delete slaveObj; + } + } else + printf("MinMatchFieldTester: instantiate_object returned NULL!\n"); + } + + if (from->FindInt32("mMin", (int32*) &fMinNum) != B_NO_ERROR) + printf("MinMatchFieldTester: Error getting mMin!\n"); +} + + +// (slave) should be allocated with new, becomes property of this object. +void +MinMatchFieldTester::AddSlave(const BitFieldTester* slave) +{ + fSlaves.AddItem((void*) slave); +} + + +status_t +MinMatchFieldTester::Archive(BMessage* into, bool deep) const +{ + status_t ret = BitFieldTester::Archive(into, deep); + + if (ret == B_NO_ERROR) { + int nr = fSlaves.CountItems(); + for (int i = 0; i < nr; i++) { + BMessage msg; + ret = ((BitFieldTester*)fSlaves.ItemAt(i))->Archive(&msg, deep); + if (ret != B_NO_ERROR) + return ret; + + into->AddMessage("mSlave", &msg); + } + } + + into->AddInt32("mMin", fMinNum); + return ret; +} + + +BArchivable* +MinMatchFieldTester::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MinMatchFieldTester")) + return new MinMatchFieldTester(from); + else + return NULL; +} + + +// Returns true if at least (fMinNum) slaves return true. +bool +MinMatchFieldTester::IsMatching(uint32 field) +{ + int nr = fSlaves.CountItems(); + if ((fMinNum == 0) && (nr == 0)) + return true; // 0 >= 0, so this should return true! + + int count = 0; + + for (int i = 0; i < nr; i++) + if ((((BitFieldTester*)fSlaves.ItemAt(i))->IsMatching(field)) + && (++count >= fMinNum)) + return true; + return false; +} diff --git a/src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.h b/src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.h new file mode 100644 index 0000000000..e72cd15827 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/BitFieldTesters.h @@ -0,0 +1,121 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef BitFieldTesters_h +#define BitFieldTesters_h + + +#include +#include +#include + + +// This file contains various BitTester classes, each of which defines a +// sequence of bit testing logics to do on a uint32. + +#ifndef __INTEL__ +#pragma export on +#endif + +// The abstract base class. Defines the interface. +_EXPORT class BitFieldTester; +class BitFieldTester : public BArchivable { +public: + BitFieldTester(); + BitFieldTester(BMessage* from); + + virtual bool IsMatching(uint32 field) = 0; + virtual status_t Archive(BMessage* into, bool deep = true) const; +}; + + +// This version always returns the value specified in the constructor. +_EXPORT class ConstantFieldTester; +class ConstantFieldTester : public BitFieldTester { +public: + ConstantFieldTester(bool result); + ConstantFieldTester(BMessage* from); + + virtual status_t Archive(BMessage* into, bool deep = true) const; + static BArchivable* Instantiate(BMessage* from); + virtual bool IsMatching(uint32 field); + +private: + bool fResult; +}; + + +// This version matches if all requiredBits are found in the field, +// and no forbiddenBits are found. +_EXPORT class HasBitsFieldTester; +class HasBitsFieldTester : public BitFieldTester { +public: + HasBitsFieldTester(uint32 requiredBits, + uint32 forbiddenBits = 0); + HasBitsFieldTester(BMessage* from); + + virtual status_t Archive(BMessage* into, bool deep = true) const; + static BArchivable* Instantiate(BMessage* from); + virtual bool IsMatching(uint32 field); + +private: + uint32 fRequiredBits; + uint32 fForbiddenBits; +}; + + +// This one negates the tester it holds. +_EXPORT class NotFieldTester; +class NotFieldTester : public BitFieldTester { +public: + // (slave) should be allocated with new, becomes property of this object. + NotFieldTester(BitFieldTester* slave); + NotFieldTester(BMessage* from); + ~NotFieldTester(); + + virtual status_t Archive(BMessage* into, bool deep = true) const; + static BArchivable* Instantiate(BMessage* from); + virtual bool IsMatching(uint32 field); + +private: + BitFieldTester* fSlave; +}; + + +// The most interesting class: This one returns true if at least (minNum) of +// its slaves return true. It can be used for OR (i.e. minNum==1), AND +// (i.e. minNum==numberofchildren), or anything in between! +_EXPORT class MinMatchFieldTester; +class MinMatchFieldTester : public BitFieldTester { +public: + MinMatchFieldTester(int minNum, + bool deleteSlaves = true); + MinMatchFieldTester(BMessage* from); + ~MinMatchFieldTester(); + + // (slave) should be allocated with new, becomes property of this object. + void AddSlave(const BitFieldTester* slave); + + virtual status_t Archive(BMessage* into, bool deep = true) const; + static BArchivable* Instantiate(BMessage* from); + virtual bool IsMatching(uint32 field); + +private: + BList fSlaves; + int32 fMinNum; + + // true if we should delete all our slaves when we are deleted. + bool fDeleteSlaves; +}; + +#ifndef __INTEL__ +#pragma export reset +#endif + +#endif diff --git a/src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.cpp b/src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.cpp new file mode 100644 index 0000000000..44cf89a8bb --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.cpp @@ -0,0 +1,1713 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + * Fredrik Modéen + */ + + +#include "CommandActuators.h" + + +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "ParseCommandLine.h" +#include "KeyInfos.h" + +#define IS_KEY_DOWN(msg) ((msg->what == B_KEY_DOWN) \ + || (msg->what == B_UNMAPPED_KEY_DOWN)) + +// Factory function +CommandActuator* +CreateCommandActuator(const char* command) +{ + CommandActuator* act = NULL; + int32 argc; + char** argv = ParseArgvFromString(command, argc); + if (command[0] == '*') { + if (argc > 0) { + char* c = argv[0] + 1; + if (strcmp(c, "InsertString") == 0) + act = new KeyStrokeSequenceCommandActuator(argc, argv); + else if (strcmp(c, "MoveMouse") == 0) + act = new MoveMouseByCommandActuator(argc, argv); + else if (strcmp(c, "MoveMouseTo") == 0) + act = new MoveMouseToCommandActuator(argc, argv); + else if (strcmp(c, "MouseButton") == 0) + act = new MouseButtonCommandActuator(argc, argv); + else if (strcmp(c, "LaunchHandler") == 0) + act = new MIMEHandlerCommandActuator(argc, argv); + else if (strcmp(c, "Multi") == 0) + act = new MultiCommandActuator(argc, argv); + else if (strcmp(c, "MouseDown") == 0) + act = new MouseDownCommandActuator(argc, argv); + else if (strcmp(c, "MouseUp") == 0) + act = new MouseUpCommandActuator(argc, argv); + else if (strcmp(c, "SendMessage") == 0) + act = new SendMessageCommandActuator(argc, argv); + else + act = new BeepCommandActuator(argc, argv); + } + } else + act = new LaunchCommandActuator(argc, argv); + + FreeArgv(argv); + return act; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// CommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +CommandActuator::CommandActuator(int32 argc, char** argv) +{ + // empty +} + + +CommandActuator::CommandActuator(BMessage* from) + : + BArchivable(from) +{ + // empty +} + + +status_t +CommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = BArchivable::Archive(into, deep); + return ret; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// LaunchCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +LaunchCommandActuator::LaunchCommandActuator(int32 argc, char** argv) + : + CommandActuator(argc, argv), + fArgv(CloneArgv(argv)), + fArgc(argc) +{ + // empty +} + + +LaunchCommandActuator::LaunchCommandActuator(BMessage* from) + : + CommandActuator(from) +{ + BList argList; + const char* temp; + int idx = 0; + while (from->FindString("largv", idx++, &temp) == B_NO_ERROR) { + if (temp) { + char* copy = new char[strlen(temp) + 1]; + strcpy(copy, temp); + argList.AddItem(copy); + } + } + + fArgc = argList.CountItems(); + fArgv = new char*[fArgc+ 1]; + + for (int i = 0; i < fArgc; i++) + fArgv[i] = (char*) argList.ItemAt(i); + + fArgv[fArgc] = NULL;// terminate the array +} + + +LaunchCommandActuator::~LaunchCommandActuator() +{ + FreeArgv(fArgv); +} + + +filter_result +LaunchCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) { + // cause KeyEventAsync() to be called asynchronously + *setAsyncData = (void*) true; + } + return B_SKIP_MESSAGE; +} + + +status_t +LaunchCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = CommandActuator::Archive(into, deep); + + for (int i = 0; i < fArgc; i++) + into->AddString("largv", fArgv[i]); + + return ret; +} + + +BArchivable* +LaunchCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "LaunchCommandActuator")) + return new LaunchCommandActuator(from); + else + return NULL; +} + + +void +LaunchCommandActuator::KeyEventAsync(const BMessage* keyMsg, + void* asyncData) +{ + if (be_roster) { + status_t err = B_OK; + BString str; + BString str1("Shortcuts Launcher Error"); + if (fArgc < 1) + str << "You didn't specify a command for this hotkey."; + else if ((err = LaunchCommand(fArgv, fArgc)) != B_NO_ERROR) { + str << "Can't launch " << fArgv[0]; + str << ", no such file exists."; + str << " Please check your Shortcuts settings."; + } + + if (fArgc < 1 || err != B_NO_ERROR) + (new BAlert(str1.String(), str.String(), "Ok"))->Go(NULL); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MouseCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MouseCommandActuator::MouseCommandActuator(int32 argc, char** argv) + : + CommandActuator(argc, argv), + fWhichButtons(B_PRIMARY_MOUSE_BUTTON) +{ + if (argc > 1) { + fWhichButtons = 0; + + for (int i = 1; i < argc; i++) { + int buttonNumber = atoi(argv[i]); + + switch(buttonNumber) { + case 1: + fWhichButtons |= B_PRIMARY_MOUSE_BUTTON; + break; + case 2: + fWhichButtons |= B_SECONDARY_MOUSE_BUTTON; + break; + case 3: + fWhichButtons |= B_TERTIARY_MOUSE_BUTTON; + break; + } + } + } +} + + +MouseCommandActuator::MouseCommandActuator(BMessage* from) + : + CommandActuator(from), + fWhichButtons(B_PRIMARY_MOUSE_BUTTON) +{ + from->FindInt32("buttons", &fWhichButtons); +} + + +MouseCommandActuator::~MouseCommandActuator() +{ + // empty +} + + +status_t +MouseCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = CommandActuator::Archive(into, deep); + into->AddInt32("buttons", fWhichButtons); + return ret; +} + + +int32 +MouseCommandActuator::_GetWhichButtons() const +{ + return fWhichButtons; +} + + +void +MouseCommandActuator::_GenerateMouseButtonEvent(bool mouseDown, + const BMessage* keyMsg, BList* outlist, BMessage* lastMouseMove) +{ + BMessage* fakeMouse = new BMessage(*lastMouseMove); + fakeMouse->what = mouseDown ? B_MOUSE_DOWN : B_MOUSE_UP; + + // Update the buttons to reflect which mouse buttons we are faking + fakeMouse->RemoveName("buttons"); + + if (mouseDown) + fakeMouse->AddInt32("buttons", fWhichButtons); + + // Trey sez you gotta keep then "when"'s increasing if you want + // click & drag to work! + int64 when; + + const BMessage* lastMessage; + + if (outlist->CountItems() > 0) { + int nr = outlist->CountItems() - 1; + lastMessage = (const BMessage*)outlist->ItemAt(nr); + } else + lastMessage =keyMsg; + + if (lastMessage->FindInt64("when", &when) == B_NO_ERROR) { + when++; + fakeMouse->RemoveName("when"); + fakeMouse->AddInt64("when", when); + } + outlist->AddItem(fakeMouse); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MouseDownCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MouseDownCommandActuator::MouseDownCommandActuator(int32 argc, char** argv) + : + MouseCommandActuator(argc, argv) +{ + // empty +} + + +MouseDownCommandActuator::MouseDownCommandActuator(BMessage* from) + : + MouseCommandActuator(from) +{ + // empty +} + + +MouseDownCommandActuator::~MouseDownCommandActuator() +{ + // empty +} + + +filter_result +MouseDownCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) + _GenerateMouseButtonEvent(true, keyMsg, outlist, lastMouseMove); + + return B_DISPATCH_MESSAGE; +} + + +status_t +MouseDownCommandActuator::Archive(BMessage* into, bool deep) const +{ + return MouseCommandActuator::Archive(into, deep); +} + + +BArchivable* +MouseDownCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MouseDownCommandActuator")) + return new MouseDownCommandActuator(from); + else + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MouseUpCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MouseUpCommandActuator::MouseUpCommandActuator(int32 argc, char** argv) + : + MouseCommandActuator(argc, argv) +{ + // empty +} + + +MouseUpCommandActuator::MouseUpCommandActuator(BMessage* from) + : + MouseCommandActuator(from) +{ + // empty +} + + +MouseUpCommandActuator::~MouseUpCommandActuator() +{ + // empty +} + + +filter_result +MouseUpCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) + _GenerateMouseButtonEvent(false, keyMsg, outlist, lastMouseMove); + return B_DISPATCH_MESSAGE; +} + + +status_t +MouseUpCommandActuator::Archive(BMessage* into, bool deep) const +{ + return MouseCommandActuator::Archive(into, deep); +} + + +BArchivable* +MouseUpCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MouseUpCommandActuator")) + return new MouseUpCommandActuator(from); + else + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MouseButtonCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MouseButtonCommandActuator::MouseButtonCommandActuator(int32 argc, char** argv) + : + MouseCommandActuator(argc, argv), + fKeyDown(false) +{ + // empty +} + + +MouseButtonCommandActuator::MouseButtonCommandActuator(BMessage* from) + : + MouseCommandActuator(from), + fKeyDown(false) +{ + // empty +} + + +MouseButtonCommandActuator::~MouseButtonCommandActuator() +{ + // empty +} + + +filter_result +MouseButtonCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg) != fKeyDown) { + _GenerateMouseButtonEvent(IS_KEY_DOWN(keyMsg), keyMsg, outlist, + lastMouseMove); + fKeyDown = IS_KEY_DOWN(keyMsg); + return B_DISPATCH_MESSAGE; + } else + // This will handle key-repeats, which we don't want turned into lots + // of B_MOUSE_DOWN messages. + return B_SKIP_MESSAGE; +} + + +status_t +MouseButtonCommandActuator::Archive(BMessage* into, bool deep) const +{ + return MouseCommandActuator::Archive(into, deep); +} + + +BArchivable* +MouseButtonCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MouseButtonCommandActuator")) + return new MouseButtonCommandActuator(from); + else + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// KeyStrokeSequenceCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +KeyStrokeSequenceCommandActuator::KeyStrokeSequenceCommandActuator(int32 argc, + char** argv) + : + CommandActuator(argc, argv) +{ + for (int s = 1; s < argc; s++) { + fSequence.Append(argv[s]); + if (s < argc - 1) + fSequence.Append(" "); + } + + // Find any insert-unicode-here sequences and replace them with spaces... + int32 nextStart; + while ((nextStart = fSequence.FindFirst("$$")) >= 0) { + int32 nextEnd = fSequence.FindFirst("$$", nextStart + 2); + if (nextEnd >= 0) { + uint32 customKey= 0; + int32 unicodeVal= 0; + uint32 customMods = 0; + BString sub; + fSequence.CopyInto(sub, nextStart + 2, nextEnd-(nextStart + 2)); + sub.ToLower(); + + if ((sub.FindFirst('-') >= 0) || ((sub.Length() > 0) + && ((sub.String()[0] < '0') || (sub.String()[0] > '9')))) { + + const char* s = sub.String(); + while (*s == '-') s++;// go past any initial dashes + + bool lastWasDash = true; + while (*s) { + if (lastWasDash) { + if (strncmp(s, "shift",5) == 0) + customMods |=B_LEFT_SHIFT_KEY| B_SHIFT_KEY; + else if (strncmp(s, "leftsh", 6) == 0) + customMods |=B_LEFT_SHIFT_KEY| B_SHIFT_KEY; + else if (strncmp(s, "rightsh",7) == 0) + customMods |=B_RIGHT_SHIFT_KEY | B_SHIFT_KEY; + else if (strncmp(s, "alt",3) == 0) + customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY; + else if (strncmp(s, "leftalt",7) == 0) + customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY; + else if (strncmp(s, "rightalt", 8) == 0) + customMods |=B_RIGHT_COMMAND_KEY | B_COMMAND_KEY; + else if (strncmp(s, "com",3) == 0) + customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY; + else if (strncmp(s, "leftcom",7) == 0) + customMods |=B_LEFT_COMMAND_KEY| B_COMMAND_KEY; + else if (strncmp(s, "rightcom", 8) == 0) + customMods |=B_RIGHT_COMMAND_KEY | B_COMMAND_KEY; + else if (strncmp(s, "con",3) == 0) + customMods |=B_LEFT_CONTROL_KEY| B_CONTROL_KEY; + else if (strncmp(s, "leftcon",7) == 0) + customMods |=B_LEFT_CONTROL_KEY| B_CONTROL_KEY; + else if (strncmp(s, "rightcon", 8) == 0) + customMods |=B_RIGHT_CONTROL_KEY | B_CONTROL_KEY; + else if (strncmp(s, "win",3) == 0) + customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY; + else if (strncmp(s, "leftwin",7) == 0) + customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY; + else if (strncmp(s, "rightwin", 8) == 0) + customMods |=B_RIGHT_OPTION_KEY| B_OPTION_KEY; + else if (strncmp(s, "opt",3) == 0) + customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY; + else if (strncmp(s, "leftopt",7) == 0) + customMods |=B_LEFT_OPTION_KEY | B_OPTION_KEY; + else if (strncmp(s, "rightopt", 8) == 0) + customMods |=B_RIGHT_OPTION_KEY| B_OPTION_KEY; + else if (strncmp(s, "menu", 4) == 0) + customMods |=B_MENU_KEY; + else if (strncmp(s, "caps", 4) == 0) + customMods |=B_CAPS_LOCK; + else if (strncmp(s, "scroll", 6) == 0) + customMods |=B_SCROLL_LOCK; + else if (strncmp(s, "num",3) == 0) + customMods |=B_NUM_LOCK; + else if (customKey == 0) { + BString arg = s; + int32 dashIdx = arg.FindFirst('-'); + + if (dashIdx >= 0) + arg.Truncate(dashIdx); + + uint32 key = (uint32)FindKeyCode(arg.String()); + + if (key > 0) { + customKey = key; + const char* u = GetKeyUTF8(key); + + //Parse the UTF8 back into an int32 + switch(strlen(u)) { + case 1: + unicodeVal = ((uint32)(u[0]&0x7F)); + break; + case 2: + unicodeVal = ((uint32)(u[1]&0x3F)) | + (((uint32)(u[0]&0x1F)) << 6); + break; + case 3: + unicodeVal = ((uint32)(u[2]&0x3F)) | + (((uint32)(u[1]&0x3F)) << 6) | + (((uint32)(u[0]&0x0F)) << 12); + break; + default: unicodeVal = 0; + break; + } + } + } + lastWasDash = false; + } else + lastWasDash = (*s == '-'); + s++; + } + + // If we have a letter, try to make it the correct case + if ((unicodeVal >= 'A') && (unicodeVal <= 'Z')) + if ((customMods &B_SHIFT_KEY) == 0) + unicodeVal += 'a'-'A'; + else if ((unicodeVal >= 'a') && (unicodeVal <= 'z')) + if ((customMods &B_SHIFT_KEY) != 0) + unicodeVal -= 'a'-'A'; + } else { + unicodeVal = strtol(&(fSequence.String())[nextStart + 2], NULL, + 0); + customMods = (uint32) -1; + } + + if (unicodeVal == 0) + unicodeVal = ' '; + + BString newStr = fSequence; + newStr.Truncate(nextStart); + fOverrides.AddItem((void*)unicodeVal); + fOverrideOffsets.AddItem((void*)newStr.Length()); + fOverrideModifiers.AddItem((void*)customMods); + fOverrideKeyCodes.AddItem((void*)customKey); + newStr.Append(((unicodeVal > 0) && (unicodeVal < 127)) ? + ((char)unicodeVal): ' ',1); + newStr.Append(&fSequence.String()[nextEnd + 2]); + fSequence = newStr; + } else + break; + } + _GenerateKeyCodes(); +} + + +KeyStrokeSequenceCommandActuator::KeyStrokeSequenceCommandActuator( + BMessage* from) + : + CommandActuator(from) +{ + const char* seq; + if (from->FindString("sequence", 0, &seq) == B_NO_ERROR) + fSequence = seq; + + int32 temp; + for (int32 i = 0; from->FindInt32("ooffsets", i, &temp) == B_NO_ERROR; + i++) { + fOverrideOffsets.AddItem((void*)temp); + + if (from->FindInt32("overrides", i, &temp) != B_NO_ERROR) + temp = ' '; + + fOverrides.AddItem((void*)temp); + + if (from->FindInt32("omods", i, &temp) != B_NO_ERROR) + temp = -1; + + fOverrideModifiers.AddItem((void*)temp); + + if (from->FindInt32("okeys", i, &temp) != B_NO_ERROR) + temp = 0; + + fOverrideKeyCodes.AddItem((void*)temp); + } + _GenerateKeyCodes(); +} + + +KeyStrokeSequenceCommandActuator::~KeyStrokeSequenceCommandActuator() +{ + delete [] fKeyCodes; + delete [] fModCodes; + delete [] fStates; +} + + +void +KeyStrokeSequenceCommandActuator::_GenerateKeyCodes() +{ + int slen = fSequence.Length(); + fKeyCodes = new int32[slen]; + fModCodes = new int32[slen]; + fStates = new uint8[slen * 16]; + + memset(fStates, 0, slen * 16); + + key_map* map; + char* keys; + get_key_map(&map, &keys); + for (int i = 0; i < slen; i++) { + uint32 overrideKey= 0; + uint32 overrideMods = (uint32)-1; + for (int32 j = fOverrideOffsets.CountItems()-1; j >= 0; j--) { + if ((int32)fOverrideOffsets.ItemAt(j) == i) { + overrideKey= (uint32) fOverrideKeyCodes.ItemAt(j); + overrideMods = (uint32) fOverrideModifiers.ItemAt(j); + break; + } + } + + uint8* states = &fStates[i * 16]; + int32& mod = fModCodes[i]; + if (overrideKey == 0) { + // Gotta do reverse-lookups to find out the raw keycodes for a + // given character. Expensive--there oughtta be a better way to do + // this. + char next = fSequence.ByteAt(i); + int32 key = _LookupKeyCode(map, keys, map->normal_map, next, states + , mod, 0); + if (key < 0) + key = _LookupKeyCode(map, keys, map->shift_map, next, states, + mod, B_LEFT_SHIFT_KEY | B_SHIFT_KEY); + + if (key < 0) + key = _LookupKeyCode(map, keys, map->caps_map, next, states, + mod, B_CAPS_LOCK); + + if (key < 0) + key = _LookupKeyCode(map, keys, map->caps_shift_map, next, + states, mod, B_LEFT_SHIFT_KEY | B_SHIFT_KEY + | B_CAPS_LOCK); + + if (key < 0) + key = _LookupKeyCode(map, keys, map->option_map, next, states, + mod, B_LEFT_OPTION_KEY | B_OPTION_KEY); + + if (key < 0) + key = _LookupKeyCode(map, keys, map->option_shift_map, next, + states, mod, B_LEFT_OPTION_KEY | B_OPTION_KEY + | B_LEFT_SHIFT_KEY | B_SHIFT_KEY); + + if (key < 0) + key = _LookupKeyCode(map, keys, map->option_caps_map, next, + states, mod, B_LEFT_OPTION_KEY | B_OPTION_KEY + | B_CAPS_LOCK); + + if (key < 0) + key = _LookupKeyCode(map, keys, map->option_caps_shift_map, + next, states, mod, B_LEFT_OPTION_KEY | B_OPTION_KEY + | B_CAPS_LOCK | B_LEFT_SHIFT_KEY | B_SHIFT_KEY); + + if (key < 0) + key = _LookupKeyCode(map, keys, map->control_map, next, states, + mod, B_CONTROL_KEY); + + fKeyCodes[i] = (key >= 0) ? key : 0; + } + + if (overrideMods != (uint32)-1) { + mod = (int32) overrideMods; + + // Clear any bits that might have been set by the lookups... + _SetStateBit(states, map->caps_key,false); + _SetStateBit(states, map->scroll_key,false); + _SetStateBit(states, map->num_key, false); + _SetStateBit(states, map->menu_key,false); + _SetStateBit(states, map->left_shift_key,false); + _SetStateBit(states, map->right_shift_key, false); + _SetStateBit(states, map->left_command_key,false); + _SetStateBit(states, map->right_command_key, false); + _SetStateBit(states, map->left_control_key,false); + _SetStateBit(states, map->right_control_key, false); + _SetStateBit(states, map->left_option_key, false); + _SetStateBit(states, map->right_option_key,false); + + // And then set any bits that were specified in our override. + if (mod & B_CAPS_LOCK) + _SetStateBit(states, map->caps_key); + + if (mod & B_SCROLL_LOCK) + _SetStateBit(states, map->scroll_key); + + if (mod & B_NUM_LOCK) + _SetStateBit(states, map->num_key); + + if (mod & B_MENU_KEY) + _SetStateBit(states, map->menu_key); + + if (mod & B_LEFT_SHIFT_KEY) + _SetStateBit(states, map->left_shift_key); + + if (mod & B_RIGHT_SHIFT_KEY) + _SetStateBit(states, map->right_shift_key); + + if (mod & B_LEFT_COMMAND_KEY) + _SetStateBit(states, map->left_command_key); + + if (mod & B_RIGHT_COMMAND_KEY) + _SetStateBit(states, map->right_command_key); + + if (mod & B_LEFT_CONTROL_KEY) + _SetStateBit(states, map->left_control_key); + + if (mod & B_RIGHT_CONTROL_KEY) + _SetStateBit(states, map->right_control_key); + + if (mod & B_LEFT_OPTION_KEY) + _SetStateBit(states, map->left_option_key); + + if (mod & B_RIGHT_OPTION_KEY) + _SetStateBit(states, map->right_option_key); + } + + if (overrideKey > 0) { + if (overrideKey > 127) + overrideKey = 0;// invalid value!? + + fKeyCodes[i] = overrideKey; + _SetStateBit(states, overrideKey); + } + } +} + + +int32 +KeyStrokeSequenceCommandActuator::_LookupKeyCode(key_map* map, char* keys, + int32 offsets[128], char c, uint8* setStates, int32& setMod, int32 setTo) + const +{ + for (int i = 0; i < 128; i++) { + if (keys[offsets[i]+ 1] == c) { + _SetStateBit(setStates, i); + + if (setTo & B_SHIFT_KEY) + _SetStateBit(setStates, map->left_shift_key); + + if (setTo & B_OPTION_KEY) + _SetStateBit(setStates, map->left_option_key); + + if (setTo & B_CONTROL_KEY) + _SetStateBit(setStates, map->left_control_key); + + if (setTo & B_CAPS_LOCK) + _SetStateBit(setStates, map->caps_key); + + setMod = setTo; + return i; + } + } + return -1; +} + + +void +KeyStrokeSequenceCommandActuator::_SetStateBit(uint8* setStates, uint32 key, + bool on) const +{ + if (on) + setStates[key / 8] |= (0x80 >> (key%8)); + else + setStates[key / 8] &= ~(0x80 >> (key%8)); +} + + +status_t +KeyStrokeSequenceCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = CommandActuator::Archive(into, deep); + into->AddString("sequence", fSequence.String()); + int32 numOverrides = fOverrideOffsets.CountItems(); + status_t tmp = B_OK; + for (int32 i = 0; i < numOverrides; i++) { + ret = into->AddInt32("ooffsets", (int32)fOverrideOffsets.ItemAt(i)); + if (ret != B_NO_ERROR) + tmp = B_ERROR; + + ret = into->AddInt32("overrides", (int32)fOverrides.ItemAt(i)); + if (ret != B_NO_ERROR) + tmp = B_ERROR; + + ret = into->AddInt32("omods", (int32)fOverrideModifiers.ItemAt(i)); + if (ret != B_NO_ERROR) + tmp = B_ERROR; + + ret = into->AddInt32("okeys", (int32)fOverrideKeyCodes.ItemAt(i)); + } + + if (tmp == B_ERROR) + return tmp; + else + return ret; +} + + +filter_result +KeyStrokeSequenceCommandActuator::KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) { + BMessage temp(*keyMsg); + int numChars = fSequence.Length(); + for (int i = 0; i < numChars; i++) { + char nextChar = fSequence.ByteAt(i); + + temp.RemoveName("modifiers"); + temp.AddInt32("modifiers", fModCodes[i]); + temp.RemoveName("key"); + temp.AddInt32("key", fKeyCodes[i]); + temp.RemoveName("raw_char"); + temp.AddInt32("raw_char", (int32) nextChar); + temp.RemoveName("byte"); + + int32 override = -1; + for (int32 j = fOverrideOffsets.CountItems()-1; j >= 0; j--) { + int32 offset = (int32) fOverrideOffsets.ItemAt(j); + if (offset == i) { + override = (int32) fOverrides.ItemAt(j); + break; + } + } + + char t[4]; + if (override >= 0) { + if (override < 0x80) { + // one-byte encoding + t[0] = (char) override; + t[1] = 0x00; + } else if (override < 0x800) { + // two-byte encoding + t[0] = 0xC0 | ((char)((override & 0x7C0)>>6)); + t[1] = 0x80 | ((char)((override & 0x03F)>>0)); + t[2] = 0x00; + } else { + // three-byte encoding + t[0] = 0xE0 | ((char)((override & 0xF000)>>12)); + t[1] = 0x80 | ((char)((override & 0x0FC0)>>6)); + t[2] = 0x80 | ((char)((override & 0x003F)>>0)); + t[3] = 0x00; + } + } else { + t[0] = nextChar; + t[1] = 0x00; + } + + temp.RemoveName("byte"); + + for (int m = 0; t[m] != 0x00; m++) + temp.AddInt8("byte", t[m]); + + temp.RemoveName("states"); + temp.AddData("states", B_UINT8_TYPE, &fStates[i * 16], 16, true, 16); + temp.RemoveName("bytes"); + temp.AddString("bytes", t); + temp.what = B_KEY_DOWN; + outlist->AddItem(new BMessage(temp)); + temp.what = B_KEY_UP; + outlist->AddItem(new BMessage(temp)); + } + return B_DISPATCH_MESSAGE; + } + else + return B_SKIP_MESSAGE; +} + + +BArchivable* +KeyStrokeSequenceCommandActuator::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "KeyStrokeSequenceCommandActuator")) + return new KeyStrokeSequenceCommandActuator(from); + else + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MIMEHandlerCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MIMEHandlerCommandActuator::MIMEHandlerCommandActuator(int32 argc, char** argv) + : + CommandActuator(argc, argv), + fMimeType((argc > 1) ? argv[1] : "") +{ + // empty +} + + +MIMEHandlerCommandActuator::MIMEHandlerCommandActuator(BMessage* from) + : + CommandActuator(from) +{ + const char* temp; + if (from->FindString("mimeType", 0, &temp) == B_NO_ERROR) + fMimeType = temp; +} + + +MIMEHandlerCommandActuator::~MIMEHandlerCommandActuator() +{ + // empty +} + + +status_t +MIMEHandlerCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = CommandActuator::Archive(into, deep); + into->AddString("mimeType", fMimeType.String()); + return ret; +} + + +filter_result +MIMEHandlerCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) + // cause KeyEventAsync() to be called asynchronously + *setAsyncData = (void*) true; + return B_SKIP_MESSAGE; +} + + +void +MIMEHandlerCommandActuator::KeyEventAsync(const BMessage* keyMsg, + void* asyncData) +{ + if (be_roster) { + BString str; + BString str1("Shortcuts MIME Launcher Error"); + status_t ret = be_roster->Launch(fMimeType.String()); + if ((ret != B_NO_ERROR) && (ret != B_ALREADY_RUNNING)) { + str << "Can't launch handler for "; + str << ", no such MIME type exists.Please check your Shortcuts"; + str << " settings. Please check your Shortcuts settings."; + (new BAlert(str1.String(), str.String(), "Ok"))->Go(NULL); + } + } +} + + +BArchivable* MIMEHandlerCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MIMEHandlerCommandActuator")) + return new MIMEHandlerCommandActuator(from); + else + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// BeepCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +BeepCommandActuator::BeepCommandActuator(int32 argc, char** argv) + : + CommandActuator(argc, argv) +{ + // empty +} + + +BeepCommandActuator::BeepCommandActuator(BMessage* from) + : + CommandActuator(from) +{ + // empty +} + + +BeepCommandActuator::~BeepCommandActuator() +{ + // empty +} + + +status_t +BeepCommandActuator::Archive(BMessage* into, bool deep) const +{ + return CommandActuator::Archive(into, deep); +} + + +BArchivable* +BeepCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "BeepCommandActuator")) + return new BeepCommandActuator(from); + else + return NULL; +} + + +filter_result +BeepCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) + beep(); + + return B_SKIP_MESSAGE; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MultiCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MultiCommandActuator::MultiCommandActuator(BMessage* from) + : + CommandActuator(from) +{ + BMessage msg; + for (int i = 0; from->FindMessage("subs", i, &msg) == B_NO_ERROR; i++) { + BArchivable* subObj = instantiate_object(&msg); + if (subObj) { + CommandActuator* ca = dynamic_cast < CommandActuator*>(subObj); + + if (ca) + fSubActuators.AddItem(ca); + else + delete subObj; + } + } +} + + +MultiCommandActuator::MultiCommandActuator(int32 argc, char** argv) + : + CommandActuator(argc, argv) +{ + for (int i = 1; i < argc; i++) { + CommandActuator* sub = CreateCommandActuator(argv[i]); + + if (sub) + fSubActuators.AddItem(sub); + else + printf("Error creating subActuator from [%s]\n", argv[i]); + } +} + + +MultiCommandActuator::~MultiCommandActuator() +{ + int numSubs = fSubActuators.CountItems(); + for (int i = 0; i < numSubs; i++) + delete ((CommandActuator*) fSubActuators.ItemAt(i)); +} + + +status_t +MultiCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = CommandActuator::Archive(into, deep); + if (ret != B_NO_ERROR) + return ret; + + int numSubs = fSubActuators.CountItems(); + for (int i = 0; i < numSubs; i++) { + BMessage msg; + ret = ((CommandActuator*)fSubActuators.ItemAt(i))->Archive(&msg, deep); + + if (ret != B_NO_ERROR) + return ret; + + into->AddMessage("subs", &msg); + } + return B_NO_ERROR; +} + + +BArchivable* +MultiCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MultiCommandActuator")) + return new MultiCommandActuator(from); + else + return NULL; +} + + +filter_result +MultiCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** asyncData, BMessage* lastMouseMove) +{ + BList* aDataList = NULL; // demand-allocated + filter_result res = B_SKIP_MESSAGE; + int numSubs = fSubActuators.CountItems(); + for (int i = 0; i < numSubs; i++) { + void* aData = NULL; + status_t next = ((CommandActuator*)fSubActuators.ItemAt(i))-> + KeyEvent(keyMsg, outlist, &aData, lastMouseMove); + + if (next == B_DISPATCH_MESSAGE) + // dispatch message if at least one sub wants it dispatched + res = B_DISPATCH_MESSAGE; + + if (aData) { + if (aDataList == NULL) + *asyncData = aDataList = new BList; + + while (aDataList->CountItems() < i - 1) + aDataList->AddItem(NULL); + aDataList->AddItem(aData); + } + } + return res; +} + + +void +MultiCommandActuator::KeyEventAsync(const BMessage* keyUpMsg, void* asyncData) +{ + BList* list = (BList*) asyncData; + int numSubs = list->CountItems(); + for (int i = 0; i < numSubs; i++) { + void* aData = list->ItemAt(i); + if (aData) + ((CommandActuator*) fSubActuators.ItemAt(i))-> + KeyEventAsync(keyUpMsg, aData); + } + delete list; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MoveMouseCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MoveMouseCommandActuator::MoveMouseCommandActuator(BMessage* from) + : + CommandActuator(from) +{ + if (from->FindFloat("xPercent", &fXPercent) != B_NO_ERROR) + fXPercent = 0.0f; + + if (from->FindFloat("yPercent", &fYPercent) != B_NO_ERROR) + fYPercent = 0.0f; + + if (from->FindFloat("xPixels", &fXPixels) != B_NO_ERROR) + fXPixels = 0; + + if (from->FindFloat("yPixels", &fYPixels) != B_NO_ERROR) + fYPixels = 0; +} + + +MoveMouseCommandActuator::MoveMouseCommandActuator(int32 argc, char** argv) + : + CommandActuator(argc, argv), + fXPercent(0.0f), + fYPercent(0.0f), + fXPixels(0), + fYPixels(0) +{ + if (argc > 1) + _ParseArg(argv[1], fXPercent, fXPixels); + + if (argc > 2) + _ParseArg(argv[2], fYPercent, fYPixels); +} + + +MoveMouseCommandActuator::~MoveMouseCommandActuator() +{ + // empty +} + + +status_t +MoveMouseCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = CommandActuator::Archive(into, deep); + into->AddFloat("xPercent", fXPercent); + into->AddFloat("yPercent", fYPercent); + into->AddFloat("xPixels", fXPixels); + into->AddFloat("yPixels", fYPixels); + return ret; +} + + +void +MoveMouseCommandActuator::CalculateCoords(float& setX, float& setY) const +{ + BScreen s; + BRect frame = s.Frame(); + setX = (frame.Width() * fXPercent) + fXPixels; + setY = (frame.Height() * fYPercent) + fYPixels; +} + + +BMessage* +MoveMouseCommandActuator::CreateMouseMovedMessage(const BMessage* origMsg, + BPoint p, BList* outlist) const +{ + // Force p into the screen space + { + BScreen s; + p.ConstrainTo(s.Frame()); + } + + BMessage* newMsg = new BMessage(B_MOUSE_MOVED); + + newMsg->AddPoint("where", p); + + int32 buttons = 0; + (void)origMsg->FindInt32("buttons", &buttons); + + if (buttons == 0) + buttons = 1; + + newMsg->AddInt32("buttons", buttons); + + // Trey sez you gotta keep then "when"'s increasing if you want click&drag + // to work! + const BMessage* lastMessage; + int nr = outlist->CountItems() - 1; + + if (outlist->CountItems() > 0) + lastMessage = (const BMessage*)outlist->ItemAt(nr); + else + lastMessage = origMsg; + + int64 when; + + if (lastMessage->FindInt64("when", &when) == B_NO_ERROR) { + when++; + newMsg->RemoveName("when"); + newMsg->AddInt64("when", when); + } + return newMsg; +} + + +static bool IsNumeric(char c); +static bool IsNumeric(char c) +{ + return (((c >= '0') && (c <= '9')) || (c == '.') || (c == '-')); +} + + +// Parse a string of the form "10", "10%", "10+ 10%", or "10%+ 10" +void +MoveMouseCommandActuator::_ParseArg(const char* arg, float& setPercent, + float& setPixels) const +{ + char* temp = new char[strlen(arg) + 1]; + strcpy(temp, arg); + + // Find the percent part, if any + char* percent = strchr(temp, '%'); + if (percent) { + // Rewind to one before the beginning of the number + char* beginNum = percent - 1; + while (beginNum >= temp) { + char c = *beginNum; + if (IsNumeric(c)) + beginNum--; + else + break; + } + + // parse the number + setPercent = atof(++beginNum)/100.0f; + + // Now white it out to ease finding the other # + while (beginNum <= percent) + *(beginNum++) = ' '; + } + + // Find the pixel part, if any + char* pixel = temp; + while (!IsNumeric(*pixel)) { + if (*pixel == '\0') + break; + pixel++; + } + setPixels = atof(pixel); + delete [] temp; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MoveMouseToCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MoveMouseToCommandActuator::MoveMouseToCommandActuator(BMessage* from) + : + MoveMouseCommandActuator(from) +{ + // empty +} + + +MoveMouseToCommandActuator::MoveMouseToCommandActuator(int32 argc, char** argv) + : + MoveMouseCommandActuator(argc, argv) +{ + // empty +} + + +MoveMouseToCommandActuator::~MoveMouseToCommandActuator() +{ + // empty +} + + +status_t +MoveMouseToCommandActuator::Archive(BMessage* into, bool deep) const +{ + return MoveMouseCommandActuator::Archive(into, deep); +} + + +BArchivable* +MoveMouseToCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MoveMouseToCommandActuator")) + return new MoveMouseToCommandActuator(from); + else + return NULL; +} + + +filter_result +MoveMouseToCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) { + float x, y; + CalculateCoords(x, y); + BPoint p(x, y); + BMessage* newMsg = CreateMouseMovedMessage(keyMsg, p, outlist); + *lastMouseMove = *newMsg; + outlist->AddItem(newMsg); + return B_DISPATCH_MESSAGE; + } + return B_SKIP_MESSAGE; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// MoveMouseByCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +MoveMouseByCommandActuator::MoveMouseByCommandActuator(BMessage* from) + : + MoveMouseCommandActuator(from) +{ + // empty +} + + +MoveMouseByCommandActuator::MoveMouseByCommandActuator(int32 argc, char** argv) + : + MoveMouseCommandActuator(argc, argv) +{ + // empty +} + + +MoveMouseByCommandActuator::~MoveMouseByCommandActuator() +{ + // empty +} + + +status_t MoveMouseByCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = MoveMouseCommandActuator::Archive(into, deep); + return ret; +} + + +BArchivable* +MoveMouseByCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "MoveMouseByCommandActuator")) + return new MoveMouseByCommandActuator(from); + else + return NULL; +} + + +filter_result +MoveMouseByCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) { + // Get the current mouse position + BPoint p; + if (lastMouseMove->FindPoint("where", &p) == B_NO_ERROR) { + // Get the desired offset + BPoint diff; + CalculateCoords(diff.x, diff.y); + p += diff; + + BMessage* newMsg = CreateMouseMovedMessage(keyMsg, p, outlist); + *lastMouseMove = *newMsg; + outlist->AddItem(newMsg); + return B_DISPATCH_MESSAGE; + } + } + return B_SKIP_MESSAGE; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// SendMessageCommandActuator +// +/////////////////////////////////////////////////////////////////////////////// +SendMessageCommandActuator::SendMessageCommandActuator(int32 argc, char** argv) + : + CommandActuator(argc, argv), + fSignature((argc > 1) ? argv[1] : "") +{ + // Parse the what code. It may be in any of the following formats: + // 12356 (int) + // 'HELO' (chars enclosed in single quotes) + // 0x12ab3c (hex) + + if (argc > 2) { + const char* whatStr = argv[2]; + if ((whatStr[0] == '\'') + && (strlen(whatStr) == 6) + && (whatStr[5] == '\'')) { + // Translate the characters into the uint32 they stand for. + // Note that we must do this in a byte-endian-independant fashion + // (no casting!) + fSendMsg.what = 0; + uint32 mult = 1; + for (int i = 0; i < 4; i++) { + fSendMsg.what += ((uint32)(whatStr[4 - i]))* mult; + mult <<= 8; + } + } else if (strncmp(whatStr, "0x", 2) == 0) + // translate hex string to decimal + fSendMsg.what = strtoul(&whatStr[2], NULL, 16); + else + fSendMsg.what = atoi(whatStr); + } else + fSendMsg.what = 0; + + for (int i = 3; i < argc; i++) { + type_code tc = B_BOOL_TYPE;// default type when no value is present + const char* arg = argv[i]; + BString argStr(arg); + const char* equals = strchr(arg, ' = '); + const char* value = "true";// default if no value is present + + if (equals) { + tc = B_STRING_TYPE;// default type when value is present + value = equals + 1; + const char* colon = strchr(arg, ':'); + if (colon > equals) + colon = NULL;// colons after the equals sign don't count + + if (colon) { + const char* typeStr = colon + 1; + if (strncasecmp(typeStr, "string", 6) == 0) + tc = B_STRING_TYPE; + else if (strncasecmp(typeStr, "int8", 4) == 0) + tc = B_INT8_TYPE; + else if (strncasecmp(typeStr, "int16", 5) == 0) + tc = B_INT16_TYPE; + else if (strncasecmp(typeStr, "int32", 5) == 0) + tc = B_INT32_TYPE; + else if (strncasecmp(typeStr, "int64", 5) == 0) + tc = B_INT64_TYPE; + else if (strncasecmp(typeStr, "bool", 4) == 0) + tc = B_BOOL_TYPE; + else if (strncasecmp(typeStr, "float", 5) == 0) + tc = B_FLOAT_TYPE; + else if (strncasecmp(typeStr, "double", 6) == 0) + tc = B_DOUBLE_TYPE; + else if (strncasecmp(typeStr, "point", 5) == 0) + tc = B_POINT_TYPE; + else if (strncasecmp(typeStr, "rect", 4) == 0) + tc = B_RECT_TYPE; + + // remove the colon and stuff + argStr = argStr.Truncate(colon - arg); + } else + // remove the equals and arg + argStr = argStr.Truncate(equals - arg); + } + + switch(tc) { + case B_STRING_TYPE: + fSendMsg.AddString(argStr.String(), value); + break; + + case B_INT8_TYPE: + fSendMsg.AddInt8(argStr.String(), (int8)atoi(value)); + break; + + case B_INT16_TYPE: + fSendMsg.AddInt16(argStr.String(), (int16)atoi(value)); + break; + + case B_INT32_TYPE: + fSendMsg.AddInt32(argStr.String(), (int32)atoi(value)); + break; + + case B_INT64_TYPE: + fSendMsg.AddInt64(argStr.String(), (int64)atoi(value)); + break; + + case B_BOOL_TYPE: + fSendMsg.AddBool(argStr.String(), ((value[0] == 't') + || (value[0] == 'T'))); + break; + + case B_FLOAT_TYPE: + fSendMsg.AddFloat(argStr.String(), atof(value)); + break; + + case B_DOUBLE_TYPE: + fSendMsg.AddDouble(argStr.String(), (double)atof(value)); + break; + + case B_POINT_TYPE: + { + float pts[2] = {0.0f, 0.0f}; + _ParseFloatArgs(pts, 2, value); + fSendMsg.AddPoint(argStr.String(), BPoint(pts[0], pts[1])); + break; + } + + case B_RECT_TYPE: + { + float pts[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + _ParseFloatArgs(pts, 4, value); + fSendMsg.AddRect(argStr.String(), + BRect(pts[0], pts[1], pts[2], pts[3])); + break; + } + } + } +} + + +void +SendMessageCommandActuator::_ParseFloatArgs(float* args, int maxArgs, + const char* str) const +{ + const char* next = str; + for (int i = 0; i < maxArgs; i++) { + args[i] = atof(next); + next = strchr(next, ','); + if (next) next++; + else break; + } +} + + +SendMessageCommandActuator::SendMessageCommandActuator(BMessage* from) + : + CommandActuator(from) +{ + const char* temp; + + if (from->FindString("signature", 0, &temp) == B_NO_ERROR) + fSignature = temp; + + (void) from->FindMessage("sendmsg", &fSendMsg); +} + + +SendMessageCommandActuator::~SendMessageCommandActuator() +{ + // empty +} + + +status_t +SendMessageCommandActuator::Archive(BMessage* into, bool deep) const +{ + status_t ret = CommandActuator::Archive(into, deep); + into->AddString("signature", fSignature.String()); + into->AddMessage("sendmsg", &fSendMsg); + return ret; +} + + +filter_result +SendMessageCommandActuator::KeyEvent(const BMessage* keyMsg, BList* outlist, + void** setAsyncData, BMessage* lastMouseMove) +{ + if (IS_KEY_DOWN(keyMsg)) + // cause KeyEventAsync() to be called asynchronously + *setAsyncData = (void*) true; + + return B_SKIP_MESSAGE; +} + + +void +SendMessageCommandActuator::KeyEventAsync(const BMessage* keyMsg, + void* asyncData) +{ + if (be_roster) { + BString str; + BString str1("Shortcuts SendMessage Error"); + if (fSignature.Length() == 0) { + str << "SendMessage: Target App Signature not specified"; + (new BAlert(str1.String(), str.String(), "Ok"))->Go(NULL); + } else { + status_t error = B_OK; + BMessenger msngr(fSignature.String(), -1, &error); + + if (error == B_OK) + msngr.SendMessage(&fSendMsg); + } + } +} + + +BArchivable* +SendMessageCommandActuator ::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "SendMessageCommandActuator")) + return new SendMessageCommandActuator(from); + else + return NULL; +} diff --git a/src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.h b/src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.h new file mode 100644 index 0000000000..8f3e136b36 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/CommandActuators.h @@ -0,0 +1,369 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef CommandActuators_h +#define CommandActuators_h + +#include +#include +#include +#include +#include + +#ifndef __INTEL__ +#pragma export on +#endif + +struct key_map; // declaration + +class CommandActuator; + +// Factory function: Given a text string, allocates and returns a +// CommandActuator. Returns NULL on failure (usually a parse error) +extern CommandActuator* CreateCommandActuator(const char* command); + +// This file contains various CommandActuator classes. Each CommandActuator +// contains code to do something. They're functor objects, really. The input +// server add-on will execute the CommandActuator associated with a key combo +// when that key combo is detected. + +// The abstract base class. Defines the interface. +_EXPORT class CommandActuator; +class CommandActuator : public BArchivable { +public: + CommandActuator(int32 argc, char** argv); + CommandActuator(BMessage* from); + + // Called by the InputFilter whenever a key is pressed or depressed. + // It's important to ensure that this method returns quickly, as the + // input_server will block while it executes. (keyMsg) is the BMessage + // that triggered this call. (outlist) is a BList that additional input + // events may be added to. If (*asyncData) is set to non-NULL, + // KeyEventAsync() will be called asynchronously with (asyncData) as + // the argument. Returns the filter_result to be given back to the + // input_server. (Defaults to B_SKIP_MESSAGE) + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** asyncData, + BMessage* lastMouseMove) + {return B_SKIP_MESSAGE;} + + // Called in a separate thread if (*setAsyncData) was set to non-NULL + // in KeyEvent(). Defaults to a no-op. + virtual void KeyEventAsync(const BMessage* keyUpMsg, + void* asyncData) {} + + virtual status_t Archive(BMessage* into, bool deep = true) + const; +}; + + +// This is the most common thing to do--launch a process. +_EXPORT class LaunchCommandActuator; +class LaunchCommandActuator : public CommandActuator { +public: + LaunchCommandActuator(int32 argc, char** argv); + LaunchCommandActuator(BMessage* from); + ~LaunchCommandActuator(); + + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage* from); + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + virtual void KeyEventAsync(const BMessage* keyMsg, + void* asyncData); + +private: + bool _GetNextWord(char** setBegin, char** setEnd) + const; + + char** fArgv; + int32 fArgc; +}; + + +// Stupid actuator--just calls beep(). +_EXPORT class BeepCommandActuator; +class BeepCommandActuator : public CommandActuator { +public: + BeepCommandActuator(int32 argc, char** argv); + BeepCommandActuator(BMessage* from); + ~BeepCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage* from); +}; + + +// This class will insert a string of keystrokes into the input stream. +_EXPORT class KeyStrokeSequenceCommandActuator; +class KeyStrokeSequenceCommandActuator : public CommandActuator { +public: + KeyStrokeSequenceCommandActuator(int32 argc, + char** argv); + KeyStrokeSequenceCommandActuator( + BMessage* from); + ~KeyStrokeSequenceCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage * from); +private: + void _GenerateKeyCodes(); + int32 _LookupKeyCode(key_map* map, char* keys, + int32 offsets[128], char key, + uint8* setStates, int32& setMod, + int32 setTo) const; + void _SetStateBit(uint8* setStates, uint32 key, + bool on = true) const; + + uint8* fStates; + int32* fKeyCodes; + int32* fModCodes; + BString fSequence; + BList fOverrides; + BList fOverrideOffsets; + BList fOverrideModifiers; + BList fOverrideKeyCodes; +}; + + +// This class will insert a string of keystrokes into the input stream. +_EXPORT class MIMEHandlerCommandActuator; +class MIMEHandlerCommandActuator : public CommandActuator { +public: + MIMEHandlerCommandActuator(int32 argc, + char** argv); + MIMEHandlerCommandActuator(BMessage* from); + ~MIMEHandlerCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + virtual void KeyEventAsync(const BMessage* keyUpMsg, + void* asyncData); + virtual status_t Archive(BMessage * into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage* from); + +private: + BString fMimeType; +}; + + +// Abstract base class for actuators that affect mouse buttons +_EXPORT class MouseCommandActuator; +class MouseCommandActuator : public CommandActuator { +public: + MouseCommandActuator(int32 argc, char** argv); + MouseCommandActuator(BMessage* from); + ~MouseCommandActuator(); + + virtual status_t Archive(BMessage* into, bool deep = true) + const; + +protected: + int32 _GetWhichButtons() const; + void _GenerateMouseButtonEvent(bool mouseDown, + const BMessage* keyMsg, BList* outlist, + BMessage* lastMouseMove); + +private: + int32 fWhichButtons; +}; + + +// This class sends a single mouse down event when activated, causing the mouse +// pointer to enter a "sticky down" state. Good for some things(like dragging). +_EXPORT class MouseDownCommandActuator; +class MouseDownCommandActuator : public MouseCommandActuator { +public: + MouseDownCommandActuator(int32 argc, + char** argv); + MouseDownCommandActuator(BMessage* from); + ~MouseDownCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable * Instantiate(BMessage * from); +}; + + +// This class sends a single mouse down up when activated, releasing any +// previously set "sticky down" state. Good for some things (like dragging). +_EXPORT class MouseUpCommandActuator; +class MouseUpCommandActuator : public MouseCommandActuator { +public: + MouseUpCommandActuator(int32 argc, + char** argv); + MouseUpCommandActuator(BMessage * from); + ~MouseUpCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage* from); +}; + + +// This class will send B_MOUSE_UP and B_MOUSE_DOWN events whenever B_KEY_UP or +// B_KEY_DOWN events are detected for its key This way a key can act sort of +// like a mouse button. +_EXPORT class MouseButtonCommandActuator; +class MouseButtonCommandActuator : public MouseCommandActuator { +public: + MouseButtonCommandActuator(int32 argc, + char** argv); + MouseButtonCommandActuator(BMessage* from); + ~MouseButtonCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage* from); + +private: + bool fKeyDown ; +}; + + +// Base class for some actuators that control the position of the mouse pointer +_EXPORT class MoveMouseCommandActuator; +class MoveMouseCommandActuator : public CommandActuator { +public: + MoveMouseCommandActuator(int32 argc, + char** argv); + MoveMouseCommandActuator(BMessage* from); + ~MoveMouseCommandActuator(); + + virtual status_t Archive(BMessage* into, bool deep) const; + +protected: + void CalculateCoords(float& setX, float& setY) + const; + BMessage* CreateMouseMovedMessage(const BMessage* origMsg + , BPoint p, BList* outlist) const; + +private: + void _ParseArg(const char* arg, float& setPercent, + float& setPixels) const; + + float fXPercent; + float fYPercent; + float fXPixels; + float fYPixels; +}; + + +// Actuator that specifies multiple sub-actuators to be executed in series +_EXPORT class MultiCommandActuator; +class MultiCommandActuator : public CommandActuator { +public: + MultiCommandActuator(int32 argc, char** argv); + MultiCommandActuator(BMessage* from); + ~MultiCommandActuator(); + + virtual status_t Archive(BMessage* into, bool deep) const; + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** asyncData, + BMessage* lastMouseMove); + virtual void KeyEventAsync(const BMessage* keyUpMsg, + void * asyncData); + static BArchivable* Instantiate(BMessage* from); + +private: + BList fSubActuators; +}; + + +// Actuator for moving a mouse relative to its current position +_EXPORT class MoveMouseToCommandActuator; +class MoveMouseToCommandActuator : public MoveMouseCommandActuator { +public: + MoveMouseToCommandActuator(int32 argc, + char** argv); + MoveMouseToCommandActuator(BMessage* from); + ~MoveMouseToCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, BList* outlist + , void** setAsyncData, + BMessage* lastMouseMove); + + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage* from); +}; + + +// Actuator for moving a mouse relative to its current position +_EXPORT class MoveMouseByCommandActuator; +class MoveMouseByCommandActuator : public MoveMouseCommandActuator { +public: + MoveMouseByCommandActuator(int32 argc, + char** argv); + MoveMouseByCommandActuator(BMessage* from); + ~MoveMouseByCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + virtual status_t Archive(BMessage * into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage * from); +}; + + +// Actuator to send BMessage to an application - written by Daniel Wesslen +_EXPORT class SendMessageCommandActuator; +class SendMessageCommandActuator : public CommandActuator { +public: + SendMessageCommandActuator(int32 argc, + char** argv); + SendMessageCommandActuator(BMessage* from); + ~SendMessageCommandActuator(); + + virtual filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, void** setAsyncData, + BMessage* lastMouseMove); + virtual void KeyEventAsync(const BMessage* keyUpMsg, + void* asyncData); + virtual status_t Archive(BMessage* into, bool deep = true) + const; + static BArchivable* Instantiate(BMessage * from); + +private: + void _ParseFloatArgs(float* outArgs, int maxArgs + , const char* str) const; + + BString fSignature; + BMessage fSendMsg; +}; + +#ifndef __INTEL__ +#pragma export reset +#endif + +#endif diff --git a/src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.cpp b/src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.cpp new file mode 100644 index 0000000000..6f64e539cd --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#include "CommandExecutor.h" + + +#include +#include +#include + + +#include + + +#include "ShortcutsFilterConstants.h" +#include "CommandActuators.h" + +CommandExecutor::CommandExecutor() +{ + // empty +} + + +CommandExecutor::~CommandExecutor() +{ + // empty +} + + +// Returns true if it is returning valid results into (*setBegin) and +// (*setEnd). If returning true, (*setBegin) now points to the first char in a +// new word, and (*setEnd) now points to the char after the last char in the +// word, which has been set to a NUL byte. +bool +CommandExecutor::GetNextWord(char** setBegin, char** setEnd) const +{ + char* next = *setEnd; // we'll start one after the end of the last one... + + while (next++) { + if (*next == '\0') + return false; // no words left! + else if (*next <= ' ') + *next = '\0'; + else + break; // found a non-whitespace char! + } + + *setBegin = next; // we found the first char! + + while (next++) { + if (*next <= ' ') { + *next = '\0'; // terminate the word + *setEnd = next; + return true; + } + } + return false; // should never get here, actually +} + + +void +CommandExecutor::MessageReceived(BMessage* msg) +{ + switch(msg->what) { + case B_UNMAPPED_KEY_DOWN: + case B_KEY_DOWN: + { + BMessage actMessage; + void* asyncData; + if ((msg->FindMessage("act", &actMessage) == B_NO_ERROR) + && (msg->FindPointer("adata", &asyncData) == B_NO_ERROR)) { + BArchivable* arcObj = instantiate_object(&actMessage); + if (arcObj) { + CommandActuator* act = dynamic_cast(arcObj); + + if (act) + act->KeyEventAsync(msg, asyncData); + delete arcObj; + } + } + break; + } + } +} diff --git a/src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.h b/src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.h new file mode 100644 index 0000000000..2682d0e776 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/CommandExecutor.h @@ -0,0 +1,31 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef CommandExecutor_h +#define CommandExecutor_h + +#include +#include +#include + +// This thread receives BMessages telling it what +// to launch, and launches them. +class CommandExecutor : public BLooper { +public: + CommandExecutor(); + ~CommandExecutor(); + + virtual void MessageReceived(BMessage* msg); + +private: + bool GetNextWord(char** setBeginWord, char** setEndWord) + const; +}; + +#endif diff --git a/src/add-ons/input_server/filters/shortcut_catcher/Jamfile b/src/add-ons/input_server/filters/shortcut_catcher/Jamfile new file mode 100644 index 0000000000..0182b9f8a0 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src add-ons input_server filters shortcut_catcher ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +# Common files used here and in the app +StaticLibrary libshortcuts_shared.a : + BitFieldTesters.cpp + CommandActuators.cpp + KeyInfos.cpp + ParseCommandLine.cpp +; + +Addon shortcut_catcher : + CommandExecutor.cpp + KeyCommandMap.cpp + ShortcutsServerFilter.cpp + : be game input_server libshortcuts_shared.a $(TARGET_LIBSUPC++) ; diff --git a/src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.cpp b/src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.cpp new file mode 100644 index 0000000000..007ea767d1 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.cpp @@ -0,0 +1,312 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#include "KeyCommandMap.h" + + +#include + + +#include +#include +#include +#include +#include +#include +#include + + +#include "ShortcutsFilterConstants.h" +#include "BitFieldTesters.h" +#include "CommandActuators.h" + +#define FILE_UPDATED 'fiUp' + +class hks { +public: + hks(int32 key, BitFieldTester* t, CommandActuator* act, const BMessage& a) + : + fKey(key), + fTester(t), + fActuator(act), + fActuatorMessage(a) + { + // empty + } + + + ~hks() + { + delete fActuator; + delete fTester; + } + + int32 GetKey() const {return fKey;} + bool DoModifiersMatch(uint32 bits) const + { + return fTester->IsMatching(bits); + } + const BMessage& GetActuatorMsg() const {return fActuatorMessage;} + CommandActuator* GetActuator() {return fActuator;} + +private: + int32 fKey; + BitFieldTester* fTester; + CommandActuator* fActuator; + const BMessage fActuatorMessage; +}; + + +KeyCommandMap::KeyCommandMap(const char* file) + : + fSpecs(NULL) +{ + fFileName = new char[strlen(file) + 1]; + strcpy(fFileName, file); + + BEntry fileEntry(fFileName); + if (fileEntry.InitCheck() == B_NO_ERROR) { + node_ref nref; + + if (fileEntry.GetNodeRef(&nref) == B_NO_ERROR) + watch_node(&nref, B_WATCH_STAT, this); + } + + BMessage msg(FILE_UPDATED); + PostMessage(&msg); + + fPort = create_port(1, SHORTCUTS_CATCHER_PORT_NAME); + _PutMessageToPort(); // advertise our BMessenger to the world +} + + +KeyCommandMap::~KeyCommandMap() +{ + if (fPort >= 0) + close_port(fPort); + + for (int i = fInjects.CountItems() - 1; i >= 0; i--) + delete (BMessage*)fInjects.ItemAt(i); + + stop_watching(this); // don't know if this is necessary, but it can't hurt + _DeleteHKSList(fSpecs); + delete [] fFileName; +} + + +void +KeyCommandMap::MouseMoved(const BMessage* mm) +{ + // Save the mouse state for later... + fLastMouseMessage = *mm; +} + + +filter_result +KeyCommandMap::KeyEvent(const BMessage* keyMsg, BList* outlist, + const BMessenger& sendTo) +{ + uint32 modifiers; + filter_result ret = B_DISPATCH_MESSAGE; // default: pass it on + + if (keyMsg->FindInt32("modifiers", (int32*) &modifiers) == B_NO_ERROR) { + int32 key; + if (keyMsg->FindInt32("key", &key) == B_NO_ERROR) { + if (fSyncSpecs.Lock()) { + if (fSpecs != NULL) { + int num = fSpecs->CountItems(); + + for (int i = 0; i < num; i++) { + hks* next = (hks*) fSpecs->ItemAt(i); + + if ((key == next->GetKey()) + && (next->DoModifiersMatch(modifiers))) { + void* asyncData = NULL; + ret = next->GetActuator()-> + KeyEvent(keyMsg, outlist, &asyncData, &fLastMouseMessage); + + if (asyncData) { + BMessage newMsg(*keyMsg); + newMsg.AddMessage("act", &next->GetActuatorMsg()); + newMsg.AddPointer("adata", asyncData); + sendTo.SendMessage(&newMsg); + } + } + } + } + fSyncSpecs.Unlock(); + } + } + } + return ret; +} + + +void +KeyCommandMap::DrainInjectedEvents(const BMessage* keyMsg, BList* outlist, + const BMessenger& sendTo) +{ + BList temp; + if (fSyncSpecs.Lock()) { + temp = fInjects; + fInjects.MakeEmpty(); + fSyncSpecs.Unlock(); + } + + int is = temp.CountItems(); + for (int i = 0; i < is; i++) { + BMessage* msg = (BMessage*)temp.ItemAt(i); + BArchivable* arc = instantiate_object(msg); + + if (arc) { + CommandActuator* act = dynamic_cast(arc); + + if (act) { + BMessage newMsg(*keyMsg); + newMsg.what = B_KEY_DOWN; + void* asyncData = NULL; + (void) act->KeyEvent(&newMsg, outlist, &asyncData, + &fLastMouseMessage); + + if (asyncData) { + newMsg.AddMessage("act", msg); + newMsg.AddPointer("adata", asyncData); + sendTo.SendMessage(&newMsg); + } + } + delete arc; + } + delete msg; + } +} + + +void +KeyCommandMap::MessageReceived(BMessage* msg) +{ + switch(msg->what) { + case EXECUTE_COMMAND: + { + BMessage subMsg; + + if (msg->FindMessage("act", &subMsg) == B_NO_ERROR) { + if (fSyncSpecs.Lock()) { + fInjects.AddItem(new BMessage(subMsg)); + fSyncSpecs.Unlock(); + + // This evil hack forces input_server to call Filter() on + // us so we can process the injected event. + BPoint lmp; + status_t err = fLastMouseMessage.FindPoint("where", &lmp); + if (err == B_NO_ERROR) + set_mouse_position((int32)lmp.x, (int32)lmp.y); + } + } + break; + } + + case REPLENISH_MESSENGER: + _PutMessageToPort(); + break; + + case B_NODE_MONITOR: + case FILE_UPDATED: + { + BMessage fileMsg; + BFile file(fFileName, B_READ_ONLY); + if ((file.InitCheck() == B_NO_ERROR) + && (fileMsg.Unflatten(&file) == B_NO_ERROR)) { + BList* newList = new BList; + + // whatever this is set to will be deleted below. + // defaults to no deletion + BList* oldList = NULL; + + int i = 0; + BMessage msg; + while (fileMsg.FindMessage("spec", i++, &msg) == B_NO_ERROR) { + uint32 key; + BMessage testerMsg; + BMessage actMsg; + + if ((msg.FindInt32("key", (int32*) &key) == B_NO_ERROR) + && ((msg.FindMessage("act", &actMsg)) == B_NO_ERROR) + && ((msg.FindMessage("modtester", &testerMsg)) + == B_NO_ERROR)) { + + BArchivable* arcObj = instantiate_object(&testerMsg); + if (arcObj) { + BitFieldTester* tester = + dynamic_cast(arcObj); + + if (tester) { + BArchivable* barcObj = + instantiate_object(&actMsg); + + if (barcObj) { + CommandActuator* act = + dynamic_cast(barcObj); + + if (act) + newList->AddItem( + new hks(key, tester, act, actMsg)); + else { + delete barcObj; + delete tester; + } + } else + delete tester; + } else + delete arcObj; + } + } + } + + if (fSyncSpecs.Lock()) { + // swap in the new list + oldList = fSpecs; + fSpecs = newList; + fSyncSpecs.Unlock(); + } else { + // wtf? This shouldn't happen... + oldList = newList; // but clean up if it does + } + _DeleteHKSList(oldList); + } + } + break; + } +} + + +// Deletes an HKS-filled BList and its contents. +void KeyCommandMap::_DeleteHKSList(BList* l) +{ + if (l != NULL) { + int num = l->CountItems(); + for (int i = 0; i < num; i++) + delete ((hks*) l->ItemAt(i)); + delete l; + } +} + + +void KeyCommandMap::_PutMessageToPort() +{ + if (fPort >= 0) { + BMessenger toMe(this); + BMessage m; + m.AddMessenger("target", toMe); + + char buf[2048]; + ssize_t fs = m.FlattenedSize(); + if ((fs <= sizeof(buf)) && (m.Flatten(buf, fs) == B_NO_ERROR)) + write_port_etc(fPort, 0, buf, fs, B_TIMEOUT, 250000); + } +} diff --git a/src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.h b/src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.h new file mode 100644 index 0000000000..b70e7c65a7 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/KeyCommandMap.h @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef KeyCommandMap_h +#define KeyCommandMap_h + +#include +#include + + +#include +#include +#include +#include + +// Maps BMessages to ShortcutsSpecs! +// The thread here gets file update messages, and updates +// the fSpecs list to match them (asynchronously). +class KeyCommandMap : public BLooper { +public: + KeyCommandMap(const char* watchFile); + ~KeyCommandMap(); + + // Called when a key is pressed or depressed, in the input_server thread. + // (keyMsg) is the KEY_DOWN message given by the Input Server. (outlist) is + // a BList that additional BMessages can be added to (to insert events into + // the input stream) (sendMessagesTo) is the address of a queue to send + // command messages to, for async effects. + filter_result KeyEvent(const BMessage* keyMsg, + BList* outlist, + const BMessenger& sendMessagesTo); + + // Called whenever the a B_MOUSE_MOVED message is received. + void MouseMoved(const BMessage* mouseMoveMsg); + void MessageReceived(BMessage* msg); + void DrainInjectedEvents(const BMessage* keyMsg, + BList* outlist, + const BMessenger& sendMessagesTo); + +private: + void _PutMessageToPort(); + void _DeleteHKSList(BList* list); + + port_id fPort; + char* fFileName; + BLocker fSyncSpecs; // locks the lists below + BList fInjects; + BList* fSpecs; + BMessage fLastMouseMessage; +}; + +#endif diff --git a/src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.cpp b/src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.cpp new file mode 100644 index 0000000000..ab20c61391 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#include "KeyInfos.h" + + +#include +#include +#include + + +#include + + +#define NUM_KEYS 256 +#define MAX_UTF8_LENGTH 5 // up to 4 chars, plus a nul terminator + +struct KeyLabelMap { + const char* fLabel; + uint8 fKeyCode; +}; + +// This is a table of keys-codes that have special, hard-coded labels. +static const struct KeyLabelMap keyLabels[] = { + {"", 0}, + {"Esc", 1}, + {"F1", 2}, + {"F2", 3}, + {"F3", 4}, + {"F4", 5}, + {"F5", 6}, + {"F6", 7}, + {"F7", 8}, + {"F8", 9}, + {"F9", 10}, + {"F10", 11}, + {"F11", 12}, + {"F12", 13}, + {"SysRq", 14}, + {"ScrlLck", 15}, + {"Pause", 16}, + {"Bcksp", 30}, + {"Insert", 31}, + {"Home", 32}, + {"PgUp", 33}, + {"Num Lock", 34}, + {"Kpd /", 35}, + {"Kpd *", 36}, + {"Kpd -", 37}, + {"Tab", 38}, + {"Delete", 52}, + {"End", 53}, + {"PgDn", 54}, + {"Kpd 7", 55}, + {"Kpd 8", 56}, + {"Kpd 9", 57}, + {"Kpd +", 58}, + {"Caps Lock", 59}, + {"Enter", 71}, + {"Kpd 4", 72}, + {"Kpd 5", 73}, + {"Kpd 6", 74}, + {"L.Shift", 75}, + {"R.Shift", 86}, + {"Up", 87}, + {"Kpd 1", 88}, + {"Kpd 2", 89}, + {"Kpd 3", 90}, + {"Kpd Entr", 91}, + {"L.Control", 92}, + {"L.Alt", 93}, + {"Space", 94}, + {"R.Alt", 95}, + {"R.Control", 96}, + {"Left", 97}, + {"Down", 98}, + {"Right", 99}, + {"Kpd 0", 100}, + {"Kpd .", 101}, + {"L.Command", 102}, + {"R.Command", 103}, + {"Menu", 104}, + {"PowerOn", 107}, +}; + +// Key description strings (e.g. "A" or "Escape"). Null if no description is +// available. +static const char* keyDescriptions[NUM_KEYS]; + +// series of optional up-to-(4+1)-byte terminated UTF-8 character strings... +static char utfDescriptions[NUM_KEYS * MAX_UTF8_LENGTH]; + +static const char* FindSpecialKeyLabelFor(uint8 keyCode, int& last); + +static const char* +FindSpecialKeyLabelFor(uint8 keyCode, int& last) +{ + while ((keyLabels[last].fKeyCode < keyCode) + && (last < (sizeof(keyLabels)/sizeof(struct KeyLabelMap))-1)) + last++; + + if (keyLabels[last].fKeyCode == keyCode) + return keyLabels[last].fLabel; + else + return NULL; +} + + +void +InitKeyIndices() +{ + int nextSpecial = 0; + key_map* map; + char* keys; + get_key_map(&map, &keys); + + for (int j = 0; j < NUM_KEYS; j++) { + keyDescriptions[j] = NULL; // default + + const char* slabel = FindSpecialKeyLabelFor(j, nextSpecial); + int keyCode = map->normal_map[j]; + + if (keyCode >= 0) { + const char* mapDesc = &keys[keyCode]; + uint8 len = *mapDesc; + + for (int m = 0; m < MAX_UTF8_LENGTH; m++) + if (m < len) + utfDescriptions[j * MAX_UTF8_LENGTH + m] = mapDesc[m + 1]; + else + utfDescriptions[j * MAX_UTF8_LENGTH + m] = '\0'; + + if (slabel) + keyDescriptions[j] = slabel; + else { + // If it's an ASCII letter, capitalize it. + char& c = utfDescriptions[j * MAX_UTF8_LENGTH]; + + if ((len == 1) && (isalpha(c))) + c = toupper(c); + + if ((len > 1)||((len == 1) && (c > ' '))) + keyDescriptions[j] = &c; + } + } else + utfDescriptions[j * MAX_UTF8_LENGTH] = 0x00; + } +} + + +const char* +GetKeyUTF8(uint8 keyIndex) +{ + return &utfDescriptions[keyIndex * MAX_UTF8_LENGTH]; +} + + +const char* +GetKeyName(uint8 keyIndex) +{ + return keyDescriptions[keyIndex]; +} + + +int +GetNumKeyIndices() +{ + return NUM_KEYS; +} + + +uint8 +FindKeyCode(const char* keyName) +{ + for (int i = 0; i < NUM_KEYS; i++) + if ((keyDescriptions[i]) + && (strcasecmp(keyName, keyDescriptions[i]) == 0)) + return i; + return 0; // default to sentinel value +} diff --git a/src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.h b/src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.h new file mode 100644 index 0000000000..a7782b72a5 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/KeyInfos.h @@ -0,0 +1,33 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef KeyInfos_h +#define KeyInfos_h + +#include + +// Returns an ASCII string for the given key index, or NULL if a bad code is +// given. +const char* GetKeyName(uint8 keyIndex); + +// Inverse of GetKeyName(). Finds the index of the given string. Returns 0 if +// the string was not found in the set of key names. +uint8 FindKeyCode(const char* keyName); + +// Returns the UTF8 value for the given key, or "\0" if none. +const char* GetKeyUTF8(uint8 keyIndex); + +// Returns the number of key indices that are known. (Currently 256). Indices +// (0...GetNumKeyIndices()-1) are valid. +int GetNumKeyIndices(); + +// Should be called at startup. +void InitKeyIndices(); + +#endif diff --git a/src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.cpp b/src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.cpp new file mode 100644 index 0000000000..18048df32b --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.cpp @@ -0,0 +1,321 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + * Fredrik Modéen + */ + + +#include "ParseCommandLine.h" + + +#include +#include +#include + + +#include +#include +#include +#include +#include + + +// This char is used to hold words together into single words... +#define GUNK_CHAR 0x01 +#define PATHTOTRACKER "/boot/system/Tracker" + +// Turn all spaces that are not-to-be-counted-as-spaces into GUNK_CHAR chars. +static void +GunkSpaces(char* string) +{ + bool insideQuote = false; + bool afterBackslash = false; + + while (*string) { + switch(*string) { + case '\"': + if (!afterBackslash) + insideQuote = !insideQuote; // toggle escapement mode + break; + + case ' ': + case '\t': + if ((insideQuote)||(afterBackslash)) + *string = GUNK_CHAR; + break; + } + afterBackslash = (*string == '\\') ? !afterBackslash : false; + string++; + } +} + + +// Removes all un-escaped quotes and backslashes from the string, in place +static void +RemoveQuotes(char* string) +{ + bool afterBackslash = false; + char* endString = strchr(string, '\0'); + char* to = string; + + while (*string) { + bool temp = (*string == '\\') ? !afterBackslash : false; + switch(*string) { + case '\"': + case '\\': + if (afterBackslash) + *(to++) = *string; + break; + + case 'n': + *(to++) = afterBackslash ? '\n' : *string; + break; + + case 't': + *(to++) = afterBackslash ? '\t' : *string; + break; + + default: + *(to++) = *string; + break; + } + afterBackslash = temp; + string++; + } + *to = '\0'; + + if (to < endString) + *(to+1) = '\0'; // needs to be double-terminated! +} + + +static bool IsValidChar(char c); + +static bool +IsValidChar(char c) +{ + return ((c > ' ')||(c == '\n')||(c == '\t')); +} + + +// Returns true if it is returning valid results into (*setBegin) & (*setEnd). +// If returning true, (*setBegin) now points to the first char in a new word, +// and (*setEnd) now points to the char after the last char in the word, which +// has been set to a NUL byte. +static bool +GetNextWord(char** setBegin, char** setEnd) +{ + char* next = *setEnd; // we'll start one after the end of the last one... + + while (next++) { + if (*next == '\0') + return false; // no words left! + else if ((IsValidChar(*next) == false) && (*next != GUNK_CHAR)) + *next = '\0'; + else + break; // found a non-whitespace char! + } + + *setBegin = next; // we found the first char! + + while (next++) { + if ((IsValidChar(*next) == false) && (*next != GUNK_CHAR)) { + *next = '\0'; // terminate the word + *setEnd = next; + return true; + } + } + + return false; // should never get here, actually +} + + +// Turns the gunk back into spaces +static void +UnGunk(char* str) +{ + char* temp = str; + while (*temp) { + if (*temp == GUNK_CHAR) + *temp = ' '; + temp++; + } +} + + +char** +ParseArgvFromString(const char* command, int32& argc) +{ + // make our own copy of the string... + int slen = strlen(command); + + // need an extra nul byte to get GetNextWord() to stop + char* cmd = new char[slen + 2]; + strcpy(cmd, command); + cmd[slen+1] = '\0'; // zero out the second nul byte + + GunkSpaces(cmd); + RemoveQuotes(cmd); + + BList wordlist; + char* beginWord = NULL, *endWord = cmd - 1; + + while (GetNextWord(&beginWord, &endWord)) + wordlist.AddItem(beginWord); + + argc = wordlist.CountItems(); + char** argv = new char* [argc + 1]; + for (int i = 0; i < argc; i++) { + char* temp = (char*) wordlist.ItemAt(i); + argv[i] = new char[strlen(temp) + 1]; + strcpy(argv[i], temp); + + // turn space-markers back into real spaces... + UnGunk(argv[i]); + } + argv[argc] = NULL; // terminate the array + + delete [] cmd; // don't need our local copy any more + return argv; +} + + +void +FreeArgv(char** argv) +{ + if (argv) { + int i = 0; + + while (argv[i]) { + delete [] argv[i]; + i++; + } + } + delete [] argv; +} + + +// Make new, independent clone of an argv array and its strings. +char** +CloneArgv(char** argv) +{ + int argc = 0; + while (argv[argc] != NULL) + argc++; + + char** newArgv = new char* [argc + 1]; + for (int i = 0; i < argc; i++) { + newArgv[i] = new char[strlen(argv[i]) + 1]; + strcpy(newArgv[i], argv[i]); + } + newArgv[argc] = NULL; + return newArgv; +} + + + +BString +ParseArgvZeroFromString(const char* command) +{ + char* ret = NULL; + + // make our own copy of the string... + int slen = strlen(command); + + // need an extra nul byte to get GetNextWord() to stop + char* cmd = new char[slen + 2]; + strcpy(cmd, command); + cmd[slen + 1] = '\0'; // zero out the second nul byte + + GunkSpaces(cmd); + RemoveQuotes(cmd); + + char* beginWord = NULL, *endWord = cmd - 1; + if (GetNextWord(&beginWord, &endWord)) { + ret = new char[strlen(beginWord) + 1]; + strcpy(ret, beginWord); + UnGunk(ret); + } + delete [] cmd; + + BString retStr(ret?ret:""); + delete [] ret; + + return retStr; +} + + +bool +DoStandardEscapes(BString& string) +{ + bool ret = false; + + // Escape any characters that might mess us up + // note: check this first, or we'll detect the slashes WE put in! + ret |= EscapeChars(string, '\\'); + ret |= EscapeChars(string, '\"'); + ret |= EscapeChars(string, ' '); + ret |= EscapeChars(string, '\t'); + return ret; +} + + +// Modifies (string) so that each instance of (badChar) in it is preceded by a +// backslash. Returns true iff modifications were made. +bool +EscapeChars(BString& string, char badChar) +{ + if (string.FindFirst(badChar) == -1) + return false; + + BString temp; + int stringLen = string.Length(); + for (int i = 0; i < stringLen; i++) { + char next = string[i]; + if (next == badChar) + temp += '\\'; + temp += next; + } + + string = temp; + return true; +} + + +// Launch the given app/project file. Put here so that Shortcuts and +// BartLauncher can share this code! +status_t +LaunchCommand(char** argv, int32 argc) +{ + BEntry entry(argv[0], true); + if (entry.Exists()) { + // See if it's a directory. If it is, ask Tracker to open it, rather + // than launch. + BDirectory testDir(&entry); + if (testDir.InitCheck() == B_NO_ERROR) { + // Hack way to do this--really I should be able to do this by + // sending a BMessage. But how? When I finally get my copy of the + // BeOS Bible, maybe then I'll find out. + const char* trackerFile = PATHTOTRACKER; + char* temp = new char[strlen(trackerFile) + strlen(argv[0]) + 10]; + sprintf(temp, "%s '%s'", trackerFile, argv[0]); + system(temp); + delete [] temp; + return B_NO_ERROR; + } else { + // It's not a directory. Must be a file. + entry_ref ref; + if (entry.GetRef(&ref) == B_NO_ERROR) { + if (argc > 1) + be_roster->Launch(&ref, argc-1, &argv[1]); + else + be_roster->Launch(&ref); + return B_NO_ERROR; + } + } + } + return B_ERROR; +} diff --git a/src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.h b/src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.h new file mode 100644 index 0000000000..533b649e3a --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/ParseCommandLine.h @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef ParseCommandString_h +#define ParseCommandString_h + +#include + +// Utility methods to extract arguments from a typed-in string. + +// Returns an NULL-terminated argv array. Sets (setArgc) to the number of +// valid arguments in the array. It becomes the responsibility of the calling +// code to delete[] the array and each string in it. If (padFront > 0), then +// (padFront) extra "slots" will be allocated at the beginning of the returned +// argv array. These slots will be NULL, and will be counted in the (setArgc) +// result. It is the caller's responsibility to fill them... +char** ParseArgvFromString(const char* string, int32& setArgc); + +// Call this to free the argv array returned by ParseArgvFromString(). +char** CloneArgv(char** argv); + +// Call this to free the argv array returned by ParseArgvFromString(). +void FreeArgv(char** argv); + +// Returns the first argument in the string. +BString ParseArgvZeroFromString(const char* string); + +// Calls EscapeChars() on (string) for the following characters: backslash, +// single quote, double quote, space, and tab. The returns string should be +// parsable as a single word by the functions above. +bool DoStandardEscapes(BString& string); + +// Modifies (string) by inserting slashes in front of each instance of badChar. +// Returns false iff no modifications were done. +bool EscapeChars(BString& string, char badChar); + +// Launch an app, Tracker style. Put here so that it can be shared amongst +// my various apps... +status_t LaunchCommand(char** argv, int32 argc); + +#endif diff --git a/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsFilterConstants.h b/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsFilterConstants.h new file mode 100644 index 0000000000..573c472ab2 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsFilterConstants.h @@ -0,0 +1,24 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef ShortcutsConstants_h +#define ShortcutsConstants_h + +#define SHORTCUTS_SETTING_FILE_NAME "shortcuts_settings" + +#define SHORTCUTS_CATCHER_PORT_NAME "ShortcutsCatcherPort" + +enum { + EXECUTE_COMMAND = 'exec', // Code: Execute "command" as given + REPLENISH_MESSENGER, // sent to tell us to write our port again + NUM_ACTION_CODES +}; + +#endif + diff --git a/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.cpp b/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.cpp new file mode 100644 index 0000000000..0c0515ce66 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#include "ShortcutsServerFilter.h" + + +#include +#include + + +#include "KeyCommandMap.h" +#include "KeyInfos.h" +#include "CommandExecutor.h" +#include "ShortcutsFilterConstants.h" + +class BInputServerFilter; + +// Called by input_server on startup. Must be exported as a "C" function! +BInputServerFilter* instantiate_input_filter() +{ + return new ShortcutsServerFilter; +} + + +ShortcutsServerFilter::ShortcutsServerFilter() + : + fExecutor(new CommandExecutor) +{ + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) + path.Append(SHORTCUTS_SETTING_FILE_NAME); + + fMap = new KeyCommandMap(path.Path()); + + InitKeyIndices(); + fMessenger = BMessenger(fExecutor); + fMap->Run(); + fExecutor->Run(); +} + + +ShortcutsServerFilter::~ShortcutsServerFilter() +{ + if (fMap->Lock()) + fMap->Quit(); + + if (fExecutor->Lock()) + fExecutor->Quit(); +} + + +status_t +ShortcutsServerFilter::InitCheck() +{ + return B_NO_ERROR; +} + + +filter_result +ShortcutsServerFilter::Filter(BMessage* msg, BList* outlist) +{ + filter_result ret = B_DISPATCH_MESSAGE; + + switch(msg->what) + { + case B_KEY_DOWN: + case B_KEY_UP: + case B_UNMAPPED_KEY_DOWN: + case B_UNMAPPED_KEY_UP: + ret = fMap->KeyEvent(msg, outlist, fMessenger); + break; + + case B_MOUSE_MOVED: + case B_MOUSE_UP: + case B_MOUSE_DOWN: + fMap->MouseMoved(msg); + break; + } + fMap->DrainInjectedEvents(msg, outlist, fMessenger); + return ret; +} diff --git a/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.h b/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.h new file mode 100644 index 0000000000..93218d2786 --- /dev/null +++ b/src/add-ons/input_server/filters/shortcut_catcher/ShortcutsServerFilter.h @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jeremy Friesner + */ + + +#ifndef ShortcutsServerFilter_h +#define ShortcutsServerFilter_h + + +#include + + +#include +#include +#include +#include + +#ifdef __POWERPC__ +#pragma export on +#endif + +// export this for the input_server +extern "C" _EXPORT BInputServerFilter* instantiate_input_filter(); + +class KeyCommandMap; +class CommandExecutor; + +class ShortcutsServerFilter : public BInputServerFilter { +public: + ShortcutsServerFilter(); + virtual ~ShortcutsServerFilter(); + virtual status_t InitCheck(); + virtual filter_result Filter(BMessage* message, BList* outlist); +private: + // Tells us what command goes with a given key + KeyCommandMap* fMap; + + // Executes the given commands + CommandExecutor* fExecutor; + + // Points to fExecutor:declaration order is important! + BMessenger fMessenger; +}; + +#ifdef __POWERPC__ +#pragma export reset +#endif + +#endif