Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#include "ofAppEGLWindow.h"
#include "ofGraphics.h" // used in runAppViaInfiniteLoop()
#include "ofAppRunner.h"
#include "ofUtils.h"
#include "ofFileUtils.h"
#include "ofGLProgrammableRenderer.h"
#include "ofGLRenderer.h"
#include "ofVectorMath.h"
#include <assert.h>
// x11
#include <X11/Xutil.h>
#include <EGL/egl.h>
// include includes for both native and X11 possibilities
#include <libudev.h>
#include <stdbool.h>
#include <stdio.h> // sprintf
#include <stdlib.h> // malloc
#include <math.h>
#include <fcntl.h> // open fcntl
#include <unistd.h> // read close
#include <linux/joystick.h>
#include "linux/kd.h" // keyboard stuff...
#include "termios.h"
#include "sys/ioctl.h"
#include <string.h> // strlen
using namespace std;
#if !defined(USING_DRM)
// native events
struct udev* udev;
struct udev_monitor* mon;
static int udev_fd = -1;
typedef map<string, int> device;
static device inputDevices;
// minimal map
const int lowercase_map[] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h',
'j', 'k', 'l', ';', '\'', '\n', 0, '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r'
};
// minimal keyboard map
const int uppercase_map[] = {
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
'_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H',
'J', 'K', 'L', ':', '"', '\n', 0, '\\', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r'
};
// keep track of a few things ...
typedef struct {
bool shiftPressed;
bool capsLocked;
} KeyboardState;
static KeyboardState kb;
static struct termios tc;
static struct termios ots;
typedef struct {
int mouseButtonState;
} MouseState;
// TODO, make this match the upcoming additions to ofWindow
#define MOUSE_BUTTON_LEFT_MASK 1
#define MOUSE_BUTTON_MIDDLE_MASK 1 << 1
#define MOUSE_BUTTON_RIGHT_MASK 2 << 1
static MouseState mb;
ofAppEGLWindow* ofAppEGLWindow::instance = NULL;
static int string_ends_with(const char *str, const char *suffix) {
if (!str || !suffix)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
static int string_begins_with(const char *str, const char *prefix) {
if (!str || !prefix)
return 0;
size_t lenstr = strlen(str);
size_t lenprefix = strlen(prefix);
if (lenprefix > lenstr)
return 0;
return strncmp(str, prefix, lenprefix) == 0;
}
// native
#define MOUSE_CURSOR_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \
{ unsigned int __bpp; unsigned char *__ip; const unsigned char *__il, *__rd; \
__bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \
__rd = (rle_data); \
while (__ip < __il) { unsigned int __l = *(__rd++); \
if (__l & 128) { __l = __l - 128; \
do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \
} else { __l *= 4; memcpy (__ip, __rd, __l); \
__ip += __l; __rd += __l; } } \
} while (0)
static const struct {
unsigned int width;
unsigned int height;
unsigned int bpp; /* 2:RGB16, 3:RGB, 4:RGBA */
unsigned char rle_pixel_data[382 + 1];
} mouse_cursor_data = {
12, 19, 4,
"\1\0\0\0\377\213\377\377\377\0\202\0\0\0\377\212\377\377\377\0\3\0\0\0\377"
"\377\377\377\377\0\0\0\377\211\377\377\377\0\1\0\0\0\377\202\377\377\377"
"\377\1\0\0\0\377\210\377\377\377\0\1\0\0\0\377\203\377\377\377\377\1\0\0"
"\0\377\207\377\377\377\0\1\0\0\0\377\204\377\377\377\377\1\0\0\0\377\206"
"\377\377\377\0\1\0\0\0\377\205\377\377\377\377\1\0\0\0\377\205\377\377\377"
"\0\1\0\0\0\377\206\377\377\377\377\1\0\0\0\377\204\377\377\377\0\1\0\0\0"
"\377\207\377\377\377\377\1\0\0\0\377\203\377\377\377\0\1\0\0\0\377\210\377"
"\377\377\377\1\0\0\0\377\202\377\377\377\0\1\0\0\0\377\211\377\377\377\377"
"\3\0\0\0\377\377\377\377\0\0\0\0\377\212\377\377\377\377\202\0\0\0\377\206"
"\377\377\377\377\206\0\0\0\377\203\377\377\377\377\1\0\0\0\377\202\377\377"
"\377\377\1\0\0\0\377\204\377\377\377\0\1\0\0\0\377\202\377\377\377\377\3"
"\0\0\0\377\377\377\377\0\0\0\0\377\202\377\377\377\377\1\0\0\0\377\203\377"
"\377\377\0\3\0\0\0\377\377\377\377\377\0\0\0\377\202\377\377\377\0\1\0\0"
"\0\377\202\377\377\377\377\1\0\0\0\377\203\377\377\377\0\202\0\0\0\377\204"
"\377\377\377\0\1\0\0\0\377\202\377\377\377\377\1\0\0\0\377\210\377\377\377"
"\0\1\0\0\0\377\202\377\377\377\377\1\0\0\0\377\211\377\377\377\0\202\0\0"
"\0\377\203\377\377\377\0",
};
// from http://cantuna.googlecode.com/svn-history/r16/trunk/src/screen.cpp
#define CASE_STR(x,y) case x: str = y; break
static const char* eglErrorString(EGLint err) {
string str;
switch (err) {
CASE_STR(EGL_SUCCESS, "no error");
CASE_STR(EGL_NOT_INITIALIZED, "EGL not, or could not be, initialized");
CASE_STR(EGL_BAD_ACCESS, "access violation");
CASE_STR(EGL_BAD_ALLOC, "could not allocate resources");
CASE_STR(EGL_BAD_ATTRIBUTE, "invalid attribute");
CASE_STR(EGL_BAD_CONTEXT, "invalid context specified");
CASE_STR(EGL_BAD_CONFIG, "invald frame buffer configuration specified");
CASE_STR(EGL_BAD_CURRENT_SURFACE, "current window, pbuffer or pixmap surface is no longer valid");
CASE_STR(EGL_BAD_DISPLAY, "invalid display specified");
CASE_STR(EGL_BAD_SURFACE, "invalid surface specified");
CASE_STR(EGL_BAD_MATCH, "bad argument match");
CASE_STR(EGL_BAD_PARAMETER, "invalid paramater");
CASE_STR(EGL_BAD_NATIVE_PIXMAP, "invalid NativePixmap");
CASE_STR(EGL_BAD_NATIVE_WINDOW, "invalid NativeWindow");
CASE_STR(EGL_CONTEXT_LOST, "APM event caused context loss");
default: str = "unknown error " + err; break;
}
return str.c_str();
}
// X11 events
#include <X11/XKBlib.h>
#ifdef TARGET_RASPBERRY_PI
// TODO: remove these when they enter system headers
// From : https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_dispmanx.h
#ifndef ELEMENT_CHANGE_LAYER
#define ELEMENT_CHANGE_LAYER (1<<0)
#endif
#ifndef ELEMENT_CHANGE_OPACITY
#define ELEMENT_CHANGE_OPACITY (1<<1)
#endif
#ifndef ELEMENT_CHANGE_DEST_RECT
#define ELEMENT_CHANGE_DEST_RECT (1<<2)
#endif
#ifndef ELEMENT_CHANGE_SRC_RECT
#define ELEMENT_CHANGE_SRC_RECT (1<<3)
#endif
#ifndef ELEMENT_CHANGE_MASK_RESOURCE
#define ELEMENT_CHANGE_MASK_RESOURCE (1<<4)
#endif
#ifndef ELEMENT_CHANGE_TRANSFORM
#define ELEMENT_CHANGE_TRANSFORM (1<<5)
#endif
#endif
//-------------------------------------------------------------------------------------
ofAppEGLWindowSettings::ofAppEGLWindowSettings()
:ofGLESWindowSettings(){
eglWindowPreference = OF_APP_WINDOW_AUTO;
eglWindowOpacity = 255;
// these are usually set as default, but set them here just to be sure
frameBufferAttributes[EGL_RED_SIZE] = 8; // 8 bits for red
frameBufferAttributes[EGL_GREEN_SIZE] = 8; // 8 bits for green
frameBufferAttributes[EGL_BLUE_SIZE] = 8; // 8 bits for blue
frameBufferAttributes[EGL_ALPHA_SIZE] = 8; // 8 bits for alpha
frameBufferAttributes[EGL_LUMINANCE_SIZE] = EGL_DONT_CARE; // 8 bits for alpha
frameBufferAttributes[EGL_DEPTH_SIZE] = 24; // 24 bits for depth
frameBufferAttributes[EGL_STENCIL_SIZE] = 8; // 8 bits for stencil
frameBufferAttributes[EGL_SAMPLES] = 1;
initialClearColor = ofColor(0.15 * 255, 0.15 * 255, 0.15 * 255, 255);
screenNum = 0; /* 0 = LCD on the raspberry pi */
layer = 0;
}
ofAppEGLWindowSettings::ofAppEGLWindowSettings(const ofGLESWindowSettings & settings)
:ofGLESWindowSettings(settings){
eglWindowPreference = OF_APP_WINDOW_AUTO;
eglWindowOpacity = 255;
// these are usually set as default, but set them here just to be sure
frameBufferAttributes[EGL_RED_SIZE] = 8; // 8 bits for red
frameBufferAttributes[EGL_GREEN_SIZE] = 8; // 8 bits for green
frameBufferAttributes[EGL_BLUE_SIZE] = 8; // 8 bits for blue
frameBufferAttributes[EGL_ALPHA_SIZE] = 8; // 8 bits for alpha
frameBufferAttributes[EGL_LUMINANCE_SIZE] = EGL_DONT_CARE; // 8 bits for alpha
frameBufferAttributes[EGL_DEPTH_SIZE] = 24; // 24 bits for depth
frameBufferAttributes[EGL_STENCIL_SIZE] = 8; // 8 bits for stencil
frameBufferAttributes[EGL_SAMPLES] = 1;
initialClearColor = ofColor(0.15 * 255, 0.15 * 255, 0.15 * 255, 255);
screenNum = 0; /* 0 = LCD on the raspberry pi */
layer = 0;
}
//------------------------------------------------------------
ofAppEGLWindow::ofAppEGLWindow() {
keyboardDetected = false;
mouseDetected = false;
threadTimeout = ofThread::INFINITE_JOIN_TIMEOUT;
bNewScreenMode = false;
buttonInUse = -1;
bEnableSetupScreen = false;
bShowCursor = true;
nFramesSinceWindowResized = 0;
mouseScaleX = 2.0f;
mouseScaleY = 2.0f;
isUsingX11 = false;
isWindowInited = false;
isSurfaceInited = false;
x11Display = NULL;
x11Screen = NULL;
x11ScreenNum = 0l;
glesVersion = 1;
if(instance!=NULL){
ofLogError("ofAppEGLWindow") << "trying to create more than one instance";
}
instance = this;
}
//------------------------------------------------------------
ofAppEGLWindow::~ofAppEGLWindow() {
close();
}
//------------------------------------------------------------
EGLDisplay ofAppEGLWindow::getEglDisplay() const {
return eglDisplay;
}
//------------------------------------------------------------
EGLSurface ofAppEGLWindow::getEglSurface() const {
return eglSurface;
}
//------------------------------------------------------------
EGLContext ofAppEGLWindow::getEglContext() const {
return eglContext;
}
#ifndef TARGET_RASPBERRY_PI_LEGACY
//------------------------------------------------------------
Display* ofAppEGLWindow::getX11Display(){
return x11Display;
}
//------------------------------------------------------------
Window ofAppEGLWindow::getX11Window(){
return x11Window;
}
#endif
//------------------------------------------------------------
EGLConfig ofAppEGLWindow::getEglConfig() const {
return eglConfig;
}
//------------------------------------------------------------
EGLint ofAppEGLWindow::getEglVersionMajor () const {
return eglVersionMajor;
}
//------------------------------------------------------------
EGLint ofAppEGLWindow::getEglVersionMinor() const {
return eglVersionMinor;
}
//------------------------------------------------------------
void ofAppEGLWindow::initNative() {
#ifdef TARGET_RASPBERRY_PI_LEGACY
initRPiNative();
#endif
}
//------------------------------------------------------------
void ofAppEGLWindow::exitNative() {
#ifdef TARGET_RASPBERRY_PI_LEGACY
exitRPiNative();
#endif
}
//------------------------------------------------------------
EGLNativeWindowType ofAppEGLWindow::getNativeWindow() {
if(!isWindowInited) {
ofLogWarning("ofAppEGLWindow") << "getNativeDisplay(): window not initialized, returning NULL";
return NULL;
}
if(isUsingX11) {
return (EGLNativeWindowType)x11Window;
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
return (EGLNativeWindowType)&dispman_native_window;
#else
ofLogNotice("ofAppEGLWindow") << "getNativeWindow(): no native window type for this system, perhaps try X11?";
return NULL;
#endif
}
}
//------------------------------------------------------------
EGLNativeDisplayType ofAppEGLWindow::getNativeDisplay() {
if(!isWindowInited) {
ofLogWarning("ofAppEGLWindow") << "getNativeDisplay(): window not initialized, returning NULL";
return 0;
}
if(isUsingX11) {
return (EGLNativeDisplayType)x11Display;
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
return (EGLNativeDisplayType)NULL;
#else
ofLogNotice("ofAppEGLWindow") << "getNativeDisplay(): no native window type for this system, perhaps try X11?";
return 0;
#endif
}
}
//------------------------------------------------------------
void ofAppEGLWindow::setup(const ofGLESWindowSettings & settings){
const Settings * glSettings = dynamic_cast<const Settings*>(&settings);
if(glSettings){
setup(*glSettings);
}else{
setup(Settings(settings));
}
}
//------------------------------------------------------------
void ofAppEGLWindow::setup(const ofAppEGLWindowSettings & _settings) {
settings = _settings;
windowMode = OF_WINDOW;
bNewScreenMode = true;
nFramesSinceWindowResized = 0;
buttonInUse = 0;
bEnableSetupScreen = true;
eglDisplayString = "";
orientation = OF_ORIENTATION_DEFAULT;
//TODO: 2.0f is an arbitrary factor that makes mouse speed ok at 1024x768,
// to be totally correct we might need to take into account screen size
// and add acceleration
mouseScaleX = 2.0f;
mouseScaleY = 2.0f;
isUsingX11 = false;
isWindowInited = false;
isSurfaceInited = false;
eglDisplay = NULL;
eglSurface = NULL;
eglContext = NULL;
eglConfig = NULL;
eglVersionMajor = -1;
eglVersionMinor = -1;
glesVersion = 1;
// X11 check
// char * pDisplay;
// pDisplay = getenv ("DISPLAY");
// bool bIsX11Available = (pDisplay != NULL);
bool bIsX11Available = getenv("DISPLAY") != NULL;
if(settings.eglWindowPreference == OF_APP_WINDOW_AUTO) {
if(bIsX11Available) {
isUsingX11 = true;
} else {
isUsingX11 = false;
}
} else if(settings.eglWindowPreference == OF_APP_WINDOW_NATIVE) {
isUsingX11 = false;
} else if(settings.eglWindowPreference == OF_APP_WINDOW_X11) {
isUsingX11 = true;
if(!bIsX11Available) {
isUsingX11 = false;
ofLogError("ofAppEGLWindow") << "init(): X11 window requested, but X11 is not available";
}
}
////////////////
// TODO remove the following ifdef once x11 is accelerated on RPI
#ifdef TARGET_RASPBERRY_PI_LEGACY
if(isUsingX11) {
isUsingX11 = false;
ofLogWarning("ofAppEGLWindow") << "init(): X11 not availble on RPI yet, using a native window instead";
}
#endif
////////////////
initNative();
glesVersion = settings.glesVersion;
// we set this here, and if we need to make a fullscreen
// app, we do it during the first loop.
windowMode = settings.windowMode;
bShowCursor = true;
nonFullscreenWindowRect.set(0,0,settings.getWidth(),settings.getHeight());
nonFullscreenWindowRect.standardize();
ofRectangle startRect = nonFullscreenWindowRect;
bNewScreenMode = false;
if(windowMode == OF_GAME_MODE) {
ofLogWarning("ofAppEGLWindow") << "setupOpenGL(): OF_GAME_MODE not supported, using OF_WINDOW";
startRect = nonFullscreenWindowRect;
} else if(windowMode == OF_FULLSCREEN) {
startRect = getScreenRect();
}
isWindowInited = createWindow(startRect);
isSurfaceInited = createSurface();
if(!isWindowInited) {
ofLogError("ofAppEGLWindow") << "setupOpenGL(): screen creation failed, window not inited";
}
setupPeripherals();
nFramesSinceWindowResized = 0;
if(settings.glesVersion>1){
currentRenderer = make_shared<ofGLProgrammableRenderer>(this);
}else{
currentRenderer = make_shared<ofGLRenderer>(this);
}
makeCurrent();
if(currentRenderer->getType()==ofGLProgrammableRenderer::TYPE){
static_cast<ofGLProgrammableRenderer*>(currentRenderer.get())->setup(settings.glesVersion,0);
}else{
static_cast<ofGLRenderer*>(currentRenderer.get())->setup();
}
}
//------------------------------------------------------------
void ofAppEGLWindow::setupPeripherals() {
if(!isUsingX11) {
// roll our own cursor!
mouseCursor.allocate(mouse_cursor_data.width,mouse_cursor_data.height,OF_IMAGE_COLOR_ALPHA);
MOUSE_CURSOR_RUN_LENGTH_DECODE(mouseCursor.getPixels().getData(),mouse_cursor_data.rle_pixel_data,mouse_cursor_data.width*mouse_cursor_data.height,mouse_cursor_data.bpp);
mouseCursor.update();
ofLogNotice("ofAppEGLWindow") << "setupPeripherals(): peripheral setup complete";
setupNativeEvents();
ofLogNotice("ofAppEGLWindow") << "setupPeripherals(): native event setup complete";
} else {
ofLogError("ofAppEGLWindow") << "setupPeripherals(): peripherals not supported on X11";
}
}
//------------------------------------------------------------
bool ofAppEGLWindow::createSurface() {
EGLNativeWindowType nativeWindow = getNativeWindow();
EGLNativeDisplayType display = getNativeDisplay();
ofLogNotice("ofAppEGLWindow") << "createSurface(): setting up EGL Display";
// get an EGL eglDisplay connection
isSurfaceInited = false;
EGLint result;
if(display==0){
eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
}else{
eglDisplay = eglGetDisplay(display);
}
if(eglDisplay == EGL_NO_DISPLAY) {
ofLogNotice("ofAppEGLWindow") << "createSurface(): eglGetDisplay returned: " << eglDisplay;
return false;
}else{
ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL Display correctly set " << eglDisplay;
}
// initialize the EGL eglDisplay connection
result = eglInitialize(eglDisplay,
&eglVersionMajor,
&eglVersionMinor);
if(result == EGL_BAD_DISPLAY) {
// eglDisplay is not an EGL connection
ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_BAD_DISPLAY";
return false;
} else if(result == EGL_NOT_INITIALIZED) {
// eglDisplay cannot be intitialized
ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_NOT_INITIALIZED";
return false;
} else if(result == EGL_FALSE) {
// eglinitialize was not initialiezd
ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_FALSE";
return false;
} else {
// result == EGL_TRUE
// success!
}
EGLint glesVersion;
int glesVersionForContext;
if(ofGetCurrentRenderer()) {
ofLogNotice("ofAppEGLWindow") << "createSurface(): current renderer type: " << ofGetCurrentRenderer()->getType();
} else {
ofLogNotice("ofAppEGLWindow") << "createSurface(): no current renderer selected";
}
if(this->glesVersion==2){
glesVersion = EGL_OPENGL_ES2_BIT;
glesVersionForContext = 2;
ofLogNotice("ofAppEGLWindow") << "createSurface(): GLES2 renderer detected";
}else{
glesVersion = EGL_OPENGL_ES_BIT;
glesVersionForContext = 1;
ofLogNotice("ofAppEGLWindow") << "createSurface(): default renderer detected";
}
ofEGLAttributeListIterator iter, iterEnd;
int i;
// each attribute has 2 values, and we need one extra for the EGL_NONE terminator
EGLint attribute_list_framebuffer_config[settings.frameBufferAttributes.size() * 2 + 3];
iter = settings.frameBufferAttributes.begin();
iterEnd = settings.frameBufferAttributes.end();
i = 0;
for(; iter != iterEnd; iter++) {
attribute_list_framebuffer_config[i++] = iter->first;
attribute_list_framebuffer_config[i++] = iter->second;
}
attribute_list_framebuffer_config[i++] = EGL_RENDERABLE_TYPE;
attribute_list_framebuffer_config[i++] = glesVersion; //openGL ES version
attribute_list_framebuffer_config[i] = EGL_NONE; // add the terminator
EGLint num_configs;
// get an appropriate EGL frame buffer configuration
// http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/eglChooseConfig.html
result = eglChooseConfig(eglDisplay,
attribute_list_framebuffer_config,
&eglConfig,
1, // we only want the first one. if we want more,
// we need to pass in an array.
// we are optimistic and don't give it more chances
// to find a good configuration
&num_configs);
if(result == EGL_FALSE) {
EGLint error = eglGetError();
ofLogError("ofAppEGLWindow") << "createSurface(): error finding valid configuration based on settings: " << eglErrorString(error);
return false;
}
if(num_configs <= 0 || eglConfig == NULL) {
ofLogError("ofAppEGLWindow") << "createSurface(): no matching configs were found, num_configs: " << num_configs;
return false;
}
// each attribute has 2 values, and we need one extra for the EGL_NONE terminator
EGLint attribute_list_window_surface[settings.windowSurfaceAttributes.size() * 2 + 1];
iter = settings.windowSurfaceAttributes.begin();
iterEnd = settings.windowSurfaceAttributes.end();
i = 0;
for(; iter != iterEnd; iter++) {
attribute_list_window_surface[i++] = iter->first;
attribute_list_window_surface[i++] = iter->second;
}
attribute_list_window_surface[i] = EGL_NONE; // add the terminator
// create a surface
eglSurface = eglCreateWindowSurface( eglDisplay, // our display handle
eglConfig, // our first config
nativeWindow, // our native window
attribute_list_window_surface); // surface attribute list
if(eglSurface == EGL_NO_SURFACE) {
EGLint error = eglGetError();
switch(error) {
case EGL_BAD_MATCH:
ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_MATCH " << eglErrorString(error);
ofLogError("ofAppEGLWindow") << "createSurface(): check window and EGLConfig attributes to determine compatibility, ";
ofLogError("ofAppEGLWindow") << "createSurface(): or verify that the EGLConfig supports rendering to a window";
break;
case EGL_BAD_CONFIG:
ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_CONFIG " << eglErrorString(error);
ofLogError("ofAppEGLWindow") << "createSurface(): verify that provided EGLConfig is valid";
break;
case EGL_BAD_NATIVE_WINDOW:
ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_NATIVE_WINDOW " << eglErrorString(error);
ofLogError("ofAppEGLWindow") << "createSurface(): verify that provided EGLNativeWindow is valid";
break;
case EGL_BAD_ALLOC:
ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_ALLOC " << eglErrorString(error);
ofLogError("ofAppEGLWindow") << "createSurface(): not enough resources available";
break;
default:
ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: << " << error << eglErrorString(error);
}
return false;
}else{
ofLogNotice("ofAppEGLWindow") << "createSurface(): surface created correctly";
}
// get an appropriate EGL frame buffer configuration
result = eglBindAPI(EGL_OPENGL_ES_API);
if(result == EGL_FALSE) {
ofLogError("ofAppEGLWindow") << "createSurface(): error binding API: " << eglErrorString(eglGetError());
return false;
}else{
ofLogNotice("ofAppEGLWindow") << "createSurface(): API bound correctly";
}
// create an EGL rendering eglContext
EGLint attribute_list_surface_context[] = {
EGL_CONTEXT_CLIENT_VERSION, glesVersionForContext,
EGL_NONE
};
eglContext = eglCreateContext(eglDisplay,
eglConfig,
EGL_NO_CONTEXT,
attribute_list_surface_context);
if(eglContext == EGL_NO_CONTEXT) {
EGLint error = eglGetError();
if(error == EGL_BAD_CONFIG) {
ofLogError("ofAppEGLWindow") << "createSurface(): error creating context: EGL_BAD_CONFIG " << eglErrorString(error);
return false;
} else {
ofLogError("ofAppEGLWindow") << "createSurface(): error creating context: " << error << " " << eglErrorString(error);
return false;
}
}
// connect the eglContext to the eglSurface
result = eglMakeCurrent(eglDisplay,
eglSurface, // draw surface
eglSurface, // read surface
eglContext);
if(eglContext == nullptr) {
EGLint error = eglGetError();
ofLogError("ofAppEGLWindow") << "createSurface(): couldn't making current surface: " << eglErrorString(error);
return false;
}
// Set background color and clear buffers
glClearColor(settings.initialClearColor.r / 255.0f,
settings.initialClearColor.g / 255.0f,
settings.initialClearColor.b / 255.0f,
settings.initialClearColor.a / 255.0f);
glClear( GL_COLOR_BUFFER_BIT );
glClear( GL_DEPTH_BUFFER_BIT );
ofLogNotice("ofAppEGLWindow") << "createSurface(): -----EGL-----";
ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION_MAJOR = " << eglVersionMajor;
ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION_MINOR = " << eglVersionMinor;
ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_CLIENT_APIS = " << eglQueryString(eglDisplay, EGL_CLIENT_APIS);
ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VENDOR = " << eglQueryString(eglDisplay, EGL_VENDOR);
ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION = " << eglQueryString(eglDisplay, EGL_VERSION);
ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_EXTENSIONS = " << eglQueryString(eglDisplay, EGL_EXTENSIONS);
ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_RENDERER = " << glGetString(GL_RENDERER);
ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_VERSION = " << glGetString(GL_VERSION);
ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_VENDOR = " << glGetString(GL_VENDOR);
ofLogNotice("ofAppEGLWindow") << "createSurface(): -------------";
isSurfaceInited = true;
return true;
}
//------------------------------------------------------------
bool ofAppEGLWindow::destroySurface() {
if(isSurfaceInited) {
ofLogNotice("ofAppEGLWindow") << "destroySurface(): destroying EGL surface";
eglSwapBuffers(eglDisplay, eglSurface);
eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(eglDisplay, eglSurface);
eglDestroyContext(eglDisplay, eglContext);
eglTerminate(eglDisplay);
isSurfaceInited = false;
eglDisplay = NULL;
eglSurface = NULL;
eglContext = NULL;
eglConfig = NULL;
eglVersionMinor = -1;
eglVersionMinor = -1;
return true;
} else {
ofLogError("ofAppEGLWindow") << "destroySurface(): attempted to destroy uninitialized window";
return false;
}
}
//------------------------------------------------------------
bool ofAppEGLWindow::destroyWindow() {
if(isWindowInited) {
if(isUsingX11) {
// TODO: double check
XDestroyWindow(x11Display,x11Window); // or XCloseWindow?
XFree(x11Screen);
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
dispman_update = vc_dispmanx_update_start(0);
if (dispman_element != DISPMANX_NO_HANDLE) {
vc_dispmanx_element_remove(dispman_update, dispman_element);
dispman_element = DISPMANX_NO_HANDLE;
}
vc_dispmanx_update_submit_sync(dispman_update);
if (dispman_display != DISPMANX_NO_HANDLE) {
vc_dispmanx_display_close(dispman_display);
dispman_display = DISPMANX_NO_HANDLE;
}
#else
ofLogNotice("ofAppEGLWindow") << "destroyWindow(): no native window type for this system, perhaps try X11?";
#endif
}
} else {
ofLogNotice("ofAppEGLWindow") << "destroyWindow(): destroying (uninited) native window (not implemented yet)";
}
return true;
}
void ofAppEGLWindow::close(){
if(!isUsingX11) {
destroyNativeEvents();
}
// we got a terminate ... so clean up.
destroySurface();
destroyWindow();
exitNative();
events().notifyExit();
events().disable();
}
//------------------------------------------------------------
void ofAppEGLWindow::makeCurrent(){
eglMakeCurrent(eglDisplay,
eglSurface, // draw surface
eglSurface, // read surface
eglContext);
}
//------------------------------------------------------------
void ofAppEGLWindow::swapBuffers(){
EGLBoolean success = eglSwapBuffers(eglDisplay, eglSurface);
if(!success) {
GLint error = eglGetError();
ofLogNotice("ofAppEGLWindow") << "display(): eglSwapBuffers failed: " << eglErrorString(error);
}
}
//--------------------------------------------
void ofAppEGLWindow::startRender() {
renderer()->startRender();
}
//--------------------------------------------
void ofAppEGLWindow::finishRender() {
renderer()->finishRender();
}
//------------------------------------------------------------
void ofAppEGLWindow::update() {
coreEvents.notifyUpdate();
}
//------------------------------------------------------------
void ofAppEGLWindow::draw() {
// take care of any requests for a new screen mode
if (windowMode != OF_GAME_MODE && bNewScreenMode){
if( windowMode == OF_FULLSCREEN){
setWindowRect(getScreenRect());
} else if( windowMode == OF_WINDOW ){
setWindowRect(nonFullscreenWindowRect);
}
bNewScreenMode = false;
}
currentRenderer->startRender();
if( bEnableSetupScreen ) currentRenderer->setupScreen();
coreEvents.notifyDraw();
if(!isUsingX11) {
if(bShowCursor){
GLboolean bIsDepthTestEnabled = GL_FALSE;
glGetBooleanv(GL_DEPTH_TEST, &bIsDepthTestEnabled);
if(bIsDepthTestEnabled == GL_TRUE) {
glDisable(GL_DEPTH_TEST);
}
bool isUsingNormalizedTexCoords = ofGetUsingNormalizedTexCoords();
if(isUsingNormalizedTexCoords) {
ofDisableNormalizedTexCoords();
}
currentRenderer->pushStyle();
currentRenderer->setBlendMode(OF_BLENDMODE_ADD);
currentRenderer->setColor(255);
mouseCursor.draw(ofGetMouseX(),ofGetMouseY());
currentRenderer->popStyle();
if(bIsDepthTestEnabled == GL_TRUE) {
glEnable(GL_DEPTH_TEST);
}
if(isUsingNormalizedTexCoords) {
ofEnableNormalizedTexCoords();
}
}
}
currentRenderer->finishRender();
EGLBoolean success = eglSwapBuffers(eglDisplay, eglSurface);
if(!success) {
GLint error = eglGetError();
ofLogNotice("ofAppEGLWindow") << "display(): eglSwapBuffers failed: " << eglErrorString(error);
}
nFramesSinceWindowResized++;
}
//------------------------------------------------------------
ofCoreEvents & ofAppEGLWindow::events(){
return coreEvents;
}
//------------------------------------------------------------
shared_ptr<ofBaseRenderer> & ofAppEGLWindow::renderer(){
return currentRenderer;
}
//------------------------------------------------------------
void ofAppEGLWindow::setupNativeEvents() {
setupNativeUDev();
setupNativeInput();
startThread();
}
//------------------------------------------------------------
void ofAppEGLWindow::destroyNativeEvents() {
destroyNativeUDev();
destroyNativeInput();
waitForThread(true, threadTimeout);
}
//------------------------------------------------------------
void ofAppEGLWindow::setWindowRect(const ofRectangle& requestedWindowRect) {
if(!isWindowInited) {
ofLogError("ofAppEGLWindow") << "setWindowRect(): window not inited";
return;
}
ofRectangle newRect = requestedWindowRect.getStandardized();
if(newRect != currentWindowRect) {
ofRectangle oldWindowRect = currentWindowRect;
if(isUsingX11) {
int ret = XMoveResizeWindow(x11Display,
x11Window,
(int)newRect.x,
(int)newRect.y,
(unsigned int)newRect.width,
(unsigned int)newRect.height);
if(ret == BadValue) {
ofLogError("ofAppEGLWindow") << "setWindowRect(): XMoveResizeWindow returned BadValue";
} else if(ret == BadWindow) {
ofLogError("ofAppEGLWindow") << "setWindowRect(): XMoveResizeWindow returned BadWindow";
} else {
// all is good
currentWindowRect = newRect;
}
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
VC_RECT_T dst_rect;
dst_rect.x = (int32_t)newRect.x;
dst_rect.y = (int32_t)newRect.y;
dst_rect.width = (int32_t)newRect.width;
dst_rect.height = (int32_t)newRect.height;
VC_RECT_T src_rect;
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = (int32_t)newRect.width << 16;
src_rect.height = (int32_t)newRect.height << 16;
DISPMANX_UPDATE_HANDLE_T dispman_update = vc_dispmanx_update_start(0);
vc_dispmanx_element_change_attributes(dispman_update,
dispman_element,
ELEMENT_CHANGE_SRC_RECT|ELEMENT_CHANGE_DEST_RECT, // we do both when resizing
0, // layer (we aren't changing it here)
0, // opactiy (we aren't changing it here)
&dst_rect,
&src_rect,
0, // mask (we aren't changing it here)
(DISPMANX_TRANSFORM_T)0);
vc_dispmanx_update_submit_sync(dispman_update);
// next time swapBuffers is called, it will be resized based on this eglwindow size data
dispman_native_window.element = dispman_element;
dispman_native_window.width = (int32_t)newRect.width;
dispman_native_window.height = (int32_t)newRect.height; // don't forget!
currentWindowRect = newRect;
#else
ofLogError("ofAppEGLWindow") << "createEGLWindow(): no native window type for this system, perhaps try X11?";
#endif
}
if(oldWindowRect.width != currentWindowRect.width || oldWindowRect.height != currentWindowRect.height) {
coreEvents.notifyWindowResized(currentWindowRect.width, currentWindowRect.height);
nFramesSinceWindowResized = 0;
}
}
}
//------------------------------------------------------------
bool ofAppEGLWindow::createWindow(const ofRectangle& requestedWindowRect) {
if(isUsingX11) {
return createX11NativeWindow(requestedWindowRect);
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
return createRPiNativeWindow(requestedWindowRect);
#else
ofLogError("ofAppEGLWindow") << "createEGLWindow(): no native window type for this system, perhaps try X11?";
return false;
#endif
}
}
//------------------------------------------------------------
int ofAppEGLWindow::getWindowWidth() {
return currentWindowRect.width;
}
//------------------------------------------------------------
int ofAppEGLWindow::getWindowHeight() {
return currentWindowRect.height;
}
//------------------------------------------------------------
void ofAppEGLWindow::pollEvents(){
if(!instance) return;
if(instance->isUsingX11) {
while(1){
XEvent event;
if (::XCheckWindowEvent(instance->x11Display, instance->x11Window, -1, &event)){
handleX11Event(event);
}else if (::XCheckTypedEvent(instance->x11Display, ClientMessage, &event)){
handleX11Event(event);
}else{
break;
}
}
} else {
queue<ofMouseEventArgs> mouseEventsCopy;
instance->lock();
mouseEventsCopy = instance->mouseEvents;
while(!instance->mouseEvents.empty()){
instance->mouseEvents.pop();
}
instance->unlock();
while(!mouseEventsCopy.empty()){
instance->coreEvents.notifyMouseEvent(mouseEventsCopy.front());
mouseEventsCopy.pop();
}
// KEYBOARD EVENTS
queue<ofKeyEventArgs> keyEventsCopy;
instance->lock();
keyEventsCopy = instance->keyEvents;
while(!instance->keyEvents.empty()){
instance->keyEvents.pop();
}
instance->unlock();
while(!keyEventsCopy.empty()){
instance->coreEvents.notifyKeyEvent(keyEventsCopy.front());
keyEventsCopy.pop();
}
}
}
//------------------------------------------------------------
void ofAppEGLWindow::hideCursor(){
bShowCursor = false;
}
//------------------------------------------------------------
void ofAppEGLWindow::showCursor(){
bShowCursor = true;
}
//------------------------------------------------------------
void ofAppEGLWindow::setWindowTitle(string title) {
ofLogNotice("ofAppEGLWindow") << "setWindowTitle(): not implemented";
}
//------------------------------------------------------------
glm::vec2 ofAppEGLWindow::getWindowSize(){
return {currentWindowRect.width, currentWindowRect.height};
}
//------------------------------------------------------------
glm::vec2 ofAppEGLWindow::getWindowPosition(){
return glm::vec2(currentWindowRect.getPosition());
}
//------------------------------------------------------------
glm::vec2 ofAppEGLWindow::getScreenSize(){
unsigned int screenWidth = 0;
unsigned int screenHeight = 0;
if(isUsingX11) {
// TODO, there must be a way to get screensize if the window is not inited
if(isWindowInited && x11Screen) {
screenWidth = XWidthOfScreen(x11Screen);
screenHeight = XHeightOfScreen(x11Screen);
} else {
ofLogError("ofAppEGLWindow") << "getScreenSize(): tried to get display size but failed, x11Screen is not inited";
}
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
int success = graphics_get_display_size(settings.screenNum, &screenWidth, &screenHeight);
if(success < 0) {
ofLogError("ofAppEGLWindow") << "getScreenSize(): tried to get display size but failed";
}
#else
ofLogError("ofAppEGLWindow") << "getScreenSize(): no native window type for this system, perhaps try X11?";
#endif
}
return {screenWidth, screenHeight};
}
//------------------------------------------------------------
int ofAppEGLWindow::getWidth(){
if( orientation == OF_ORIENTATION_DEFAULT || orientation == OF_ORIENTATION_180 ){
return currentWindowRect.width;
}
return currentWindowRect.height;
}
//------------------------------------------------------------
int ofAppEGLWindow::getHeight(){
if( orientation == OF_ORIENTATION_DEFAULT || orientation == OF_ORIENTATION_180 ){
return currentWindowRect.height;
}
return currentWindowRect.width;
}
//------------------------------------------------------------
void ofAppEGLWindow::setOrientation(ofOrientation orientationIn){
orientation = orientationIn;
}
//------------------------------------------------------------
ofOrientation ofAppEGLWindow::getOrientation(){
return orientation;
}
//------------------------------------------------------------
bool ofAppEGLWindow::doesHWOrientation() {
return false;
}
//------------------------------------------------------------
void ofAppEGLWindow::setWindowPosition(int x, int y){
if(!isWindowInited) {
ofLogError("ofAppEGLWindow") << "setWindowPosition(): window not inited";
return;
}
if(isUsingX11) {
int ret = XMoveWindow(x11Display,
x11Window,
x,
y);
if(ret == BadValue) {
ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadValue";
} else if(ret == BadWindow) {
ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadWindow";
} else {
currentWindowRect.x = x;
currentWindowRect.y = y;
nonFullscreenWindowRect = currentWindowRect;
}
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
// keep it in bounds
auto screenSize = getScreenSize();
x = ofClamp(x, 0, screenSize.x - currentWindowRect.width);
y = ofClamp(y, 0, screenSize.y - currentWindowRect.height);
VC_RECT_T dst_rect;
dst_rect.x = (int32_t)x;
dst_rect.y = (int32_t)y;
dst_rect.width = (int32_t)currentWindowRect.width;
dst_rect.height = (int32_t)currentWindowRect.height;
dispman_update = vc_dispmanx_update_start(0);
vc_dispmanx_element_change_attributes(dispman_update,
dispman_native_window.element,
ELEMENT_CHANGE_DEST_RECT,
0,
0,
&dst_rect,
NULL,
0,
(DISPMANX_TRANSFORM_T)0);
vc_dispmanx_update_submit_sync(dispman_update);
currentWindowRect.x = x;
currentWindowRect.y = y;
nonFullscreenWindowRect = currentWindowRect;
#else
ofLogError("ofAppEGLWindow") << "setWindowPosition(): no native window type for this system, perhaps try X11?";
#endif
}
}
//------------------------------------------------------------
void ofAppEGLWindow::setWindowShape(int w, int h){
if(!isWindowInited) {
ofLogError("ofAppEGLWindow") << "setWindowPosition(): window not inited";
return;
}
if(isUsingX11) {
int ret = XResizeWindow(x11Display,
x11Window,
(unsigned int)w,
(unsigned int)h);
if(ret == BadValue) {
ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadValue";
} else if(ret == BadWindow) {
ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadWindow";
} else {
currentWindowRect.width = w;
currentWindowRect.height = h;
nonFullscreenWindowRect = currentWindowRect;
}
} else {
#ifdef TARGET_RASPBERRY_PI_LEGACY
setWindowRect(ofRectangle(currentWindowRect.x,currentWindowRect.y,w,h));
nonFullscreenWindowRect = currentWindowRect;
#else
ofLogError("ofAppEGLWindow") << "setWindowPosition(): no native window type for this system, perhaps try X11?";
#endif
}
}
//------------------------------------------------------------
ofWindowMode ofAppEGLWindow::getWindowMode(){
return windowMode;
}
//------------------------------------------------------------
void ofAppEGLWindow::toggleFullscreen(){
if( windowMode == OF_GAME_MODE) return;
if( windowMode == OF_WINDOW ){
setFullscreen(true);
}else{
setFullscreen(false);
}
}
//------------------------------------------------------------
void ofAppEGLWindow::setFullscreen(bool fullscreen){
if( windowMode == OF_GAME_MODE) return;
if(fullscreen && windowMode != OF_FULLSCREEN){
bNewScreenMode = true;
windowMode = OF_FULLSCREEN;
}else if(!fullscreen && windowMode != OF_WINDOW) {
bNewScreenMode = true;
windowMode = OF_WINDOW;
}
}
//------------------------------------------------------------
void ofAppEGLWindow::enableSetupScreen(){
bEnableSetupScreen = true;
}
//------------------------------------------------------------
void ofAppEGLWindow::disableSetupScreen(){
bEnableSetupScreen = false;
}
//------------------------------------------------------------
ofRectangle ofAppEGLWindow::getScreenRect(){
auto screenSize = getScreenSize();
return ofRectangle(0,0,screenSize.x,screenSize.y);
}
//------------------------------------------------------------
void ofAppEGLWindow::setVerticalSync(bool enabled){
eglSwapInterval(eglDisplay, enabled ? 1 : 0);
}
//------------------------------------------------------------
void ofAppEGLWindow::threadedFunction(){
// TODO: a way to setup mouse and keyboard if
// they are not plugged in upon start
// This can be done with our udev device callbacks
while(isThreadRunning()) {
readNativeUDevEvents();
readNativeInputEvents();
// sleep briefly
ofSleepMillis(20);
}
}
//------------------------------------------------------------
// PLATFORM SPECIFIC RPI
//------------------------------------------------------------
//------------------------------------------------------------
void ofAppEGLWindow::setupNativeUDev() {
udev = udev_new(); // create new udev object
if(!udev) {
ofLogError("ofAppEGLWindow") << "setupNativeUDev(): couldn't create udev object";
} else {
ofLogNotice("ofAppEGLWindow") << "setupNativeUDev(): created udev object";
// setup udev to monitor for input devices
mon = udev_monitor_new_from_netlink(udev, "udev");
// just listen for input devices
udev_monitor_filter_add_match_subsystem_devtype(mon, "input", NULL);
udev_monitor_enable_receiving(mon);
// get the file descriptor for the mon (used w/ select);
udev_fd = udev_monitor_get_fd(mon);
}
if(udev_fd < 0) {
ofLogError("ofAppEGLWindow") << "setupNativeUDev(): did not create udev object, udev_fd < 0";
}
}
//------------------------------------------------------------
void ofAppEGLWindow::destroyNativeUDev() {
udev_unref(udev); // clean up
}
void ofAppEGLWindow::setupNativeInput(){
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *entry;
struct udev_device *dev;
bool isMouse;
ofLogNotice("ofAppEGLWindow") << "setupNativeInput()";
/* Create a list of the devices in the 'input' subsystem. */
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "input");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(entry, devices)
{
/* Get the filename of the /sys entry for the device
and create a udev_device object (dev) representing it */
const char * name = udev_list_entry_get_name(entry);
dev = udev_device_new_from_syspath(udev, name);
const char * sysname = udev_device_get_sysname(dev);
const char * devnode = udev_device_get_devnode(dev);
const char * devpath = udev_device_get_devpath(dev);
const char * devtype = udev_device_get_devtype(dev);
dev_t devnum = udev_device_get_devnum(dev);
const char * driver = udev_device_get_driver(dev);
const char * prop_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
const char * prop_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
ofLogNotice() << "Got device";
ofLogNotice() << " - node: " << devnode;
ofLogNotice() << " - sysname: " << sysname;
ofLogNotice() << " - devpath: " << devpath;
ofLogNotice() << " - devtype: " << devtype;
ofLogNotice() << " - driver: " << driver;
ofLogNotice() << " - devnum: " << devnum;
ofLogNotice() << " - ID_INPUT_KEYBOARD: " << prop_keyboard;
ofLogNotice() << " - ID_INPUT_MOUSE: " << prop_mouse;
if(prop_mouse){
isMouse = true;
}else{
isMouse = false;
}
if(devnode && (prop_keyboard || prop_mouse) && string_begins_with(sysname, "event")){
addInput(devnode, isMouse);
}
if(prop_keyboard){
keyboardDetected = true;
}
if(prop_mouse){
mouseDetected = true;
}
udev_device_unref(dev);
}
/* Free the enumerator object */
udev_enumerate_unref(enumerate);
if(!mouseDetected){
ofLogError("ofAppEGLWindow") << "setupNativeInput(): did not open mouse";
}
if(!keyboardDetected){
ofLogError("ofAppEGLWindow") << "setupKeyboard(): did not open keyboard";
}
// save current terminal settings
tcgetattr (STDIN_FILENO, &tc);
ots = tc;
// disable echo on our temporary settings
tc.c_lflag &= ~ECHO;
tc.c_lflag |= ECHONL;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc);
mb.mouseButtonState = 0;
kb.shiftPressed = false;
kb.capsLocked = false;
printInput();
}
void ofAppEGLWindow::addInput(const char * node, bool isMouse){
if(node == NULL){
return;
}
removeInput(node);
int fd = open(node, O_RDONLY | O_NONBLOCK);
if(fd >= 0){
char deviceNameBuffer[256] = "Unknown Device";
ioctl(fd, EVIOCGNAME(sizeof(deviceNameBuffer)), deviceNameBuffer);
ofLogNotice("ofAppEGLWindow") << "addInput(): input device name = " << deviceNameBuffer;
if(isMouse){
struct input_absinfo mabsx;
if(ioctl(fd, EVIOCGABS(0), &mabsx) < 0){
ofLogError("ofAppEGLWindow") << "ioctl GABS failed";
} else {
mouseAbsXMin = mabsx.minimum;
mouseAbsXMax = mabsx.maximum;
ofLogNotice("ofAppEGLWindow") << "mouse x axis min, max: " << mouseAbsXMin << ", " << mouseAbsXMax;
}
// Do that for the y axis. EVIOCGABS(1): 1 stands for y axis.
struct input_absinfo mabsy;
if(ioctl(fd, EVIOCGABS(1), &mabsy) < 0){
ofLogError("ofAppEGLWindow") << "ioctl GABS failed";
}else{
mouseAbsYMin = mabsy.minimum;
mouseAbsYMax = mabsy.maximum;
ofLogNotice("ofAppEGLWindow") << "mouse y axis min, max: " << mouseAbsYMin << ", " << mouseAbsYMax;
}
}
inputDevices[node] = fd;
}
}
void ofAppEGLWindow::removeInput(const char * node){
if(node == NULL)
return;
device::iterator iter = inputDevices.find(node);
if(iter != inputDevices.end()){
::close(iter->second);
inputDevices.erase(iter);
}
}
void ofAppEGLWindow::printInput(){
ofLogNotice("--- Input Device List ---");
for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
ofLogNotice() << " - " << iter->first;
}
ofLogNotice("-------------------------");
}
void ofAppEGLWindow::destroyNativeInput(){
ofLogNotice("ofAppEGLWindow") << "destroyNativeInput()";
for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
if(iter->second >= 0){
::close(iter->second);
}
}
inputDevices.clear();
tcsetattr (STDIN_FILENO, TCSAFLUSH, &ots);
}
//------------------------------------------------------------
void ofAppEGLWindow::processInput(int fd, const char * node){
// http://www.diegm.uniud.it/loghi/CE2/kbd.pdf
// http://cgit.freedesktop.org/~whot/evtest/plain/evtest.c
// https://strcpy.net/b/archives/2010/11/17/abusing_the_linux_input_subsystem/index.html
static ofKeyEventArgs keyEvent;
static ofMouseEventArgs mouseEvent;
struct input_event ev;
char key = 0;
bool pushKeyEvent = false;
bool pushMouseEvent = false;
bool axisValuePending = false;
int nBytesRead = read(fd, &ev, sizeof(struct input_event));
while(nBytesRead >= 0){
// ofLogNotice("Input event ") << "dev: " << node << " ,type: " << ev.type << " ,code: " << ev.code << " ,value: " << ev.value << " ,pending: " << axisValuePending;
if(ev.type == EV_KEY){
if(ev.code == BTN_LEFT){
if(ev.value == 0){ // release
mouseEvent.button = OF_MOUSE_BUTTON_LEFT;
mouseEvent.type = ofMouseEventArgs::Released;
mb.mouseButtonState &= ~MOUSE_BUTTON_LEFT_MASK;
pushMouseEvent = true;
}else if(ev.value == 1){ // press
mb.mouseButtonState |= MOUSE_BUTTON_LEFT_MASK;
mouseEvent.type = ofMouseEventArgs::Pressed;
mouseEvent.button = OF_MOUSE_BUTTON_LEFT;
pushMouseEvent = true;
}
}else if(ev.code == BTN_MIDDLE){
if(ev.value == 0){ // release
mouseEvent.button = OF_MOUSE_BUTTON_MIDDLE;
mouseEvent.type = ofMouseEventArgs::Released;
mb.mouseButtonState &= ~MOUSE_BUTTON_MIDDLE_MASK;
pushMouseEvent = true;
}else if(ev.value == 1){ // press
mb.mouseButtonState |= MOUSE_BUTTON_MIDDLE_MASK;
mouseEvent.type = ofMouseEventArgs::Pressed;
mouseEvent.button = OF_MOUSE_BUTTON_MIDDLE;
pushMouseEvent = true;
}
}else if(ev.code == BTN_RIGHT){
if(ev.value == 0){ // release
mouseEvent.button = OF_MOUSE_BUTTON_RIGHT;
mouseEvent.type = ofMouseEventArgs::Released;
mb.mouseButtonState &= ~MOUSE_BUTTON_RIGHT_MASK;
pushMouseEvent = true;
}else if(ev.value == 1){ // press
mb.mouseButtonState |= MOUSE_BUTTON_RIGHT_MASK;
mouseEvent.type = ofMouseEventArgs::Pressed;
mouseEvent.button = OF_MOUSE_BUTTON_RIGHT;
pushMouseEvent = true;
}
}else{
if(ev.value == 0){
// key released
keyEvent.type = ofKeyEventArgs::Released;
}else if(ev.value == 1){
// key pressed
keyEvent.type = ofKeyEventArgs::Pressed;
}else if(ev.value == 2){
// key repeated
keyEvent.type = ofKeyEventArgs::Pressed;
}else{
// unknown ev.value
}
switch (ev.code) {
case KEY_RIGHTSHIFT:
case KEY_LEFTSHIFT:
kb.shiftPressed = ev.value;
break;
case KEY_RIGHTCTRL:
case KEY_LEFTCTRL:
break;
case KEY_CAPSLOCK:
if (ev.value == 1) {
if (kb.capsLocked) {
kb.capsLocked = 0;
} else {
kb.capsLocked = 1;
}
}
break;
case KEY_ESC:
pushKeyEvent = true;
keyEvent.key = OF_KEY_ESC;
break;
case KEY_BACKSPACE:
pushKeyEvent = true;
keyEvent.key = OF_KEY_BACKSPACE;
break;
case KEY_DELETE:
pushKeyEvent = true;
keyEvent.key = OF_KEY_DEL;
break;
case KEY_F1:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F1;
break;
case KEY_F2:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F2;
break;
case KEY_F3:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F3;
break;
case KEY_F4:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F4;
break;
case KEY_F5:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F5;
break;
case KEY_F6:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F6;
break;
case KEY_F7:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F7;
break;
case KEY_F8:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F8;
break;
case KEY_F9:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F9;
break;
case KEY_F10:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F10;
break;
case KEY_F11:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F11;
break;
case KEY_F12:
pushKeyEvent = true;
keyEvent.key = OF_KEY_F12;
break;
case KEY_LEFT:
pushKeyEvent = true;
keyEvent.key = OF_KEY_LEFT;
break;
case KEY_UP:
pushKeyEvent = true;
keyEvent.key = OF_KEY_UP;
break;
case KEY_RIGHT:
pushKeyEvent = true;
keyEvent.key = OF_KEY_RIGHT;
break;
case KEY_DOWN:
pushKeyEvent = true;
keyEvent.key = OF_KEY_DOWN;
break;
case KEY_PAGEUP:
pushKeyEvent = true;
keyEvent.key = OF_KEY_PAGE_UP;
break;
case KEY_PAGEDOWN:
pushKeyEvent = true;
keyEvent.key = OF_KEY_PAGE_DOWN;
break;
case KEY_HOME:
pushKeyEvent = true;
keyEvent.key = OF_KEY_HOME;
break;
case KEY_END:
pushKeyEvent = true;
keyEvent.key = OF_KEY_END;
break;
case KEY_INSERT:
pushKeyEvent = true;
keyEvent.key = OF_KEY_INSERT;
break;
case KEY_ENTER:
case KEY_KPENTER:
pushKeyEvent = true;
keyEvent.key = OF_KEY_RETURN;
break;
default:
// VERY RUDIMENTARY KEY MAPPING WITH MAPS ABOVE
if(ev.code < sizeof(lowercase_map)){
if(kb.shiftPressed){
key = uppercase_map[ev.code];
if(kb.capsLocked) keyEvent.key = tolower(key);
keyEvent.key = key;
pushKeyEvent = true;
}else{
key = lowercase_map[ev.code];
if(kb.capsLocked) key = toupper(key);
keyEvent.key = key;
pushKeyEvent = true;
}
}else{
ofLogNotice("ofAppEGLWindow") << "readKeyboardEvents(): input_event.code is outside of our small range";
}
}
}
}else if(ev.type == EV_REL || ev.type == EV_ABS){
int axis = ev.code;
int amount = ev.value;
switch(axis) {
case 0:
if(ev.type == EV_REL){
mouseEvent.x += amount * mouseScaleX;
}else{
mouseEvent.x = amount * (float)currentWindowRect.width / (float)mouseAbsXMax;
}
mouseEvent.x = ofClamp(mouseEvent.x, 0, currentWindowRect.width);
axisValuePending = true;
break;
case 1:
if(ev.type == EV_REL){
mouseEvent.y += amount * mouseScaleY;
}else{
mouseEvent.y = amount * (float)currentWindowRect.height / (float)mouseAbsYMax;
}
mouseEvent.y = ofClamp(mouseEvent.y, 0, currentWindowRect.height);
axisValuePending = true;
break;
default:
ofLogNotice("ofAppEGLWindow") << "readMouseEvents(): unknown mouse axis (perhaps it's the scroll wheel?)";
break;
}
}else if(ev.type == EV_MSC){
}else if(ev.type == EV_SYN){
// EV_SYN Used as markers to separate events. Events may be
// separated in time or in space, such as with the multitouch protocol.
// EV_SYN events are sent when axis value (one or a pair) are changed
if(axisValuePending){
// ofLog() << "EV_SYN pending end : " << iter->second;
// TODO, this state doesn't make as much sense when the mouse is not dragging
if(mb.mouseButtonState > 0){
// dragging (what if dragging w/ more than one button?)
mouseEvent.type = ofMouseEventArgs::Dragged;
}else{
// just moving
mouseEvent.type = ofMouseEventArgs::Moved;
}
mouseEvent.button = mb.mouseButtonState;
pushMouseEvent = true;
axisValuePending = false;
}
//ofLogVerbose("ofAppEGLWindow") << "readMouseEvents(): EV_SYN";
}
if(pushKeyEvent){
lock();
keyEvents.push(keyEvent);
unlock();
pushKeyEvent = false;
}
if(pushMouseEvent){
// lock the thread for a moment while we copy the data
lock();
mouseEvents.push(mouseEvent);
unlock();
pushMouseEvent = false;
}
nBytesRead = read(fd, &ev,sizeof(struct input_event));
}
}
//------------------------------------------------------------
void ofAppEGLWindow::readNativeUDevEvents() {
// look for devices being attatched / detatched
fd_set fds;
struct timeval tv;
int ret;
struct udev_device *dev;
bool is_mouse = false;
FD_ZERO(&fds);
FD_SET(udev_fd, &fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select(udev_fd+1, &fds, NULL, NULL, &tv);
/* Check if our file descriptor has received data. */
if(ret > 0 && FD_ISSET(udev_fd, &fds)){
/* Make the call to receive the device.
select() ensured that this will not block. */
dev = udev_monitor_receive_device(mon);
if(dev){
const char * devnode = udev_device_get_devnode(dev);
const char * devpath = udev_device_get_devpath(dev);
const char * sysname = udev_device_get_sysname(dev);
const char * subsystem = udev_device_get_subsystem(dev);
const char * devtype = udev_device_get_devtype(dev);
dev_t devnum = udev_device_get_devnum(dev);
const char * driver = udev_device_get_driver(dev);
const char * action = udev_device_get_action(dev);
const char * prop_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
const char * prop_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
ofLogNotice("readNativeUDevEvents") << "udev monitor receive devixe";
ofLogNotice() << " - node: " << devnode;
ofLogNotice() << " - devpath: " << devpath;
ofLogNotice() << " - sysname: " << sysname;
ofLogNotice() << " - subsystem: " << subsystem;
ofLogNotice() << " - devtype: " << devtype;
ofLogNotice() << " - devnum: " << devnum;
ofLogNotice() << " - driver: " << driver;
ofLogNotice() << " - action: " << action;
ofLogNotice() << " - ID_INPUT_KEYBOARD: " << prop_keyboard;
ofLogNotice() << " - ID_INPUT_MOUSE: " << prop_mouse;
if(prop_mouse){
is_mouse = true;
}
if(devnode && (prop_keyboard || prop_mouse) && string_begins_with(sysname, "event")){
if(strcmp(action, "add") == 0){
addInput(devnode, is_mouse);
}else if(strcmp(action, "remove") == 0){
removeInput(devnode);
}
}
udev_device_unref(dev);
}else{
ofLogNotice("ofAppEGLWindow") << "readNativeUDevEvents(): device returned by receive_device() is NULL";
}
}
}
void ofAppEGLWindow::readNativeInputEvents(){
for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
processInput(iter->second, iter->first.c_str());
}
}
#ifdef TARGET_RASPBERRY_PI_LEGACY
//------------------------------------------------------------
void ofAppEGLWindow::initRPiNative() {
bcm_host_init();
memset(&dispman_native_window, 0x0, sizeof(EGL_DISPMANX_WINDOW_T));
dispman_element = DISPMANX_NO_HANDLE;
dispman_display = DISPMANX_NO_HANDLE;
dispman_update = DISPMANX_NO_HANDLE;
memset(&dispman_clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
dispman_transform = DISPMANX_NO_ROTATE;
memset(&dispman_alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); // zero dispman_alpha
}
//------------------------------------------------------------
void ofAppEGLWindow::exitRPiNative() {
bcm_host_deinit();
}
//------------------------------------------------------------
bool ofAppEGLWindow::createRPiNativeWindow(const ofRectangle& requestedWindowRect){
ofRectangle screenRect = getScreenRect();
// make sure our requested window rectangle does not exceed the native
// screen size, or start outside of it.
ofRectangle windowRect = screenRect.getIntersection(requestedWindowRect);
ofLogNotice("ofAppEGLWindow") << "setupRPiNativeWindow(): screenRect: " << screenRect.width << "x" << screenRect.height;
ofLogNotice("ofAppEGLWindow") << "setupRPiNativeWindow(): windowRect: " << windowRect.width << "x" << windowRect.height;
//////////////////////////
VC_RECT_T dst_rect;
dst_rect.x = (int32_t)windowRect.x;
dst_rect.y = (int32_t)windowRect.y;
dst_rect.width = (int32_t)windowRect.width;
dst_rect.height = (int32_t)windowRect.height;
VC_RECT_T src_rect;
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = dst_rect.width << 16;
src_rect.height = dst_rect.height << 16;
memset(&dispman_alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); // zero dispman_alpha
dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
dispman_alpha.opacity = ofClamp(settings.eglWindowOpacity,0,255);
dispman_alpha.mask = 0;
memset(&dispman_clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
// there are other values for dispman_transform, but they do not seem to have an effect
dispman_transform = DISPMANX_NO_ROTATE;
// get the zero display
dispman_display = vc_dispmanx_display_open(settings.screenNum);
// begin the display manager interaction
dispman_update = vc_dispmanx_update_start( 0 );
// add a "display manager element" with our parameters so
// that it can fill in the structures. we will pass this
// filled dispman_element to our native window, which will
// be used to construct the EGL surface, etc.
dispman_element = vc_dispmanx_element_add ( dispman_update,
dispman_display,
settings.layer, // layer
&dst_rect, // dst rect
(DISPMANX_RESOURCE_HANDLE_T)0, // src
&src_rect, // src rect
DISPMANX_PROTECTION_NONE, // ?
&dispman_alpha, // alpha
&dispman_clamp, // clamp
dispman_transform // transform
);
if(dispman_element == DISPMANX_NO_HANDLE) {
ofLogError("ofAppEGLWindow") << "setupRPiNativeWindow(): dispman_element == DISPMANX_NO_HANDLE";
return false;
} else if(dispman_element == (unsigned)DISPMANX_INVALID) {
ofLogError("ofAppEGLWindow") << "setupRPiNativeWindow(): dispman_element == DISPMANX_INVALID";
return false;
}
// set dispman_native_window to zero
memset(&dispman_native_window, 0x0, sizeof(EGL_DISPMANX_WINDOW_T));
dispman_native_window.element = dispman_element;
dispman_native_window.width = (int32_t)windowRect.width;
dispman_native_window.height = (int32_t)windowRect.height;
// set background to black (not required)
vc_dispmanx_display_set_background(dispman_update, dispman_display, 0x00, 0x00, 0x00);
// finished with display manager update, so sync
vc_dispmanx_update_submit_sync( dispman_update );
currentWindowRect = windowRect;
return true;
}
#endif
//------------------------------------------------------------
// X11 BELOW
//------------------------------------------------------------
bool ofAppEGLWindow::createX11NativeWindow(const ofRectangle& requestedWindowRect){
// X11 variables
x11Window = 0;
x11Display = 0;
x11ScreenNum = 0; // TODO: settings.screenNum?
x11Screen = 0;
XVisualInfo* x11Visual = 0; // TODO does this need to be deleted?
Colormap x11Colormap = 0;
/*
Step 0 - Create a NativeWindowType that we can use it for OpenGL ES output
*/
Window sRootWindow;
XSetWindowAttributes sWA;
unsigned int ui32Mask;
int i32Depth;
//ofRectangle screenRect = getScreenRect();
// make sure our requested window rectangle does not exceed the native
// screen size, or start outside of it.
ofRectangle windowRect = requestedWindowRect.getStandardized();//screenRect.getIntersection(requestedWindowRect);
// Initializes the display and screen
x11Display = XOpenDisplay( 0 );
if (!x11Display) {
ofLogError("ofAppEGLWindow") << "unable to open X display";
return false;
}
x11ScreenNum = XDefaultScreen( x11Display );
x11Screen = XDefaultScreenOfDisplay(x11Display);
// Gets the window parameters
sRootWindow = RootWindow(x11Display, x11ScreenNum);
i32Depth = DefaultDepth(x11Display, x11ScreenNum);
x11Visual = new XVisualInfo();
XMatchVisualInfo( x11Display,
x11ScreenNum,
i32Depth,
TrueColor,
x11Visual);
if (!x11Visual) {
ofLogError("ofAppEGLWindow") << "unable to acquire XVisualInfo";
return false;
}
x11Colormap = XCreateColormap( x11Display, sRootWindow, x11Visual->visual, AllocNone );
delete x11Visual;
// set the colormap window attribuet
sWA.colormap = x11Colormap;
// Add to these for handling other events
sWA.event_mask = 0;
sWA.event_mask |= StructureNotifyMask;
sWA.event_mask |= ExposureMask;
sWA.event_mask |= ButtonPressMask;
sWA.event_mask |= ButtonReleaseMask;
sWA.event_mask |= PointerMotionMask;
sWA.event_mask |= KeyPressMask;
sWA.event_mask |= KeyReleaseMask;
// setup background pixel attributes
ui32Mask = 0;
ui32Mask |= CWBackPixel;
ui32Mask |= CWBorderPixel;
ui32Mask |= CWEventMask;
ui32Mask |= CWColormap;
// Creates the X11 window
x11Window = XCreateWindow(x11Display, // Specifies the connection to the X server.
sRootWindow, // Specifies the parent window.
(int)windowRect.x, (int)windowRect.y, // Specify the x and y coordinates,
// which are the top-left outside corner
// of the window's borders and are relative
// to the inside of the parent window's borders.
(unsigned int)windowRect.width, (unsigned int)windowRect.height, // Specify the width and height, which are the
// created window's inside dimensions and do
// not include the created window's borders.
0, // Specifies the width of the created
// window's border in pixels.
CopyFromParent, // Specifies the window's depth.
// A depth of CopyFromParent means
// the depth is taken from the parent.
InputOutput, // Specifies the created window's class.
// You can pass InputOutput, InputOnly,
// or CopyFromParent. A class of CopyFromParent
// means the class is taken from the parent.
CopyFromParent, // Specifies the visual type.
// A visual of CopyFromParent means the visual type
// is taken from the parent.
ui32Mask, // Specifies which window attributes are
// defined in the attributes argument. This mask is
// the bitwise inclusive OR of the valid attribute
// mask bits. If valuemask is zero, the attributes
// are ignored and are not referenced.
&sWA //Specifies the background pixel value of the window.
);
XMapWindow(x11Display, x11Window);
XFlush(x11Display);
// check success?
currentWindowRect = windowRect;
return true;
}
//------------------------------------------------------------
static KeySym KeyCodeToKeySym(Display * display, KeyCode keycode, unsigned int event_mask) {
KeySym keysym = NoSymbol;
//Get the map
XkbDescPtr keyboard_map = XkbGetMap(display, XkbAllClientInfoMask, XkbUseCoreKbd);
if (keyboard_map) {
//What is diff between XkbKeyGroupInfo and XkbKeyNumGroups?
unsigned char info = XkbKeyGroupInfo(keyboard_map, keycode);
unsigned int num_groups = XkbKeyNumGroups(keyboard_map, keycode);
//Get the group
unsigned int group = 0x00;
switch (XkbOutOfRangeGroupAction(info)) {
case XkbRedirectIntoRange:
/* If the RedirectIntoRange flag is set, the four least significant
* bits of the groups wrap control specify the index of a group to
* which all illegal groups correspond. If the specified group is
* also out of range, all illegal groups map to Group1.
*/
group = XkbOutOfRangeGroupInfo(info);
if (group >= num_groups) {
group = 0;
}
break;
case XkbClampIntoRange:
/* If the ClampIntoRange flag is set, out-of-range groups correspond
* to the nearest legal group. Effective groups larger than the
* highest supported group are mapped to the highest supported group;
* effective groups less than Group1 are mapped to Group1 . For
* example, a key with two groups of symbols uses Group2 type and
* symbols if the global effective group is either Group3 or Group4.
*/
group = num_groups - 1;
break;
case XkbWrapIntoRange:
/* If neither flag is set, group is wrapped into range using integer
* modulus. For example, a key with two groups of symbols for which
* groups wrap uses Group1 symbols if the global effective group is
* Group3 or Group2 symbols if the global effective group is Group4.
*/
default:
if (num_groups != 0) {
group %= num_groups;
}
break;
}
XkbKeyTypePtr key_type = XkbKeyKeyType(keyboard_map, keycode, group);
unsigned int active_mods = event_mask & key_type->mods.mask;
int i, level = 0;
for (i = 0; i < key_type->map_count; i++) {
if (key_type->map[i].active && key_type->map[i].mods.mask == active_mods) {
level = key_type->map[i].level;
}
}
keysym = XkbKeySymEntry(keyboard_map, keycode, level, group);
XkbFreeClientMap(keyboard_map, XkbAllClientInfoMask, true);
}
return keysym;
}
//------------------------------------------------------------
void ofAppEGLWindow::handleX11Event(const XEvent& event){
ofMouseEventArgs mouseEvent;
ofKeyEventArgs keyEvent;
switch (event.type){
case KeyPress:
case KeyRelease:
{
KeySym key = KeyCodeToKeySym(instance->x11Display,event.xkey.keycode,event.xkey.state);
keyEvent.key = key;
if (event.type == KeyPress) {
keyEvent.type = ofKeyEventArgs::Pressed;
if(key == 65307){
keyEvent.key = OF_KEY_ESC;
}
} else if (event.type == KeyRelease){
keyEvent.type = ofKeyEventArgs::Released;
}
instance->coreEvents.notifyKeyEvent(keyEvent);
}
break;
case ButtonPress:
case ButtonRelease:
mouseEvent.x = static_cast<float>(event.xbutton.x);
mouseEvent.y = static_cast<float>(event.xbutton.y);
mouseEvent.button = event.xbutton.button;
if (event.type == ButtonPress){
mouseEvent.type = ofMouseEventArgs::Pressed;
} else {
mouseEvent.type = ofMouseEventArgs::Released;
}
instance->coreEvents.notifyMouseEvent(mouseEvent);
break;
case MotionNotify:
//cout << "motion notify" << endl;
mouseEvent.x = static_cast<float>(event.xmotion.x);
mouseEvent.y = static_cast<float>(event.xmotion.y);
mouseEvent.button = event.xbutton.button;
if(ofGetMousePressed()) {
mouseEvent.type = ofMouseEventArgs::Dragged;
} else {
mouseEvent.type = ofMouseEventArgs::Moved;
}
instance->coreEvents.notifyMouseEvent(mouseEvent);
break;
case ConfigureNotify:
instance->currentWindowRect.x = event.xconfigure.x;
instance->currentWindowRect.y = event.xconfigure.y;
instance->currentWindowRect.width = event.xconfigure.width;
instance->currentWindowRect.height = event.xconfigure.height;
instance->nonFullscreenWindowRect = instance->currentWindowRect;
instance->coreEvents.notifyWindowResized(event.xconfigure.width,event.xconfigure.height);
break;
/*case ClientMessage:{
if (event.xclient.message_type == wmProtocols_ &&
event.xclient.format == 32 &&
event.xclient.data.l[0] == (long) wmDeleteWindow_)
{
if (listener())
{
if (listener()->onClose(wrapper() ? *wrapper() : *(WindowInterface*)this))
isShuttingDown_ = true;
}
else
{
isShuttingDown_ = true;
}
}
break;
}*/
}
}
#else
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <drm_fourcc.h>
#include <gbm.h>
//#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#ifndef EGL_KHR_platform_gbm
#define EGL_KHR_platform_gbm 1
#define EGL_PLATFORM_GBM_KHR 0x31D7
#endif /* EGL_KHR_platform_gbm */
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
class CRTC
{
public:
drmModeCrtc *crtc;
drmModeObjectProperties *props;
drmModePropertyRes **props_info;
CRTC()
{
crtc = NULL;
props = NULL;
props_info = NULL;
}
};
class Connector {
public:
drmModeConnector *connector;
drmModeObjectProperties *props;
drmModePropertyRes **props_info;
Connector()
{
connector = NULL;
props = NULL;
props_info = NULL;
}
};
class DRM
{
public:
int fd;
CRTC crtc;
Connector connector;
int crtc_index;
int kms_in_fence_fd;
int kms_out_fence_fd;
drmModeModeInfo *mode;
uint32_t crtc_id;
uint32_t connector_id;
int (*run)(const struct gbm *gbm, const struct egl *egl);
DRM()
{
fd = 0;
mode = NULL;
}
};
class GBM
{
public:
struct gbm_device *dev;
struct gbm_surface *surface;
uint32_t format;
int width, height;
GBM()
{
format = 0;
width = 0;
height = 0;
}
};
class EGL {
public:
EGLDisplay display;
EGLConfig config;
EGLContext context;
EGLSurface surface;
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR;
PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR;
PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR;
bool modifiers_supported;
void (*draw)(unsigned i);
EGL()
{
display = NULL;
config = NULL;
context = NULL;
surface = NULL;
}
};
static bool has_ext(const char *extension_list, const char *ext)
{
ofLog() << "extension_list: " << extension_list;
ofLog() << "ext: " << ext;
const char *ptr = extension_list;
int len = strlen(ext);
if (ptr == NULL || *ptr == '\0')
return false;
while (true) {
ptr = strstr(ptr, ext);
if (!ptr)
return false;
if (ptr[len] == ' ' || ptr[len] == '\0')
return true;
ptr += len;
}
}
static int
match_config_to_visual(EGLDisplay egl_display,
EGLint visual_id,
EGLConfig *configs,
int count)
{
int i;
for (i = 0; i < count; ++i) {
EGLint id;
if (!eglGetConfigAttrib(egl_display,
configs[i], EGL_NATIVE_VISUAL_ID,
&id))
continue;
if (id == visual_id)
return i;
}
return -1;
}
static bool
egl_choose_config(EGLDisplay egl_display, const EGLint *attribs,
EGLint visual_id, EGLConfig *config_out)
{
EGLint count = 0;
EGLint matched = 0;
int config_index = -1;
if (!eglGetConfigs(egl_display, NULL, 0, &count) || count < 1) {
printf("No EGL configs to choose from.\n");
return false;
}
EGLConfig configs[count];
if (!eglChooseConfig(egl_display, attribs, configs,
count, &matched) || !matched) {
printf("No EGL configs with appropriate attributes.\n");
goto out;
}
if (!visual_id)
config_index = 0;
if (config_index == -1)
config_index = match_config_to_visual(egl_display,
visual_id,
configs,
matched);
if (config_index != -1)
*config_out = configs[config_index];
out:
if (config_index == -1)
return false;
return true;
}
#define CASE_STR(x,y) case x: str = y; break
static string eglErrorString(EGLint err) {
string str;
switch (err) {
CASE_STR(EGL_SUCCESS, "EGL_SUCCESS");
CASE_STR(EGL_NOT_INITIALIZED, "EGL_NOT_INITIALIZED");
CASE_STR(EGL_BAD_ACCESS, "EGL_BAD_ACCESS");
CASE_STR(EGL_BAD_ALLOC, "EGL_BAD_ACCESS");
CASE_STR(EGL_BAD_ATTRIBUTE, "EGL_BAD_ATTRIBUTE");
CASE_STR(EGL_BAD_CONTEXT, "EGL_BAD_CONTEXT");
CASE_STR(EGL_BAD_CONFIG, "EGL_BAD_CONFIG");
CASE_STR(EGL_BAD_CURRENT_SURFACE, "EGL_BAD_CURRENT_SURFACE");
CASE_STR(EGL_BAD_DISPLAY, "EGL_BAD_DISPLAY");
CASE_STR(EGL_BAD_SURFACE, "EGL_BAD_SURFACE");
CASE_STR(EGL_BAD_MATCH, "EGL_BAD_MATCH");
CASE_STR(EGL_BAD_PARAMETER, "EGL_BAD_PARAMETER");
CASE_STR(EGL_BAD_NATIVE_PIXMAP, "EGL_BAD_NATIVE_PIXMAP");
CASE_STR(EGL_BAD_NATIVE_WINDOW, "EGL_BAD_NATIVE_WINDOW");
CASE_STR(EGL_CONTEXT_LOST, "EGL_CONTEXT_LOST");
default: str = "unknown error " + err; break;
}
return str;
}
/*
void get_proc_client(EGL* egl, const char *ext, const char *name)
{
if (has_ext(egl_exts_client, ext))
{
egl->name = (void *)eglGetProcAddress(name);
}
}*/
EGL init_egl(GBM& gbm, int samples)
{
EGL egl;
EGLint major, minor;
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SAMPLES, samples,
EGL_NONE
};
const char *egl_exts_client, *egl_exts_dpy, *gl_exts;
egl_exts_client = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
egl.modifiers_supported = has_ext(egl_exts_dpy,
"EGL_EXT_image_dma_buf_import_modifiers");
egl.eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
egl.eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
egl.eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
egl.glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
egl.eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR");
egl.eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)eglGetProcAddress("eglDestroySyncKHR");
egl.eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)eglGetProcAddress("eglClientWaitSyncKHR");
egl.display = egl.eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm.dev, NULL);
if (!eglInitialize(egl.display, &major, &minor)) {
ofLog(OF_LOG_VERBOSE, "failed to initialize\n");
}
egl_exts_dpy = eglQueryString(egl.display, EGL_EXTENSIONS);
/*get_proc_dpy(EGL_KHR_image_base, eglCreateImageKHR);
get_proc_dpy(EGL_KHR_image_base, eglDestroyImageKHR);
get_proc_dpy(EGL_KHR_fence_sync, eglCreateSyncKHR);
get_proc_dpy(EGL_KHR_fence_sync, eglDestroySyncKHR);
get_proc_dpy(EGL_KHR_fence_sync, eglWaitSyncKHR);
get_proc_dpy(EGL_KHR_fence_sync, eglClientWaitSyncKHR);
get_proc_dpy(EGL_ANDROID_native_fence_sync, eglDupNativeFenceFDANDROID);*/
egl.modifiers_supported = has_ext(egl_exts_dpy,
"EGL_EXT_image_dma_buf_import_modifiers");
ofLog() << "egl.modifiers_supported: " << egl.modifiers_supported;
ofLog(OF_LOG_VERBOSE, "Using display %p with EGL version %d.%d\n",
egl.display, major, minor);
ofLog(OF_LOG_VERBOSE, "===================================\n");
ofLog(OF_LOG_VERBOSE, "EGL information:\n");
ofLog(OF_LOG_VERBOSE, " version: \"%s\"\n", eglQueryString(egl.display, EGL_VERSION));
ofLog(OF_LOG_VERBOSE, " vendor: \"%s\"\n", eglQueryString(egl.display, EGL_VENDOR));
ofLog(OF_LOG_VERBOSE, " client extensions: \"%s\"\n", egl_exts_client);
ofLog(OF_LOG_VERBOSE, " display extensions: \"%s\"\n", egl_exts_dpy);
ofLog(OF_LOG_VERBOSE, "===================================\n");
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
ofLog(OF_LOG_VERBOSE, "failed to bind api EGL_OPENGL_ES_API\n");
}
if (!egl_choose_config(egl.display, config_attribs, gbm.format,
&egl.config)) {
ofLog(OF_LOG_VERBOSE, "failed to choose config\n");
}else
{
ofLog() << "egl_choose_config PASS";
}
EGLint attribute_list_surface_context[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
egl.context = eglCreateContext(egl.display,
egl.config,
EGL_NO_CONTEXT,
attribute_list_surface_context);
if(egl.context == EGL_NO_CONTEXT) {
EGLint error = eglGetError();
if(error == EGL_BAD_CONFIG) {
ofLogError() << "error creating context: EGL_BAD_CONFIG: " << eglErrorString(error);
//return false;
} else {
ofLogError() << "error creating context: " << eglErrorString(error);
//return false;
}
}else
{
ofLog() << "egl.context IS VALID: ", egl.context;
}
egl.surface = eglCreateWindowSurface(egl.display, egl.config,
(EGLNativeWindowType)gbm.surface, NULL);
if (egl.surface == EGL_NO_SURFACE) {
EGLint error = eglGetError();
ofLogError() << "error creating surface: " << eglErrorString(error) << " error: " << error;
}else
{
ofLog() << "egl.surface IS VALID: ", egl.surface;
}
/* connect the context to the surface */
return egl;
}
int get_resources(int fd, drmModeRes **resources)
{
*resources = drmModeGetResources(fd);
if (*resources == NULL)
return -1;
return 0;
}
#define MAX_DRM_DEVICES 64
uint32_t find_crtc_for_encoder(const drmModeRes *resources,
const drmModeEncoder *encoder) {
int i;
for (i = 0; i < resources->count_crtcs; i++) {
/* possible_crtcs is a bitmask as described here:
* https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api
*/
const uint32_t crtc_mask = 1 << i;
const uint32_t crtc_id = resources->crtcs[i];
if (encoder->possible_crtcs & crtc_mask) {
return crtc_id;
}
}
/* no match found */
return -1;
}
uint32_t find_crtc_for_connector(DRM& drm, drmModeRes *resources, drmModeConnector *connector) {
int i;
for (i = 0; i < connector->count_encoders; i++) {
const uint32_t encoder_id = connector->encoders[i];
drmModeEncoder *encoder = drmModeGetEncoder(drm.fd, encoder_id);
if (encoder) {
const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder);
drmModeFreeEncoder(encoder);
if (crtc_id != 0) {
return crtc_id;
}
}
}
/* no match found */
return -1;
}
int find_drm_device(drmModeRes **resources)
{
drmDevicePtr devices[MAX_DRM_DEVICES] = { NULL };
int num_devices, fd = -1;
num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
if (num_devices < 0) {
ofLog(OF_LOG_VERBOSE, "drmGetDevices2 failed: %s\n", strerror(-num_devices));
return -1;
}
ofLog() << "num_devices: " << num_devices;
for (int i = 0; i < num_devices; i++) {
drmDevicePtr device = devices[i];
int ret;
if (!(device->available_nodes & (1 << DRM_NODE_PRIMARY)))
continue;
/* OK, it's a primary device. If we can get the
* drmModeResources, it means it's also a
* KMS-capable device.
*/
fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR);
if (fd < 0)
continue;
ret = get_resources(fd, resources);
if (!ret)
break;
close(fd);
fd = -1;
}
drmFreeDevices(devices, num_devices);
if (fd < 0)
ofLog(OF_LOG_VERBOSE, "no drm device found!\n");
ofLog() << "fd: " << fd;
return fd;
}
int init_drm(DRM& drm, const char *device, const char *mode_str, unsigned int vrefresh)
{
drmModeRes *resources;
drmModeConnector *connector = NULL;
drmModeEncoder *encoder = NULL;
int i, ret, area;
if (device) {
drm.fd = open(device, O_RDWR);
ret = get_resources(drm.fd, &resources);
if (ret < 0 && errno == EOPNOTSUPP)
ofLog(OF_LOG_VERBOSE, "%s does not look like a modeset device\n", device);
} else {
drm.fd = find_drm_device(&resources);
}
if (drm.fd < 0) {
ofLog(OF_LOG_VERBOSE, "could not open drm device\n");
return -1;
}
if (!resources) {
ofLog(OF_LOG_VERBOSE, "drmModeGetResources failed: %s\n", strerror(errno));
return -1;
}
/* find a connected connector: */
for (i = 0; i < resources->count_connectors; i++) {
connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
if (connector->connection == DRM_MODE_CONNECTED) {
/* it's connected, let's use this! */
ofLog() << "FOUND CONNECTION";
break;
}
drmModeFreeConnector(connector);
connector = NULL;
}
if (!connector) {
/* we could be fancy and listen for hotplug events and wait for
* a connector..
*/
ofLog(OF_LOG_VERBOSE, "no connected connector!\n");
return -1;
}
/* find user requested mode: */
/*
if (mode_str && *mode_str) {
for (i = 0; i < connector->count_modes; i++) {
drmModeModeInfo *current_mode = &connector->modes[i];
if (strcmp(current_mode->name, mode_str) == 0) {
if (vrefresh == 0 || current_mode->vrefresh == vrefresh) {
drm.mode = current_mode;
break;
}
}
}
if (!drm.mode)
{
ofLog(OF_LOG_VERBOSE, "requested mode not found, using default mode!\n");
}else
{
ofLog() << "FOUND drm.mode: " << drm.mode;
}
}*/
/* find preferred mode or the highest resolution mode: */
if (!drm.mode) {
for (i = 0, area = 0; i < connector->count_modes; i++) {
drmModeModeInfo *current_mode = &connector->modes[i];
if (current_mode->type & DRM_MODE_TYPE_PREFERRED) {
drm.mode = current_mode;
if(current_mode->hdisplay)
{
ofLog() << "current_mode hdisplay: " << current_mode->hdisplay;
}else
{
ofLog() << "NO current_mode hdisplay";
}
break;
}
int current_area = current_mode->hdisplay * current_mode->vdisplay;
if (current_area > area) {
drm.mode = current_mode;
area = current_area;
}
ofLog() << "current_area: " << current_area;
}
}
if (!drm.mode) {
ofLog(OF_LOG_VERBOSE, "could not find mode!\n");
return -1;
}
/* find encoder: */
for (i = 0; i < resources->count_encoders; i++) {
encoder = drmModeGetEncoder(drm.fd, resources->encoders[i]);
if (encoder->encoder_id == connector->encoder_id)
break;
drmModeFreeEncoder(encoder);
encoder = NULL;
}
if (encoder) {
drm.crtc_id = encoder->crtc_id;
} else {
uint32_t crtc_id = find_crtc_for_connector(drm, resources, connector);
if (crtc_id == 0) {