diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..baddc26301 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2281,7 +2281,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } else if (isOption && !isShifted && !isMeta) { Menu::getInstance()->triggerOption(MenuOption::ScriptEditor); } else if (!isOption && !isShifted && isMeta) { - takeSnapshot(); + takeSnapshot(true); } break; @@ -5071,7 +5071,7 @@ void Application::toggleLogDialog() { } } -void Application::takeSnapshot() { +void Application::takeSnapshot(bool notify) { QMediaPlayer* player = new QMediaPlayer(); QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); @@ -5079,7 +5079,7 @@ void Application::takeSnapshot() { QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot()); - emit DependencyManager::get()->snapshotTaken(path); + emit DependencyManager::get()->snapshotTaken(path, notify); } float Application::getRenderResolutionScale() const { diff --git a/interface/src/Application.h b/interface/src/Application.h index 0af65f665f..ea6a117cfa 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -247,6 +247,8 @@ public: float getAvatarSimrate() const { return _avatarSimCounter.rate(); } float getAverageSimsPerSecond() const { return _simCounter.rate(); } + + void takeSnapshot(bool notify); signals: void svoImportRequested(const QString& url); @@ -373,8 +375,6 @@ private: int sendNackPackets(); - void takeSnapshot(); - MyAvatar* getMyAvatar() const; void checkSkeleton() const; diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index b165cda135..fb7be86096 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -199,3 +199,10 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) { qDebug() << "Copying"; QApplication::clipboard()->setText(text); } + +void WindowScriptingInterface::takeSnapshot(bool notify) { + // only evil-doers call takeSnapshot from a random thread + qApp->postLambdaEvent([&] { + qApp->takeSnapshot(notify); + }); +} diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 9d73111333..1abcb9db35 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -54,12 +54,13 @@ public slots: QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); void copyToClipboard(const QString& text); + void takeSnapshot(bool notify); signals: void domainChanged(const QString& domainHostname); void svoImportRequested(const QString& url); void domainConnectionRefused(const QString& reasonMessage, int reasonCode); - void snapshotTaken(const QString& path); + void snapshotTaken(const QString& path, bool notify); private slots: WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e0c87fbbed..a4db2811e0 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -662,10 +662,15 @@ void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { QImage OpenGLDisplayPlugin::getScreenshot() const { using namespace oglplus; - QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888); + auto width = _compositeFramebuffer->size.x; + if (isHmd()) { + width /= 2; + } + + QImage screenshot(width, _compositeFramebuffer->size.y, QImage::Format_RGBA8888); withMainThreadContext([&] { Framebuffer::Bind(Framebuffer::Target::Read, _compositeFramebuffer->fbo); - Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits()); + Context::ReadPixels(0, 0, width, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits()); }); return screenshot.mirrored(false, true); } diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 817d63582d..2e707d95ce 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -27,3 +27,4 @@ Script.load("system/controllers/grab.js"); Script.load("system/controllers/teleport.js"); Script.load("system/dialTone.js"); Script.load("system/firstPersonHMD.js"); +Script.load("system/snapshot.js"); diff --git a/scripts/system/assets/images/tools/snap.svg b/scripts/system/assets/images/tools/snap.svg new file mode 100755 index 0000000000..c540f307ae --- /dev/null +++ b/scripts/system/assets/images/tools/snap.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 98a31d708c..34f9814d8a 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -527,12 +527,14 @@ function onDomainConnectionRefused(reason) { createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED); } -function onSnapshotTaken(path) { - var imageProperties = { - path: Script.resolvePath("file:///" + path), - aspectRatio: Window.innerWidth / Window.innerHeight +function onSnapshotTaken(path, notify) { + if (notify) { + var imageProperties = { + path: Script.resolvePath("file:///" + path), + aspectRatio: Window.innerWidth / Window.innerHeight + } + createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties); } - createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties); } // handles mouse clicks on buttons diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js new file mode 100644 index 0000000000..87ea3f29ec --- /dev/null +++ b/scripts/system/snapshot.js @@ -0,0 +1,66 @@ +// +// snapshot.js +// +// Created by David Kelly on 1 August 2016 +// Copyright 2016 High Fidelity, Inc +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var SNAPSHOT_DELAY = 500; // 500ms +var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); +var resetOverlays; +var button = toolBar.addButton({ + objectName: "snapshot", + imageURL: Script.resolvePath("assets/images/tools/snap.svg"), + visible: true, + buttonState: 1, + defaultState: 1, + hoverState: 1, + alpha: 0.9, +}); + +function onClicked() { + // update button states + resetOverlays = Menu.isOptionChecked("Overlays"); + Window.snapshotTaken.connect(resetButtons); + + button.writeProperty("buttonState", 0); + button.writeProperty("defaultState", 0); + button.writeProperty("hoverState", 2); + + // hide overlays if they are on + if (resetOverlays) { + Menu.setIsOptionChecked("Overlays", false); + } + + // hide hud + toolBar.writeProperty("visible", false); + + // take snapshot (with no notification) + Script.setTimeout(function () { + Window.takeSnapshot(false); + }, SNAPSHOT_DELAY); +} + +function resetButtons(path, notify) { + // show overlays if they were on + if (resetOverlays) { + Menu.setIsOptionChecked("Overlays", true); + } + // show hud + toolBar.writeProperty("visible", true); + + // update button states + button.writeProperty("buttonState", 1); + button.writeProperty("defaultState", 1); + button.writeProperty("hoverState", 3); + Window.snapshotTaken.disconnect(resetButtons); +} + +button.clicked.connect(onClicked); + +Script.scriptEnding.connect(function () { + toolBar.removeButton("snapshot"); + button.clicked.disconnect(onClicked); +});