From a94e61350c94119de9924f3de6c704a10a96c0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Tue, 17 Jun 2008 16:36:54 +0000 Subject: [PATCH] The code for an handwriting input method front-end, currently doesn't do much more than draw the strokes over the screen. The idea is to have a base backend class that will be used to call several backends like Lipi Toolkit or others. WIP. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25997 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../input_server/methods/pen/DumpMessage.cpp | 256 ++++++++++++++++++ .../input_server/methods/pen/DumpMessage.h | 10 + .../input_server/methods/pen/PenIcon.h | 21 ++ .../methods/pen/PenInputBackend.cpp | 15 + .../methods/pen/PenInputBackend.h | 19 ++ .../methods/pen/PenInputInkWindow.cpp | 126 +++++++++ .../methods/pen/PenInputInkWindow.h | 26 ++ .../methods/pen/PenInputLooper.cpp | 255 +++++++++++++++++ .../input_server/methods/pen/PenInputLooper.h | 69 +++++ .../methods/pen/PenInputServerMethod.cpp | 217 +++++++++++++++ .../methods/pen/PenInputServerMethod.h | 45 +++ .../methods/pen/PenInputStrings.h | 12 + .../methods/pen/RestartInputServer.sh | 18 ++ src/add-ons/input_server/methods/pen/_APP_ | 1 + .../methods/pen/compat/StringIO.cpp | 88 ++++++ .../methods/pen/compat/StringIO.h | 49 ++++ src/add-ons/input_server/methods/pen/makefile | 192 +++++++++++++ 17 files changed, 1419 insertions(+) create mode 100644 src/add-ons/input_server/methods/pen/DumpMessage.cpp create mode 100644 src/add-ons/input_server/methods/pen/DumpMessage.h create mode 100644 src/add-ons/input_server/methods/pen/PenIcon.h create mode 100644 src/add-ons/input_server/methods/pen/PenInputBackend.cpp create mode 100644 src/add-ons/input_server/methods/pen/PenInputBackend.h create mode 100644 src/add-ons/input_server/methods/pen/PenInputInkWindow.cpp create mode 100644 src/add-ons/input_server/methods/pen/PenInputInkWindow.h create mode 100644 src/add-ons/input_server/methods/pen/PenInputLooper.cpp create mode 100644 src/add-ons/input_server/methods/pen/PenInputLooper.h create mode 100644 src/add-ons/input_server/methods/pen/PenInputServerMethod.cpp create mode 100644 src/add-ons/input_server/methods/pen/PenInputServerMethod.h create mode 100644 src/add-ons/input_server/methods/pen/PenInputStrings.h create mode 100755 src/add-ons/input_server/methods/pen/RestartInputServer.sh create mode 120000 src/add-ons/input_server/methods/pen/_APP_ create mode 100644 src/add-ons/input_server/methods/pen/compat/StringIO.cpp create mode 100644 src/add-ons/input_server/methods/pen/compat/StringIO.h create mode 100644 src/add-ons/input_server/methods/pen/makefile diff --git a/src/add-ons/input_server/methods/pen/DumpMessage.cpp b/src/add-ons/input_server/methods/pen/DumpMessage.cpp new file mode 100644 index 0000000000..be88ead071 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/DumpMessage.cpp @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include "DumpMessage.h" + +//#define WHAT_ALWAYS_HEX 1 + +const char *msg_header_comment = "// new BMessage\n"; // avoids mime to think it's a .bmp ("BM") + +// '_' also widely used in what codes +inline int myisprint(int c) +{ + if (isalnum(c)) + return 1; + return (c == '_')?1:0; +} + +status_t HexDumpToStream(const void *data, size_t len, BDataIO &stream, const char *prefix = NULL) +{ + const unsigned char *p = (unsigned char *)data; + char buffer[100]; + size_t i, j; + for (i=0; i= len) + sprintf(buffer, " "); + //else if (p[i+j] < 255 && p[i+j] >= 0x20) + else if (isalpha(p[i+j])) + sprintf(buffer, "%c", p[i+j]); + else + sprintf(buffer, "."); + stream.Write(buffer, 1); + } + sprintf(buffer, "'\n"); + stream.Write(buffer, strlen(buffer)); + } + return B_OK; +} + +/* look up human readable names from an other BMessage */ +bool LookUpFieldName(const char **name, const char *field_name, BMessage *names) +{ + if (names == NULL) + return false; + if (names->FindString(field_name, name) == B_OK) + return true; + return false; +} + +status_t DumpMessageToStream(BMessage *message, BDataIO &stream, int tabCount, BMessage *names) +{ + int32 index; + void *cookie = NULL; + const char *field_name; + type_code field_code; + int32 field_count; + char buffer[80]; + char tabs[20]; + const char *easy_name; + + if (message == NULL) + return EINVAL; + + if (tabCount < 1) + stream.Write(msg_header_comment, strlen(msg_header_comment)); + + memset(tabs, '\t', (++tabCount) + 1); + tabs[tabCount+1] = '\0'; + //tabCount; + +#ifndef WHAT_ALWAYS_HEX + if ( myisprint(message->what & 0x0ff) && + myisprint((message->what >> 8) & 0x0ff) && + myisprint((message->what >> 16) & 0x0ff) && + myisprint((message->what >> 24) & 0x0ff)) + sprintf(buffer, "BMessage('%c%c%c%c') {\n", + (char)(message->what >> 24) & 0x0ff, + (char)(message->what >> 16) & 0x0ff, + (char)(message->what >> 8) & 0x0ff, + (char)message->what & 0x0ff); + else +#endif + sprintf(buffer, "BMessage(0x%08lx) {\n", message->what); +// stream.Write(tabs+2, tabCount-2); + stream.Write(buffer, strlen(buffer)); + +#ifdef B_BEOS_VERSION_DANO + while (message->GetNextName(&cookie, + &field_name, + &field_code, + &field_count) == B_OK) { +#else +#warning mem leak likely! (name=char *) + for (int which=0; message->GetInfo(B_ANY_TYPE, which, + (char **)&field_name, &field_code, &field_count) == B_OK; which++) { +#endif + if (LookUpFieldName(&easy_name, field_name, names)) { + stream.Write(tabs+1, tabCount); + stream.Write("// ", 3); + stream.Write(easy_name, strlen(easy_name)); + stream.Write("\n", 1); + } + + for (index=0; index < field_count; index++) { + stream.Write(tabs+1, tabCount); + stream.Write(field_name, strlen(field_name)); + if (field_count > 1) { + sprintf(buffer, "[%ld]", index); + stream.Write(buffer, strlen(buffer)); + } + stream.Write(" = ", 3); + + switch (field_code) { + case 'MSGG': + { + BMessage m; + if (message->FindMessage(field_name, index, &m) >= B_OK) + DumpMessageToStream(&m, stream, tabCount, names); + } + break; +#ifdef B_BEOS_VERSION_DANO + case 'FONt': + { + BFont f; + if (message->FindFlat(field_name, index, &f) >= B_OK) + stream << f; + stream.Write("\n", 1); + } + break; + case B_RGB_COLOR_TYPE: + { + rgb_color c; + if (message->FindRGBColor(field_name, index, &c) >= B_OK) { + sprintf(buffer, "rgb_color(%d,%d,%d,%d)", + c.red, c.green, c.blue, c.alpha); + stream.Write(buffer, strlen(buffer)); + } + stream.Write("\n", 1); + } + break; +#else +#warning IMPLEMENT ME +#endif + case B_BOOL_TYPE: + { + bool value; + if (message->FindBool(field_name, index, &value) >= B_OK) { + sprintf(buffer, "bool(%s)", value?"true":"false"); + stream.Write(buffer, strlen(buffer)); + } + stream.Write("\n", 1); + } + break; + case B_INT32_TYPE: + { + int32 value; + if (message->FindInt32(field_name, index, &value) >= B_OK) { +#if 1 + if (value == 0) + sprintf(buffer, "int32(0 or (nil))"); + else +#endif +// sprintf(buffer, "int32(%d)", value); + sprintf(buffer, "int32(%ld or 0x%lx)", value, value); + stream.Write(buffer, strlen(buffer)); + } + stream.Write("\n", 1); + } + break; + case B_FLOAT_TYPE: + { + float value; + if (message->FindFloat(field_name, index, &value) >= B_OK) { + sprintf(buffer, "float(%f)", value); + stream.Write(buffer, strlen(buffer)); + } + stream.Write("\n", 1); + } + break; + case B_STRING_TYPE: + { + const char *value; + if (message->FindString(field_name, index, &value) >= B_OK) { + BString str(value); + str.CharacterEscape("\\\"\n", '\\'); + //sprintf(buffer, "string(\"%s\", %ld bytes)", str.String(), strlen(value)); + // DO NOT use buffer! + str.Prepend("string(\""); + str << "\", " << strlen(value) << " bytes)"; + stream.Write(str.String(), strlen(str.String())); + } + stream.Write("\n", 1); + } + break; + case B_POINT_TYPE: + { + BPoint value; + if (message->FindPoint(field_name, index, &value) >= B_OK) { + sprintf(buffer, "BPoint(%1.1f, %1.1f)", value.x, value.y); + stream.Write(buffer, strlen(buffer)); + } + stream.Write("\n", 1); + } + break; + default: + { + const void *data; + ssize_t numBytes = 0; + if (message->FindData(field_name, field_code, index, &data, &numBytes) != B_OK) { + //stream.Write("\n", 1); + break; + } + + if ( isalnum(field_code & 0x0ff) && + isalnum((field_code >> 8) & 0x0ff) && + isalnum((field_code >> 16) & 0x0ff) && + isalnum((field_code >> 24) & 0x0ff)) + sprintf(buffer, "'%c%c%c%c' %ld bytes:\n", + (char)(field_code >> 24) & 0x0ff, + (char)(field_code >> 16) & 0x0ff, + (char)(field_code >> 8) & 0x0ff, + (char)field_code & 0x0ff, + numBytes); + else + sprintf(buffer, "0x%08lx %ld bytes:\n", field_code, numBytes); + stream.Write(buffer, strlen(buffer)); + stream.Write("\n", 1); + HexDumpToStream(data, numBytes, stream, tabs); + } + break; + } + } + } + stream.Write(tabs+2, tabCount-1); + stream.Write("}\n", 2); + return B_OK; +} + diff --git a/src/add-ons/input_server/methods/pen/DumpMessage.h b/src/add-ons/input_server/methods/pen/DumpMessage.h new file mode 100644 index 0000000000..c1431d62ce --- /dev/null +++ b/src/add-ons/input_server/methods/pen/DumpMessage.h @@ -0,0 +1,10 @@ +#ifndef _DUMP_MESSAGE_H +#define _DUMP_MESSAGE_H + +class BMessage; +class BDataIO; + +status_t DumpMessageToStream(BMessage *message, BDataIO &stream, int tabCount = 0, BMessage *names = NULL); + +#endif /* _DUMP_MESSAGE_H */ + diff --git a/src/add-ons/input_server/methods/pen/PenIcon.h b/src/add-ons/input_server/methods/pen/PenIcon.h new file mode 100644 index 0000000000..865c245674 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenIcon.h @@ -0,0 +1,21 @@ +// will need better, stippi ? +// should have icons showing modes (word/char/num + caps) +const uchar PenIconData[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x4b, 0x4b, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x4b, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x4b, 0xff, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x4b, 0x4b, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x4b, 0x4b, 0x4b, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x4b, 0xff, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x4b, 0x4b, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x4b, 0x4b, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x4b, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x4b, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x4b, 0xff, 0xff, 0x00, 0x4b, 0x4b, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x4b, 0x4b, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x4b, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + diff --git a/src/add-ons/input_server/methods/pen/PenInputBackend.cpp b/src/add-ons/input_server/methods/pen/PenInputBackend.cpp new file mode 100644 index 0000000000..eead3e1f8f --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputBackend.cpp @@ -0,0 +1,15 @@ +#include +#include "PenInputBackend.h" + + +PenInputBackend::PenInputBackend(const char *name) + : BHandler(name) +{ +} + +PenInputBackend::~PenInputBackend() +{ +} + + + diff --git a/src/add-ons/input_server/methods/pen/PenInputBackend.h b/src/add-ons/input_server/methods/pen/PenInputBackend.h new file mode 100644 index 0000000000..379b01c0f6 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputBackend.h @@ -0,0 +1,19 @@ +#ifndef _PEN_INPUT_BACKEND_H +#define _PEN_INPUT_BACKEND_H + +#include + + +class PenInputBackend : public BHandler { +protected: + PenInputBackend(const char *name); + virtual ~PenInputBackend(); + +public: + status_t InitCheck() const { return fInitStatus; }; + +private: + status_t fInitStatus; +}; + +#endif /* _PEN_INPUT_BACKEND_H */ diff --git a/src/add-ons/input_server/methods/pen/PenInputInkWindow.cpp b/src/add-ons/input_server/methods/pen/PenInputInkWindow.cpp new file mode 100644 index 0000000000..3e3c556efb --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputInkWindow.cpp @@ -0,0 +1,126 @@ +/* + Copyright 2007, Francois Revol. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include +#include +#include + +#if DEBUG +//#include +#include +#include +#include +#include +#include "DumpMessage.h" +#endif + +#include "PenInputInkWindow.h" +#include "PenInputLooper.h" +#include "PenInputStrings.h" + +const rgb_color kInkColor = { 100, 100, 255, 0 }; + +class PenInputInkView : public BView { +public: + PenInputInkView(BRect frame, PenInputInkWindow *window); + virtual ~PenInputInkView(); + virtual void Draw(BRect udpateRect); +private: + PenInputInkWindow *fWindow; +}; + +PenInputInkView::PenInputInkView(BRect frame, PenInputInkWindow *window) + : BView(frame, "PenInkView", B_FOLLOW_ALL, B_WILL_DRAW), + fWindow(window) +{ + +} + +PenInputInkView::~PenInputInkView() +{ +} +void PenInputInkView::Draw(BRect udpateRect) +{ + /* ugh ? */ + if (!fWindow) + return; + + StrokeShape(&fWindow->fStrokes); + //DEBUG:StrokeRect(fWindow->fStrokes.Bounds()); +} + + + +PenInputInkWindow::PenInputInkWindow(BRect frame) + : BWindow(frame, "PenInputInkWindow", + B_NO_BORDER_WINDOW_LOOK, + B_FLOATING_ALL_WINDOW_FEEL, + B_NOT_MOVABLE | B_NOT_CLOSABLE | B_NOT_ZOOMABLE | + B_NOT_MINIMIZABLE | B_NOT_RESIZABLE | B_AVOID_FOCUS, + B_ALL_WORKSPACES), + fStrokeCount(0) +{ + PRINT(("%s\n", __FUNCTION__)); + fInkView = new PenInputInkView(Bounds(), this); + fInkView->SetViewColor(B_TRANSPARENT_32_BIT); + fInkView->SetLowColor(B_TRANSPARENT_32_BIT); + //fInkView->SetHighColor(0,255,0); + fInkView->SetHighColor(kInkColor); + fInkView->SetPenSize(2.0); + AddChild(fInkView); +} + +PenInputInkWindow::~PenInputInkWindow() +{ + PRINT(("%s\n", __FUNCTION__)); +} + +void PenInputInkWindow::MessageReceived(BMessage *message) +{ + BPoint where; + switch (message->what) { + case MSG_BEGIN_INK: + fStrokeCount = 0; + fStrokes.Clear(); + // fall through + case MSG_SHOW_WIN: + if (IsHidden()) + Show(); + break; + case MSG_END_INK: + case MSG_HIDE_WIN: + if (!IsHidden()) + Hide(); + break; + case B_MOUSE_MOVED: + if (message->FindPoint("where", &where) == B_OK) { + fStrokes.LineTo(where); + fStrokeCount++; + //fInkView->Invalidate(fStrokes.Bounds()); // XXX + fInkView->Invalidate(Bounds()); // XXX + } + break; + case B_MOUSE_UP: + if (message->FindPoint("where", &where) == B_OK) { + fStrokes.LineTo(where); + fStrokeCount++; + //fInkView->Invalidate(fStrokes.Bounds()); // XXX + fInkView->Invalidate(Bounds()); // XXX + } + break; + case B_MOUSE_DOWN: + if (message->FindPoint("where", &where) == B_OK) { + fStrokes.MoveTo(where); + //fStrokeCount++; + //fInkView->Invalidate(fStrokes.Bounds()); // XXX + fInkView->Invalidate(Bounds()); // XXX + } + break; + default: + BWindow::MessageReceived(message); + return; + } +} + diff --git a/src/add-ons/input_server/methods/pen/PenInputInkWindow.h b/src/add-ons/input_server/methods/pen/PenInputInkWindow.h new file mode 100644 index 0000000000..a8ce87efe2 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputInkWindow.h @@ -0,0 +1,26 @@ +/* + Copyright 2005, Francois Revol. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include +#include + +class PenInputInkView; + +class PenInputInkWindow : public BWindow +{ +public: + PenInputInkWindow(BRect frame); + virtual ~PenInputInkWindow(); + void MessageReceived(BMessage *message); + +private: + friend class PenInputInkView; + friend class PenInputLooper;//DEBUG + void ClearStrokes(); + PenInputInkView *fInkView; + BShape fStrokes; + int32 fStrokeCount; +}; + diff --git a/src/add-ons/input_server/methods/pen/PenInputLooper.cpp b/src/add-ons/input_server/methods/pen/PenInputLooper.cpp new file mode 100644 index 0000000000..6e22411eac --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputLooper.cpp @@ -0,0 +1,255 @@ +/* + Copyright 2007, Francois Revol. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#define DEBUG 1 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if DEBUG +//#include +#include +#include +#include +#include +#include "DumpMessage.h" +#endif + +#include "PenInputLooper.h" +#include "PenInputBackend.h" +#include "PenInputInkWindow.h" +#include "PenInputServerMethod.h" +#include "PenInputStrings.h" + +PenInputLooper::PenInputLooper(PenInputServerMethod *method) + : BLooper("PenInputLooper", B_NORMAL_PRIORITY), + fOwner(method), + fInkWindow(NULL), + fBackend(NULL), + fDeskbarMenu(NULL), + fMouseDown(false), + fStroking(false), + fThresholdRunner(NULL), + fCachedMouseDown(NULL), + fShowInk(true), + fMouseDownThreshold(100000) +{ + PRINT(("%s\n", __FUNCTION__)); + + // + fDeskbarMenu = new BMenu(_T("Options")); + BMenu *subMenu; + + + subMenu = new BMenu(_T("Backend")); + + BMessage *msg; + msg = new BMessage(MSG_SET_BACKEND); + BMenuItem *item; + // for (i = 0; i < fBackends.CountItems(); i++) ... + item = new BMenuItem(_T("Default"), msg); + item->SetMarked(true); + subMenu->AddItem(item); + + item = new BMenuItem(subMenu); + fDeskbarMenu->AddItem(item); + + msg = new BMessage(MSG_SHOW_INK); + item = new BMenuItem(_T("Show Ink"), msg); + item->SetMarked(true); + fDeskbarMenu->AddItem(item); + + fDeskbarMenu->SetFont(be_plain_font); + fOwner->SetMenu(fDeskbarMenu, BMessenger(NULL, this)); + +#if 0 + BRegion region; + fOwner->GetScreenRegion(®ion); + fInkWindow = new PenInputInkWindow(region.Frame()); +#endif + BScreen screen; + fInkWindow = new PenInputInkWindow(screen.Frame()); + fInkWindow->Show(); + fInkWindow->Lock(); + fInkWindow->Hide(); + fInkWindow->Unlock(); + fInkWindowMsgr = BMessenger(NULL, fInkWindow); + + BMessage m(MSG_CHECK_PEN_DOWN); + fThresholdRunner = new BMessageRunner(BMessenger(NULL, this), &m, fMouseDownThreshold, 0); + + Run(); +} + +void PenInputLooper::Quit() +{ + PRINT(("%s\n", __FUNCTION__)); + delete fThresholdRunner; + fInkWindow->Lock(); + fInkWindow->Quit(); + fOwner->SetMenu(NULL, BMessenger()); + delete fDeskbarMenu; + BLooper::Quit(); +} + + +void PenInputLooper::DispatchMessage(BMessage *message, BHandler *handler) +{ + switch (message->what) { +#if 0 + case B_MOUSE_MOVED: + case B_MOUSE_DOWN: + case B_MOUSE_UP: + + case B_INPUT_METHOD_EVENT: + //case B_REPLY: + if (fBackend) { + fBackend->MessageReceived(message); + return; + } +#endif + default: + return BLooper::DispatchMessage(message, handler); + } +} + +void PenInputLooper::MessageReceived(BMessage *message) +{ + int32 v; + int32 buttons; + bool b; +#if 1 + { + //message->Flatten(&fDebugFile); + BStringIO sio; + DumpMessageToStream(message, sio); + fInkWindow->Lock(); + sio << BString("StrokesCount: "); + BString tmp; + tmp << fInkWindow->fStrokeCount; + sio << tmp; + sio << BString("\nStrokes.Bounds: "); + sio << (fInkWindow->fStrokes.Bounds()); + sio << BString("\n"); + fInkWindow->Unlock(); + fOwner->fDebugAlert->Lock(); + fOwner->fDebugAlert->TextView()->Insert("Looper:\n"); + fOwner->fDebugAlert->TextView()->Insert(sio.String()); + fOwner->fDebugAlert->Unlock(); + } +#endif + switch (message->what) { + case MSG_CHECK_PEN_DOWN: + if (fCachedMouseDown) { + /* nothing happened, we want to move the mouse */ + EnqueueMessage(fCachedMouseDown); + fCachedMouseDown = NULL; + } + break; + case B_MOUSE_MOVED: + if (!fMouseDown) { /* definitely not writing */ + /* pass it along */ + EnqueueMessage(DetachCurrentMessage()); + break; + } + if (fShowInk) { + BMessage *msg = new BMessage(*message); + fInkWindowMsgr.SendMessage(msg); + } + if (fBackend) + fBackend->MessageReceived(message); + else + EnqueueMessage(DetachCurrentMessage()); + break; + case B_MOUSE_DOWN: + if (message->FindInt32("buttons", &buttons) == B_OK) { + if (!(buttons & B_PRIMARY_MOUSE_BUTTON)) { + /* pass it along */ + EnqueueMessage(DetachCurrentMessage()); + break; + } + } + fMouseDown = true; + if (fShowInk) { + { + BMessage *msg = new BMessage(MSG_BEGIN_INK); + fInkWindowMsgr.SendMessage(msg); + } + BMessage *msg = new BMessage(*message); + fInkWindowMsgr.SendMessage(msg); + } + if (fBackend) + fBackend->MessageReceived(message); + break; + case B_MOUSE_UP: + if (message->FindInt32("buttons", &buttons) == B_OK) { + if (!fMouseDown || (buttons & B_PRIMARY_MOUSE_BUTTON)) { + /* pass it along */ + EnqueueMessage(DetachCurrentMessage()); + break; + } + } + fMouseDown = false; + if (fShowInk) { + BMessage *msg = new BMessage(*message); + fInkWindowMsgr.SendMessage(msg); + { + BMessage *msg = new BMessage(MSG_END_INK); + fInkWindowMsgr.SendMessage(msg); + } + } + if (fBackend) + fBackend->MessageReceived(message); + break; + case B_INPUT_METHOD_EVENT: + if (fBackend) + fBackend->MessageReceived(message); + break; + case MSG_METHOD_ACTIVATED: + if (message->FindBool(MSGF_ACTIVE, &b) < B_OK) + b = false; + HandleMethodActivated(b); + break; + case B_REPLY: + + default: + BLooper::MessageReceived(message); + break; + } +} + +void PenInputLooper::EnqueueMessage(BMessage *message) +{ + fOwner->EnqueueMessage(message); +} + +status_t PenInputLooper::InitCheck() +{ + return B_OK; +} + +void PenInputLooper::HandleMethodActivated(bool active) +{ +/* + if (fShowInk && active) + fInkWindowMsgr.SendMessage(MSG_SHOW_WIN); + if (fShowInk && !active) + fInkWindowMsgr.SendMessage(MSG_END_INK); +*/ +} + diff --git a/src/add-ons/input_server/methods/pen/PenInputLooper.h b/src/add-ons/input_server/methods/pen/PenInputLooper.h new file mode 100644 index 0000000000..891222fc67 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputLooper.h @@ -0,0 +1,69 @@ +/* + Copyright 2007, Francois Revol. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _PEN_INPUT_LOOPER_H +#define _PEN_INPUT_LOOPER_H + +#include +#include +#include + +/* internal messages */ + +#define MSG_METHOD_ACTIVATED 'IMAc' +#define MSGF_ACTIVE "active" /* bool */ + +#define MSG_BEGIN_INK 'InkB' +#define MSG_END_INK 'InkE' + +#define MSG_SHOW_WIN 'ShoW' +#define MSG_HIDE_WIN 'HidW' + +#define MSG_CHECK_PEN_DOWN 'ChkP' + +/* menu messages */ + +#define MSG_SET_BACKEND 'SetB' +#define MSGF_BACKEND "backend" /* string */ + +#define MSG_SHOW_INK 'InkS' /* toggle */ + + +class BMenu; +class BWindow; +class PenInputServerMethod; +class PenInputInkWindow; +class PenInputBackend; + +class PenInputLooper : public BLooper +{ +public: + PenInputLooper(PenInputServerMethod *method); + virtual void Quit(); + void DispatchMessage(BMessage *message, BHandler *handler); + void MessageReceived(BMessage *message); + void EnqueueMessage(BMessage *message); + status_t InitCheck(); + +// virtual ~PenInputLooper(); + void MethodActivated(bool active); + +private: + friend class PenInputInkWindow; + void HandleMethodActivated(bool active); + PenInputServerMethod *fOwner; + PenInputInkWindow *fInkWindow; + PenInputBackend *fBackend; + BMenu *fDeskbarMenu; + BMessenger fInkWindowMsgr; + bool fMouseDown; + bool fStroking; + BMessageRunner *fThresholdRunner; + BMessage *fCachedMouseDown; + /* config */ + bool fShowInk; + bigtime_t fMouseDownThreshold; +}; + +#endif /* _PEN_INPUT_LOOPER_H */ diff --git a/src/add-ons/input_server/methods/pen/PenInputServerMethod.cpp b/src/add-ons/input_server/methods/pen/PenInputServerMethod.cpp new file mode 100644 index 0000000000..ccdc373b67 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputServerMethod.cpp @@ -0,0 +1,217 @@ +/* + Copyright 2007, Francois Revol. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +//#define DEBUG 1 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#if DEBUG +//#include +#include +#include +#include +#include +#include "DumpMessage.h" +#endif + +#include +#include + +#include "PenIcon.h" + +#include "PenInputServerMethod.h" +#include "PenInputLooper.h" +#include "PenInputStrings.h" + +BInputServerMethod* instantiate_input_method() +{ + PRINT(("%s\n", __FUNCTION__)); + return (new PenInputServerMethod()); +} + + +PenInputServerMethod::PenInputServerMethod() + : BInputServerMethod("Pen", PenIconData), + fEnabled(false) +{ + PRINT(("%s\n", __FUNCTION__)); +#if DEBUG + //fDebugFile.SetTo("/tmp/PenInputMethodMessages.txt", B_READ_WRITE|B_CREATE_FILE); + fDebugAlert = new BAlert("PenInput Debug", "Plip \n\n\n\n\n\n\n\n\n\n\n\n\n", "Ok"); + fDebugAlert->SetLook(B_TITLED_WINDOW_LOOK); + fDebugAlert->TextView()->MakeSelectable(); + fDebugAlert->TextView()->SelectAll(); + fDebugAlert->TextView()->Delete(); + fDebugAlert->ButtonAt(0)->SetEnabled(false); + BRegion r; + GetScreenRegion(&r); + BString s; + s << r.CountRects() << " rects\n"; + fDebugAlert->TextView()->Insert(s.String()); + fDebugAlert->Go(NULL); + + +#endif +} + +PenInputServerMethod::~PenInputServerMethod() +{ + PRINT(("%s\n", __FUNCTION__)); + SetMenu(NULL, BMessenger()); +#if DEBUG + fDebugAlert->Lock(); + fDebugAlert->Quit(); +#endif + BLooper *looper = NULL; + fLooper.Target(&looper); + if (looper != NULL) + { + if (looper->Lock()) + looper->Quit(); + } +} + +status_t PenInputServerMethod::InitCheck() +{ + PenInputLooper *looper; + status_t err; + PRINT(("%s\n", __FUNCTION__)); + looper = new PenInputLooper(this); + looper->Lock(); + err = looper->InitCheck(); + looper->Unlock(); + fLooper = BMessenger(NULL, looper); + return err; +} + + +filter_result PenInputServerMethod::Filter(BMessage *message, BList *outList) +{ + status_t err; + filter_result res = B_DISPATCH_MESSAGE; + + if (!IsEnabled()) + return B_DISPATCH_MESSAGE; + + +#if 0//DEBUG + //message->Flatten(&fDebugFile); + BStringIO sio; + DumpMessageToStream(message, sio); + fDebugAlert->Lock(); + fDebugAlert->TextView()->Insert(sio.String()); + fDebugAlert->Unlock(); +#endif + switch (message->what) { + case B_KEY_UP: + case B_KEY_DOWN: + case B_UNMAPPED_KEY_UP: + case B_UNMAPPED_KEY_DOWN: + case B_MODIFIERS_CHANGED: + case B_MOUSE_WHEEL_CHANGED: + return B_DISPATCH_MESSAGE; + default: + //case B_MOUSE_MOVED: + fLooper.SendMessage(message); + return B_SKIP_MESSAGE; + } + + +#if 0 + if (message->what == B_MOUSE_MOVED) { + BMessage *mDown = new BMessage(B_KEY_DOWN); + BMessage *mUp; + char states[16]; + mDown->AddInt32("modifiers", 0x0); + mDown->AddInt32("key", 94); + mDown->AddInt32("raw_char", 32); + mDown->AddData("states", 'UBYT', states, sizeof(states)); + mDown->AddString("bytes", " "); + mDown->AddData("byte", 'BYTE', " ", 1); + mUp = new BMessage(*mDown); + mUp->what = B_KEY_UP; + outList->AddItem(mDown); + outList->AddItem(mUp); + } +#endif + if (message->what == B_MOUSE_DOWN) { + int32 buttons; + int32 modifiers; + BPoint where; + if (message->FindInt32("buttons", &buttons) == B_OK) { + /* replace first with a button that likely won't exist, + * and so shouldn't cause any side effect (hmm err...) */ + /* XXX: use get_mouse_map() ? */ + if (buttons == B_PRIMARY_MOUSE_BUTTON) + //message->ReplaceInt32("buttons", 0x0000); + message->what = B_MOUSE_UP; + } + +#if 0 + outList->AddItem(new BMessage(*message)); + BMessage *m = new BMessage(B_MOUSE_MOVED); + if (message->FindInt32("buttons", &buttons) == B_OK) + m->AddInt32("buttons", buttons); + if (message->FindInt32("modifiers", &modifiers) == B_OK) + m->AddInt32("modifiers", modifiers); + if (message->FindPoint("where", &where) == B_OK) + m->AddPoint("where", where); + outList->AddItem(m); +#endif +#if DEBUG + fDebugAlert->Lock(); + fDebugAlert->TextView()->Insert(">>>\n"); + fDebugAlert->Unlock(); +#endif +#if 0 + m = new BMessage(B_INPUT_METHOD_EVENT); + m->AddInt32("be:opcode", B_INPUT_METHOD_STARTED); + m->AddMessenger("be:reply_to", this); + EnqueueMessage(m); + m = new BMessage(B_INPUT_METHOD_EVENT); + m->AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST); + m->AddString("be:string", " "); + EnqueueMessage(m); + m = new BMessage(B_INPUT_METHOD_EVENT); + m->AddInt32("be:opcode", B_INPUT_METHOD_STOPPED); + EnqueueMessage(m); + sleep(3); +#endif +#if DEBUG + fDebugAlert->Lock(); + fDebugAlert->TextView()->Insert("<<<\n"); + fDebugAlert->Unlock(); +#endif + } + + + + return (res); +} + +status_t PenInputServerMethod::MethodActivated(bool active) +{ + fEnabled = active; + + BMessage msg(MSG_METHOD_ACTIVATED); + if (active) + msg.AddBool(MSGF_ACTIVE, true); + fLooper.SendMessage( &msg ); + + return B_OK; +} + diff --git a/src/add-ons/input_server/methods/pen/PenInputServerMethod.h b/src/add-ons/input_server/methods/pen/PenInputServerMethod.h new file mode 100644 index 0000000000..1814ae5150 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputServerMethod.h @@ -0,0 +1,45 @@ +/* + Copyright 2005, Francois Revol. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _PEN_INPUT_SERVER_METHOD_H +#define _PEN_INPUT_SERVER_METHOD_H + +#include +#include +#include + +#if DEBUG +//#include +class BAlert; +#endif +class BList; +class BMessage; +class PenInputLooper; + + +extern "C" _EXPORT BInputServerMethod* instantiate_input_method(); + +class PenInputServerMethod : public BInputServerMethod +{ +public: + PenInputServerMethod(); + virtual ~PenInputServerMethod(); + virtual status_t InitCheck(); + virtual filter_result Filter(BMessage *message, BList *outList); + virtual status_t MethodActivated(bool active); + + bool IsEnabled() const { return fEnabled; }; + +private: + friend PenInputLooper;//DEBUG + bool fEnabled; + //BLocker fLocker; + BMessenger fLooper; +#if DEBUG + //BFile fDebugFile; + BAlert *fDebugAlert; +#endif +}; + +#endif /* _PEN_INPUT_SERVER_METHOD_H */ diff --git a/src/add-ons/input_server/methods/pen/PenInputStrings.h b/src/add-ons/input_server/methods/pen/PenInputStrings.h new file mode 100644 index 0000000000..37602a803a --- /dev/null +++ b/src/add-ons/input_server/methods/pen/PenInputStrings.h @@ -0,0 +1,12 @@ +#ifndef _PEN_INPUT_STRINGS_H +#define _PEN_INPUT_STRINGS_H + + +#ifndef _T +#define _T(s) s +#endif + +/* more to come */ + + +#endif /* _PEN_INPUT_STRINGS_H */ diff --git a/src/add-ons/input_server/methods/pen/RestartInputServer.sh b/src/add-ons/input_server/methods/pen/RestartInputServer.sh new file mode 100755 index 0000000000..b9f71d71cf --- /dev/null +++ b/src/add-ons/input_server/methods/pen/RestartInputServer.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +quit application/x-vnd.Be-input_server + +tagfile=/tmp/dokillinputserver +tmout=$((30)) + +touch $tagfile +(alert "All is fine" "Ok"; rm $tagfile) & + +sleep $tmout + +if [ -e $tagfile ]; then + rm ~/config/add-ons/input_server/methods/Pen + quit application/x-vnd.Be-input_server + kill -9 input_server + rm $tagfile +fi diff --git a/src/add-ons/input_server/methods/pen/_APP_ b/src/add-ons/input_server/methods/pen/_APP_ new file mode 120000 index 0000000000..f4052072b7 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/_APP_ @@ -0,0 +1 @@ +/system/servers/input_server \ No newline at end of file diff --git a/src/add-ons/input_server/methods/pen/compat/StringIO.cpp b/src/add-ons/input_server/methods/pen/compat/StringIO.cpp new file mode 100644 index 0000000000..454a00a342 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/compat/StringIO.cpp @@ -0,0 +1,88 @@ +/* + * minimalistic Dano-like BStringIO + * (c) 2007, François Revol. + */ +#include +#ifndef B_BEOS_VERSION_DANO + +#include +#include +#include +//#include + +// stripped down BStringIO + + + +BStringIO::BStringIO() +{ + fString = new BString; +} + +BStringIO::~BStringIO() +{ + delete fString; +} + +ssize_t +BStringIO::ReadAt(off_t pos, void *buffer, size_t size) +{ + return EIO; +} + +ssize_t +BStringIO::WriteAt(off_t pos, const void *buffer, size_t size) +{ + if (pos > (2147483647L)/*INT32_MAX*/) + return EINVAL; + if (fString->Length() < pos) + fString->Insert(' ', (int32)(pos - fString->Length()), fString->Length()); + fString->Remove((int32)pos, size); + fString->Insert((const char *)buffer, size, (int32)pos); + return size; +} + +off_t +BStringIO::Seek(off_t pos, uint32 seek_mode) +{ + switch (seek_mode) { + case SEEK_CUR: + fPosition += pos; + return fPosition; + case SEEK_SET: + fPosition = pos; + return fPosition; + case SEEK_END: + fPosition = fString->Length() - pos; + if (fPosition < 0) + fPosition = 0; + return fPosition; + default: + return EINVAL; + } +} + +off_t +BStringIO::Position() const +{ + return fPosition; +} + +status_t +BStringIO::SetSize(off_t size) +{ + return EINVAL; +} + +const char * +BStringIO::String() const +{ + return fString->String(); +} + + +// ops +BStringIO & BStringIO::operator<<(const BString & s) {this->BPositionIO::Write(s.String(), s.Length()); return *this;}; +BStringIO & BStringIO::operator<<(const BRect & r) {BString s; s << "Rect{" << r.left << r.top << r.right << r.bottom << "}"; this->BPositionIO::Write(s.String(), s.Length()); return *this;}; + +#endif /* B_BEOS_VERSION_DANO */ diff --git a/src/add-ons/input_server/methods/pen/compat/StringIO.h b/src/add-ons/input_server/methods/pen/compat/StringIO.h new file mode 100644 index 0000000000..7eb9ac5052 --- /dev/null +++ b/src/add-ons/input_server/methods/pen/compat/StringIO.h @@ -0,0 +1,49 @@ +/* + * minimalistic Dano-ilke BStringIO + * (c) 2007, François Revol. + */ +#include +#ifdef B_BEOS_VERSION_DANO +#include_next +#else + +#ifndef _STRING_IO_H +#define _STRING_IO_H + +#include +#include +//#include + +// stripped down BStringIO + +class BStringIO : public BPositionIO { +public: + BStringIO(); +virtual ~BStringIO(); + +virtual ssize_t ReadAt(off_t pos, void *buffer, size_t size); +virtual ssize_t WriteAt(off_t pos, const void *buffer, size_t size); + +virtual off_t Seek(off_t pos, uint32 seek_mode); +virtual off_t Position() const; +virtual status_t SetSize(off_t size); + +// void SetBlockSize(size_t blocksize); + +const char* String() const; +// size_t StringLength() const; + +BStringIO & operator<<(const BString & s); +BStringIO & operator<<(const BRect & r); + + +private: + off_t fPosition; + BString *fString; +}; + + + +#endif /* _STRING_IO_H */ + +#endif \ No newline at end of file diff --git a/src/add-ons/input_server/methods/pen/makefile b/src/add-ons/input_server/methods/pen/makefile new file mode 100644 index 0000000000..b7a552ec9e --- /dev/null +++ b/src/add-ons/input_server/methods/pen/makefile @@ -0,0 +1,192 @@ +## ********************************* ## +## Zeta Generic Makefile v3.0 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME := Pen + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +# MODULE: Kernel Module +# DECOR: A window decorator project +TYPE := SHARED + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS := PenInputServerMethod.cpp \ + PenInputLooper.cpp \ + PenInputInkWindow.cpp \ + PenInputBackend.cpp \ + DumpMessage.cpp \ + compat/StringIO.cpp + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS := + +# Specify your RDEF files, if any. +RDEFS := + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS := _APP_ be + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS := + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS := compat + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS := + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE := + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +DEFINES := DEBUG=1 + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS := + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS := + +# specify debug settings +# if TRUE will allow application to be run from a source-level +# debugger. Note that this will disable all optimzation. +DEBUGGER := + +# specify additional compiler flags for all files +COMPILER_FLAGS := + +# specify additional linker flags +LINKER_FLAGS := + +# specify the version of this particular item +# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL") +#E This may also be specified in a resource. +APP_VERSION := + +# (for TYPE == DRIVER only) Specify desired location of driver in the /dev +# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will +# instruct the driverinstall rule to place a symlink to your driver's binary in +# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at +# /dev/video/usb when loaded. Default is "misc". +DRIVER_PATH := + +# Specify if you want the object files to be somewhere besides the default location. +OBJ_DIR := + +# Specify a non default placement for the target +TARGET_DIR := + +# Specify a directory for the 'install' target. +INSTALL_DIR := + +# Specify the name of this makefile. +# If you leave this blank, the makefile will not be considered as part of the +# dependenies for the project, and the project will not be rebuilt when the makefile +# is changed +MAKEFILE := + +# Specify TRUE if you want the install target to create links in the BeMenu +MENU_LINKS := + +# Related to MENU_LINKS, specify the name of the direcotry in the BeMenu +# you wish the link to go in. If the directory does not exist, it will be +# created. +APP_MENU := + +# If, for some reason, you don't want to use the dependencies (flex and yacc seem to choke +# on them), set this to false +DODEPS := + +# Set this variable if you have an svg text file you wish to use as your targets +# icon. +SVG_ICON := + +# If you have some fancy custom build steps to do, specify them here +EXTRA_BUILD_STEPS = + +# If you have some other files that should trigger a re-link, such as libs in the same +# project that may get rebuilt, specify the full path to them here. +EXTRA_DEPS := + +########################################################################################### +# The following variables are commented out here because the can be very useful to just +# set at the command line or in the env at time of compiling, allowing you to leave your +# makefile the same, but change the build types easily. + + +# If you wish to have the program output a profiling session file which can be read by bprof, +# set this to 'true' +#BUILD_PROFILE := + +# If you wish to have a debug build, +# set this to 'true' +#BUILD_DEBUG := + +# If you wish to have a build which can do memory checking when MALLOC_DEBUG=15 is set, +# set this to 'true' +#CHECK_MEM := + +# If you want to see the complete build line for every file, then set this to 'true', +# otherwise it will tell you at the end what the build flags were. +#CHATTY := + + + +## include the makefile-engine +include $(BUILDHOME)/etc/makefile-engine