diff --git a/examples/windowing/transparentWindow/README.md b/examples/windowing/transparentWindow/README.md new file mode 100644 index 00000000000..0e8c0ebb30a --- /dev/null +++ b/examples/windowing/transparentWindow/README.md @@ -0,0 +1,61 @@ +# About transparentWindow + +![Screenshot of Example, stored as exampleName/screenshot.png (or .gif or .jpg)](transparentWindow.png) + + +### Learning Objectives + +This example demonstrates how to set and get window properties for GLFW Transparency setup and GLFW attribute for mouse passThrough this transparency to context behind the window + +In the code, pay attention to: + +* Use of ````ofSetWindowTransparencyInput()```` to set if mouse input passThrough is enabled or disabled for the window +* Use of ````mouseMoved()```` used to check if within bounds to disable passThrough to region developer elected +* The GLFW events that are disabled (keyboard press/release, mouse press/release) when passThrough is enabled, and the events that work like mouseMove, mouseExit, mouseEnter for the Window +* Use of ````ofSetFullscreen()```` to toggle between fullscreen and windowed modes + +main.cpp setup +``` +ofGLFWWindowSettings settings; + settings.transparent = true; + settings.transparentInput = true; + settings.floating = true; + settings.decorated = false; +``` + +### Expected Behavior + +When launching this application you will see the following: + +* A Transparent window with guides drawn for the edges 1280x720 +* The top quandrant has an area that when you mouseMove to passThrough will be disabled +* The transparent quadrant below that when mouseMove to - passThrough is enabled +* A GLFW ```floating window``` with no titlebar shown even when set +* A ball that is constantly boucing off the interior of the window, affecting the position of the window +* Text at the top that reports the mode of the window, and some commands for toggling logs, ( fullscreen or normal ) etc + +When the key ````g```` is pressed: + +* The Guide Rectangles will not be drawn and you will see the full transparency of the Window. Toggle on or off + +When the key ````h```` is pressed: + +* passThrough will be enabled if in state of disabled. toggling it to disable will not work due to the GLFW events. See note + +When the key ````f```` is pressed: + +* If the window is in normal mode, the window will go in fullscreen mode, hiding the cursor +* If the window is in fullscreen mode, the window will return to the normal mode and show the cursor + +When the key ````l```` is pressed: + +* Verbose Logs / Notice Logs - Toggle + +Instructions for use: + +* Running the application will result in the transparent window slightly moving position as the ball hits an interior wall + + +### Other classes used in this file + +This Example uses no other classes. \ No newline at end of file diff --git a/examples/windowing/transparentWindow/src/main.cpp b/examples/windowing/transparentWindow/src/main.cpp new file mode 100644 index 00000000000..3f4f3864474 --- /dev/null +++ b/examples/windowing/transparentWindow/src/main.cpp @@ -0,0 +1,21 @@ +#include "ofMain.h" +#include "ofApp.h" + +//======================================================================== +int main( ){ + + //Use ofGLFWWindowSettings for more options like multi-monitor fullscreen + ofGLFWWindowSettings settings; + settings.transparent = true; + settings.mousePassThrough = true; + settings.floating = true; + settings.decorated = false; + settings.windowMode = OF_WINDOW; + settings.setGLVersion(4, 1); + settings.setSize(1280, 720); + auto window = ofCreateWindow(settings); + + ofRunApp(window, std::make_shared()); + ofRunMainLoop(); + +} diff --git a/examples/windowing/transparentWindow/src/ofApp.cpp b/examples/windowing/transparentWindow/src/ofApp.cpp new file mode 100644 index 00000000000..811911fa5a6 --- /dev/null +++ b/examples/windowing/transparentWindow/src/ofApp.cpp @@ -0,0 +1,274 @@ +#include "ofApp.h" + +//-------------------------------------------------------------- +void ofApp::setup(){ + if(bLogVerbose){ + ofSetLogLevel(OF_LOG_VERBOSE); + } + int screenW = ofGetScreenWidth(); + int screenH = ofGetScreenHeight(); + ofSetWindowPosition(screenW/2-1280/2, screenH/2-720/2); + fontRenderer.load(OF_TTF_MONO, 14); + lineHeight = fontRenderer.getLineHeight(); + + bFullscreen = 0; + ofBackground(0, 0, 0, 0); + ofSetWindowTitle("GLFW Transparency"); // should not be shown with floating + ofSetFrameRate(60); + uint64_t stepNanos = ofGetFixedStepForFps(60); + ofSetTimeModeFixedRate(stepNanos); + + ballPositionX = 500; + ballPositionY = 500; + ballVelocityX = ofRandom(-5,5); + ballVelocityY = ofRandom(-5,5); + + bgColor = ofColor(240, 248, 255); // Light Blue + textColor = ofColor(0, 51, 102); // Dark Blue + primaryBtnColor = ofColor(51, 153, 255); // Bright Blue + secondaryBtnColor = ofColor(204, 204, 255); // Light Blue + highlightColor = ofColor(255, 204, 204); // Soft Pink +} + +//-------------------------------------------------------------- +void ofApp::update(){ + + if(bFullscreen){ + ofHideCursor(); + } else{ + ofShowCursor(); + } + + ballPositionX += ballVelocityX; + ballPositionY += ballVelocityY; + + int posx = ofGetWindowPositionX(); + int posy = ofGetWindowPositionY(); + + if (ballPositionX < 0){ + ballPositionX = 0; + ballVelocityX *= -1; + if (!bFullscreen){ + ofSetWindowPosition(posx-10, posy); + } + } else if (ballPositionX > ofGetWidth()){ + ballPositionX = ofGetWidth(); + ballVelocityX *= -1; + if (!bFullscreen){ + ofSetWindowPosition(posx+10, posy); + } + } + + if (ballPositionY < 0){ + ballPositionY = 0; + ballVelocityY *= -1; + if (!bFullscreen){ + ofSetWindowPosition(posx, posy-10); + } + } else if (ballPositionY > ofGetHeight()){ + ballPositionY = ofGetHeight(); + ballVelocityY *= -1; + if (!bFullscreen){ + ofSetWindowPosition(posx, posy+10); + } + } + +} + +//-------------------------------------------------------------- +void ofApp::draw(){ + ofSetupScreen(); + int width = ofGetWidth(); + int height = ofGetHeight(); + + // Based on @geluso Draw 100 circles https://github.com/geluso + for(int i = 0; i < 100; i++) { + float speed = 1.74f; + float sinX = sin(i + ofGetElapsedTimef() * speed) * 100; + float sinAlpha = ofMap(sin(i + ofGetElapsedTimef() * speed),-1, 1, 0, 200); + float sinRadius = ofMap(sin(i * ofGetElapsedTimef() * 0.05),-1, 1, 5, 30); + ofSetColor(255,0,0,sinAlpha); + ofDrawCircle(i * 13, sinX + ofGetWidth()*0.5, sinRadius); + } + + if(bDrawGuides) { // Draw edge of transparent window for clarity + int screenWidth = ofGetWidth(); + int screenHeight = ofGetHeight(); + ofSetColor(secondaryBtnColor); + ofDrawRectangle(0, 0, screenWidth, 6); + ofDrawRectangle(0, screenHeight - 6, screenWidth, 6); + ofDrawRectangle(0, 0, 6, screenHeight); + ofDrawRectangle(screenWidth - 6, 0, 6, screenHeight); + // draw top rect for disabling passThrough example + ofSetColor(highlightColor); + ofSetColor(ofColor(80, 80, 80, 196)); + ofDrawRectangle(0, 0, screenWidth, 300); + ofSetColor(0x000000); + ofDrawLine(0, 0, 0, height); + ofDrawLine(width, 0, width, height); + ofDrawLine(0, height, width, height); + } + + ofSetColor(textColor); + centerX = ofGetWidth() / 2; + centerY = 200; + baseY = centerY - 2 * lineSpacing; + if (bAllowPassThrough) { + ofSetColor(primaryBtnColor); + ofDrawRectangle(40, 10 + 1 * lineSpacing, 550, 200); + ofSetColor(textColor); + fontRenderer.drawString("Note: When GLFW mouse passThrough is Enabled", 50, 10 + 2 * lineSpacing); + fontRenderer.drawString("GLFW Events are Disabled for that Window", 50, 10 + 3 * lineSpacing); + fontRenderer.drawString("Disabled: Keyboard Events/Mouse Press/Release", 50, 10 + 4 * lineSpacing); + fontRenderer.drawString("Enabled: mouseMoved/mouseExit/mouseEnter", 50, 10 + 5 * lineSpacing); + } else { + ofSetColor(primaryBtnColor); + ofDrawRectangle(40, 10 + 1 * lineSpacing, 550, 200); + ofSetColor(textColor); + fontRenderer.drawString("Note: When GLFW mouse passThrough is Disabled", 50, 10 + 2 * lineSpacing); + fontRenderer.drawString("GLFW Events are all enabled!", 50, 10 + 3 * lineSpacing); + fontRenderer.drawString("This happens dynamically via GLFW Attribute", 50, 10 + 4 * lineSpacing); + } + + ofSetColor(highlightColor); + if (bAllowPassThrough) { + fontRenderer.drawString("GLFW Mouse passThrough enabled", centerX, 2 * lineSpacing); + if(bForcePassThrough) { + fontRenderer.drawString("Enabled: Force Mouse passThrough - Click the Blue Circle", centerX, 1 * lineSpacing); + } + } else { + fontRenderer.drawString("press h to enable GLFW passThrough or mouse below box", centerX, 2 * lineSpacing); + } + ofSetColor(secondaryBtnColor); + + if (!bFullscreen) { + fontRenderer.drawString("press f to enter fullscreen", centerX, baseY); + fontRenderer.drawString("window is normal / floating", centerX, baseY + lineSpacing); + } else { + fontRenderer.drawString("press f to exit fullscreen", centerX, baseY); + fontRenderer.drawString("window is fullscreen", centerX, baseY + lineSpacing); + } + + if (bDrawGuides) { + fontRenderer.drawString("press g to disable drawing edges", centerX, baseY + 2 * lineSpacing); + } else { + fontRenderer.drawString("press g to enable drawing edges", centerX , baseY + 2 * lineSpacing); + } + + if (bLogVerbose) { + fontRenderer.drawString("press L to disable Verbose Log", centerX , baseY + 3 * lineSpacing); + } else { + fontRenderer.drawString("press L to enable Verbose Log", centerX , baseY + 3 * lineSpacing); + } + + fontRenderer.drawString("window pos ("+ofToString(ofGetWindowPositionX())+", "+ofToString( ofGetWindowPositionY())+")", 20, 30); + + ofSetColor(primaryBtnColor); + ofDrawCircle(ballPositionX, ballPositionY, 15); +} + + +//-------------------------------------------------------------- +void ofApp::keyPressed(int key){ + + if(key == 'f' || key == 'F'){ + bFullscreen = !bFullscreen; + if(!bFullscreen){ + ofSetWindowShape(1280,720); + ofSetFullscreen(false); + int screenW = ofGetScreenWidth(); + int screenH = ofGetScreenHeight(); + ofSetWindowPosition(screenW/2-1280/2, screenH/2-720/2); + } else if(bFullscreen == 1){ + ofSetFullscreen(true); + } + ofLogNotice("ofApp") << "bFullscreen:" << bFullscreen; + + } else if(key == 'g' || key == 'G'){ + bDrawGuides = !bDrawGuides; + ofLogNotice("ofApp") << "bDrawGuides:" << bDrawGuides; + + } else if(key == 'h' || key == 'H'){ + bAllowPassThrough = !bAllowPassThrough; + bForcePassThrough = !bForcePassThrough; + ofSetWindowMousePassThrough(bAllowPassThrough); + ofLogNotice("ofApp") << "ofSetWindowMousePassThrough:" << bAllowPassThrough << " Force:" << bForcePassThrough; + + } else if(key == 'l' || key == 'L' ){ + bLogVerbose = !bLogVerbose; + if(bLogVerbose) + ofSetLogLevel(OF_LOG_VERBOSE); + else + ofSetLogLevel(OF_LOG_NOTICE); + ofLogNotice("ofApp") << "bLogVerbose:" << bLogVerbose; + } +} + +//-------------------------------------------------------------- +void ofApp::keyReleased(int key){ + ofLogVerbose("ofApp") << "keyReleased:" << key; +} + +//-------------------------------------------------------------- +void ofApp::mouseMoved(int x, int y ){ + ofLogVerbose("ofApp") << "mouseMoved: x:" << x << " y:" << y; + + float distance = ofDist(x, y, ballPositionX, ballPositionY); + if(distance <= 15 || y < 300) { + if(bForcePassThrough == true && distance <= 15){ + bForcePassThrough = false; + } + if(bAllowPassThrough && (bForcePassThrough == false || distance <= 15)) { + bAllowPassThrough = false; + ofLogNotice("ofApp") << "ofSetWindowMousePassThrough:" << bAllowPassThrough; + ofSetWindowMousePassThrough(bAllowPassThrough); + } + } else { + if(!bAllowPassThrough) { + bAllowPassThrough = true; + ofLogNotice("ofApp") << "ofSetWindowMousePassThrough:" << bAllowPassThrough; + ofSetWindowMousePassThrough(bAllowPassThrough); + } + } +} + +//-------------------------------------------------------------- +void ofApp::mouseDragged(int x, int y, int button){ + +} + +//-------------------------------------------------------------- +void ofApp::mousePressed(int x, int y, int button){ + ofLogNotice("ofApp") << "mousePressed:" << button << " x:" << x << " y:" << y; +} + +//-------------------------------------------------------------- +void ofApp::mouseReleased(int x, int y, int button){ + ofLogNotice("ofApp") << "mouseReleased:" << button << " x:" << x << " y:" << y; +} + +//-------------------------------------------------------------- +void ofApp::mouseEntered(int x, int y){ + ofLogNotice("ofApp") << "mouseEntered the ofWindow: " << " x:" << x << " y:" << y; +} + +//-------------------------------------------------------------- +void ofApp::mouseExited(int x, int y){ + ofLogNotice("ofApp") << "mouseExited the ofWindow: " << " x:" << x << " y:" << y; +} + +//-------------------------------------------------------------- +void ofApp::windowResized(int w, int h){ + +} + +//-------------------------------------------------------------- +void ofApp::gotMessage(ofMessage msg){ + +} + +//-------------------------------------------------------------- +void ofApp::dragEvent(ofDragInfo dragInfo){ + +} + diff --git a/examples/windowing/transparentWindow/src/ofApp.h b/examples/windowing/transparentWindow/src/ofApp.h new file mode 100644 index 00000000000..c2bbc8d9814 --- /dev/null +++ b/examples/windowing/transparentWindow/src/ofApp.h @@ -0,0 +1,53 @@ +#pragma once + +#include "ofMain.h" + +class ofApp : public ofBaseApp { + + public: + + void setup() override; + void update() override; + void draw() override; + + void keyPressed(int key) override; + void keyReleased(int key) override; + void mouseMoved(int x, int y ) override; + void mouseDragged(int x, int y, int button) override; + void mousePressed(int x, int y, int button) override; + void mouseReleased(int x, int y, int button) override; + void mouseEntered(int x, int y) override; + void mouseExited(int x, int y) override; + void windowResized(int w, int h) override; + void dragEvent(ofDragInfo dragInfo) override; + void gotMessage(ofMessage msg) override; + + ofTrueTypeFont fontRenderer; + + bool bFullscreen; + bool bAllowPassThrough = true; + bool bForcePassThrough = false; + bool bDrawGuides = true; + bool bLogVerbose = false; + + float ballPositionX; + float ballPositionY; + float ballVelocityX; + float ballVelocityY; + + const float margin = 20; + const float lineSpacing = 30; + float lineHeight; + + float centerX; + float centerY; + float baseY; + + ofColor bgColor; + ofColor textColor; + ofColor primaryBtnColor; + ofColor secondaryBtnColor; + ofColor highlightColor; + +}; + diff --git a/examples/windowing/transparentWindow/transparentWindow.png b/examples/windowing/transparentWindow/transparentWindow.png new file mode 100644 index 00000000000..2653620d2b0 Binary files /dev/null and b/examples/windowing/transparentWindow/transparentWindow.png differ diff --git a/libs/openFrameworks/app/ofAppBaseWindow.h b/libs/openFrameworks/app/ofAppBaseWindow.h index 0a0f5cff50d..8823ca773b7 100644 --- a/libs/openFrameworks/app/ofAppBaseWindow.h +++ b/libs/openFrameworks/app/ofAppBaseWindow.h @@ -60,6 +60,7 @@ class ofAppBaseWindow{ virtual void setFullscreen(bool fullscreen){} virtual void toggleFullscreen(){} + virtual void setWindowMousePassthrough(bool allowPassthrough){} virtual void enableSetupScreen(){} virtual void disableSetupScreen(){} diff --git a/libs/openFrameworks/app/ofAppGLFWWindow.cpp b/libs/openFrameworks/app/ofAppGLFWWindow.cpp index d9b41992203..7d58c7b5f0d 100644 --- a/libs/openFrameworks/app/ofAppGLFWWindow.cpp +++ b/libs/openFrameworks/app/ofAppGLFWWindow.cpp @@ -202,6 +202,13 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) { glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #if (GLFW_VERSION_MAJOR >= 3 && GLFW_VERSION_MINOR > 2) || (GLFW_VERSION_MAJOR > 3) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, settings.transparent); +#endif +#if (GLFW_VERSION_MAJOR >= 3 && GLFW_VERSION_MINOR >= 4) + if( settings.mousePassThrough && settings.transparent && settings.decorated) { + ofLogError("ofAppGLFWWindow") << "window is decorated and has transparent input pass through. use floating..."; + } + glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, settings.mousePassThrough); + glfwWindowHint(GLFW_FLOATING, settings.floating); #endif currentRenderer = std::make_shared(this); } else { @@ -1068,6 +1075,15 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) { } } + //------------------------------------------------------------ + void ofAppGLFWWindow::setWindowMousePassThrough(bool allowPassThrough) { + if(settings.mousePassThrough == allowPassThrough) return; + settings.mousePassThrough = allowPassThrough; +#if (GLFW_VERSION_MAJOR >= 3 && GLFW_VERSION_MINOR >= 4) + glfwSetWindowAttrib(getGLFWWindow(), GLFW_MOUSE_PASSTHROUGH, settings.mousePassThrough); +#endif + } + //------------------------------------------------------------ void ofAppGLFWWindow::setOrientation(ofOrientation orientation) { this->orientation = orientation; diff --git a/libs/openFrameworks/app/ofAppGLFWWindow.h b/libs/openFrameworks/app/ofAppGLFWWindow.h index cf2e7ac947a..7767956b285 100644 --- a/libs/openFrameworks/app/ofAppGLFWWindow.h +++ b/libs/openFrameworks/app/ofAppGLFWWindow.h @@ -52,8 +52,10 @@ class ofGLFWWindowSettings : public ofGLWindowSettings { bool visible = true; bool iconified = false; bool decorated = true; + bool floating = false; bool resizable = true; bool transparent = false; + bool mousePassThrough = false; bool maximized = false; int monitor = 0; bool multiMonitorFullScreen = false; @@ -121,6 +123,7 @@ class ofAppGLFWWindow : public ofAppBaseGLWindow { void setFullscreen(bool fullscreen); void toggleFullscreen(); + void setWindowMousePassThrough(bool allowPassThrough); void enableSetupScreen(); void disableSetupScreen(); @@ -198,7 +201,7 @@ class ofAppGLFWWindow : public ofAppBaseGLWindow { static void keyboard_cb(GLFWwindow* windowP_, int key, int scancode, int action, int mods); static void char_cb(GLFWwindow* windowP_, uint32_t key); static void resize_cb(GLFWwindow* windowP_, int w, int h); - static void position_cb(GLFWwindow* windowP_, int x, int y); + static void position_cb(GLFWwindow* windowP_, int x, int y); static void framebuffer_size_cb(GLFWwindow* windowP_, int w, int h); static void exit_cb(GLFWwindow* windowP_); static void scroll_cb(GLFWwindow* windowP_, double x, double y); diff --git a/libs/openFrameworks/app/ofAppRunner.cpp b/libs/openFrameworks/app/ofAppRunner.cpp index b1bc0e8bc75..df84e27ffbb 100644 --- a/libs/openFrameworks/app/ofAppRunner.cpp +++ b/libs/openFrameworks/app/ofAppRunner.cpp @@ -465,6 +465,11 @@ void ofToggleFullscreen(){ mainLoop()->getCurrentWindow()->toggleFullscreen(); } +//-------------------------------------- +void ofSetWindowMousePassThrough(bool allowPassThrough){ + mainLoop()->getCurrentWindow()->setWindowMousePassthrough(allowPassThrough); +} + //-------------------------------------- void ofSetFullscreen(bool fullscreen){ mainLoop()->getCurrentWindow()->setFullscreen(fullscreen); diff --git a/libs/openFrameworks/app/ofAppRunner.h b/libs/openFrameworks/app/ofAppRunner.h index 24ba2ce9dcf..86f4f01e1e8 100644 --- a/libs/openFrameworks/app/ofAppRunner.h +++ b/libs/openFrameworks/app/ofAppRunner.h @@ -102,6 +102,7 @@ void ofEnableSetupScreen(); void ofDisableSetupScreen(); void ofSetFullscreen(bool fullscreen); void ofToggleFullscreen(); +void ofSetWindowMousePassThrough(bool allowPassThrough); //-------------------------- sync void ofSetVerticalSync(bool bSync);