diff --git a/Makefile b/Makefile index 22be08724..9dc9aa5a7 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,14 @@ ifdef FORCE_EXTERNAL apps_list = ${EPSILON_APPS} endif +ifdef HOME_DISPLAY_EXTERNALS + ifneq ($(filter external,$(apps_list)),) + SFLAGS += -DHOME_DISPLAY_EXTERNALS + else + $(warning HOME_DISPLAY_EXTERNALS is set but external isn't included, ignoring flag.) + endif +endif + .PHONY: info info: @echo "EPSILON_VERSION = $(EPSILON_VERSION)" diff --git a/apps/external/Makefile b/apps/external/Makefile index 6a7df2c48..2c9266c7a 100644 --- a/apps/external/Makefile +++ b/apps/external/Makefile @@ -1,3 +1,12 @@ +ifdef HOME_DISPLAY_EXTERNALS + +app_external_src = $(addprefix apps/external/,\ + extapp_api.cpp \ + archive.cpp \ +) + +else + apps += External::App app_headers += apps/external/app.h @@ -9,6 +18,10 @@ app_external_src = $(addprefix apps/external/,\ pointer_text_table_cell.cpp \ ) +$(eval $(call depends_on_image,apps/external/app.cpp,apps/external/external_icon.png)) + +endif + SFLAGS += -Iapps/external/ EXTAPP_PATH ?= apps/external/app/ @@ -34,4 +47,3 @@ i18n_files += $(addprefix apps/external/,\ base.universal.i18n\ ) -$(eval $(call depends_on_image,apps/external/app.cpp,apps/external/external_icon.png)) diff --git a/apps/external/archive.cpp b/apps/external/archive.cpp index e21f19d41..bbe04a487 100644 --- a/apps/external/archive.cpp +++ b/apps/external/archive.cpp @@ -115,9 +115,44 @@ size_t numberOfFiles() { return count; } +bool executableAtIndex(size_t index, File &entry) { + File dummy; + size_t count; + size_t final_count = 0; + + for (count = 0; fileAtIndex(count, dummy); count++) { + if (dummy.isExecutable) { + if (final_count == index) { + entry = dummy; + return true; + } + final_count++; + } + } + + return false; +} + +size_t numberOfExecutables() { + File dummy; + size_t count; + size_t final_count = 0; + + for (count = 0; fileAtIndex(count, dummy); count++) + if (dummy.isExecutable) + final_count++; + + return final_count; +} + + + #else bool fileAtIndex(size_t index, File &entry) { + if (index != 0) + return false; + entry.name = "Built-in"; entry.data = NULL; entry.dataLength = 0; @@ -125,6 +160,14 @@ bool fileAtIndex(size_t index, File &entry) { return true; } +bool executableAtIndex(size_t index, File &entry) { + return fileAtIndex(index, entry); +} + +size_t numberOfExecutables() { + return 1; +} + extern "C" void extapp_main(void); uint32_t executeFile(const char *name, void * heap, const uint32_t heapSize) { diff --git a/apps/external/archive.h b/apps/external/archive.h index 80b71d0e1..670bd138f 100644 --- a/apps/external/archive.h +++ b/apps/external/archive.h @@ -19,6 +19,8 @@ struct File { bool fileAtIndex(size_t index, File &entry); int indexFromName(const char *name); size_t numberOfFiles(); +size_t numberOfExecutables(); +bool executableAtIndex(size_t index, File &entry); uint32_t executeFile(const char *name, void * heap, const uint32_t heapSize); } diff --git a/apps/home/app.cpp b/apps/home/app.cpp index 5c5cc8f9c..51194e4f7 100644 --- a/apps/home/app.cpp +++ b/apps/home/app.cpp @@ -24,9 +24,18 @@ App::Descriptor * App::Snapshot::descriptor() { return &descriptor; } +void App::didBecomeActive(Window * window) { + ::App::didBecomeActive(window); + m_window = window; +} + +void App::redraw() { + m_window->redraw(true); +} + App::App(Snapshot * snapshot) : ::App(snapshot, &m_controller, I18n::Message::Warning), - m_controller(&m_modalViewController, snapshot) + m_controller(&m_modalViewController, snapshot, this) { } diff --git a/apps/home/app.h b/apps/home/app.h index 59325a440..a912ec3ee 100644 --- a/apps/home/app.h +++ b/apps/home/app.h @@ -18,6 +18,8 @@ public: App * unpack(Container * container) override; Descriptor * descriptor() override; }; + void redraw(); + virtual void didBecomeActive(Window * window); static App * app() { return static_cast(Container::activeApp()); } @@ -25,9 +27,18 @@ public: return static_cast(::App::snapshot()); } TELEMETRY_ID("Home"); +#if HOME_DISPLAY_EXTERNALS + int heapSize() { return k_externalHeapSize; } + char * heap() { return m_externalHeap; } +#endif private: App(Snapshot * snapshot); Controller m_controller; +#if HOME_DISPLAY_EXTERNALS + static constexpr int k_externalHeapSize = 80000; + char m_externalHeap[k_externalHeapSize]; +#endif + Window * m_window; }; } diff --git a/apps/home/app_cell.cpp b/apps/home/app_cell.cpp index 27600e379..9f028d57d 100644 --- a/apps/home/app_cell.cpp +++ b/apps/home/app_cell.cpp @@ -31,6 +31,12 @@ void AppCell::layoutSubviews(bool force) { m_nameView.setFrame(KDRect((bounds().width()-nameSize.width())/2-k_nameWidthMargin, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, nameSize.width()+2*k_nameWidthMargin, nameSize.height()+2*k_nameHeightMargin), force); } +void AppCell::setExtAppDescriptor(const char* name) { + // m_iconView.setImage(descriptor->icon()); + m_nameView.setText(name); + layoutSubviews(); +} + void AppCell::setAppDescriptor(::App::Descriptor * descriptor) { m_iconView.setImage(descriptor->icon()); m_nameView.setMessage(descriptor->name()); diff --git a/apps/home/app_cell.h b/apps/home/app_cell.h index 73cb7e0f9..b479d54f6 100644 --- a/apps/home/app_cell.h +++ b/apps/home/app_cell.h @@ -17,6 +17,7 @@ public: void setVisible(bool visible); void reloadCell() override; void setAppDescriptor(::App::Descriptor * appDescriptor); + void setExtAppDescriptor(const char* name); private: static constexpr KDCoordinate k_iconMargin = 22; static constexpr KDCoordinate k_iconWidth = 55; diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp index 5969657a3..b2ddc3d3c 100644 --- a/apps/home/controller.cpp +++ b/apps/home/controller.cpp @@ -6,6 +6,10 @@ extern "C" { #include } +#ifdef HOME_DISPLAY_EXTERNALS +#include "../external/archive.h" +#endif + namespace Home { Controller::ContentView::ContentView(Controller * controller, SelectableTableViewDataSource * selectionDataSource) : @@ -49,16 +53,45 @@ void Controller::ContentView::layoutSubviews(bool force) { m_selectableTableView.setFrame(bounds(), force); } -Controller::Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource) : +Controller::Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource, ::App * app) : ViewController(parentResponder), m_view(this, selectionDataSource) { + m_app = app; } bool Controller::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK || event == Ion::Events::EXE) { AppsContainer * container = AppsContainer::sharedAppsContainer(); - ::App::Snapshot * selectedSnapshot = container->appSnapshotAtIndex(selectionDataSource()->selectedRow()*k_numberOfColumns+selectionDataSource()->selectedColumn()+1); + + int index = selectionDataSource()->selectedRow()*k_numberOfColumns+selectionDataSource()->selectedColumn()+1; + +#ifdef HOME_DISPLAY_EXTERNALS + if (index >= container->numberOfApps()) { + External::Archive::File executable; + if (External::Archive::executableAtIndex(index - container->numberOfApps(), executable)) { + uint32_t res = External::Archive::executeFile(executable.name, ((App *)m_app)->heap(), ((App *)m_app)->heapSize()); + ((App*)m_app)->redraw(); + switch(res) { + case 0: + break; + case 1: + Container::activeApp()->displayWarning(I18n::Message::ExternalAppApiMismatch); + break; + case 2: + Container::activeApp()->displayWarning(I18n::Message::StorageMemoryFull1); + break; + default: + Container::activeApp()->displayWarning(I18n::Message::ExternalAppExecError); + break; + } + return true; + } + + } else { +#endif + + ::App::Snapshot * selectedSnapshot = container->appSnapshotAtIndex(index); if (((GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Dutch || GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::NoSymNoText) && selectedSnapshot->descriptor()->examinationLevel() < 2) || ((GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Standard || GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::NoSym) && selectedSnapshot->descriptor()->examinationLevel() < 1)) { App::app()->displayWarning(I18n::Message::ForbidenAppInExamMode1, I18n::Message::ForbidenAppInExamMode2); @@ -67,6 +100,9 @@ bool Controller::handleEvent(Ion::Events::Event event) { assert(switched); (void) switched; // Silence compilation warning about unused variable. } +#ifdef HOME_DISPLAY_EXTERNALS + } +#endif return true; } @@ -134,7 +170,19 @@ void Controller::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { AppsContainer * container = AppsContainer::sharedAppsContainer(); int appIndex = (j*k_numberOfColumns+i)+1; if (appIndex >= container->numberOfApps()) { +#ifdef HOME_DISPLAY_EXTERNALS + External::Archive::File app_file; + + + if (External::Archive::executableAtIndex(appIndex - container->numberOfApps(), app_file)) { + appCell->setExtAppDescriptor(app_file.name); + appCell->setVisible(true); + } else { + appCell->setVisible(false); + } +#else appCell->setVisible(false); +#endif } else { appCell->setVisible(true); ::App::Descriptor * descriptor = container->appSnapshotAtIndex(appIndex)->descriptor(); @@ -145,7 +193,11 @@ void Controller::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { int Controller::numberOfIcons() const { AppsContainer * container = AppsContainer::sharedAppsContainer(); assert(container->numberOfApps() > 0); +#ifdef HOME_DISPLAY_EXTERNALS + return container->numberOfApps() - 1 + External::Archive::numberOfExecutables(); +#else return container->numberOfApps() - 1; +#endif } void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) { @@ -162,7 +214,7 @@ void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previo * background complete redrawing but the code is a bit * clumsy. */ if (t->selectedRow() == numberOfRows()-1) { - m_view.reloadBottomRow(this, container->numberOfApps()-1, k_numberOfColumns); + m_view.reloadBottomRow(this, this->numberOfIcons(), k_numberOfColumns); } /* To prevent the selectable table view to select cells that are unvisible, * we reselect the previous selected cell as soon as the selected cell is @@ -170,7 +222,7 @@ void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previo * stay on a unvisible cell and to initialize the first cell on a visible one * (so the previous one is always visible). */ int appIndex = (t->selectedColumn()+t->selectedRow()*k_numberOfColumns)+1; - if (appIndex >= container->numberOfApps()) { + if (appIndex >= this->numberOfIcons() + 1) { t->selectCellAtLocation(previousSelectedCellX, previousSelectedCellY); } } diff --git a/apps/home/controller.h b/apps/home/controller.h index c49848480..f7c4b1e72 100644 --- a/apps/home/controller.h +++ b/apps/home/controller.h @@ -8,7 +8,7 @@ namespace Home { class Controller : public ViewController, public SimpleTableViewDataSource, public SelectableTableViewDelegate { public: - Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource); + Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource, App * app); View * view() override; @@ -49,6 +49,7 @@ private: static constexpr int k_cellWidth = 104; ContentView m_view; AppCell m_cells[k_maxNumberOfCells]; + App * m_app; }; } diff --git a/escher/include/escher/message_text_view.h b/escher/include/escher/message_text_view.h index 3a20ac48d..a879a0465 100644 --- a/escher/include/escher/message_text_view.h +++ b/escher/include/escher/message_text_view.h @@ -15,6 +15,7 @@ public: KDSize minimalSizeForOptimalDisplay() const override; private: I18n::Message m_message; + const char* m_text = 0; }; #endif diff --git a/escher/src/message_text_view.cpp b/escher/src/message_text_view.cpp index 013defe6d..64e3bf841 100644 --- a/escher/src/message_text_view.cpp +++ b/escher/src/message_text_view.cpp @@ -9,11 +9,13 @@ MessageTextView::MessageTextView(const KDFont * font, I18n::Message message, flo } const char * MessageTextView::text() const { + if (m_text) + return m_text; return I18n::translate(m_message); } void MessageTextView::setText(const char * text) { - assert(false); + m_text = text; } void MessageTextView::setMessage(I18n::Message message) {