Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions example_xwindow/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
obj
*.xcworkspace
*.xcuserdatad
*~
config.make
13 changes: 13 additions & 0 deletions example_xwindow/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Attempt to load a config.make file.
# If none is found, project defaults in config.project.make will be used.
ifneq ($(wildcard config.make),)
include config.make
endif

# make sure the the OF_ROOT location is defined
ifndef OF_ROOT
OF_ROOT=$(realpath ../../..)
endif

# call the project makefile!
include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk
23 changes: 23 additions & 0 deletions example_xwindow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# X Window Example

This example is very delicate. Here are some issues:

- Cross Platform: Since this uses `ofGstUtils` (gstreamer), it will not work on windows. It wouldn't too hard to try and extend this to use a different OF [video backend](https://github.com/openframeworks/openFrameworks/tree/master/libs/openFrameworks/video) one
- Messing with the X Window too much; Resize and Fullscreen. Its fine if you don't mess with it after it starts.

This example also removes any sources that no longer exist. I bet it'll be frustrating to setup as the whole source is removed if the x window isn't found deleting any mapping progress. Read the logs to see what is going on with the capture status.

There is no audio and no keyboard or mouse event passing. The idea is that those things will come from the X Window itself.

Enjoy! :)

![Demo](xwindow.gif)

Since my desktop uses wayland I tricked mozilla and the example to start in the same session using:

`WAYLAND_DISPLAY= XDG_SESSION_TYPE=x11 DISPLAY=:0 firefox-esr`

&

`WAYLAND_DISPLAY= XDG_SESSION_TYPE=x11 DISPLAY=:0 ./example_xwindow`

3 changes: 3 additions & 0 deletions example_xwindow/addons.make
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ofxGui
ofxPiMapper
ofxXmlSettings
3 changes: 3 additions & 0 deletions example_xwindow/addons.make.norpi
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ofxGui
ofxPiMapper
ofxXmlSettings
Empty file.
1 change: 1 addition & 0 deletions example_xwindow/bin/data/ofxpimapper.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<surfaces />
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
14 changes: 14 additions & 0 deletions example_xwindow/example_xwindow.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"folders": [
{
"path": "."
},
{
"path": "${workspaceRoot}/../../../../libs/openFrameworks"
},
{
"path": "${workspaceRoot}/../../../"
}
],
"settings": {}
}
57 changes: 57 additions & 0 deletions example_xwindow/src/XSource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "XSource.h"

XSource::XSource(Window win, const std::string& windowName) {
targetWindow = win;
name = windowName;
}

void XSource::setup(){
XWindowAttributes windowAttributes;
Display* display = XOpenDisplay(nullptr);
XGetWindowAttributes(display, targetWindow, &windowAttributes);
XCloseDisplay(display);

allocate(windowAttributes.width, windowAttributes.height);

std::string pipeline = "ximagesrc xid=" + std::to_string(targetWindow) + " use-damage=false ! "
"video/x-raw,format=BGRx,framerate=60/1 ! queue";

ofLogNotice() << "Initializing GStreamer pipeline: " << pipeline;

if (!videoUtils.setPipeline(pipeline, OF_PIXELS_BGRX, true, windowAttributes.width, windowAttributes.height)) {
ofLogError() << "GStreamer pipeline failed to initialize!";
return;
}

videoUtils.startPipeline();
}

// Don't do any drawing here
void XSource::update(){
videoUtils.update();
if (videoUtils.isFrameNew()) {
videoPixels = videoUtils.getPixels();

if (videoPixels.isAllocated()) {
// Allocate texture once
if (!videoTexture.isAllocated()) {
videoTexture.allocate(videoPixels.getWidth(), videoPixels.getHeight(), GL_RGBA);
}
// Upload new frame to texture
videoTexture.loadData(videoPixels);
}
}

}


void XSource::draw(){
ofClear(0);

if (videoTexture.isAllocated()) {
videoTexture.draw(0, 0, fbo->getWidth(), fbo->getHeight());
} else {
ofLogError() << "Video texture is not allocated!";
}

}
26 changes: 26 additions & 0 deletions example_xwindow/src/XSource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include "ofMain.h"
#include "FboSource.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "ofGstUtils.h"

class XSource : public ofx::piMapper::FboSource {
public:

XSource(Window win, const std::string& windowName);
~XSource();

void setup();
void update();
void draw();

private:

Window targetWindow;
ofGstVideoUtils videoUtils;
ofTexture videoTexture;
ofPixels videoPixels;

};
24 changes: 24 additions & 0 deletions example_xwindow/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "ofApp.h"
#include <string>
#include <vector>

int main(int argc, char * argv[]){
bool fullscreen = false;

std::vector<std::string> arguments = std::vector<std::string>(argv, argv + argc);
for(size_t i = 0; i < arguments.size(); ++i){
if(arguments.at(i) == "-f"){
fullscreen = true;
break;
}
}

if(fullscreen){
ofSetupOpenGL(1920, 1080, OF_FULLSCREEN);
}else{
ofSetupOpenGL(800, 450, OF_WINDOW);
}


ofRunApp(new ofApp());
}
136 changes: 136 additions & 0 deletions example_xwindow/src/ofApp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include "ofApp.h"

void ofApp::setup(){
ofBackground(0);
ofSetFrameRate(60);

//ofSetFrameRate(30);
ofSetVerticalSync(false);

getXWindowNames();

piMapper.setup();

ofSetFullscreen(false);
ofSetFullscreen(true);

ofSetEscapeQuitsApp(true);

}

