From be3c180fec71866a91b5f9297708d581bc1d6435 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Mon, 23 Sep 2013 18:30:04 +0200 Subject: Added instant messaging invoker --- CMakeLists.txt | 28 +- README.md | 4 +- apps/samples/vrml/ffmpeg-server.invoked.scxml | 246 ++++++++++++++ apps/samples/vrml/ffmpeg-server.scxml | 245 -------------- apps/samples/vrml/viewer.html | 5 + apps/samples/vrml/viewer.js | 191 ++++++++--- apps/samples/vrml/vrml-server.scxml | 20 +- apps/uscxml-browser.cpp | 17 +- config.h.in | 9 +- contrib/cmake/FindFFMPEG.cmake | 241 +++++++++----- contrib/cmake/FindGLIB2.cmake | 237 ++++++++++++++ contrib/cmake/FindLibPurple.cmake | 35 ++ docs/Ubuntu-LTS.md | 46 +++ src/uscxml/Factory.cpp | 11 + src/uscxml/Interpreter.cpp | 2 +- src/uscxml/URL.cpp | 28 ++ src/uscxml/URL.h | 4 +- .../concurrency/eventqueue/DelayedEventQueue.cpp | 29 ++ .../concurrency/eventqueue/DelayedEventQueue.h | 8 + .../datamodel/ecmascript/v8/V8DataModel.cpp | 39 ++- .../plugins/datamodel/ecmascript/v8/V8DataModel.h | 2 + .../plugins/element/respond/RespondElement.cpp | 2 +- src/uscxml/plugins/invoker/CMakeLists.txt | 21 ++ src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h | 3 +- .../invoker/filesystem/dirmon/DirMonInvoker.cpp | 31 +- .../invoker/filesystem/dirmon/DirMonInvoker.h | 2 +- .../openscenegraph/converter/OSGConverter.cpp | 26 +- src/uscxml/plugins/invoker/im/IMInvoker.cpp | 361 +++++++++++++++++++++ src/uscxml/plugins/invoker/im/IMInvoker.h | 117 +++++++ .../plugins/invoker/sample/SampleInvoker.cpp | 1 - src/uscxml/server/HTTPServer.cpp | 31 +- src/uscxml/server/HTTPServer.h | 1 - test/CMakeLists.txt | 12 +- test/samples/uscxml/test-instant-messaging.scxml | 20 ++ 34 files changed, 1616 insertions(+), 459 deletions(-) create mode 100644 apps/samples/vrml/ffmpeg-server.invoked.scxml delete mode 100644 apps/samples/vrml/ffmpeg-server.scxml create mode 100644 contrib/cmake/FindGLIB2.cmake create mode 100644 contrib/cmake/FindLibPurple.cmake create mode 100644 docs/Ubuntu-LTS.md create mode 100644 src/uscxml/plugins/invoker/im/IMInvoker.cpp create mode 100644 src/uscxml/plugins/invoker/im/IMInvoker.h create mode 100644 test/samples/uscxml/test-instant-messaging.scxml diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a940d9..2921af0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ SET(BUILD_TYPE_HELP "Choose the type of build, options are: None(CMAKE_CXX_FLAGS IF(DEFINED CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING ${BUILD_TYPE_HELP}) ELSE() - SET(CMAKE_BUILD_TYPE Debug CACHE STRING ${BUILD_TYPE_HELP}) + SET(CMAKE_BUILD_TYPE Release CACHE STRING ${BUILD_TYPE_HELP}) ENDIF() project(uscxml) @@ -514,7 +514,7 @@ if (APPLE OR IOS) endif() -if (OFF AND APPLE OR IOS) +if (APPLE OR IOS) find_library(JSC_LIBRARY JavaScriptCore) list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY}) set(JSC_FOUND ON) @@ -566,6 +566,24 @@ if (LIBICAL_FOUND) endif() set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_STATIC}) +set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SHARED}) +find_package(LibPurple) +if (LIBPURPLE_FOUND) + find_package(ICONV) + find_package(GLIB2) + if (GLIB2_FOUND AND ICONV_FOUND) + include_directories (${LIBPURPLE_INCLUDE_DIR}) + list (APPEND USCXML_OPT_LIBS ${LIBPURPLE_LIBRARY}) + include_directories (${GLIB2_INCLUDE_DIRS}) + list (APPEND USCXML_OPT_LIBS ${GLIB2_LIBRARIES}) + include_directories (${ICONV_INCLUDE_DIR}) + list (APPEND USCXML_OPT_LIBS ${ICONV_LIBRARIES}) + else() + set(LIBPURPLE_FOUND OFF) + endif() +endif() +set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_STATIC}) + if (WIN32) find_package(UMUNDO COMPONENTS convenience) @@ -770,6 +788,12 @@ else() SET(PATH_SEPERATOR "/") endif() +if(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_BUILD_TYPE_RELEASE ON) +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_BUILD_TYPE_DEBUG ON) +endif() + # enable config.h style compile time options and add as "uscxml/config.h" configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/uscxml/config.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/contrib/ctest/CTestCustom.ctest.in ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.ctest @ONCE) diff --git a/README.md b/README.md index c7557b6..71ef366 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ instructions](https://github.com/tklab-tud/uscxml/blob/master/docs/BUILDING.md). In order to use the interpreter, you need to #include "uscxml/Interpreter.h" and instantiate objects of uscxml::Interpreter. -### Blocking Interpretation with SCXML from URL +### Non-Blocking Interpretation with SCXML from URL Interpreter scxml = Interpreter::fromURL("http://www.example.com/fancy.scxml"); scxml.start(); // non-blocking @@ -108,7 +108,7 @@ This will perform a single iteration on the invoked components with a maximum of or return immediately. You will have to call this method every now and then if you are using e.g. the scenegraph invoker. -### Non-Blocking Interpretation with inline SCXML +### Blocking Interpretation with inline SCXML Interpreter scxml = Interpreter::fromXML(""); scxml.interpret(); // blocking diff --git a/apps/samples/vrml/ffmpeg-server.invoked.scxml b/apps/samples/vrml/ffmpeg-server.invoked.scxml new file mode 100644 index 0000000..bac0792 --- /dev/null +++ b/apps/samples/vrml/ffmpeg-server.invoked.scxml @@ -0,0 +1,246 @@ + + + + + + {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/samples/vrml/ffmpeg-server.scxml b/apps/samples/vrml/ffmpeg-server.scxml deleted file mode 100644 index 1fd4038..0000000 --- a/apps/samples/vrml/ffmpeg-server.scxml +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - {} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/samples/vrml/viewer.html b/apps/samples/vrml/viewer.html index 6e139e7..77bee0a 100644 --- a/apps/samples/vrml/viewer.html +++ b/apps/samples/vrml/viewer.html @@ -22,6 +22,11 @@ height:200px; padding:7px; } + + .dijitMenuItemLabel { + font-size: 12px; + } + .tundra .dijitTooltipContainer { background-color:rgba(200,200,200,0.5); background:rgba(200,200,200,0.5); diff --git a/apps/samples/vrml/viewer.js b/apps/samples/vrml/viewer.js index 583f06f..a3f3b70 100644 --- a/apps/samples/vrml/viewer.js +++ b/apps/samples/vrml/viewer.js @@ -96,9 +96,10 @@ function VRMLViewer(element, params) { codec !== "y41p" && codec !== "yuv4") continue; + console.log(codec); selectElem.options.push({ label: result.video[codec].longName, value: codec }); if (codec === "mpeg4") - selectElem[selectElem.options.length - 1].selected = true; + selectElem.options[selectElem.options.length - 1].selected = true; } } }); @@ -107,17 +108,26 @@ function VRMLViewer(element, params) { this.refreshServer = function(server) { self.serverURL = server; self.localStorage.put("vrmlServer", self.serverURL, null); -// self.progressElem.appendChild(self.progress.domNode); -// self.progress.start(); + if (self.fileStandby) { self.fileStandby.show(); } self.xhr.get({ // The URL to request url: server, handleAs:"json", headers:{"X-Requested-With":null}, + error: function(result) { + if (self.browseButton) { self.browseButton.setAttribute('label', 'Browse'); } + if (self.fileStandby) { self.fileStandby.hide(); } + var allItems = self.fileStore.query(); + for (var i = 0; i < allItems.total; i++) { + self.fileStore.remove(allItems[i].id); + } + }, load: function(result) { -// self.progress.stop(); - for (id in self.fileStore.query) { - self.fileStore.remove(id); + if (self.browseButton) { self.browseButton.setAttribute('label', 'Refresh'); } + if (self.fileStandby) { self.fileStandby.hide(); } + var allItems = self.fileStore.query(); + for (var i = 0; i < allItems.total; i++) { + self.fileStore.remove(allItems[i].id); } (function fillstore(tree, parentId) { for (key in tree) { @@ -126,7 +136,7 @@ function VRMLViewer(element, params) { } else { self.fileStore.add({id:parentId+key, name:key, parent:parentId}); fillstore(tree[key], parentId+key); - } + } } } (result.models, "root", "")); } @@ -172,7 +182,7 @@ function VRMLViewer(element, params) { "dijit/Tree", "dijit/form/TextBox", "dijit/form/Button", - "dojox/mobile/ProgressIndicator", + "dojox/widget/Standby", "dijit/form/DropDownButton", "dijit/TooltipDialog", "dojo/dnd/Moveable", @@ -192,7 +202,7 @@ function VRMLViewer(element, params) { Tree, TextBox, Button, - ProgressIndicator, + Standby, DropDownButton, TooltipDialog, Moveable, @@ -212,7 +222,6 @@ function VRMLViewer(element, params) { self.element = element; self.xhr = xhr; - self.progress = new ProgressIndicator({size:40, center:false}); self.localStorage = dojox.storage.manager.getProvider(); self.localStorage.initialize(); @@ -286,9 +295,10 @@ function VRMLViewer(element, params) { // fetch special dom nodes for content self.messageBox = dojo.query("div.messages", element)[0]; self.imgElem = dojo.query("img.model", element)[0]; - self.filesDropDownElem = dojo.query("td.filesDropDown", element)[0]; - self.movieDropDownElem = dojo.query("div.movieDropDown", element)[0]; - self.movieAddButtonElem = dojo.query("button.movieAddButton", element)[0]; + + /** + * === POSE MANIPULATION AND RESET ==================== + */ self.resetButtonElem = dojo.query("button.resetButton", element)[0]; self.progressElem = dojo.query("div.progress", element)[0]; @@ -407,6 +417,12 @@ function VRMLViewer(element, params) { yLabel.innerHTML = 'Y:' + self.pose.y; }; + /** + * === FILES DROPDOWN ==================== + */ + + self.filesDropDownElem = dojo.query("td.filesDropDown", element)[0]; + self.createAvatar = function(item, mode) { if (mode == 'avatar') { // create your avatar if you want @@ -480,6 +496,16 @@ function VRMLViewer(element, params) { value: self.serverURL, style: "width: 65%", + onKeyUp: function(e) { + if (self.browseButton) { + if (this.get("value") !== self.serverURL) { + self.browseButton.setAttribute('label', 'Browse'); + } else { + self.browseButton.setAttribute('label', 'Refresh'); + } + } + }, + onKeyDown: function(e) { var code = e.keyCode || e.which; if( code === 13 ) { @@ -506,15 +532,34 @@ function VRMLViewer(element, params) { self.filesDropDown = new DropDownButton({ label: "Files", dropDown: self.filesToolTip }); self.filesDropDownElem.appendChild(self.filesDropDown.domNode); - self.movieDropDownContent = domConst.toDom('
'); + self.fileStandby = new Standby({target: self.filesDropDownContent }); + self.filesDropDownContent.appendChild(self.fileStandby.domNode); + /** + * === MOVIE DROPDOWN ==================== + */ + + self.movieDropDownElem = dojo.query("div.movieDropDown", element)[0]; + self.movieAddButtonElem = dojo.query("button.movieAddButton", element)[0]; + + self.movieDropDownContent = domConst.toDom( + '
\ +
\ +
\ +
' + ); + + self.movieFormatLengthRowElem = dojo.query("tr.movieFormatLengthRow", self.movieDropDownContent)[0]; + self.movieWidthHeightLengthRowElem = dojo.query("tr.movieWidthHeightLengthRow", self.movieDropDownContent)[0]; + self.movieDnDArea = dojo.query("div.dndArea", self.movieDropDownContent)[0]; + self.createMovieThumb = function(item, mode) { if (mode == 'avatar') { // when dragged var avatar = dojo.create( 'div', { innerHTML: item.data }); var avatarPose = dojo.clone(self.pose); - avatarPose.width=60; - avatarPose.height=60; + avatarPose.width = 60; + avatarPose.height = 60; var avatarImgUrl = urlSuffixForPose(avatarPose); avatar.innerHTML = ' '; item.srcEcc = "VRMLViewer"; @@ -524,7 +569,6 @@ function VRMLViewer(element, params) { item.pose = avatarPose; return {node: avatar, data: item, type: item.type}; } else { - // var thumb = dojo.create( 'div', { innerHTML: item.data }); // when added to list var thumb = domConst.toDom("\ @@ -535,6 +579,7 @@ function VRMLViewer(element, params) { \ \ \ + \ \ \
Frame:
Transition:
\ @@ -547,6 +592,23 @@ function VRMLViewer(element, params) { var removeImgElem = dojo.query("img.removeThumb", thumb)[0]; var relFrameLengthElem = dojo.query("div.relFrameLength", thumb)[0]; var relTransitionLengthElem = dojo.query("div.relTransitionLength", thumb)[0]; + var fillInSeriesElem = dojo.query("div.fillInSeries", thumb)[0]; + + item.getThisAndNeighborsFromDnD = function() { + var thisAndNeighbors = {}; + self.addToMovieHandler.forInItems(function(obj, key, ctx) { + if (obj.data === item) { + thisAndNeighbors.this = { key: key, obj: obj }; + } else { + thisAndNeighbors.before = { key: key, obj: obj }; + } + if (thisAndNeighbors.this) { + thisAndNeighbors.after = { key: key, obj: obj }; + return thisAndNeighbors; + } + }); + return thisAndNeighbors; + }; item.relFrameLengthSlider = new HorizontalSlider({ value: 50, @@ -555,21 +617,33 @@ function VRMLViewer(element, params) { }, relFrameLengthElem); item.relTransitionLengthSlider = new HorizontalSlider({ - value: 80, + value: 100, title: "Relative Duration of Transition", style: "width:150px;" }, relTransitionLengthElem); removeImgElem.onclick = function() { - self.addToMovieHandler.forInItems(function(obj, key, ctx) { - if (obj.data === item) { - // haha - what a mess! - self.addToMovieHandler.selectNone(); - self.addToMovieHandler.selection[key] = obj; - self.addToMovieHandler.deleteSelectedNodes(); - } - }); + var thisItem = item.getThisAndNeighborsFromDnD(); + if (thisItem.this) { + // haha - what a mess! + self.addToMovieHandler.selectNone(); + self.addToMovieHandler.selection[thisItem.this.key] = thisItem.this.obj; + self.addToMovieHandler.deleteSelectedNodes(); + } + // disable create button if this was the last one + if (!thisItem.after || !thisItem.before) { + self.movieCreateButton.setAttribute('disabled', true); + } } + + item.fillInSeriesButton = new Button({ + label: "Insert Series", + style: "display: none;", + onClick: function(){ + alert("foo"); + } + }, fillInSeriesElem); + removeImgElem.src = self.resRoot + "img/close.png"; var thumbPose = dojo.clone(self.pose); @@ -590,31 +664,18 @@ function VRMLViewer(element, params) { } }; - self.addToMovieHandler = new Source(self.movieDropDownContent, {copyOnly: true, creator: self.createMovieThumb}); + self.addToMovieHandler = new Source(self.movieDnDArea, {copyOnly: true, creator: self.createMovieThumb}); self.movieFormatSelection = new Selector({ name: "movieFormat", + style: "width: 320px", options: [] }); self.populateMovieCodecs(self.serverURL + '/movie/codecs', self.movieFormatSelection); - - self.movieDropDownContent.appendChild(dojo.create( 'div', { - innerHTML: "Format: ", - style: "margin-right: 1em; margin-left: 0.2em; display:inline;" - })); - self.movieDropDownContent.appendChild(self.movieFormatSelection.domNode); - self.movieDurationSpinner = new NumberSpinner({ - value: 10, - smallDelta: 1, - style: "width: 40px", - constraints: { min:0, places:0 }, - }); - self.movieDropDownContent.appendChild(self.movieDurationSpinner.domNode); - self.movieDropDownContent.appendChild(dojo.create( 'div', { - innerHTML: "sec ", - style: "margin-right: 1em; margin-left: 0.2em; display:inline;" - })); + self.movieFormatLengthRowElem.appendChild(dojo.create('td', { innerHTML: 'Format:'} )); + self.movieFormatLengthRowElem.appendChild(dojo.create('td', { colspan: "2"})); + self.movieFormatLengthRowElem.lastChild.appendChild(self.movieFormatSelection.domNode); self.movieHeightSpinner = new NumberSpinner({ value: 400, @@ -622,11 +683,6 @@ function VRMLViewer(element, params) { style: "width: 60px", constraints: { min:40, places:0 }, }); - self.movieDropDownContent.appendChild(self.movieHeightSpinner.domNode); - self.movieDropDownContent.appendChild(dojo.create( 'div', { - innerHTML: "x", - style: "margin-right: 0.5em; margin-left: 0.5em; display:inline;" - })); self.movieWidthSpinner = new NumberSpinner({ value: 600, @@ -634,11 +690,12 @@ function VRMLViewer(element, params) { style: "width: 60px", constraints: { min:40, places:0 }, }); - self.movieDropDownContent.appendChild(self.movieWidthSpinner.domNode); self.movieCreateButton = new Button({ label: "Create", + disabled: true, onClick: function(){ + var form = document.createElement("form"); form.setAttribute("method", "post"); @@ -670,12 +727,39 @@ function VRMLViewer(element, params) { form.appendChild(hiddenField); + // this will not save the returned binary file + // self.xhr.post({ + // form: form, + // load: function(data){ + // alert("asd"); + // } + // }); + document.body.appendChild(form); form.submit(); document.body.removeChild(form); } }); - self.movieDropDownContent.appendChild(self.movieCreateButton.domNode); + + self.movieDurationSpinner = new NumberSpinner({ + value: 10, + smallDelta: 1, + style: "width: 40px", + constraints: { min:0, places:0 }, + }); + + // append format duration cell + self.movieWidthHeightLengthRowElem.appendChild(dojo.create('td', { innerHTML: 'Size:'} )); + var movieDimensionCell = dojo.create('td'); + movieDimensionCell.appendChild(self.movieWidthSpinner.domNode); + movieDimensionCell.appendChild(dojo.create('span', { innerHTML: "x"} )); + movieDimensionCell.appendChild(self.movieHeightSpinner.domNode); + movieDimensionCell.appendChild(self.movieDurationSpinner.domNode); + movieDimensionCell.appendChild(dojo.create('span', { innerHTML: "sec"} )); + self.movieWidthHeightLengthRowElem.appendChild(movieDimensionCell); + + self.movieWidthHeightLengthRowElem.appendChild(dojo.create('td', { align: "right"})); + self.movieWidthHeightLengthRowElem.lastChild.appendChild(self.movieCreateButton.domNode); self.movieToolTip = new TooltipDialog({ content:self.movieDropDownContent }); @@ -689,8 +773,13 @@ function VRMLViewer(element, params) { label: "+", style: "margin-left: -10px; display: none;", onClick: function(){ + if (self.movieFormatSelection.options.length == 0) { + self.populateMovieCodecs(self.serverURL + '/movie/codecs', self.movieFormatSelection); + } // we could pass item.data here to creator self.addToMovieHandler.insertNodes(false, [ { } ]); + self.movieCreateButton.setAttribute('disabled', false); + } }, self.movieAddButtonElem); diff --git a/apps/samples/vrml/vrml-server.scxml b/apps/samples/vrml/vrml-server.scxml index 20d63a6..5ee28dd 100644 --- a/apps/samples/vrml/vrml-server.scxml +++ b/apps/samples/vrml/vrml-server.scxml @@ -129,13 +129,12 @@ - models[_event.key].mtime)"> @@ -186,6 +191,7 @@
+
@@ -198,7 +204,7 @@ - + @@ -209,12 +215,14 @@ _event.data.pathComponent[_event.data.pathComponent.length - 1].indexOf('.') !== -1"> + diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp index 34cf47a..fa108f8 100644 --- a/apps/uscxml-browser.cpp +++ b/apps/uscxml-browser.cpp @@ -39,6 +39,7 @@ class VerboseMonitor : public uscxml::InterpreterMonitor { } }; +#ifdef CMAKE_BUILD_TYPE_DEBUG #ifdef HAS_EXECINFO_H void printBacktrace(void** array, int size) { @@ -105,6 +106,7 @@ void customTerminate() { #endif abort(); } +#endif void printUsageAndExit() { printf("uscxml-browser version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n"); @@ -126,22 +128,25 @@ void printUsageAndExit() { int main(int argc, char** argv) { using namespace uscxml; - std::set_terminate(customTerminate); - #if defined(HAS_SIGNAL_H) && !defined(WIN32) signal(SIGPIPE, SIG_IGN); #endif +#ifdef CMAKE_BUILD_TYPE_DEBUG + std::set_terminate(customTerminate); +#endif + if (argc < 2) { printUsageAndExit(); } bool verbose = false; bool useDot = false; + bool glogIsInitialized = false; size_t port = 8080; - google::InitGoogleLogging(argv[0]); + google::LogToStderr(); - + #ifndef _WIN32 opterr = 0; #endif @@ -150,6 +155,7 @@ int main(int argc, char** argv) { switch(option) { case 'l': google::InitGoogleLogging(optarg); + glogIsInitialized = true; break; case 'e': uscxml::Factory::pluginPath = optarg; @@ -171,6 +177,9 @@ int main(int argc, char** argv) { } } + if (!glogIsInitialized) + google::InitGoogleLogging(argv[0]); + // for (int i = 0; i < argc; i++) // std::cout << argv[i] << std::endl; // std::cout << optind << std::endl; diff --git a/config.h.in b/config.h.in index af772c6..93e4e4a 100644 --- a/config.h.in +++ b/config.h.in @@ -29,12 +29,6 @@ #cmakedefine PATH_SEPERATOR '@PATH_SEPERATOR@' -/** Loglevels */ -#cmakedefine LOGLEVEL_COMMON @LOGLEVEL_COMMON@ -#cmakedefine LOGLEVEL_DISC @LOGLEVEL_DISC@ -#cmakedefine LOGLEVEL_NET @LOGLEVEL_NET@ -#cmakedefine LOGLEVEL_S11N @LOGLEVEL_S11N@ - /** version */ #cmakedefine USCXML_VERSION_MAJOR "@USCXML_VERSION_MAJOR@" #cmakedefine USCXML_VERSION_MINOR "@USCXML_VERSION_MINOR@" @@ -43,6 +37,8 @@ /** build type */ #cmakedefine CMAKE_BUILD_TYPE "@CMAKE_BUILD_TYPE@" +#cmakedefine CMAKE_BUILD_TYPE_DEBUG +#cmakedefine CMAKE_BUILD_TYPE_RELEASE #cmakedefine CMAKE_COMPILER_STRING "@CMAKE_COMPILER_STRING@" /** miscellaneous */ @@ -69,6 +65,7 @@ #cmakedefine OPENSCENEGRAPH_FOUND #cmakedefine PROTOBUF_FOUND #cmakedefine CORELOCATION_FOUND +#cmakedefine LIBPURPLE_FOUND /** Header files we found */ #cmakedefine HAS_UNISTD_H diff --git a/contrib/cmake/FindFFMPEG.cmake b/contrib/cmake/FindFFMPEG.cmake index 397bcba..5f64790 100644 --- a/contrib/cmake/FindFFMPEG.cmake +++ b/contrib/cmake/FindFFMPEG.cmake @@ -1,93 +1,164 @@ -# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil) -# Once done this will define +# Locate ffmpeg +# This module defines +# FFMPEG_LIBRARIES +# FFMPEG_FOUND, if false, do not try to link to ffmpeg +# FFMPEG_INCLUDE_DIR, where to find the headers # -# FFMPEG_FOUND - system has ffmpeg or libav -# FFMPEG_INCLUDE_DIR - the ffmpeg include directory -# FFMPEG_LIBRARIES - Link these to use ffmpeg -# FFMPEG_LIBAVCODEC -# FFMPEG_LIBAVFORMAT -# FFMPEG_LIBAVUTIL -# -# Copyright (c) 2008 Andreas Schneider -# Modified for other libraries by Lasse Kärkkäinen -# Modified for Hedgewars by Stepik777 -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. +# $FFMPEG_DIR is an environment variable that would +# correspond to the ./configure --prefix=$FFMPEG_DIR # +# Created by Robert Osfield. + + +#In ffmpeg code, old version use "#include " and newer use "#include " +#In OSG ffmpeg plugin, we used "#include " for compatibility with old version of ffmpeg +#With the new version of FFmpeg, a file named "time.h" was added that breaks compatability with the old version of ffmpeg. + +#We have to search the path which contain the header.h (usefull for old version) +#and search the path which contain the libname/header.h (usefull for new version) + +#Then we need to include ${FFMPEG_libname_INCLUDE_DIRS} (in old version case, use by ffmpeg header and osg plugin code) +# (in new version case, use by ffmpeg header) +#and ${FFMPEG_libname_INCLUDE_DIRS/libname} (in new version case, use by osg plugin code) + + +# Macro to find header and lib directories +# example: FFMPEG_FIND(AVFORMAT avformat avformat.h) +MACRO(FFMPEG_FIND varname shortname headername) + # old version of ffmpeg put header in $prefix/include/[ffmpeg] + # so try to find header in include directory -if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) - # in cache already - set(FFMPEG_FOUND TRUE) -else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) - # use pkg-config to get the directories and then use these values - # in the FIND_PATH() and FIND_LIBRARY() calls - find_package(PkgConfig) - if (PKG_CONFIG_FOUND) - pkg_check_modules(_FFMPEG_AVCODEC libavcodec) - pkg_check_modules(_FFMPEG_AVFORMAT libavformat) - pkg_check_modules(_FFMPEG_AVUTIL libavutil) - pkg_check_modules(_FFMPEG_SWSCALE libswscale) -# pkg_check_modules(_FFMPEG_SWRESAMPLE libswresample) - endif (PKG_CONFIG_FOUND) - - find_path(FFMPEG_AVCODEC_INCLUDE_DIR - NAMES libavcodec/avcodec.h - PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS} /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES ffmpeg libav - ) - - find_library(FFMPEG_LIBAVCODEC - NAMES avcodec - PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib - ) - - find_library(FFMPEG_LIBAVFORMAT - NAMES avformat - PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib - ) - - find_library(FFMPEG_LIBAVUTIL - NAMES avutil - PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib - ) - - find_library(FFMPEG_LIBSWSCALE - NAMES swscale - PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib - ) - - # find_library(FFMPEG_SWRESAMPLE - # NAMES swresample - # PATHS ${_FFMPEG_SWRESAMPLE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib - # ) - - if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT) - set(FFMPEG_FOUND TRUE) - endif() - - if (FFMPEG_FOUND) - set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR}) - - set(FFMPEG_LIBRARIES - ${FFMPEG_LIBAVCODEC} - ${FFMPEG_LIBAVFORMAT} - ${FFMPEG_LIBAVUTIL} - ${FFMPEG_LIBSWSCALE} -# ${FFMPEG_SWRESAMPLE} + FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername} + PATHS + ${FFMPEG_ROOT}/include + $ENV{FFMPEG_DIR}/include + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + /usr/freeware/include + PATH_SUFFIXES ffmpeg + DOC "Location of FFMPEG Headers" ) - endif (FFMPEG_FOUND) + FIND_PATH(FFMPEG_${varname}_INCLUDE_DIRS ${headername} + PATHS + ${FFMPEG_ROOT}/include + $ENV{FFMPEG_DIR}/include + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + /usr/freeware/include + PATH_SUFFIXES ffmpeg + DOC "Location of FFMPEG Headers" + ) + + FIND_LIBRARY(FFMPEG_${varname}_LIBRARIES + NAMES ${shortname} + PATHS + ${FFMPEG_ROOT}/lib + $ENV{FFMPEG_DIR}/lib + ~/Library/Frameworks + /Library/Frameworks + /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 + /sw/lib + /opt/local/lib + /opt/csw/lib + /opt/lib + /usr/freeware/lib64 + DOC "Location of FFMPEG Libraries" + ) + + IF (FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) + SET(FFMPEG_${varname}_FOUND 1) + ENDIF(FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) + +ENDMACRO(FFMPEG_FIND) + +SET(FFMPEG_ROOT "$ENV{FFMPEG_DIR}" CACHE PATH "Location of FFMPEG") + +# find stdint.h +IF(WIN32) + + FIND_PATH(FFMPEG_STDINT_INCLUDE_DIR stdint.h + PATHS + ${FFMPEG_ROOT}/include + $ENV{FFMPEG_DIR}/include + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + /usr/freeware/include + PATH_SUFFIXES ffmpeg + DOC "Location of FFMPEG stdint.h Header" + ) + + IF (FFMPEG_STDINT_INCLUDE_DIR) + SET(STDINT_OK TRUE) + ENDIF() + +ELSE() + + SET(STDINT_OK TRUE) + +ENDIF() + +FFMPEG_FIND(LIBAVFORMAT avformat avformat.h) +FFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h) +FFMPEG_FIND(LIBAVCODEC avcodec avcodec.h) +FFMPEG_FIND(LIBAVUTIL avutil avutil.h) +FFMPEG_FIND(LIBSWSCALE swscale swscale.h) # not sure about the header to look for here. + +SET(FFMPEG_FOUND "NO") +# Note we don't check FFMPEG_LIBSWSCALE_FOUND here, it's optional. +IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND AND FFMPEG_LIBSWSCALE_FOUND AND STDINT_OK) + + SET(FFMPEG_FOUND "YES") + + SET(FFMPEG_INCLUDE_DIRS + ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} + ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS} + ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} + ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS} + ) + +# Using the new include style for FFmpeg prevents issues with #include + IF (FFMPEG_STDINT_INCLUDE_DIR) + SET(FFMPEG_INCLUDE_DIRS + ${FFMPEG_INCLUDE_DIRS} + ${FFMPEG_STDINT_INCLUDE_DIR} + ) + ENDIF() + + + SET(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS}) - if (FFMPEG_FOUND) - if (NOT FFMPEG_FIND_QUIETLY) - message(STATUS "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}") - endif (NOT FFMPEG_FIND_QUIETLY) - else (FFMPEG_FOUND) - if (FFMPEG_FIND_REQUIRED) - message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil") - endif (FFMPEG_FIND_REQUIRED) - endif (FFMPEG_FOUND) + # Note we don't add FFMPEG_LIBSWSCALE_LIBRARIES here, it will be added if found later. + SET(FFMPEG_LIBRARIES + ${FFMPEG_LIBAVFORMAT_LIBRARIES} + ${FFMPEG_LIBSWSCALE_LIBRARIES} + ${FFMPEG_LIBAVDEVICE_LIBRARIES} + ${FFMPEG_LIBAVCODEC_LIBRARIES} + ${FFMPEG_LIBAVUTIL_LIBRARIES}) -endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) +ENDIF() +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFMPEG DEFAULT_MSG FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +MARK_AS_ADVANCED(FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIR) diff --git a/contrib/cmake/FindGLIB2.cmake b/contrib/cmake/FindGLIB2.cmake new file mode 100644 index 0000000..e62d843 --- /dev/null +++ b/contrib/cmake/FindGLIB2.cmake @@ -0,0 +1,237 @@ +# - Try to find GLib2 +# Once done this will define +# +# GLIB2_FOUND - system has GLib2 +# GLIB2_INCLUDE_DIRS - the GLib2 include directory +# GLIB2_LIBRARIES - Link these to use GLib2 +# +# HAVE_GLIB_GREGEX_H glib has gregex.h header and +# supports g_regex_match_simple +# +# Copyright (c) 2006 Andreas Schneider +# Copyright (c) 2006 Philippe Bernery +# Copyright (c) 2007 Daniel Gollub +# Copyright (c) 2007 Alban Browaeys +# Copyright (c) 2008 Michael Bell +# Copyright (c) 2008-2009 Bjoern Ricks +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + + +IF (GLIB2_LIBRARIES AND GLIB2_INCLUDE_DIRS ) + # in cache already + SET(GLIB2_FOUND TRUE) +ELSE (GLIB2_LIBRARIES AND GLIB2_INCLUDE_DIRS ) + + if (NOT APPLE) + INCLUDE(FindPkgConfig) + + ## Glib + IF ( GLIB2_FIND_REQUIRED ) + SET( _pkgconfig_REQUIRED "REQUIRED" ) + ELSE ( GLIB2_FIND_REQUIRED ) + SET( _pkgconfig_REQUIRED "" ) + ENDIF ( GLIB2_FIND_REQUIRED ) + + IF ( GLIB2_MIN_VERSION ) + PKG_SEARCH_MODULE( GLIB2 ${_pkgconfig_REQUIRED} glib-2.0>=${GLIB2_MIN_VERSION} ) + ELSE ( GLIB2_MIN_VERSION ) + PKG_SEARCH_MODULE( GLIB2 ${_pkgconfig_REQUIRED} glib-2.0 ) + ENDIF ( GLIB2_MIN_VERSION ) + IF ( PKG_CONFIG_FOUND ) + IF ( GLIB2_FOUND ) + SET ( GLIB2_CORE_FOUND TRUE ) + ELSE ( GLIB2_FOUND ) + SET ( GLIB2_CORE_FOUND FALSE ) + ENDIF ( GLIB2_FOUND ) + ENDIF ( PKG_CONFIG_FOUND ) + endif() + + # Look for glib2 include dir and libraries w/o pkgconfig + IF ( NOT GLIB2_FOUND AND NOT PKG_CONFIG_FOUND OR APPLE ) + FIND_PATH( + _glibconfig_include_DIR + NAMES + glibconfig.h + PATHS + /opt/gnome/lib64 + /opt/gnome/lib + /opt/lib/ + /opt/local/lib + /sw/lib/ + /usr/lib64 + /usr/lib + /usr/local/include + ${CMAKE_LIBRARY_PATH} + PATH_SUFFIXES + glib-2.0/include + ) + + FIND_PATH( + _glib2_include_DIR + NAMES + glib.h + PATHS + /opt/gnome/include + /opt/local/include + /sw/include + /usr/include + /usr/local/include + PATH_SUFFIXES + glib-2.0 + ) + + #MESSAGE(STATUS "Glib headers: ${_glib2_include_DIR}") + + FIND_LIBRARY( + _glib2_link_DIR + NAMES + glib-2.0 + glib + PATHS + /opt/gnome/lib + /opt/local/lib + /sw/lib + /usr/lib + /usr/local/lib + ) + IF ( _glib2_include_DIR AND _glib2_link_DIR ) + SET ( _glib2_FOUND TRUE ) + ENDIF ( _glib2_include_DIR AND _glib2_link_DIR ) + + + IF ( _glib2_FOUND ) + SET ( GLIB2_INCLUDE_DIRS ${_glib2_include_DIR} ${_glibconfig_include_DIR} ) + SET ( GLIB2_LIBRARIES ${_glib2_link_DIR} ) + SET ( GLIB2_CORE_FOUND TRUE ) + ELSE ( _glib2_FOUND ) + SET ( GLIB2_CORE_FOUND FALSE ) + ENDIF ( _glib2_FOUND ) + + # Handle dependencies + # libintl + IF ( NOT LIBINTL_FOUND ) + FIND_PATH(LIBINTL_INCLUDE_DIR + NAMES + libintl.h + PATHS + /opt/gnome/include + /opt/local/include + /sw/include + /usr/include + /usr/local/include + ) + + FIND_LIBRARY(LIBINTL_LIBRARY + NAMES + intl + PATHS + /opt/gnome/lib + /opt/local/lib + /sw/lib + /usr/local/lib + /usr/lib + ) + + IF (LIBINTL_LIBRARY AND LIBINTL_INCLUDE_DIR) + SET (LIBINTL_FOUND TRUE) + ENDIF (LIBINTL_LIBRARY AND LIBINTL_INCLUDE_DIR) + ENDIF ( NOT LIBINTL_FOUND ) + + # libiconv + IF ( NOT LIBICONV_FOUND ) + FIND_PATH(LIBICONV_INCLUDE_DIR + NAMES + iconv.h + PATHS + /opt/gnome/include + /opt/local/include + /opt/local/include + /sw/include + /sw/include + /usr/local/include + /usr/include + PATH_SUFFIXES + glib-2.0 + ) + + FIND_LIBRARY(LIBICONV_LIBRARY + NAMES + iconv + PATHS + /opt/gnome/lib + /opt/local/lib + /sw/lib + /usr/lib + /usr/local/lib + ) + + IF (LIBICONV_LIBRARY AND LIBICONV_INCLUDE_DIR) + SET (LIBICONV_FOUND TRUE) + ENDIF (LIBICONV_LIBRARY AND LIBICONV_INCLUDE_DIR) + ENDIF ( NOT LIBICONV_FOUND ) + + IF (LIBINTL_FOUND) + SET (GLIB2_LIBRARIES ${GLIB2_LIBRARIES} ${LIBINTL_LIBRARY}) + SET (GLIB2_INCLUDE_DIRS ${GLIB2_INCLUDE_DIRS} ${LIBINTL_INCLUDE_DIR}) + ENDIF (LIBINTL_FOUND) + + IF (LIBICONV_FOUND) + SET (GLIB2_LIBRARIES ${GLIB2_LIBRARIES} ${LIBICONV_LIBRARY}) + SET (GLIB2_INCLUDE_DIRS ${GLIB2_INCLUDE_DIRS} ${LIBICONV_INCLUDE_DIR}) + ENDIF (LIBICONV_FOUND) + + ENDIF ( NOT GLIB2_FOUND AND NOT PKG_CONFIG_FOUND OR APPLE ) + ## + + IF (GLIB2_CORE_FOUND AND GLIB2_INCLUDE_DIRS AND GLIB2_LIBRARIES) + SET (GLIB2_FOUND TRUE) + ENDIF (GLIB2_CORE_FOUND AND GLIB2_INCLUDE_DIRS AND GLIB2_LIBRARIES) + + IF (GLIB2_FOUND) + IF (NOT GLIB2_FIND_QUIETLY) + MESSAGE (STATUS "Found GLib2: ${GLIB2_LIBRARIES} ${GLIB2_INCLUDE_DIRS}") + ENDIF (NOT GLIB2_FIND_QUIETLY) + ELSE (GLIB2_FOUND) + IF (GLIB2_FIND_REQUIRED) + MESSAGE (SEND_ERROR "Could not find GLib2") + ENDIF (GLIB2_FIND_REQUIRED) + ENDIF (GLIB2_FOUND) + + # show the GLIB2_INCLUDE_DIRS and GLIB2_LIBRARIES variables only in the advanced view + MARK_AS_ADVANCED(GLIB2_INCLUDE_DIRS GLIB2_LIBRARIES) + MARK_AS_ADVANCED(LIBICONV_INCLUDE_DIR LIBICONV_LIBRARY) + MARK_AS_ADVANCED(LIBINTL_INCLUDE_DIR LIBINTL_LIBRARY) + +ENDIF (GLIB2_LIBRARIES AND GLIB2_INCLUDE_DIRS) + +IF ( WIN32 ) + # include libiconv for win32 + IF ( NOT LIBICONV_FOUND ) + FIND_PATH(LIBICONV_INCLUDE_DIR iconv.h PATH_SUFFIXES glib-2.0) + + FIND_LIBRARY(LIBICONV_LIBRARY NAMES iconv) + + IF (LIBICONV_LIBRARY AND LIBICONV_INCLUDE_DIR) + SET (LIBICONV_FOUND TRUE) + ENDIF (LIBICONV_LIBRARY AND LIBICONV_INCLUDE_DIR) + ENDIF ( NOT LIBICONV_FOUND ) + IF (LIBICONV_FOUND) + SET (GLIB2_LIBRARIES ${GLIB2_LIBRARIES} ${LIBICONV_LIBRARY}) + SET (GLIB2_INCLUDE_DIRS ${GLIB2_INCLUDE_DIRS} ${LIBICONV_INCLUDE_DIR}) + ENDIF (LIBICONV_FOUND) +ENDIF ( WIN32 ) + +IF ( GLIB2_FOUND ) + # Check if system has a newer version of glib + # which supports g_regex_match_simple + INCLUDE( CheckIncludeFiles ) + SET( CMAKE_REQUIRED_INCLUDES ${GLIB2_INCLUDE_DIRS} ) + CHECK_INCLUDE_FILES ( glib/gregex.h HAVE_GLIB_GREGEX_H ) + CHECK_INCLUDE_FILES ( glib/gchecksum.h HAVE_GLIB_GCHECKSUM_H ) + # Reset CMAKE_REQUIRED_INCLUDES + SET( CMAKE_REQUIRED_INCLUDES "" ) +ENDIF( GLIB2_FOUND ) \ No newline at end of file diff --git a/contrib/cmake/FindLibPurple.cmake b/contrib/cmake/FindLibPurple.cmake new file mode 100644 index 0000000..fd9b3bc --- /dev/null +++ b/contrib/cmake/FindLibPurple.cmake @@ -0,0 +1,35 @@ +FIND_PATH(LIBPURPLE_INCLUDE_DIR purple.h + PATH_SUFFIXES include/libpurple src/libpurple + PATHS + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +FIND_LIBRARY(LIBPURPLE_LIBRARY_RELEASE + NAMES purple libpurple libpurple-static +) +if (LIBPURPLE_LIBRARY_RELEASE) + list(APPEND LIBPURPLE_LIBRARY optimized ${LIBPURPLE_LIBRARY_RELEASE}) +endif() + +FIND_LIBRARY(LIBPURPLE_LIBRARY_DEBUG + NAMES purple_d libpurple_d libpurple-static_d +) +if (LIBPURPLE_LIBRARY_DEBUG) + list(APPEND LIBPURPLE_LIBRARY debug ${LIBPURPLE_LIBRARY_DEBUG}) +else() + if (UNIX) + list(APPEND LIBPURPLE_LIBRARY debug ${LIBPURPLE_LIBRARY_RELEASE}) + endif() +endif() + +# message("LIBPURPLE_LIBRARY: ${LIBPURPLE_LIBRARY}") +# message("LIBPURPLE_INCLUDE_DIR: ${LIBPURPLE_INCLUDE_DIR}") + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPURPLE DEFAULT_MSG LIBPURPLE_LIBRARY LIBPURPLE_INCLUDE_DIR) +MARK_AS_ADVANCED(LIBPURPLE_LIBRARY LIBPURPLE_INCLUDE_DIR) diff --git a/docs/Ubuntu-LTS.md b/docs/Ubuntu-LTS.md new file mode 100644 index 0000000..15e58cc --- /dev/null +++ b/docs/Ubuntu-LTS.md @@ -0,0 +1,46 @@ +Install Ubuntu LTS 12 + +For core functionality without fancy plugins: + +$ sudo apt-get install cmake +$ sudo apt-get install build-essential +$ sudo apt-get install libxml2-dev +$ sudo apt-get install libcurl4-openssl-dev + +For OpenSceneGraph invoker (version in repositories lacks vrml plugin): + +$ sudo apt-get install libpng12-dev +$ sudo apt-get install libjpeg-dev +$ sudo apt-get install libboost-all-dev +$ sudo apt-get install libltdl-dev +$ sudo apt-get install libopenscenegraph-dev # just for the dependencies +$ sudo apt-get remove libopenscenegraph-dev + +$ wget http://sourceforge.net/projects/openvrml/files/latest/download +$ tar xvjf openvrml* +$ cd openvrml* +$ ./configure --disable-render-text-node --disable-script-node-javascript --disable-script-node-java --disable-gl-renderer --disable-xembed --disable-player --disable-examples --disable-mozilla-plugin +$ sudo make install +$ cd .. + +$ sudo apt-get install git +$ git clone https://github.com/openscenegraph/osg +$ cd osg +$ nano src/osgPlugins/vrml/IndexedFaceSet.cpp # Edit line 207 and remove the else block + +$ mkdir build && cd build +$ cmake .. +$ make -j4 +$ sudo make install + +For ffmpeg invoker: + +Follow http://ffmpeg.org/trac/ffmpeg/wiki/UbuntuCompilationGuide for a version of recent version of ffmpeg +on ubuntu. The various libav* packages that come with the distribution are inadequate. + +Install uscxml: +$ git clone https://github.com/tklab-tud/uscxml.git +$ cd uscxml +$ mkdir build && cd build +$ cmake .. +$ make \ No newline at end of file diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp index 643c358..ae7af11 100644 --- a/src/uscxml/Factory.cpp +++ b/src/uscxml/Factory.cpp @@ -49,6 +49,10 @@ # include "uscxml/plugins/invoker/calendar/CalendarInvoker.h" # endif +# ifdef LIBPURPLE_FOUND +# include "uscxml/plugins/invoker/im/IMInvoker.h" +# endif + #ifdef OPENAL_FOUND # include "uscxml/plugins/invoker/audio/OpenALInvoker.h" #endif @@ -168,6 +172,13 @@ Factory::Factory() { } #endif +#ifdef LIBPURPLE_FOUND + { + IMInvoker* invoker = new IMInvoker(); + registerInvoker(invoker); + } +#endif + #if (defined OPENAL_FOUND && (defined LIBSNDFILE_FOUND || defined AUDIOTOOLBOX_FOUND)) { OpenALInvoker* invoker = new OpenALInvoker(); diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 3946bef..7e61ed7 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -905,7 +905,7 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node& element) { } if (_dataModel) { try { - _dataModel.assign("_invokers['" + invokeReq.invokeid + "']", invoker.getDataModelVariables()); +// _dataModel.assign("_invokers['" + invokeReq.invokeid + "']", invoker.getDataModelVariables()); } catch(...) { LOG(ERROR) << "Exception caught while assigning datamodel variables from invoker " << invokeReq.invokeid; } diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp index 1b15dee..8332c1d 100644 --- a/src/uscxml/URL.cpp +++ b/src/uscxml/URL.cpp @@ -56,6 +56,34 @@ std::string URL::tmpDir() { return tmpDir; } +std::map URL::mimeTypes; + std::string URL::getMimeType(const std::string extension, std::string magic) { + if (mimeTypes.empty()) { + mimeTypes["txt"] = "text/plain"; + mimeTypes["c"] = "text/plain"; + mimeTypes["h"] = "text/plain"; + mimeTypes["html"] = "text/html"; + mimeTypes["htm"] = "text/htm"; + mimeTypes["css"] = "text/css"; + mimeTypes["bmp"] = "image/bmp"; + mimeTypes["gif"] = "image/gif"; + mimeTypes["jpg"] = "image/jpeg"; + mimeTypes["jpeg"] = "image/jpeg"; + mimeTypes["mpg"] = "video/mpeg"; + mimeTypes["mov"] = "video/quicktime"; + mimeTypes["png"] = "image/png"; + mimeTypes["pdf"] = "application/pdf"; + mimeTypes["ps"] = "application/postscript"; + mimeTypes["tif"] = "image/tiff"; + mimeTypes["tiff"] = "image/tiff"; + } + + if (mimeTypes.find(extension) != mimeTypes.end()) { + return mimeTypes[extension]; + } + return ""; +} + std::string URL::getTmpFilename(const std::string& suffix) { std::string tmpFilename = tmpDir(); diff --git a/src/uscxml/URL.h b/src/uscxml/URL.h index c00d24a..26b9437 100644 --- a/src/uscxml/URL.h +++ b/src/uscxml/URL.h @@ -246,7 +246,9 @@ public: } static std::string tmpDir(); - + static std::map mimeTypes; + static std::string getMimeType(const std::string extension, std::string magic = ""); + friend class URLFetcher; friend std::ostream & operator<<(std::ostream &stream, const URL& p); diff --git a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp index 14028fa..cbb82eb 100644 --- a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp +++ b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp @@ -1,6 +1,7 @@ #include "DelayedEventQueue.h" #include #include +#include namespace uscxml { @@ -36,6 +37,27 @@ void DelayedEventQueue::run(void* instance) { } } +void DelayedEventQueue::addEvent(std::string eventId, int fd, short opMask, void (*callback)(void*, const std::string eventId), void* userData, bool persist) { + if(_callbackData.find(eventId) != _callbackData.end()) { + cancelEvent(eventId); + } + + if (persist) + opMask |= EV_PERSIST; + + struct event* event = event_new(_eventLoop, fd, opMask, DelayedEventQueue::fileCallback, &_callbackData[eventId]); + + _callbackData[eventId].eventId = eventId; + _callbackData[eventId].userData = userData; + _callbackData[eventId].eventQueue = this; + _callbackData[eventId].callback = callback; + _callbackData[eventId].event = event; + _callbackData[eventId].persist = false; + + event_add(event, NULL); + +} + void DelayedEventQueue::addEvent(std::string eventId, void (*callback)(void*, const std::string eventId), uint32_t delayMs, void* userData, bool persist) { if(_callbackData.find(eventId) != _callbackData.end()) { cancelEvent(eventId); @@ -83,6 +105,13 @@ void DelayedEventQueue::stop() { void DelayedEventQueue::dummyCallback(evutil_socket_t fd, short what, void *arg) { } +void DelayedEventQueue::fileCallback(evutil_socket_t fd, short what, void *arg) { + struct callbackData *data = (struct callbackData*)arg; + tthread::lock_guard lock(data->eventQueue->_mutex); + std::string eventId = data->eventId; // copy eventId + data->callback(data->userData, eventId); +} + void DelayedEventQueue::timerCallback(evutil_socket_t fd, short what, void *arg) { struct callbackData *data = (struct callbackData*)arg; tthread::lock_guard lock(data->eventQueue->_mutex); diff --git a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h index 7ce766b..0b72719 100644 --- a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h +++ b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h @@ -18,6 +18,12 @@ namespace uscxml { class DelayedEventQueue { public: + enum OpMask { + FD_READ = EV_READ, + FD_WRITE = EV_WRITE, + FD_SIGNAL = EV_SIGNAL + }; + struct callbackData { void *userData; void (*callback)(void*, const std::string eventId); @@ -30,6 +36,7 @@ public: DelayedEventQueue(); virtual ~DelayedEventQueue(); + void addEvent(std::string eventId, int fd, short opMask, void (*callback)(void*, const std::string eventId), void* userData, bool persist = true); void addEvent(std::string eventId, void (*callback)(void*, const std::string eventId), uint32_t delayMs, void* userData, bool persist = false); void cancelEvent(std::string eventId); @@ -42,6 +49,7 @@ public: } static void timerCallback(evutil_socket_t fd, short what, void *arg); + static void fileCallback(evutil_socket_t fd, short what, void *arg); static void dummyCallback(evutil_socket_t fd, short what, void *arg); bool _isStarted; diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp index 0e72c67..d479a63 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp @@ -124,11 +124,14 @@ boost::shared_ptr V8DataModel::create(InterpreterImpl* interprete V8DataModel::getIOProcessors, V8DataModel::setWithException, v8::External::New(reinterpret_cast(dm.get()))); + context->Global()->SetAccessor(v8::String::New("_invokers"), + V8DataModel::getInvokers, + V8DataModel::setWithException, + v8::External::New(reinterpret_cast(dm.get()))); dm->_contexts.push_back(context); // instantiate objects - we have to have a context for that! - dm->eval(Element(), "_invokers = {};"); dm->eval(Element(), "_x = {};"); return dm; @@ -147,21 +150,35 @@ void V8DataModel::setWithException(v8::Local property, v8::Local V8DataModel::getIOProcessors(v8::Local property, const v8::AccessorInfo& info) { V8DataModel* dataModel = V8DOM::toClassPtr(info.Data()); - if (dataModel->_ioProcessors.IsEmpty()) { - dataModel->_ioProcessors = v8::Persistent::New(v8::Object::New()); - //v8::Handle ioProcessorObj = v8::Object::New(); - std::map ioProcessors = dataModel->_interpreter->getIOProcessors(); - std::map::const_iterator ioProcIter = ioProcessors.begin(); - while(ioProcIter != ioProcessors.end()) { + dataModel->_ioProcessors = v8::Persistent::New(v8::Object::New()); + //v8::Handle ioProcessorObj = v8::Object::New(); + std::map ioProcessors = dataModel->_interpreter->getIOProcessors(); + std::map::const_iterator ioProcIter = ioProcessors.begin(); + while(ioProcIter != ioProcessors.end()) { // std::cout << ioProcIter->first << std::endl; - dataModel->_ioProcessors->Set(v8::String::New(ioProcIter->first.c_str()), - dataModel->getDataAsValue(ioProcIter->second.getDataModelVariables())); - ioProcIter++; - } + dataModel->_ioProcessors->Set(v8::String::New(ioProcIter->first.c_str()), + dataModel->getDataAsValue(ioProcIter->second.getDataModelVariables())); + ioProcIter++; } return dataModel->_ioProcessors; } +v8::Handle V8DataModel::getInvokers(v8::Local property, const v8::AccessorInfo& info) { + V8DataModel* dataModel = V8DOM::toClassPtr(info.Data()); + + dataModel->_invokers = v8::Persistent::New(v8::Object::New()); + //v8::Handle ioProcessorObj = v8::Object::New(); + std::map invokers = dataModel->_interpreter->getInvokers(); + std::map::const_iterator invokerIter = invokers.begin(); + while(invokerIter != invokers.end()) { + // std::cout << ioProcIter->first << std::endl; + dataModel->_invokers->Set(v8::String::New(invokerIter->first.c_str()), + dataModel->getDataAsValue(invokerIter->second.getDataModelVariables())); + invokerIter++; + } + return dataModel->_invokers; +} + void V8DataModel::pushContext() { _contexts.push_back(_contexts.back().New(_contexts.back())); } diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h index 0dffa27..1672919 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h @@ -81,7 +81,9 @@ protected: Arabica::DOM::V8DOM* _dom; v8::Persistent _ioProcessors; + v8::Persistent _invokers; static v8::Handle getIOProcessors(v8::Local property, const v8::AccessorInfo& info); + static v8::Handle getInvokers(v8::Local property, const v8::AccessorInfo& info); static v8::Handle getAttribute(v8::Local property, const v8::AccessorInfo& info); static void setWithException(v8::Local property, v8::Local value, const v8::AccessorInfo& info); diff --git a/src/uscxml/plugins/element/respond/RespondElement.cpp b/src/uscxml/plugins/element/respond/RespondElement.cpp index ae81fed..9da3002 100644 --- a/src/uscxml/plugins/element/respond/RespondElement.cpp +++ b/src/uscxml/plugins/element/respond/RespondElement.cpp @@ -98,7 +98,7 @@ void RespondElement::enterElement(const Arabica::DOM::Node& node) { size_t lastDot; if ((lastDot = file.path().find_last_of(".")) != std::string::npos) { std::string extension = file.path().substr(lastDot + 1); - std::string mimeType = HTTPServer::mimeTypeForExtension(extension); + std::string mimeType = URL::getMimeType(extension); if (mimeType.length() > 0) { httpReply.headers["Content-Type"] = mimeType; } diff --git a/src/uscxml/plugins/invoker/CMakeLists.txt b/src/uscxml/plugins/invoker/CMakeLists.txt index db0d61f..5f767c9 100644 --- a/src/uscxml/plugins/invoker/CMakeLists.txt +++ b/src/uscxml/plugins/invoker/CMakeLists.txt @@ -131,6 +131,27 @@ if (FFMPEG_FOUND) endif() +# instant messaging invoker + +if (LIBPURPLE_FOUND) + set(USCXML_INVOKERS "im ${USCXML_INVOKERS}") + file(GLOB_RECURSE LIBPURPLE_INVOKER + im/*.cpp + im/*.h + ) + source_group("Invoker\\im" FILES ${LIBPURPLE_INVOKER}) + if (BUILD_AS_PLUGINS) + add_library( + invoker_im SHARED + ${LIBPURPLE_INVOKER}) + target_link_libraries(invoker_im uscxml) + set_target_properties(invoker_im PROPERTIES FOLDER "Plugin Invoker") + else() + list (APPEND USCXML_FILES ${LIBPURPLE_INVOKER}) + endif() +endif() + + # calendar invoker if (LIBICAL_FOUND) diff --git a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h index e95b32e..ecce031 100644 --- a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h +++ b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.h @@ -46,7 +46,8 @@ protected: frame_count(0), width(0), height(0), - sws_flags(SWS_BICUBIC) {} + sws_flags(SWS_BICUBIC), + sws_ctx(NULL) {} virtual ~EncodingContext() { if (sws_ctx) diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp index 1292e3a..559e441 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp @@ -49,7 +49,27 @@ boost::shared_ptr DirMonInvoker::create(InterpreterImpl* interprete } Data DirMonInvoker::getDataModelVariables() { + tthread::lock_guard lock(_mutex); + Data data; + data.compound["dir"] = Data(_dir, Data::VERBATIM); + + std::set::iterator suffixIter = _suffixes.begin(); + while(suffixIter != _suffixes.end()) { + data.compound["suffixes"].array.push_back(Data(*suffixIter, Data::VERBATIM)); + suffixIter++; + } + + std::map entries = _watcher->getAllEntries(); + std::map::iterator entryIter = entries.begin(); + while(entryIter != entries.end()) { + data.compound["file"].compound[entryIter->first].compound["mtime"] = toStr(entryIter->second.st_mtime); + data.compound["file"].compound[entryIter->first].compound["ctime"] = toStr(entryIter->second.st_mtime); + data.compound["file"].compound[entryIter->first].compound["atime"] = toStr(entryIter->second.st_mtime); + data.compound["file"].compound[entryIter->first].compound["size"] = toStr(entryIter->second.st_mtime); + entryIter++; + } + return data; } @@ -115,7 +135,10 @@ void DirMonInvoker::invoke(const InvokeRequest& req) { void DirMonInvoker::run(void* instance) { while(((DirMonInvoker*)instance)->_isRunning) { - ((DirMonInvoker*)instance)->_watcher->updateEntries(); + { + tthread::lock_guard lock(((DirMonInvoker*)instance)->_mutex); + ((DirMonInvoker*)instance)->_watcher->updateEntries(); + } tthread::this_thread::sleep_for(tthread::chrono::milliseconds(20)); } } @@ -233,7 +256,7 @@ DirectoryWatch::~DirectoryWatch() { } } - + void DirectoryWatch::reportAsDeleted() { std::map::iterator fileIter = _knownEntries.begin(); while(fileIter != _knownEntries.end()) { @@ -248,8 +271,8 @@ void DirectoryWatch::reportAsDeleted() { monIter++; } } - _knownEntries.erase(fileIter->first); - fileIter++; + _knownEntries.erase(fileIter++); +// fileIter++; } assert(_knownDirs.size() == 0); assert(_knownEntries.size() == 0); diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h index 59eee81..f5aa4bc 100644 --- a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h +++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h @@ -44,7 +44,7 @@ public: std::map dirEntries = dirIter->second->getAllEntries(); std::map::iterator dirEntryIter = dirEntries.begin(); while(dirEntryIter != dirEntries.end()) { - entries[dirIter->first + PATH_SEPERATOR + dirEntryIter->first] = dirEntryIter->second; + entries[dirIter->first + '/' + dirEntryIter->first] = dirEntryIter->second; dirEntryIter++; } dirIter++; diff --git a/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp b/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp index 0e8430c..a6f303a 100644 --- a/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp +++ b/src/uscxml/plugins/invoker/graphics/openscenegraph/converter/OSGConverter.cpp @@ -247,7 +247,7 @@ void OSGConverter::process(const SendRequest& req) { osgViewer::ScreenCaptureHandler* captureHandler = new osgViewer::ScreenCaptureHandler(cOp, -1); { -// tthread::lock_guard lock(_viewerMutex); + tthread::lock_guard lock(_viewerMutex); osgViewer::Viewer viewer; osg::ref_ptr gc; @@ -259,7 +259,9 @@ void OSGConverter::process(const SendRequest& req) { osg::ref_ptr traits = new osg::GraphicsContext::Traits(ds); traits->width = width; traits->height = height; + // this fails with ubuntu in a VM in parallels traits->pbuffer = true; + gc = osg::GraphicsContext::createGraphicsContext(traits.get()); if (!gc.valid()) { @@ -267,6 +269,11 @@ void OSGConverter::process(const SendRequest& req) { return; } + if (!traits->width || !traits->height) { + LOG(ERROR) << "Traits returned with zero dimensions"; + return; + } + GLenum pbuffer = gc->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT; viewer.setCameraManipulator(new osgGA::TrackballManipulator()); @@ -300,6 +307,11 @@ void OSGConverter::process(const SendRequest& req) { void OSGConverter::reportSuccess(const SendRequest& req, const Data& content) { Event event(req); + + std::string format; + Event::getParam(req.params, "format", format); + + event.data.compound["mimetype"] = Data(URL::getMimeType(format), Data::VERBATIM); if (event.name.length() == 0) event.name = "convert"; @@ -519,6 +531,10 @@ void OSGConverter::dumpMatrix(const osg::Matrix& m) { void OSGConverter::NameRespectingWriteToFile::operator()(const osg::Image& image, const unsigned int context_id) { +// std::cout << "NameRespectingWriteToFile" << std::endl; +// std::cout << image.s() << std::endl; +// std::cout << image.t() << std::endl; + // write to memory first std::string format; if (_req.params.find("format") != _req.params.end()) { @@ -533,7 +549,9 @@ void OSGConverter::NameRespectingWriteToFile::operator()(const osg::Image& image // osgDB got confused when we write to a stringstream std::string tempFile = URL::getTmpFilename(format); - osgDB::writeImageFile(image, tempFile, op); + if (!osgDB::writeImageFile(image, tempFile, op)) { + _converter->reportFailure(_req); + } char* buffer = NULL; size_t length = 0; @@ -547,7 +565,9 @@ void OSGConverter::NameRespectingWriteToFile::operator()(const osg::Image& image file.read(buffer, length); } - remove(tempFile.c_str()); + std::cout << tempFile << std::endl; + +// remove(tempFile.c_str()); // osg::ref_ptr writerFormat = osgDB::Registry::instance()->getReaderWriterForExtension(format); // if(!writerFormat.valid()) // _converter->reportFailure(_req); diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.cpp b/src/uscxml/plugins/invoker/im/IMInvoker.cpp new file mode 100644 index 0000000..a7fd146 --- /dev/null +++ b/src/uscxml/plugins/invoker/im/IMInvoker.cpp @@ -0,0 +1,361 @@ +#include "IMInvoker.h" +#include + +#ifdef BUILD_AS_PLUGINS +#include +#endif + +namespace uscxml { + +#ifdef BUILD_AS_PLUGINS +PLUMA_CONNECTOR +bool connect(pluma::Host& host) { + host.add( new IMInvokerProvider() ); + return true; +} +#endif + +Data IMInvoker::_pluginData; +GHashTable* IMInvoker::_uiInfo = NULL; +GRand* IMInvoker::_gRand = NULL; + +PurpleEventLoopUiOps IMInvoker::_uiEventLoopOps = +{ + purpleEventTimeoutAdd, + purpleEventTimeoutRemove, + purpleEventInputAdd, + purpleEventInputRemove, + purpleEventInputGetError, + purpleEventTimeoutAddSec, + + /* padding */ + NULL, + NULL, + NULL +}; + +PurpleAccountUiOps IMInvoker::_accountUIOps = { + accountNotifyAdded, + accountStatusChanged, + accountRequestAdd, + accountRequestAuthorize, + accountCloseRequest, + NULL, + NULL, + NULL +}; + +/*** Conversation uiops ***/ +static void +null_write_conv(PurpleConversation *conv, const char *who, const char *alias, + const char *message, PurpleMessageFlags flags, time_t mtime) +{ + const char *name; + if (alias && *alias) + name = alias; + else if (who && *who) + name = who; + else + name = NULL; + + printf("(%s) %s %s: %s\n", purple_conversation_get_name(conv), + purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), + name, message); +} + +static PurpleConversationUiOps null_conv_uiops = +{ + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + null_write_conv, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL +}; + +static void +null_ui_init(void) +{ + /** + * This should initialize the UI components for all the modules. Here we + * just initialize the UI for conversations. + */ + purple_conversations_set_ui_ops(&null_conv_uiops); +} + +static PurpleCoreUiOps null_core_uiops = +{ + NULL, + NULL, + null_ui_init, + NULL, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +DelayedEventQueue* IMInvoker::_eventQueue = NULL; + +void IMInvoker::initLibPurple(void *userdata, const std::string event) { + _uiInfo = g_hash_table_new(g_str_hash, g_str_equal); + _gRand = g_rand_new(); + + /* Set a custom user directory (optional) */ + //purple_util_set_user_dir(CUSTOM_USER_DIRECTORY); + + /* We do not want any debugging for now to keep the noise to a minimum. */ + purple_debug_set_enabled(false); + + purple_core_set_ui_ops(&null_core_uiops); +// purple_eventloop_set_ui_ops(&glib_eventloops); + purple_eventloop_set_ui_ops(&_uiEventLoopOps); + + purple_plugins_add_search_path("/usr/local/lib/purple-3"); + // purple_plugins_probe(G_MODULE_SUFFIX); + + if (!purple_core_init("uscxml")) { + LOG(ERROR) << "libpurple initialization failed." << std::endl; + return; + } + + /* Load the preferences. */ + purple_prefs_load(); + purple_plugins_load_saved("/purple/uscxml/plugins/saved"); + + GList *l; + PurplePlugin *plugin; + + for (l = purple_plugins_get_all(); l != NULL; l = l->next) { + plugin = (PurplePlugin *)l->data; + + Data pluginData; + if (plugin->info->id) pluginData.compound["id"] = Data(plugin->info->id, Data::VERBATIM); + if (plugin->info->homepage) pluginData.compound["homepage"] = Data(plugin->info->homepage, Data::VERBATIM); + if (plugin->info->author) pluginData.compound["author"] = Data(plugin->info->author, Data::VERBATIM); + if (plugin->info->description) pluginData.compound["description"] = Data(plugin->info->description, Data::VERBATIM); + if (plugin->info->name) pluginData.compound["name"] = Data(plugin->info->name, Data::VERBATIM); + if (plugin->info->summary) pluginData.compound["summary"] = Data(plugin->info->summary, Data::VERBATIM); + if (plugin->info->version) pluginData.compound["version"] = Data(plugin->info->version, Data::VERBATIM); + if (plugin->info->major_version) pluginData.compound["majorVersion"] = Data(toStr(plugin->info->major_version), Data::VERBATIM); + if (plugin->info->minor_version) pluginData.compound["minorVersion"] = Data(toStr(plugin->info->minor_version), Data::VERBATIM); + + if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) + _pluginData.compound["protocol"].compound[plugin->info->id] = pluginData; + + } + +} + +void IMInvoker::send(void *userdata, const std::string event) { + EventContext* ctx = (EventContext*)userdata; + delete(ctx); +} + +static void +signed_on(PurpleConnection *gc, gpointer null) +{ + PurpleAccount *account = purple_connection_get_account(gc); + printf("Account connected: %s %s\n", purple_account_get_username(account), purple_account_get_protocol_id(account)); +} + +void IMInvoker::invoke(void *userdata, const std::string event) { + EventContext* ctx = (EventContext*)userdata; + IMInvoker* instance = ctx->instance; + + std::string username; + Event::getParam(ctx->invokeReq.params, "username", username); + std::string protocolId; + Event::getParam(ctx->invokeReq.params, "protocol", protocolId); + std::string password; + Event::getParam(ctx->invokeReq.params, "password", password); + + instance->_account = purple_account_new(username.c_str(), protocolId.c_str()); + purple_account_set_password(instance->_account, password.c_str(), NULL, NULL); + + purple_accounts_set_ui_ops(&_accountUIOps); + + purple_account_set_enabled(instance->_account, "uscxml", true); + + PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); + purple_savedstatus_activate(status); + + int handle; + purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, + PURPLE_CALLBACK(signed_on), NULL); + + delete(ctx); +} + +IMInvoker::IMInvoker() { + _account = NULL; + if (!_eventQueue) { + _eventQueue = new DelayedEventQueue(); + _eventQueue->addEvent("initLibPurple", IMInvoker::initLibPurple, 0, NULL); + _eventQueue->start(); + + + } + +} + +IMInvoker::~IMInvoker() { + if (_account) { + purple_account_destroy(_account); + } +}; + +boost::shared_ptr IMInvoker::create(InterpreterImpl* interpreter) { + boost::shared_ptr invoker = boost::shared_ptr(new IMInvoker()); + return invoker; +} + +Data IMInvoker::getDataModelVariables() { + return _pluginData; +} + +void IMInvoker::send(const SendRequest& req) { + EventContext* ctx = new EventContext(); + ctx->sendReq = req; + ctx->instance = this; + _eventQueue->addEvent(req.sendid, IMInvoker::invoke, 0, ctx); + return; +} + +void IMInvoker::cancel(const std::string sendId) { +} + +void IMInvoker::invoke(const InvokeRequest& req) { + + EventContext* ctx = new EventContext(); + ctx->invokeReq = req; + ctx->instance = this; + _eventQueue->addEvent(req.sendid, IMInvoker::invoke, 0, ctx); + return; +} + +guint IMInvoker::purpleEventTimeoutAdd(guint interval, GSourceFunc function, gpointer data) { + PurpleEventContext* ctx = new PurpleEventContext(); + ctx->function = function; + ctx->input = NULL; + ctx->data = data; + uintptr_t ptr = reinterpret_cast(ctx); + + _eventQueue->addEvent(toStr(ptr), purpleCallback, interval, ctx); + return ptr; +} + +gboolean IMInvoker::purpleEventTimeoutRemove(guint handle) { + _eventQueue->cancelEvent(toStr(handle)); + return true; +} + +guint IMInvoker::purpleEventTimeoutAddSec(guint interval, GSourceFunc function, gpointer data) { + return purpleEventTimeoutAdd(interval * 1000, function, data); +} + +guint IMInvoker::purpleEventInputAdd(int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer data) { + PurpleEventContext* ctx = new PurpleEventContext(); + ctx->function = NULL; + ctx->input = func; + ctx->inputFD = fd; + ctx->cond = cond; + ctx->data = data; + + short opMask = 0; + if (cond & PURPLE_INPUT_READ) + opMask |= DelayedEventQueue::FD_READ; + if (cond & PURPLE_INPUT_WRITE) + opMask |= DelayedEventQueue::FD_WRITE; + + guint eventId = g_rand_int(_gRand); + _eventQueue->addEvent(toStr(eventId), fd, opMask, purpleCallback, ctx, true); + return eventId; +} + +gboolean IMInvoker::purpleEventInputRemove(guint handle) { + _eventQueue->cancelEvent(toStr(handle)); + return true; +} + +int IMInvoker::purpleEventInputGetError(int fd, int *error) { + std::cout << "purpleEventInputGetError" << std::endl; + return 0; +} + +void IMInvoker::purpleCallback(void *userdata, const std::string event) { + PurpleEventContext* ctx = (PurpleEventContext*)userdata; + if (ctx->function) { + ctx->function(ctx->data); + delete ctx; + } else if(ctx->input) { + ctx->input(ctx->data, ctx->inputFD, ctx->cond); + } +} + +void IMInvoker::purplePrefsInit(void) {} +void IMInvoker::purpleDebugInit(void) {} +void IMInvoker::purpleUIInit(void) {} +void IMInvoker::purpleQuit(void) {} + +GHashTable* IMInvoker::purpleGetUIInfo(void) { + return _uiInfo; +} + +void IMInvoker::accountNotifyAdded(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, + const char *message) { + std::cout << "accountNotifyAdded" << std::endl; +} + +void IMInvoker::accountStatusChanged(PurpleAccount *account, + PurpleStatus *status) { + std::cout << "accountStatusChanged" << std::endl; + +} + +void IMInvoker::accountRequestAdd(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, + const char *message) { + std::cout << "accountRequestAdd" << std::endl; +} + +void* IMInvoker::accountRequestAuthorize(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, + const char *message, + gboolean on_list, + PurpleAccountRequestAuthorizationCb authorize_cb, + PurpleAccountRequestAuthorizationCb deny_cb, + void *user_data) { + // always accept all requests + authorize_cb(message, user_data); + return user_data; +} + +void IMInvoker::accountCloseRequest(void *ui_handle) { + std::cout << "accountCloseRequest" << std::endl; +} + + +} \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.h b/src/uscxml/plugins/invoker/im/IMInvoker.h new file mode 100644 index 0000000..fb02e46 --- /dev/null +++ b/src/uscxml/plugins/invoker/im/IMInvoker.h @@ -0,0 +1,117 @@ +#ifndef IMINVOKER_H_FNWG0XCQ +#define IMINVOKER_H_FNWG0XCQ + +#include + +//extern "C" { +#include +//} + +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +namespace uscxml { + +class IMInvoker : public InvokerImpl { +public: + struct EventContext { + InvokeRequest invokeReq; + SendRequest sendReq; + IMInvoker* instance; + }; + + IMInvoker(); + virtual ~IMInvoker(); + virtual boost::shared_ptr create(InterpreterImpl* interpreter); + + virtual std::set getNames() { + std::set names; + names.insert("im"); + names.insert("instant-messaging"); + names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#instant-messaging"); + return names; + } + + virtual Data getDataModelVariables(); + virtual void send(const SendRequest& req); + virtual void cancel(const std::string sendId); + virtual void invoke(const InvokeRequest& req); + +protected: + static bool _libPurpleIsInitialized; + static Data _pluginData; + static PurpleAccountUiOps _accountUIOps; + static PurpleEventLoopUiOps _uiEventLoopOps; + static PurpleCoreUiOps _uiCoreOps; + static GHashTable* _uiInfo; + static GRand* _gRand; + + static DelayedEventQueue* _eventQueue; + + // these are only being called from the delayed queue's thread + static void initLibPurple(void *userdata, const std::string event); + static void send(void *userdata, const std::string event); + static void invoke(void *userdata, const std::string event); + + // libpurple ui operations + static guint purpleEventTimeoutAdd(guint interval, GSourceFunc function, gpointer data); + static gboolean purpleEventTimeoutRemove(guint handle); + static guint purpleEventInputAdd(int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer user_data); + static gboolean purpleEventInputRemove(guint handle); + static int purpleEventInputGetError(int fd, int *error); + static guint purpleEventTimeoutAddSec(guint interval, GSourceFunc function, gpointer data); + + // callback contexts + struct PurpleEventContext { + PurpleInputFunction input; + PurpleInputCondition cond; + int inputFD; + GSourceFunc function; + gpointer data; + }; + static void purpleCallback(void *userdata, const std::string event); + + // libpurple core operations + static void purplePrefsInit(void); + static void purpleDebugInit(void); + static void purpleUIInit(void); + static void purpleQuit(void); + static GHashTable* purpleGetUIInfo(void); + + // account operations + static void accountNotifyAdded(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, + const char *message); + static void accountStatusChanged(PurpleAccount *account, + PurpleStatus *status); + static void accountRequestAdd(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, + const char *message); + static void* accountRequestAuthorize(PurpleAccount *account, + const char *remote_user, + const char *id, + const char *alias, + const char *message, + gboolean on_list, + PurpleAccountRequestAuthorizationCb authorize_cb, + PurpleAccountRequestAuthorizationCb deny_cb, + void *user_data); + static void accountCloseRequest(void *ui_handle); + + + PurpleAccount* _account; +}; + +#ifdef BUILD_AS_PLUGINS +PLUMA_INHERIT_PROVIDER(IMInvoker, InvokerImpl); +#endif + +} + + +#endif /* end of include guard: IMINVOKER_H_FNWG0XCQ */ diff --git a/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp b/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp index 4d514ef..bb17370 100644 --- a/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp +++ b/src/uscxml/plugins/invoker/sample/SampleInvoker.cpp @@ -23,7 +23,6 @@ SampleInvoker::~SampleInvoker() { boost::shared_ptr SampleInvoker::create(InterpreterImpl* interpreter) { boost::shared_ptr invoker = boost::shared_ptr(new SampleInvoker()); - invoker->_interpreter = interpreter; return invoker; } diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index 0521d74..8799fc2 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -71,7 +71,6 @@ HTTPServer::~HTTPServer() { HTTPServer* HTTPServer::_instance = NULL; tthread::recursive_mutex HTTPServer::_instanceMutex; -std::map HTTPServer::mimeTypes; HTTPServer* HTTPServer::getInstance(int port) { // tthread::lock_guard lock(_instanceMutex); @@ -80,25 +79,6 @@ HTTPServer* HTTPServer::getInstance(int port) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); #endif - // this is but a tiny list, supply a content-type
yourself - mimeTypes["txt"] = "text/plain"; - mimeTypes["c"] = "text/plain"; - mimeTypes["h"] = "text/plain"; - mimeTypes["html"] = "text/html"; - mimeTypes["htm"] = "text/htm"; - mimeTypes["css"] = "text/css"; - mimeTypes["bmp"] = "image/bmp"; - mimeTypes["gif"] = "image/gif"; - mimeTypes["jpg"] = "image/jpeg"; - mimeTypes["jpeg"] = "image/jpeg"; - mimeTypes["mpg"] = "video/mpeg"; - mimeTypes["mov"] = "video/quicktime"; - mimeTypes["png"] = "image/png"; - mimeTypes["pdf"] = "application/pdf"; - mimeTypes["ps"] = "application/postscript"; - mimeTypes["tif"] = "image/tiff"; - mimeTypes["tiff"] = "image/tiff"; - #ifndef _WIN32 evthread_use_pthreads(); #else @@ -110,12 +90,6 @@ HTTPServer* HTTPServer::getInstance(int port) { return _instance; } -std::string HTTPServer::mimeTypeForExtension(const std::string& ext) { - if (mimeTypes.find(ext) != mimeTypes.end()) - return mimeTypes[ext]; - return ""; -} - /** * This callback is registered for all HTTP requests */ @@ -165,7 +139,10 @@ void HTTPServer::httpRecvReqCallback(struct evhttp_request *req, void *callbackD request.data.compound["httpMajor"] = Data(toStr((unsigned short)req->major), Data::VERBATIM); request.data.compound["httpMinor"] = Data(toStr((unsigned short)req->minor), Data::VERBATIM); request.data.compound["uri"] = Data(HTTPServer::getBaseURL() + req->uri, Data::VERBATIM); - request.data.compound["path"] = Data(evhttp_uri_get_path(evhttp_request_get_evhttp_uri(req)), Data::VERBATIM); + + char* pathCStr = evhttp_decode_uri(evhttp_uri_get_path(evhttp_request_get_evhttp_uri(req))); + request.data.compound["path"] = Data(pathCStr, Data::VERBATIM); + free(pathCStr); raw << " " << request.data.compound["path"].atom; const char* query = evhttp_uri_get_query(evhttp_request_get_evhttp_uri(req)); diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h index d216d5e..3e0d91c 100644 --- a/src/uscxml/server/HTTPServer.h +++ b/src/uscxml/server/HTTPServer.h @@ -45,7 +45,6 @@ public: static std::string getBaseURL(); static void reply(const Reply& reply); - static std::string mimeTypeForExtension(const std::string& ext); static bool registerServlet(const std::string& path, HTTPServlet* servlet); ///< Register a servlet, returns false if path is already taken static void unregisterServlet(HTTPServlet* servlet); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5fcad93..de7fa70 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,12 +18,12 @@ if (SWI_FOUND) add_test(test-prolog-swi ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-prolog.scxml) endif() -if (FFMPEG_FOUND) - add_executable(test-ffmpeg src/test-ffmpeg.cpp) - target_link_libraries(test-ffmpeg uscxml) - add_test(test-ffmpeg ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ffmpeg) - set_target_properties(test-ffmpeg PROPERTIES FOLDER "Tests") -endif() +# if (FFMPEG_FOUND) +# add_executable(test-ffmpeg src/test-ffmpeg.cpp) +# target_link_libraries(test-ffmpeg uscxml) +# add_test(test-ffmpeg ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-ffmpeg) +# set_target_properties(test-ffmpeg PROPERTIES FOLDER "Tests") +# endif() if (V8_FOUND) add_test(test-ecmascript ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-ecmascript.scxml) diff --git a/test/samples/uscxml/test-instant-messaging.scxml b/test/samples/uscxml/test-instant-messaging.scxml new file mode 100644 index 0000000..569ec59 --- /dev/null +++ b/test/samples/uscxml/test-instant-messaging.scxml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file -- cgit v0.12