void ofApp::update(){
piMapper.update();
}

void ofApp::draw(){
piMapper.draw();
}


void ofApp::keyPressed(int key){
piMapper.keyPressed(key);
}

void ofApp::keyReleased(int key){
piMapper.keyReleased(key);
}

void ofApp::mousePressed(int x, int y, int button){
piMapper.mousePressed(x, y, button);
}

void ofApp::mouseReleased(int x, int y, int button){
piMapper.mouseReleased(x, y, button);
}

void ofApp::mouseDragged(int x, int y, int button){
piMapper.mouseDragged(x, y, button);
}

void ofApp::getXWindowNames(){
Display *display = XOpenDisplay(nullptr);
if (!display) {
ofLogError("XSource App") << "Cannot open X display";
return;
}

Atom netWmName = XInternAtom(display, "_OB_APP_TITLE", false);
Atom property = XInternAtom(display, "_NET_CLIENT_LIST", true);
Atom actualType;
int actualFormat;
unsigned long numItems, bytesAfter;
unsigned char *data = nullptr;
vector<string> windowNameList;
std::unordered_map<string, int> nameCounts;

if (XGetWindowProperty(display, DefaultRootWindow(display), property, 0, 1024, false,
XA_WINDOW, &actualType, &actualFormat, &numItems, &bytesAfter, &data) == Success && data) {
Window *windows = (Window *)data;

ofLog() << "============================";
ofLog() << " X11 WINDOWS AVAILABLE";
ofLog() << "============================";

for (unsigned long i = 0; i < numItems; i++) {
// Get window name
char *name = nullptr;
XTextProperty prop;
bool usedXFetchName = false;

if (XGetTextProperty(display, windows[i], &prop, netWmName) && prop.nitems) {
name = (char *)prop.value;
}
if (!name) {
XFetchName(display, windows[i], &name);
usedXFetchName = true;
}

int count = name ? nameCounts[std::string(name)]++ : 0;
string uniqueName = name ? std::string(name) + " " + std::to_string(count) : "Unnamed Window " + std::to_string(windows[i]);
ofLog() << "Window ID: " << windows[i] << " | Name: " << uniqueName;
piMapper.registerFboSource(new XSource(windows[i], uniqueName));
windowNameList.push_back(uniqueName);

if (usedXFetchName && name) {
XFree(name); // Free only if XFetchName() was used
} else if (prop.nitems) {
XFree(prop.value); // Free XGetTextProperty() result
}

}
XFree(data);
} else {
ofLogError() << "Failed to get window list from X11.";
}

// Cleanup
XCloseDisplay(display);
filterInvalidSurfaces(windowNameList);
}

void ofApp::filterInvalidSurfaces(const vector<string>& windowNameList) {
ofxXmlSettings xml;
if (!xml.load(PIMAPPER_SETTINGS_FILE)) {
ofLogError() << "Failed to load " << PIMAPPER_SETTINGS_FILE;
return;
}

xml.pushTag("surfaces");
int numSurfaces = xml.getNumTags("surface");

for (int i = numSurfaces - 1; i >= 0; --i) { // Iterate backwards to allow deletion
xml.pushTag("surface", i);
std::string sourceName = xml.getValue("source:source-name", "");

bool removeSurface = std::find(windowNameList.begin(), windowNameList.end(), sourceName) == windowNameList.end();

xml.popTag(); // Exit "surface" tag before removing

if (removeSurface) {
xml.removeTag("surface", i);
}
}

xml.popTag(); // Exit "surfaces" tag
xml.save(PIMAPPER_SETTINGS_FILE);
}
29 changes: 29 additions & 0 deletions example_xwindow/src/ofApp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "ofMain.h"
#include "ofxPiMapper.h"
#include "XSource.h"
#include "VideoSource.h"
#include <X11/Xatom.h>
#include "SettingsLoader.h"


class ofApp : public ofBaseApp {
public:
void setup();
void update();
void draw();

void keyPressed(int key);
void keyReleased(int key);

void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void mouseDragged(int x, int y, int button);

ofxPiMapper piMapper;

private:
void getXWindowNames();
void filterInvalidSurfaces(const vector<string>& windowNameList);
};
Binary file added example_xwindow/xwindow.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/MediaServer/MediaServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ std::vector<std::string> MediaServer::getImageNames(){

std::vector<std::string> MediaServer::getFboSourceNames(){
std::vector<std::string> fboSourceNames;
for(int i = 0; i < fboSources.size(); i++){
for(size_t i = 0; i < fboSources.size(); i++){
fboSourceNames.push_back(fboSources[i]->getName());
}
return fboSourceNames;
Expand Down
3 changes: 1 addition & 2 deletions src/Surfaces/GridWarpSurface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void GridWarpSurface::draw(){
}

void GridWarpSurface::moveBy(Vec3 v){
for(int i = 0; i < mesh.getVertices().size(); i++){
for(size_t i = 0; i < mesh.getVertices().size(); i++){
mesh.getVertices()[i] += v.toOf();
}

Expand Down Expand Up @@ -131,7 +131,6 @@ ofPolyline GridWarpSurface::getTextureHitArea(){
Vec2 textureSize = Vec2(source->getTexture()->getWidth(), source->getTexture()->getHeight());

int vertsPerRow = _gridCols + 1;
int vertsPerCol = _gridRows + 1;

int a = 0;
int b = _gridCols;
Expand Down
Loading