framework2
2302 строки · 70.4 Кб
1#include "ofAppEGLWindow.h"
2
3#include "ofGraphics.h" // used in runAppViaInfiniteLoop()
4#include "ofAppRunner.h"
5#include "ofUtils.h"
6#include "ofFileUtils.h"
7#include "ofGLProgrammableRenderer.h"
8#include "ofGLRenderer.h"
9#include "ofVectorMath.h"
10#include <assert.h>
11// x11
12#include <X11/Xutil.h>
13#include <EGL/egl.h>
14
15// include includes for both native and X11 possibilities
16#include <libudev.h>
17#include <stdbool.h>
18#include <stdio.h> // sprintf
19#include <stdlib.h> // malloc
20#include <math.h>
21#include <fcntl.h> // open fcntl
22#include <unistd.h> // read close
23#include <linux/joystick.h>
24
25#include "linux/kd.h" // keyboard stuff...
26#include "termios.h"
27#include "sys/ioctl.h"
28
29#include <string.h> // strlen
30
31// native events
32struct udev* udev;
33struct udev_monitor* mon;
34static int udev_fd = -1;
35
36typedef map<string, int> device;
37static device inputDevices;
38
39// minimal map
40const int lowercase_map[] = {
410, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
42'-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
43'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h',
44'j', 'k', 'l', ';', '\'', '\n', 0, '\\', 'z', 'x', 'c', 'v',
45'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0,
460, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
470, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
480, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r'
49
50};
51
52// minimal keyboard map
53const int uppercase_map[] = {
540, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
55'_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
56'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H',
57'J', 'K', 'L', ':', '"', '\n', 0, '\\', 'Z', 'X', 'C', 'V',
58'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0,
590, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
600, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
610, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r'
62};
63
64// keep track of a few things ...
65typedef struct {
66bool shiftPressed;
67bool capsLocked;
68} KeyboardState;
69
70static KeyboardState kb;
71
72static struct termios tc;
73static struct termios ots;
74
75typedef struct {
76int mouseButtonState;
77} MouseState;
78
79typedef map<int, int> TouchState;
80typedef map<int, ofVec2f> TouchPosition;
81
82// TODO, make this match the upcoming additions to ofWindow
83#define MOUSE_BUTTON_LEFT_MASK 1
84#define MOUSE_BUTTON_MIDDLE_MASK 1 << 1
85#define MOUSE_BUTTON_RIGHT_MASK 2 << 1
86
87static MouseState mb;
88static TouchState mt;
89static TouchPosition mtp;
90ofAppEGLWindow* ofAppEGLWindow::instance = NULL;
91
92static int string_ends_with(const char *str, const char *suffix) {
93if (!str || !suffix)
94return 0;
95size_t lenstr = strlen(str);
96size_t lensuffix = strlen(suffix);
97if (lensuffix > lenstr)
98return 0;
99return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
100}
101
102static int string_begins_with(const char *str, const char *prefix) {
103if (!str || !prefix)
104return 0;
105size_t lenstr = strlen(str);
106size_t lenprefix = strlen(prefix);
107if (lenprefix > lenstr)
108return 0;
109return strncmp(str, prefix, lenprefix) == 0;
110}
111
112// native
113#define MOUSE_CURSOR_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \
114{ unsigned int __bpp; unsigned char *__ip; const unsigned char *__il, *__rd; \
115__bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \
116__rd = (rle_data); \
117while (__ip < __il) { unsigned int __l = *(__rd++); \
118if (__l & 128) { __l = __l - 128; \
119do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \
120} else { __l *= 4; memcpy (__ip, __rd, __l); \
121__ip += __l; __rd += __l; } } \
122} while (0)
123static const struct {
124unsigned int width;
125unsigned int height;
126unsigned int bpp; /* 2:RGB16, 3:RGB, 4:RGBA */
127unsigned char rle_pixel_data[382 + 1];
128} mouse_cursor_data = {
12912, 19, 4,
130"\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"
131"\377\377\377\377\0\0\0\377\211\377\377\377\0\1\0\0\0\377\202\377\377\377"
132"\377\1\0\0\0\377\210\377\377\377\0\1\0\0\0\377\203\377\377\377\377\1\0\0"
133"\0\377\207\377\377\377\0\1\0\0\0\377\204\377\377\377\377\1\0\0\0\377\206"
134"\377\377\377\0\1\0\0\0\377\205\377\377\377\377\1\0\0\0\377\205\377\377\377"
135"\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"
136"\377\207\377\377\377\377\1\0\0\0\377\203\377\377\377\0\1\0\0\0\377\210\377"
137"\377\377\377\1\0\0\0\377\202\377\377\377\0\1\0\0\0\377\211\377\377\377\377"
138"\3\0\0\0\377\377\377\377\0\0\0\0\377\212\377\377\377\377\202\0\0\0\377\206"
139"\377\377\377\377\206\0\0\0\377\203\377\377\377\377\1\0\0\0\377\202\377\377"
140"\377\377\1\0\0\0\377\204\377\377\377\0\1\0\0\0\377\202\377\377\377\377\3"
141"\0\0\0\377\377\377\377\0\0\0\0\377\202\377\377\377\377\1\0\0\0\377\203\377"
142"\377\377\0\3\0\0\0\377\377\377\377\377\0\0\0\377\202\377\377\377\0\1\0\0"
143"\0\377\202\377\377\377\377\1\0\0\0\377\203\377\377\377\0\202\0\0\0\377\204"
144"\377\377\377\0\1\0\0\0\377\202\377\377\377\377\1\0\0\0\377\210\377\377\377"
145"\0\1\0\0\0\377\202\377\377\377\377\1\0\0\0\377\211\377\377\377\0\202\0\0"
146"\0\377\203\377\377\377\0",
147};
148
149// from http://cantuna.googlecode.com/svn-history/r16/trunk/src/screen.cpp
150#define CASE_STR(x,y) case x: str = y; break
151
152static const char* eglErrorString(EGLint err) {
153string str;
154switch (err) {
155CASE_STR(EGL_SUCCESS, "no error");
156CASE_STR(EGL_NOT_INITIALIZED, "EGL not, or could not be, initialized");
157CASE_STR(EGL_BAD_ACCESS, "access violation");
158CASE_STR(EGL_BAD_ALLOC, "could not allocate resources");
159CASE_STR(EGL_BAD_ATTRIBUTE, "invalid attribute");
160CASE_STR(EGL_BAD_CONTEXT, "invalid context specified");
161CASE_STR(EGL_BAD_CONFIG, "invald frame buffer configuration specified");
162CASE_STR(EGL_BAD_CURRENT_SURFACE, "current window, pbuffer or pixmap surface is no longer valid");
163CASE_STR(EGL_BAD_DISPLAY, "invalid display specified");
164CASE_STR(EGL_BAD_SURFACE, "invalid surface specified");
165CASE_STR(EGL_BAD_MATCH, "bad argument match");
166CASE_STR(EGL_BAD_PARAMETER, "invalid paramater");
167CASE_STR(EGL_BAD_NATIVE_PIXMAP, "invalid NativePixmap");
168CASE_STR(EGL_BAD_NATIVE_WINDOW, "invalid NativeWindow");
169CASE_STR(EGL_CONTEXT_LOST, "APM event caused context loss");
170default: str = "unknown error " + err; break;
171}
172return str.c_str();
173}
174
175
176// X11 events
177#include <X11/XKBlib.h>
178
179
180#ifdef TARGET_RASPBERRY_PI
181// TODO: remove these when they enter system headers
182// From : https://github.com/raspberrypi/userland/blob/master/interface/vmcs_host/vc_vchi_dispmanx.h
183#ifndef ELEMENT_CHANGE_LAYER
184#define ELEMENT_CHANGE_LAYER (1<<0)
185#endif
186#ifndef ELEMENT_CHANGE_OPACITY
187#define ELEMENT_CHANGE_OPACITY (1<<1)
188#endif
189#ifndef ELEMENT_CHANGE_DEST_RECT
190#define ELEMENT_CHANGE_DEST_RECT (1<<2)
191#endif
192#ifndef ELEMENT_CHANGE_SRC_RECT
193#define ELEMENT_CHANGE_SRC_RECT (1<<3)
194#endif
195#ifndef ELEMENT_CHANGE_MASK_RESOURCE
196#define ELEMENT_CHANGE_MASK_RESOURCE (1<<4)
197#endif
198#ifndef ELEMENT_CHANGE_TRANSFORM
199#define ELEMENT_CHANGE_TRANSFORM (1<<5)
200#endif
201#endif
202
203
204//-------------------------------------------------------------------------------------
205ofAppEGLWindowSettings::ofAppEGLWindowSettings()
206:ofGLESWindowSettings(){
207eglWindowPreference = OF_APP_WINDOW_AUTO;
208eglWindowOpacity = 255;
209
210// these are usually set as default, but set them here just to be sure
211frameBufferAttributes[EGL_RED_SIZE] = 8; // 8 bits for red
212frameBufferAttributes[EGL_GREEN_SIZE] = 8; // 8 bits for green
213frameBufferAttributes[EGL_BLUE_SIZE] = 8; // 8 bits for blue
214frameBufferAttributes[EGL_ALPHA_SIZE] = 8; // 8 bits for alpha
215frameBufferAttributes[EGL_LUMINANCE_SIZE] = EGL_DONT_CARE; // 8 bits for alpha
216frameBufferAttributes[EGL_DEPTH_SIZE] = 24; // 24 bits for depth
217frameBufferAttributes[EGL_STENCIL_SIZE] = 8; // 8 bits for stencil
218frameBufferAttributes[EGL_SAMPLES] = 1;
219
220initialClearColor = ofColor(0.15 * 255, 0.15 * 255, 0.15 * 255, 255);
221
222screenNum = 0; /* 0 = LCD on the raspberry pi */
223layer = 0;
224}
225
226ofAppEGLWindowSettings::ofAppEGLWindowSettings(const ofGLESWindowSettings & settings)
227:ofGLESWindowSettings(settings){
228eglWindowPreference = OF_APP_WINDOW_AUTO;
229eglWindowOpacity = 255;
230
231// these are usually set as default, but set them here just to be sure
232frameBufferAttributes[EGL_RED_SIZE] = 8; // 8 bits for red
233frameBufferAttributes[EGL_GREEN_SIZE] = 8; // 8 bits for green
234frameBufferAttributes[EGL_BLUE_SIZE] = 8; // 8 bits for blue
235frameBufferAttributes[EGL_ALPHA_SIZE] = 8; // 8 bits for alpha
236frameBufferAttributes[EGL_LUMINANCE_SIZE] = EGL_DONT_CARE; // 8 bits for alpha
237frameBufferAttributes[EGL_DEPTH_SIZE] = 24; // 24 bits for depth
238frameBufferAttributes[EGL_STENCIL_SIZE] = 8; // 8 bits for stencil
239frameBufferAttributes[EGL_SAMPLES] = 1;
240
241initialClearColor = ofColor(0.15 * 255, 0.15 * 255, 0.15 * 255, 255);
242
243screenNum = 0; /* 0 = LCD on the raspberry pi */
244layer = 0;
245}
246
247//------------------------------------------------------------
248ofAppEGLWindow::ofAppEGLWindow() {
249keyboardDetected = false;
250mouseDetected = false;
251threadTimeout = ofThread::INFINITE_JOIN_TIMEOUT;
252bNewScreenMode = false;
253buttonInUse = -1;
254bEnableSetupScreen = false;
255bShowCursor = true;
256nFramesSinceWindowResized = 0;
257mouseScaleX = 2.0f;
258mouseScaleY = 2.0f;
259isUsingX11 = false;
260isWindowInited = false;
261isSurfaceInited = false;
262x11Display = NULL;
263x11Screen = NULL;
264x11ScreenNum = 0l;
265glesVersion = 1;
266
267if(instance!=NULL){
268ofLogError("ofAppEGLWindow") << "trying to create more than one instance";
269}
270instance = this;
271}
272
273//------------------------------------------------------------
274ofAppEGLWindow::~ofAppEGLWindow() {
275close();
276}
277
278//------------------------------------------------------------
279EGLDisplay ofAppEGLWindow::getEglDisplay() const {
280return eglDisplay;
281}
282
283//------------------------------------------------------------
284EGLSurface ofAppEGLWindow::getEglSurface() const {
285return eglSurface;
286}
287
288//------------------------------------------------------------
289EGLContext ofAppEGLWindow::getEglContext() const {
290return eglContext;
291}
292
293#ifndef TARGET_RASPBERRY_PI_LEGACY
294//------------------------------------------------------------
295Display* ofAppEGLWindow::getX11Display(){
296return x11Display;
297}
298
299//------------------------------------------------------------
300Window ofAppEGLWindow::getX11Window(){
301return x11Window;
302}
303#endif
304//------------------------------------------------------------
305EGLConfig ofAppEGLWindow::getEglConfig() const {
306return eglConfig;
307}
308
309//------------------------------------------------------------
310EGLint ofAppEGLWindow::getEglVersionMajor () const {
311return eglVersionMajor;
312}
313
314//------------------------------------------------------------
315EGLint ofAppEGLWindow::getEglVersionMinor() const {
316return eglVersionMinor;
317}
318
319//------------------------------------------------------------
320void ofAppEGLWindow::initNative() {
321#ifdef TARGET_RASPBERRY_PI_LEGACY
322initRPiNative();
323#endif
324}
325
326//------------------------------------------------------------
327void ofAppEGLWindow::exitNative() {
328#ifdef TARGET_RASPBERRY_PI_LEGACY
329exitRPiNative();
330#endif
331}
332
333//------------------------------------------------------------
334EGLNativeWindowType ofAppEGLWindow::getNativeWindow() {
335if(!isWindowInited) {
336ofLogWarning("ofAppEGLWindow") << "getNativeDisplay(): window not initialized, returning NULL";
337return NULL;
338}
339
340if(isUsingX11) {
341return (EGLNativeWindowType)x11Window;
342} else {
343#ifdef TARGET_RASPBERRY_PI_LEGACY
344return (EGLNativeWindowType)&dispman_native_window;
345#else
346ofLogNotice("ofAppEGLWindow") << "getNativeWindow(): no native window type for this system, perhaps try X11?";
347return NULL;
348#endif
349}
350}
351
352//------------------------------------------------------------
353EGLNativeDisplayType ofAppEGLWindow::getNativeDisplay() {
354if(!isWindowInited) {
355ofLogWarning("ofAppEGLWindow") << "getNativeDisplay(): window not initialized, returning NULL";
356return 0;
357}
358
359if(isUsingX11) {
360return (EGLNativeDisplayType)x11Display;
361} else {
362#ifdef TARGET_RASPBERRY_PI_LEGACY
363return (EGLNativeDisplayType)NULL;
364#else
365ofLogNotice("ofAppEGLWindow") << "getNativeDisplay(): no native window type for this system, perhaps try X11?";
366return 0;
367#endif
368}
369}
370
371//------------------------------------------------------------
372void ofAppEGLWindow::setup(const ofGLESWindowSettings & settings){
373const Settings * glSettings = dynamic_cast<const Settings*>(&settings);
374if(glSettings){
375setup(*glSettings);
376}else{
377setup(Settings(settings));
378}
379}
380
381//------------------------------------------------------------
382void ofAppEGLWindow::setup(const ofAppEGLWindowSettings & _settings) {
383settings = _settings;
384windowMode = OF_WINDOW;
385bNewScreenMode = true;
386nFramesSinceWindowResized = 0;
387buttonInUse = 0;
388bEnableSetupScreen = true;
389eglDisplayString = "";
390orientation = OF_ORIENTATION_DEFAULT;
391
392//TODO: 2.0f is an arbitrary factor that makes mouse speed ok at 1024x768,
393// to be totally correct we might need to take into account screen size
394// and add acceleration
395mouseScaleX = 2.0f;
396mouseScaleY = 2.0f;
397
398isUsingX11 = false;
399isWindowInited = false;
400isSurfaceInited = false;
401
402eglDisplay = NULL;
403eglSurface = NULL;
404eglContext = NULL;
405eglConfig = NULL;
406eglVersionMajor = -1;
407eglVersionMinor = -1;
408glesVersion = 1;
409
410// X11 check
411// char * pDisplay;
412// pDisplay = getenv ("DISPLAY");
413// bool bIsX11Available = (pDisplay != NULL);
414
415bool bIsX11Available = getenv("DISPLAY") != NULL;
416
417if(settings.eglWindowPreference == OF_APP_WINDOW_AUTO) {
418if(bIsX11Available) {
419isUsingX11 = true;
420} else {
421isUsingX11 = false;
422}
423} else if(settings.eglWindowPreference == OF_APP_WINDOW_NATIVE) {
424isUsingX11 = false;
425} else if(settings.eglWindowPreference == OF_APP_WINDOW_X11) {
426isUsingX11 = true;
427if(!bIsX11Available) {
428isUsingX11 = false;
429ofLogError("ofAppEGLWindow") << "init(): X11 window requested, but X11 is not available";
430}
431}
432
433////////////////
434// TODO remove the following ifdef once x11 is accelerated on RPI
435#ifdef TARGET_RASPBERRY_PI_LEGACY
436if(isUsingX11) {
437isUsingX11 = false;
438ofLogWarning("ofAppEGLWindow") << "init(): X11 not availble on RPI yet, using a native window instead";
439}
440#endif
441////////////////
442
443initNative();
444
445glesVersion = settings.glesVersion;
446// we set this here, and if we need to make a fullscreen
447// app, we do it during the first loop.
448windowMode = settings.windowMode;
449bShowCursor = true;
450
451nonFullscreenWindowRect.set(0,0,settings.getWidth(),settings.getHeight());
452nonFullscreenWindowRect.standardize();
453
454ofRectangle startRect = nonFullscreenWindowRect;
455bNewScreenMode = false;
456
457if(windowMode == OF_GAME_MODE) {
458ofLogWarning("ofAppEGLWindow") << "setupOpenGL(): OF_GAME_MODE not supported, using OF_WINDOW";
459startRect = nonFullscreenWindowRect;
460} else if(windowMode == OF_FULLSCREEN) {
461startRect = getScreenRect();
462}
463
464isWindowInited = createWindow(startRect);
465isSurfaceInited = createSurface();
466
467if(!isWindowInited) {
468ofLogError("ofAppEGLWindow") << "setupOpenGL(): screen creation failed, window not inited";
469}
470
471setupPeripherals();
472
473nFramesSinceWindowResized = 0;
474
475if(settings.glesVersion>1){
476currentRenderer = make_shared<ofGLProgrammableRenderer>(this);
477}else{
478currentRenderer = make_shared<ofGLRenderer>(this);
479}
480
481makeCurrent();
482if(currentRenderer->getType()==ofGLProgrammableRenderer::TYPE){
483static_cast<ofGLProgrammableRenderer*>(currentRenderer.get())->setup(settings.glesVersion,0);
484}else{
485static_cast<ofGLRenderer*>(currentRenderer.get())->setup();
486}
487}
488
489//------------------------------------------------------------
490void ofAppEGLWindow::setupPeripherals() {
491if(!isUsingX11) {
492// roll our own cursor!
493mouseCursor.allocate(mouse_cursor_data.width,mouse_cursor_data.height,OF_IMAGE_COLOR_ALPHA);
494MOUSE_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);
495mouseCursor.update();
496ofLogNotice("ofAppEGLWindow") << "setupPeripherals(): peripheral setup complete";
497setupNativeEvents();
498ofLogNotice("ofAppEGLWindow") << "setupPeripherals(): native event setup complete";
499
500} else {
501ofLogError("ofAppEGLWindow") << "setupPeripherals(): peripherals not supported on X11";
502}
503}
504
505//------------------------------------------------------------
506bool ofAppEGLWindow::createSurface() {
507
508EGLNativeWindowType nativeWindow = getNativeWindow();
509EGLNativeDisplayType display = getNativeDisplay();
510
511ofLogNotice("ofAppEGLWindow") << "createSurface(): setting up EGL Display";
512// get an EGL eglDisplay connection
513
514isSurfaceInited = false;
515
516EGLint result;
517
518if(display==0){
519eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
520}else{
521eglDisplay = eglGetDisplay(display);
522}
523
524if(eglDisplay == EGL_NO_DISPLAY) {
525ofLogNotice("ofAppEGLWindow") << "createSurface(): eglGetDisplay returned: " << eglDisplay;
526return false;
527}else{
528ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL Display correctly set " << eglDisplay;
529}
530
531// initialize the EGL eglDisplay connection
532result = eglInitialize(eglDisplay,
533&eglVersionMajor,
534&eglVersionMinor);
535
536if(result == EGL_BAD_DISPLAY) {
537// eglDisplay is not an EGL connection
538ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_BAD_DISPLAY";
539return false;
540} else if(result == EGL_NOT_INITIALIZED) {
541// eglDisplay cannot be intitialized
542ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_NOT_INITIALIZED";
543return false;
544} else if(result == EGL_FALSE) {
545// eglinitialize was not initialiezd
546ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_FALSE";
547return false;
548} else {
549// result == EGL_TRUE
550// success!
551}
552
553EGLint glesVersion;
554int glesVersionForContext;
555
556if(ofGetCurrentRenderer()) {
557ofLogNotice("ofAppEGLWindow") << "createSurface(): current renderer type: " << ofGetCurrentRenderer()->getType();
558} else {
559ofLogNotice("ofAppEGLWindow") << "createSurface(): no current renderer selected";
560}
561
562if(this->glesVersion==2){
563glesVersion = EGL_OPENGL_ES2_BIT;
564glesVersionForContext = 2;
565ofLogNotice("ofAppEGLWindow") << "createSurface(): GLES2 renderer detected";
566}else{
567glesVersion = EGL_OPENGL_ES_BIT;
568glesVersionForContext = 1;
569ofLogNotice("ofAppEGLWindow") << "createSurface(): default renderer detected";
570}
571
572ofEGLAttributeListIterator iter, iterEnd;
573int i;
574
575// each attribute has 2 values, and we need one extra for the EGL_NONE terminator
576EGLint attribute_list_framebuffer_config[settings.frameBufferAttributes.size() * 2 + 3];
577
578iter = settings.frameBufferAttributes.begin();
579iterEnd = settings.frameBufferAttributes.end();
580i = 0;
581for(; iter != iterEnd; iter++) {
582attribute_list_framebuffer_config[i++] = iter->first;
583attribute_list_framebuffer_config[i++] = iter->second;
584}
585attribute_list_framebuffer_config[i++] = EGL_RENDERABLE_TYPE;
586attribute_list_framebuffer_config[i++] = glesVersion; //openGL ES version
587attribute_list_framebuffer_config[i] = EGL_NONE; // add the terminator
588
589EGLint num_configs;
590
591// get an appropriate EGL frame buffer configuration
592// http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/eglChooseConfig.html
593result = eglChooseConfig(eglDisplay,
594attribute_list_framebuffer_config,
595&eglConfig,
5961, // we only want the first one. if we want more,
597// we need to pass in an array.
598// we are optimistic and don't give it more chances
599// to find a good configuration
600&num_configs);
601
602if(result == EGL_FALSE) {
603EGLint error = eglGetError();
604ofLogError("ofAppEGLWindow") << "createSurface(): error finding valid configuration based on settings: " << eglErrorString(error);
605return false;
606}
607
608if(num_configs <= 0 || eglConfig == NULL) {
609ofLogError("ofAppEGLWindow") << "createSurface(): no matching configs were found, num_configs: " << num_configs;
610return false;
611}
612
613
614// each attribute has 2 values, and we need one extra for the EGL_NONE terminator
615EGLint attribute_list_window_surface[settings.windowSurfaceAttributes.size() * 2 + 1];
616
617iter = settings.windowSurfaceAttributes.begin();
618iterEnd = settings.windowSurfaceAttributes.end();
619
620i = 0;
621for(; iter != iterEnd; iter++) {
622attribute_list_window_surface[i++] = iter->first;
623attribute_list_window_surface[i++] = iter->second;
624}
625attribute_list_window_surface[i] = EGL_NONE; // add the terminator
626
627// create a surface
628eglSurface = eglCreateWindowSurface( eglDisplay, // our display handle
629eglConfig, // our first config
630nativeWindow, // our native window
631attribute_list_window_surface); // surface attribute list
632
633if(eglSurface == EGL_NO_SURFACE) {
634EGLint error = eglGetError();
635switch(error) {
636case EGL_BAD_MATCH:
637ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_MATCH " << eglErrorString(error);
638ofLogError("ofAppEGLWindow") << "createSurface(): check window and EGLConfig attributes to determine compatibility, ";
639ofLogError("ofAppEGLWindow") << "createSurface(): or verify that the EGLConfig supports rendering to a window";
640break;
641case EGL_BAD_CONFIG:
642ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_CONFIG " << eglErrorString(error);
643ofLogError("ofAppEGLWindow") << "createSurface(): verify that provided EGLConfig is valid";
644break;
645case EGL_BAD_NATIVE_WINDOW:
646ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_NATIVE_WINDOW " << eglErrorString(error);
647ofLogError("ofAppEGLWindow") << "createSurface(): verify that provided EGLNativeWindow is valid";
648break;
649case EGL_BAD_ALLOC:
650ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_ALLOC " << eglErrorString(error);
651ofLogError("ofAppEGLWindow") << "createSurface(): not enough resources available";
652break;
653default:
654ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: << " << error << eglErrorString(error);
655}
656
657return false;
658}else{
659ofLogNotice("ofAppEGLWindow") << "createSurface(): surface created correctly";
660}
661
662// get an appropriate EGL frame buffer configuration
663result = eglBindAPI(EGL_OPENGL_ES_API);
664
665if(result == EGL_FALSE) {
666ofLogError("ofAppEGLWindow") << "createSurface(): error binding API: " << eglErrorString(eglGetError());
667return false;
668}else{
669ofLogNotice("ofAppEGLWindow") << "createSurface(): API bound correctly";
670}
671
672// create an EGL rendering eglContext
673EGLint attribute_list_surface_context[] = {
674EGL_CONTEXT_CLIENT_VERSION, glesVersionForContext,
675EGL_NONE
676};
677
678eglContext = eglCreateContext(eglDisplay,
679eglConfig,
680EGL_NO_CONTEXT,
681attribute_list_surface_context);
682
683if(eglContext == EGL_NO_CONTEXT) {
684EGLint error = eglGetError();
685if(error == EGL_BAD_CONFIG) {
686ofLogError("ofAppEGLWindow") << "createSurface(): error creating context: EGL_BAD_CONFIG " << eglErrorString(error);
687return false;
688} else {
689ofLogError("ofAppEGLWindow") << "createSurface(): error creating context: " << error << " " << eglErrorString(error);
690return false;
691}
692}
693
694// connect the eglContext to the eglSurface
695result = eglMakeCurrent(eglDisplay,
696eglSurface, // draw surface
697eglSurface, // read surface
698eglContext);
699
700if(eglContext == nullptr) {
701EGLint error = eglGetError();
702ofLogError("ofAppEGLWindow") << "createSurface(): couldn't making current surface: " << eglErrorString(error);
703return false;
704}
705
706// Set background color and clear buffers
707glClearColor(settings.initialClearColor.r / 255.0f,
708settings.initialClearColor.g / 255.0f,
709settings.initialClearColor.b / 255.0f,
710settings.initialClearColor.a / 255.0f);
711glClear( GL_COLOR_BUFFER_BIT );
712glClear( GL_DEPTH_BUFFER_BIT );
713
714ofLogNotice("ofAppEGLWindow") << "createSurface(): -----EGL-----";
715ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION_MAJOR = " << eglVersionMajor;
716ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION_MINOR = " << eglVersionMinor;
717ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_CLIENT_APIS = " << eglQueryString(eglDisplay, EGL_CLIENT_APIS);
718ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VENDOR = " << eglQueryString(eglDisplay, EGL_VENDOR);
719ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION = " << eglQueryString(eglDisplay, EGL_VERSION);
720ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_EXTENSIONS = " << eglQueryString(eglDisplay, EGL_EXTENSIONS);
721ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_RENDERER = " << glGetString(GL_RENDERER);
722ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_VERSION = " << glGetString(GL_VERSION);
723ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_VENDOR = " << glGetString(GL_VENDOR);
724ofLogNotice("ofAppEGLWindow") << "createSurface(): -------------";
725
726isSurfaceInited = true;
727
728return true;
729}
730
731//------------------------------------------------------------
732bool ofAppEGLWindow::destroySurface() {
733if(isSurfaceInited) {
734ofLogNotice("ofAppEGLWindow") << "destroySurface(): destroying EGL surface";
735eglSwapBuffers(eglDisplay, eglSurface);
736eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
737eglDestroySurface(eglDisplay, eglSurface);
738eglDestroyContext(eglDisplay, eglContext);
739eglTerminate(eglDisplay);
740isSurfaceInited = false;
741
742eglDisplay = NULL;
743eglSurface = NULL;
744eglContext = NULL;
745eglConfig = NULL;
746eglVersionMinor = -1;
747eglVersionMinor = -1;
748
749return true;
750} else {
751ofLogError("ofAppEGLWindow") << "destroySurface(): attempted to destroy uninitialized window";
752return false;
753}
754}
755
756//------------------------------------------------------------
757bool ofAppEGLWindow::destroyWindow() {
758if(isWindowInited) {
759if(isUsingX11) {
760// TODO: double check
761XDestroyWindow(x11Display,x11Window); // or XCloseWindow?
762XFree(x11Screen);
763} else {
764#ifdef TARGET_RASPBERRY_PI_LEGACY
765dispman_update = vc_dispmanx_update_start(0);
766if (dispman_element != DISPMANX_NO_HANDLE) {
767vc_dispmanx_element_remove(dispman_update, dispman_element);
768dispman_element = DISPMANX_NO_HANDLE;
769}
770
771vc_dispmanx_update_submit_sync(dispman_update);
772
773if (dispman_display != DISPMANX_NO_HANDLE) {
774vc_dispmanx_display_close(dispman_display);
775dispman_display = DISPMANX_NO_HANDLE;
776}
777#else
778ofLogNotice("ofAppEGLWindow") << "destroyWindow(): no native window type for this system, perhaps try X11?";
779#endif
780}
781
782} else {
783ofLogNotice("ofAppEGLWindow") << "destroyWindow(): destroying (uninited) native window (not implemented yet)";
784}
785
786return true;
787}
788
789
790void ofAppEGLWindow::close(){
791if(!isUsingX11) {
792destroyNativeEvents();
793}
794
795// we got a terminate ... so clean up.
796destroySurface();
797destroyWindow();
798
799exitNative();
800events().notifyExit();
801events().disable();
802}
803
804//------------------------------------------------------------
805void ofAppEGLWindow::makeCurrent(){
806eglMakeCurrent(eglDisplay,
807eglSurface, // draw surface
808eglSurface, // read surface
809eglContext);
810}
811
812//------------------------------------------------------------
813void ofAppEGLWindow::swapBuffers(){
814EGLBoolean success = eglSwapBuffers(eglDisplay, eglSurface);
815if(!success) {
816GLint error = eglGetError();
817ofLogNotice("ofAppEGLWindow") << "display(): eglSwapBuffers failed: " << eglErrorString(error);
818}
819}
820
821//--------------------------------------------
822void ofAppEGLWindow::startRender() {
823renderer()->startRender();
824}
825
826//--------------------------------------------
827void ofAppEGLWindow::finishRender() {
828renderer()->finishRender();
829}
830
831//------------------------------------------------------------
832void ofAppEGLWindow::update() {
833coreEvents.notifyUpdate();
834}
835
836
837//------------------------------------------------------------
838void ofAppEGLWindow::draw() {
839// take care of any requests for a new screen mode
840if (windowMode != OF_GAME_MODE && bNewScreenMode){
841if( windowMode == OF_FULLSCREEN){
842setWindowRect(getScreenRect());
843} else if( windowMode == OF_WINDOW ){
844setWindowRect(nonFullscreenWindowRect);
845}
846bNewScreenMode = false;
847}
848
849currentRenderer->startRender();
850if( bEnableSetupScreen ) currentRenderer->setupScreen();
851
852coreEvents.notifyDraw();
853
854if(!isUsingX11) {
855if(bShowCursor){
856GLboolean bIsDepthTestEnabled = GL_FALSE;
857glGetBooleanv(GL_DEPTH_TEST, &bIsDepthTestEnabled);
858
859if(bIsDepthTestEnabled == GL_TRUE) {
860glDisable(GL_DEPTH_TEST);
861}
862
863bool isUsingNormalizedTexCoords = ofGetUsingNormalizedTexCoords();
864if(isUsingNormalizedTexCoords) {
865ofDisableNormalizedTexCoords();
866}
867
868currentRenderer->pushStyle();
869currentRenderer->setBlendMode(OF_BLENDMODE_ADD);
870currentRenderer->setColor(255);
871mouseCursor.draw(ofGetMouseX(),ofGetMouseY());
872
873currentRenderer->popStyle();
874
875if(bIsDepthTestEnabled == GL_TRUE) {
876glEnable(GL_DEPTH_TEST);
877}
878
879if(isUsingNormalizedTexCoords) {
880ofEnableNormalizedTexCoords();
881}
882}
883}
884currentRenderer->finishRender();
885
886EGLBoolean success = eglSwapBuffers(eglDisplay, eglSurface);
887if(!success) {
888GLint error = eglGetError();
889ofLogNotice("ofAppEGLWindow") << "display(): eglSwapBuffers failed: " << eglErrorString(error);
890}
891
892nFramesSinceWindowResized++;
893
894}
895
896//------------------------------------------------------------
897ofCoreEvents & ofAppEGLWindow::events(){
898return coreEvents;
899}
900
901//------------------------------------------------------------
902shared_ptr<ofBaseRenderer> & ofAppEGLWindow::renderer(){
903return currentRenderer;
904}
905
906//------------------------------------------------------------
907void ofAppEGLWindow::setupNativeEvents() {
908setupNativeUDev();
909setupNativeInput();
910startThread();
911}
912
913//------------------------------------------------------------
914void ofAppEGLWindow::destroyNativeEvents() {
915destroyNativeUDev();
916destroyNativeInput();
917waitForThread(true, threadTimeout);
918}
919
920//------------------------------------------------------------
921void ofAppEGLWindow::setWindowRect(const ofRectangle& requestedWindowRect) {
922if(!isWindowInited) {
923ofLogError("ofAppEGLWindow") << "setWindowRect(): window not inited";
924return;
925}
926
927ofRectangle newRect = requestedWindowRect.getStandardized();
928
929if(newRect != currentWindowRect) {
930ofRectangle oldWindowRect = currentWindowRect;
931
932if(isUsingX11) {
933int ret = XMoveResizeWindow(x11Display,
934x11Window,
935(int)newRect.x,
936(int)newRect.y,
937(unsigned int)newRect.width,
938(unsigned int)newRect.height);
939if(ret == BadValue) {
940ofLogError("ofAppEGLWindow") << "setWindowRect(): XMoveResizeWindow returned BadValue";
941} else if(ret == BadWindow) {
942ofLogError("ofAppEGLWindow") << "setWindowRect(): XMoveResizeWindow returned BadWindow";
943} else {
944// all is good
945currentWindowRect = newRect;
946}
947} else {
948#ifdef TARGET_RASPBERRY_PI_LEGACY
949
950VC_RECT_T dst_rect;
951dst_rect.x = (int32_t)newRect.x;
952dst_rect.y = (int32_t)newRect.y;
953dst_rect.width = (int32_t)newRect.width;
954dst_rect.height = (int32_t)newRect.height;
955
956VC_RECT_T src_rect;
957src_rect.x = 0;
958src_rect.y = 0;
959src_rect.width = (int32_t)newRect.width << 16;
960src_rect.height = (int32_t)newRect.height << 16;
961
962DISPMANX_UPDATE_HANDLE_T dispman_update = vc_dispmanx_update_start(0);
963
964vc_dispmanx_element_change_attributes(dispman_update,
965dispman_element,
966ELEMENT_CHANGE_SRC_RECT|ELEMENT_CHANGE_DEST_RECT, // we do both when resizing
9670, // layer (we aren't changing it here)
9680, // opactiy (we aren't changing it here)
969&dst_rect,
970&src_rect,
9710, // mask (we aren't changing it here)
972(DISPMANX_TRANSFORM_T)0);
973
974
975vc_dispmanx_update_submit_sync(dispman_update);
976
977// next time swapBuffers is called, it will be resized based on this eglwindow size data
978dispman_native_window.element = dispman_element;
979dispman_native_window.width = (int32_t)newRect.width;
980dispman_native_window.height = (int32_t)newRect.height; // don't forget!
981
982currentWindowRect = newRect;
983
984#else
985ofLogError("ofAppEGLWindow") << "createEGLWindow(): no native window type for this system, perhaps try X11?";
986#endif
987}
988
989if(oldWindowRect.width != currentWindowRect.width || oldWindowRect.height != currentWindowRect.height) {
990coreEvents.notifyWindowResized(currentWindowRect.width, currentWindowRect.height);
991nFramesSinceWindowResized = 0;
992}
993}
994}
995
996
997//------------------------------------------------------------
998bool ofAppEGLWindow::createWindow(const ofRectangle& requestedWindowRect) {
999if(isUsingX11) {
1000return createX11NativeWindow(requestedWindowRect);
1001} else {
1002#ifdef TARGET_RASPBERRY_PI_LEGACY
1003return createRPiNativeWindow(requestedWindowRect);
1004#else
1005ofLogError("ofAppEGLWindow") << "createEGLWindow(): no native window type for this system, perhaps try X11?";
1006return false;
1007#endif
1008}
1009}
1010
1011//------------------------------------------------------------
1012int ofAppEGLWindow::getWindowWidth() {
1013return currentWindowRect.width;
1014}
1015
1016//------------------------------------------------------------
1017int ofAppEGLWindow::getWindowHeight() {
1018return currentWindowRect.height;
1019}
1020
1021//------------------------------------------------------------
1022void ofAppEGLWindow::pollEvents(){
1023if(!instance) return;
1024if(instance->isUsingX11) {
1025while(1){
1026XEvent event;
1027if (::XCheckWindowEvent(instance->x11Display, instance->x11Window, -1, &event)){
1028handleX11Event(event);
1029}else if (::XCheckTypedEvent(instance->x11Display, ClientMessage, &event)){
1030handleX11Event(event);
1031}else{
1032break;
1033}
1034}
1035} else {
1036queue<ofMouseEventArgs> mouseEventsCopy;
1037instance->lock();
1038mouseEventsCopy = instance->mouseEvents;
1039while(!instance->mouseEvents.empty()){
1040instance->mouseEvents.pop();
1041}
1042instance->unlock();
1043while(!mouseEventsCopy.empty()){
1044instance->coreEvents.notifyMouseEvent(mouseEventsCopy.front());
1045mouseEventsCopy.pop();
1046}
1047
1048// KEYBOARD EVENTS
1049queue<ofKeyEventArgs> keyEventsCopy;
1050instance->lock();
1051keyEventsCopy = instance->keyEvents;
1052while(!instance->keyEvents.empty()){
1053instance->keyEvents.pop();
1054}
1055instance->unlock();
1056while(!keyEventsCopy.empty()){
1057instance->coreEvents.notifyKeyEvent(keyEventsCopy.front());
1058keyEventsCopy.pop();
1059}
1060
1061queue<ofTouchEventArgs> touchEventsCopy;
1062instance->lock();
1063touchEventsCopy = instance->touchEvents;
1064while(!instance->touchEvents.empty()){
1065instance->touchEvents.pop();
1066}
1067instance->unlock();
1068while(!touchEventsCopy.empty()){
1069instance->coreEvents.notifyTouchEvent(touchEventsCopy.front());
1070touchEventsCopy.pop();
1071}
1072}
1073}
1074
1075//------------------------------------------------------------
1076void ofAppEGLWindow::hideCursor(){
1077bShowCursor = false;
1078}
1079
1080//------------------------------------------------------------
1081void ofAppEGLWindow::showCursor(){
1082bShowCursor = true;
1083}
1084
1085//------------------------------------------------------------
1086void ofAppEGLWindow::setWindowTitle(string title) {
1087ofLogNotice("ofAppEGLWindow") << "setWindowTitle(): not implemented";
1088}
1089
1090//------------------------------------------------------------
1091glm::vec2 ofAppEGLWindow::getWindowSize(){
1092return {currentWindowRect.width, currentWindowRect.height};
1093}
1094
1095//------------------------------------------------------------
1096glm::vec2 ofAppEGLWindow::getWindowPosition(){
1097return glm::vec2(currentWindowRect.getPosition());
1098}
1099
1100//------------------------------------------------------------
1101glm::vec2 ofAppEGLWindow::getScreenSize(){
1102unsigned int screenWidth = 0;
1103unsigned int screenHeight = 0;
1104
1105if(isUsingX11) {
1106// TODO, there must be a way to get screensize if the window is not inited
1107if(isWindowInited && x11Screen) {
1108screenWidth = XWidthOfScreen(x11Screen);
1109screenHeight = XHeightOfScreen(x11Screen);
1110} else {
1111ofLogError("ofAppEGLWindow") << "getScreenSize(): tried to get display size but failed, x11Screen is not inited";
1112}
1113
1114} else {
1115#ifdef TARGET_RASPBERRY_PI_LEGACY
1116int success = graphics_get_display_size(settings.screenNum, &screenWidth, &screenHeight);
1117if(success < 0) {
1118ofLogError("ofAppEGLWindow") << "getScreenSize(): tried to get display size but failed";
1119}
1120
1121#else
1122ofLogError("ofAppEGLWindow") << "getScreenSize(): no native window type for this system, perhaps try X11?";
1123#endif
1124
1125}
1126
1127return {screenWidth, screenHeight};
1128}
1129
1130//------------------------------------------------------------
1131int ofAppEGLWindow::getWidth(){
1132if( orientation == OF_ORIENTATION_DEFAULT || orientation == OF_ORIENTATION_180 ){
1133return currentWindowRect.width;
1134}
1135return currentWindowRect.height;
1136}
1137
1138//------------------------------------------------------------
1139int ofAppEGLWindow::getHeight(){
1140if( orientation == OF_ORIENTATION_DEFAULT || orientation == OF_ORIENTATION_180 ){
1141return currentWindowRect.height;
1142}
1143return currentWindowRect.width;
1144}
1145
1146//------------------------------------------------------------
1147void ofAppEGLWindow::setOrientation(ofOrientation orientationIn){
1148orientation = orientationIn;
1149}
1150
1151//------------------------------------------------------------
1152ofOrientation ofAppEGLWindow::getOrientation(){
1153return orientation;
1154}
1155
1156//------------------------------------------------------------
1157bool ofAppEGLWindow::doesHWOrientation() {
1158return false;
1159}
1160
1161//------------------------------------------------------------
1162void ofAppEGLWindow::setWindowPosition(int x, int y){
1163if(!isWindowInited) {
1164ofLogError("ofAppEGLWindow") << "setWindowPosition(): window not inited";
1165return;
1166}
1167
1168if(isUsingX11) {
1169int ret = XMoveWindow(x11Display,
1170x11Window,
1171x,
1172y);
1173if(ret == BadValue) {
1174ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadValue";
1175} else if(ret == BadWindow) {
1176ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadWindow";
1177} else {
1178currentWindowRect.x = x;
1179currentWindowRect.y = y;
1180nonFullscreenWindowRect = currentWindowRect;
1181}
1182} else {
1183#ifdef TARGET_RASPBERRY_PI_LEGACY
1184
1185// keep it in bounds
1186auto screenSize = getScreenSize();
1187x = ofClamp(x, 0, screenSize.x - currentWindowRect.width);
1188y = ofClamp(y, 0, screenSize.y - currentWindowRect.height);
1189
1190VC_RECT_T dst_rect;
1191dst_rect.x = (int32_t)x;
1192dst_rect.y = (int32_t)y;
1193dst_rect.width = (int32_t)currentWindowRect.width;
1194dst_rect.height = (int32_t)currentWindowRect.height;
1195
1196dispman_update = vc_dispmanx_update_start(0);
1197
1198vc_dispmanx_element_change_attributes(dispman_update,
1199dispman_native_window.element,
1200ELEMENT_CHANGE_DEST_RECT,
12010,
12020,
1203&dst_rect,
1204NULL,
12050,
1206(DISPMANX_TRANSFORM_T)0);
1207
1208
1209vc_dispmanx_update_submit_sync(dispman_update);
1210
1211currentWindowRect.x = x;
1212currentWindowRect.y = y;
1213nonFullscreenWindowRect = currentWindowRect;
1214
1215#else
1216ofLogError("ofAppEGLWindow") << "setWindowPosition(): no native window type for this system, perhaps try X11?";
1217#endif
1218}
1219
1220}
1221
1222//------------------------------------------------------------
1223void ofAppEGLWindow::setWindowShape(int w, int h){
1224if(!isWindowInited) {
1225ofLogError("ofAppEGLWindow") << "setWindowPosition(): window not inited";
1226return;
1227}
1228
1229if(isUsingX11) {
1230int ret = XResizeWindow(x11Display,
1231x11Window,
1232(unsigned int)w,
1233(unsigned int)h);
1234if(ret == BadValue) {
1235ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadValue";
1236} else if(ret == BadWindow) {
1237ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadWindow";
1238} else {
1239currentWindowRect.width = w;
1240currentWindowRect.height = h;
1241nonFullscreenWindowRect = currentWindowRect;
1242}
1243} else {
1244#ifdef TARGET_RASPBERRY_PI_LEGACY
1245setWindowRect(ofRectangle(currentWindowRect.x,currentWindowRect.y,w,h));
1246nonFullscreenWindowRect = currentWindowRect;
1247#else
1248ofLogError("ofAppEGLWindow") << "setWindowPosition(): no native window type for this system, perhaps try X11?";
1249#endif
1250}
1251}
1252
1253//------------------------------------------------------------
1254ofWindowMode ofAppEGLWindow::getWindowMode(){
1255return windowMode;
1256}
1257
1258//------------------------------------------------------------
1259void ofAppEGLWindow::toggleFullscreen(){
1260if( windowMode == OF_GAME_MODE) return;
1261
1262if( windowMode == OF_WINDOW ){
1263setFullscreen(true);
1264}else{
1265setFullscreen(false);
1266}
1267
1268}
1269
1270//------------------------------------------------------------
1271void ofAppEGLWindow::setFullscreen(bool fullscreen){
1272if( windowMode == OF_GAME_MODE) return;
1273
1274if(fullscreen && windowMode != OF_FULLSCREEN){
1275bNewScreenMode = true;
1276windowMode = OF_FULLSCREEN;
1277}else if(!fullscreen && windowMode != OF_WINDOW) {
1278bNewScreenMode = true;
1279windowMode = OF_WINDOW;
1280}
1281}
1282
1283//------------------------------------------------------------
1284void ofAppEGLWindow::enableSetupScreen(){
1285bEnableSetupScreen = true;
1286}
1287
1288//------------------------------------------------------------
1289void ofAppEGLWindow::disableSetupScreen(){
1290bEnableSetupScreen = false;
1291}
1292
1293//------------------------------------------------------------
1294ofRectangle ofAppEGLWindow::getScreenRect(){
1295auto screenSize = getScreenSize();
1296return ofRectangle(0,0,screenSize.x,screenSize.y);
1297}
1298
1299//------------------------------------------------------------
1300void ofAppEGLWindow::setVerticalSync(bool enabled){
1301eglSwapInterval(eglDisplay, enabled ? 1 : 0);
1302}
1303
1304//------------------------------------------------------------
1305void ofAppEGLWindow::threadedFunction(){
1306// TODO: a way to setup mouse and keyboard if
1307// they are not plugged in upon start
1308// This can be done with our udev device callbacks
1309
1310while(isThreadRunning()) {
1311readNativeUDevEvents();
1312readNativeInputEvents();
1313// sleep briefly
1314ofSleepMillis(20);
1315}
1316}
1317
1318//------------------------------------------------------------
1319// PLATFORM SPECIFIC RPI
1320//------------------------------------------------------------
1321
1322//------------------------------------------------------------
1323void ofAppEGLWindow::setupNativeUDev() {
1324
1325udev = udev_new(); // create new udev object
1326if(!udev) {
1327ofLogError("ofAppEGLWindow") << "setupNativeUDev(): couldn't create udev object";
1328} else {
1329ofLogNotice("ofAppEGLWindow") << "setupNativeUDev(): created udev object";
1330// setup udev to monitor for input devices
1331mon = udev_monitor_new_from_netlink(udev, "udev");
1332// just listen for input devices
1333udev_monitor_filter_add_match_subsystem_devtype(mon, "input", NULL);
1334udev_monitor_enable_receiving(mon);
1335// get the file descriptor for the mon (used w/ select);
1336udev_fd = udev_monitor_get_fd(mon);
1337}
1338
1339if(udev_fd < 0) {
1340ofLogError("ofAppEGLWindow") << "setupNativeUDev(): did not create udev object, udev_fd < 0";
1341}
1342
1343}
1344
1345//------------------------------------------------------------
1346void ofAppEGLWindow::destroyNativeUDev() {
1347udev_unref(udev); // clean up
1348}
1349
1350void ofAppEGLWindow::setupNativeInput(){
1351struct udev_enumerate *enumerate;
1352struct udev_list_entry *devices, *entry;
1353struct udev_device *dev;
1354bool isMouse;
1355
1356ofLogNotice("ofAppEGLWindow") << "setupNativeInput()";
1357
1358/* Create a list of the devices in the 'input' subsystem. */
1359enumerate = udev_enumerate_new(udev);
1360udev_enumerate_add_match_subsystem(enumerate, "input");
1361
1362udev_enumerate_scan_devices(enumerate);
1363
1364devices = udev_enumerate_get_list_entry(enumerate);
1365
1366udev_list_entry_foreach(entry, devices)
1367{
1368/* Get the filename of the /sys entry for the device
1369and create a udev_device object (dev) representing it */
1370const char * name = udev_list_entry_get_name(entry);
1371
1372dev = udev_device_new_from_syspath(udev, name);
1373
1374const char * sysname = udev_device_get_sysname(dev);
1375const char * devnode = udev_device_get_devnode(dev);
1376const char * devpath = udev_device_get_devpath(dev);
1377const char * devtype = udev_device_get_devtype(dev);
1378dev_t devnum = udev_device_get_devnum(dev);
1379const char * driver = udev_device_get_driver(dev);
1380const char * prop_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
1381const char * prop_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
1382const char * prop_touch = udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
1383
1384ofLogNotice() << "Got device";
1385ofLogNotice() << " - node: " << devnode;
1386ofLogNotice() << " - sysname: " << sysname;
1387ofLogNotice() << " - devpath: " << devpath;
1388ofLogNotice() << " - devtype: " << devtype;
1389ofLogNotice() << " - driver: " << driver;
1390ofLogNotice() << " - devnum: " << devnum;
1391ofLogNotice() << " - ID_INPUT_KEYBOARD: " << prop_keyboard;
1392ofLogNotice() << " - ID_INPUT_MOUSE: " << prop_mouse;
1393ofLogNotice() << " - ID_INPUT_TOUCHSCREEN: " << prop_touch;
1394
1395if(prop_mouse || prop_touch){
1396isMouse = true;
1397}else{
1398isMouse = false;
1399}
1400
1401if(devnode && (prop_keyboard || prop_mouse || prop_touch) && string_begins_with(sysname, "event")){
1402addInput(devnode, isMouse);
1403}
1404if(prop_keyboard){
1405keyboardDetected = true;
1406}
1407if(prop_mouse || prop_touch){
1408mouseDetected = true;
1409}
1410
1411udev_device_unref(dev);
1412}
1413/* Free the enumerator object */
1414udev_enumerate_unref(enumerate);
1415
1416if(!mouseDetected){
1417ofLogError("ofAppEGLWindow") << "setupNativeInput(): did not open mouse";
1418}
1419if(!keyboardDetected){
1420ofLogError("ofAppEGLWindow") << "setupKeyboard(): did not open keyboard";
1421}
1422
1423// save current terminal settings
1424tcgetattr (STDIN_FILENO, &tc);
1425ots = tc;
1426// disable echo on our temporary settings
1427tc.c_lflag &= ~ECHO;
1428tc.c_lflag |= ECHONL;
1429tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc);
1430
1431mb.mouseButtonState = 0;
1432
1433kb.shiftPressed = false;
1434kb.capsLocked = false;
1435
1436printInput();
1437}
1438
1439void ofAppEGLWindow::addInput(const char * node, bool isMouse){
1440if(node == NULL){
1441return;
1442}
1443
1444removeInput(node);
1445
1446int fd = open(node, O_RDONLY | O_NONBLOCK);
1447if(fd >= 0){
1448char deviceNameBuffer[256] = "Unknown Device";
1449ioctl(fd, EVIOCGNAME(sizeof(deviceNameBuffer)), deviceNameBuffer);
1450ofLogNotice("ofAppEGLWindow") << "addInput(): input device name = " << deviceNameBuffer;
1451
1452if(isMouse){
1453struct input_absinfo mabsx;
1454if(ioctl(fd, EVIOCGABS(0), &mabsx) < 0){
1455ofLogError("ofAppEGLWindow") << "ioctl GABS failed";
1456} else {
1457mouseAbsXMin = mabsx.minimum;
1458mouseAbsXMax = mabsx.maximum;
1459ofLogNotice("ofAppEGLWindow") << "mouse x axis min, max: " << mouseAbsXMin << ", " << mouseAbsXMax;
1460}
1461
1462// Do that for the y axis. EVIOCGABS(1): 1 stands for y axis.
1463struct input_absinfo mabsy;
1464if(ioctl(fd, EVIOCGABS(1), &mabsy) < 0){
1465ofLogError("ofAppEGLWindow") << "ioctl GABS failed";
1466}else{
1467mouseAbsYMin = mabsy.minimum;
1468mouseAbsYMax = mabsy.maximum;
1469ofLogNotice("ofAppEGLWindow") << "mouse y axis min, max: " << mouseAbsYMin << ", " << mouseAbsYMax;
1470}
1471}
1472
1473inputDevices[node] = fd;
1474}
1475}
1476
1477void ofAppEGLWindow::removeInput(const char * node){
1478if(node == NULL)
1479return;
1480
1481device::iterator iter = inputDevices.find(node);
1482if(iter != inputDevices.end()){
1483::close(iter->second);
1484inputDevices.erase(iter);
1485}
1486}
1487
1488void ofAppEGLWindow::printInput(){
1489ofLogNotice("--- Input Device List ---");
1490for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
1491ofLogNotice() << " - " << iter->first;
1492}
1493ofLogNotice("-------------------------");
1494}
1495
1496void ofAppEGLWindow::destroyNativeInput(){
1497ofLogNotice("ofAppEGLWindow") << "destroyNativeInput()";
1498
1499for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
1500if(iter->second >= 0){
1501::close(iter->second);
1502}
1503}
1504
1505inputDevices.clear();
1506
1507tcsetattr (STDIN_FILENO, TCSAFLUSH, &ots);
1508}
1509
1510//------------------------------------------------------------
1511void ofAppEGLWindow::processInput(int fd, const char * node){
1512// http://www.diegm.uniud.it/loghi/CE2/kbd.pdf
1513// http://cgit.freedesktop.org/~whot/evtest/plain/evtest.c
1514// https://strcpy.net/b/archives/2010/11/17/abusing_the_linux_input_subsystem/index.html
1515static ofTouchEventArgs touchEvent;
1516static ofKeyEventArgs keyEvent;
1517static ofMouseEventArgs mouseEvent;
1518struct input_event ev;
1519char key = 0;
1520
1521bool pushKeyEvent = false;
1522bool pushMouseEvent = false;
1523bool pushTouchEvent = false;
1524bool axisValuePending = false;
1525bool touchAxisValuePending = false;
1526
1527int nBytesRead = read(fd, &ev, sizeof(struct input_event));
1528while(nBytesRead >= 0){
1529if(ev.type == EV_KEY){
1530if(ev.code == BTN_LEFT){
1531ofLogNotice("ofAppEGLWindow") << "BTN_LEFT" << endl;
1532if(ev.value == 0){ // release
1533mouseEvent.button = OF_MOUSE_BUTTON_LEFT;
1534mouseEvent.type = ofMouseEventArgs::Released;
1535mb.mouseButtonState &= ~MOUSE_BUTTON_LEFT_MASK;
1536pushMouseEvent = true;
1537}else if(ev.value == 1){ // press
1538mb.mouseButtonState |= MOUSE_BUTTON_LEFT_MASK;
1539mouseEvent.type = ofMouseEventArgs::Pressed;
1540mouseEvent.button = OF_MOUSE_BUTTON_LEFT;
1541pushMouseEvent = true;
1542}
1543}else if(ev.code == BTN_MIDDLE){
1544ofLogNotice("ofAppEGLWindow") << "BTN_MIDDLE" << endl;
1545if(ev.value == 0){ // release
1546mouseEvent.button = OF_MOUSE_BUTTON_MIDDLE;
1547mouseEvent.type = ofMouseEventArgs::Released;
1548mb.mouseButtonState &= ~MOUSE_BUTTON_MIDDLE_MASK;
1549pushMouseEvent = true;
1550}else if(ev.value == 1){ // press
1551mb.mouseButtonState |= MOUSE_BUTTON_MIDDLE_MASK;
1552mouseEvent.type = ofMouseEventArgs::Pressed;
1553mouseEvent.button = OF_MOUSE_BUTTON_MIDDLE;
1554pushMouseEvent = true;
1555}
1556}else if(ev.code == BTN_RIGHT){
1557ofLogNotice("ofAppEGLWindow") << "BTN_RIGHT" << endl;
1558if(ev.value == 0){ // release
1559mouseEvent.button = OF_MOUSE_BUTTON_RIGHT;
1560mouseEvent.type = ofMouseEventArgs::Released;
1561mb.mouseButtonState &= ~MOUSE_BUTTON_RIGHT_MASK;
1562pushMouseEvent = true;
1563}else if(ev.value == 1){ // press
1564mb.mouseButtonState |= MOUSE_BUTTON_RIGHT_MASK;
1565mouseEvent.type = ofMouseEventArgs::Pressed;
1566mouseEvent.button = OF_MOUSE_BUTTON_RIGHT;
1567pushMouseEvent = true;
1568}
1569}else if(ev.code == BTN_TOUCH){
1570if(ev.value == 0){ // release
1571touchEvent.type = ofTouchEventArgs::up;
1572touchEvent.id = 0;
1573mt[touchEvent.id] = 0;
1574pushTouchEvent = true;
1575
1576}else if(ev.value == 1){ // press
1577touchEvent.type = ofTouchEventArgs::down;
1578touchEvent.id = 0;
1579mt[touchEvent.id] = 1;
1580pushTouchEvent = true;
1581}
1582}else{
1583if(ev.value == 0){
1584// key released
1585keyEvent.type = ofKeyEventArgs::Released;
1586}else if(ev.value == 1){
1587// key pressed
1588keyEvent.type = ofKeyEventArgs::Pressed;
1589}else if(ev.value == 2){
1590// key repeated
1591keyEvent.type = ofKeyEventArgs::Pressed;
1592}else{
1593// unknown ev.value
1594}
1595switch (ev.code) {
1596case KEY_RIGHTSHIFT:
1597case KEY_LEFTSHIFT:
1598kb.shiftPressed = ev.value;
1599break;
1600case KEY_RIGHTCTRL:
1601case KEY_LEFTCTRL:
1602break;
1603case KEY_CAPSLOCK:
1604if (ev.value == 1) {
1605if (kb.capsLocked) {
1606kb.capsLocked = 0;
1607} else {
1608kb.capsLocked = 1;
1609}
1610}
1611break;
1612case KEY_ESC:
1613pushKeyEvent = true;
1614keyEvent.key = OF_KEY_ESC;
1615break;
1616case KEY_BACKSPACE:
1617pushKeyEvent = true;
1618keyEvent.key = OF_KEY_BACKSPACE;
1619break;
1620case KEY_DELETE:
1621pushKeyEvent = true;
1622keyEvent.key = OF_KEY_DEL;
1623break;
1624case KEY_F1:
1625pushKeyEvent = true;
1626keyEvent.key = OF_KEY_F1;
1627break;
1628case KEY_F2:
1629pushKeyEvent = true;
1630keyEvent.key = OF_KEY_F2;
1631break;
1632case KEY_F3:
1633pushKeyEvent = true;
1634keyEvent.key = OF_KEY_F3;
1635break;
1636case KEY_F4:
1637pushKeyEvent = true;
1638keyEvent.key = OF_KEY_F4;
1639break;
1640case KEY_F5:
1641pushKeyEvent = true;
1642keyEvent.key = OF_KEY_F5;
1643break;
1644case KEY_F6:
1645pushKeyEvent = true;
1646keyEvent.key = OF_KEY_F6;
1647break;
1648case KEY_F7:
1649pushKeyEvent = true;
1650keyEvent.key = OF_KEY_F7;
1651break;
1652case KEY_F8:
1653pushKeyEvent = true;
1654keyEvent.key = OF_KEY_F8;
1655break;
1656case KEY_F9:
1657pushKeyEvent = true;
1658keyEvent.key = OF_KEY_F9;
1659break;
1660case KEY_F10:
1661pushKeyEvent = true;
1662keyEvent.key = OF_KEY_F10;
1663break;
1664case KEY_F11:
1665pushKeyEvent = true;
1666keyEvent.key = OF_KEY_F11;
1667break;
1668case KEY_F12:
1669pushKeyEvent = true;
1670keyEvent.key = OF_KEY_F12;
1671break;
1672case KEY_LEFT:
1673pushKeyEvent = true;
1674keyEvent.key = OF_KEY_LEFT;
1675break;
1676case KEY_UP:
1677pushKeyEvent = true;
1678keyEvent.key = OF_KEY_UP;
1679break;
1680case KEY_RIGHT:
1681pushKeyEvent = true;
1682keyEvent.key = OF_KEY_RIGHT;
1683break;
1684case KEY_DOWN:
1685pushKeyEvent = true;
1686keyEvent.key = OF_KEY_DOWN;
1687break;
1688case KEY_PAGEUP:
1689pushKeyEvent = true;
1690keyEvent.key = OF_KEY_PAGE_UP;
1691break;
1692case KEY_PAGEDOWN:
1693pushKeyEvent = true;
1694keyEvent.key = OF_KEY_PAGE_DOWN;
1695break;
1696case KEY_HOME:
1697pushKeyEvent = true;
1698keyEvent.key = OF_KEY_HOME;
1699break;
1700case KEY_END:
1701pushKeyEvent = true;
1702keyEvent.key = OF_KEY_END;
1703break;
1704case KEY_INSERT:
1705pushKeyEvent = true;
1706keyEvent.key = OF_KEY_INSERT;
1707break;
1708case KEY_ENTER:
1709case KEY_KPENTER:
1710pushKeyEvent = true;
1711keyEvent.key = OF_KEY_RETURN;
1712break;
1713default:
1714// VERY RUDIMENTARY KEY MAPPING WITH MAPS ABOVE
1715if(ev.code < sizeof(lowercase_map)){
1716if(kb.shiftPressed){
1717key = uppercase_map[ev.code];
1718if(kb.capsLocked) keyEvent.key = tolower(key);
1719keyEvent.key = key;
1720pushKeyEvent = true;
1721}else{
1722key = lowercase_map[ev.code];
1723if(kb.capsLocked) key = toupper(key);
1724keyEvent.key = key;
1725pushKeyEvent = true;
1726}
1727}else{
1728ofLogNotice("ofAppEGLWindow") << "readKeyboardEvents(): input_event.code is outside of our small range";
1729}
1730}
1731}
1732}else if (ev.type == EV_REL){
1733int axis = ev.code;
1734int amount = ev.value;
1735switch (axis)
1736{
1737case REL_X:
1738mouseEvent.x += amount * mouseScaleX;
1739mouseEvent.x = ofClamp(mouseEvent.x, 0, currentWindowRect.width);
1740axisValuePending = true;
1741break;
1742case REL_Y:
1743mouseEvent.y += amount * mouseScaleY;
1744mouseEvent.y = ofClamp(mouseEvent.y, 0, currentWindowRect.height);
1745axisValuePending = true;
1746break;
1747default:
1748ofLogNotice("ofAppEGLWindow") << "readMouseEvents(): unknown mouse axis (perhaps it's the scroll wheel?): axis " << axis << " amount " << amount << endl;
1749break;
1750}
1751}else if (ev.type == EV_ABS){
1752int axis = ev.code;
1753int amount = ev.value;
1754switch (axis)
1755{
1756// do not need this mouse returns REL_X/REL_Y
1757case ABS_X:
1758// mouseEvent.x = amount * (float)currentWindowRect.width / (float)mouseAbsXMax;
1759// mouseEvent.x = ofClamp(mouseEvent.x, 0, currentWindowRect.width);
1760// axisValuePending = true;
1761break;
1762case ABS_Y:
1763// mouseEvent.y = amount * (float)currentWindowRect.height / (float)mouseAbsYMax;
1764// mouseEvent.y = ofClamp(mouseEvent.y, 0, currentWindowRect.height);
1765// axisValuePending = true;
1766break;
1767case ABS_MT_TOOL_TYPE:
1768break;
1769case ABS_MT_SLOT:
1770touchEvent.id = amount;
1771break;
1772case ABS_MT_TRACKING_ID:
1773if (amount == -1)
1774{
1775if( mt[touchEvent.id] == 1){
1776touchEvent.type = ofTouchEventArgs::up;
1777mt[touchEvent.id] = 0;
1778pushTouchEvent = true;
1779}
1780}
1781else
1782{
1783if (mt[touchEvent.id] == 0){
1784touchEvent.type = ofTouchEventArgs::down;
1785mt[touchEvent.id] = 1;
1786pushTouchEvent = true;
1787}
1788touchAxisValuePending = true;
1789}
1790break;
1791case ABS_MT_POSITION_X:
1792mtp[touchEvent.id].x = amount * (float)currentWindowRect.width / (float)mouseAbsXMax;
1793mtp[touchEvent.id].x = ofClamp(mtp[touchEvent.id].x, 0, currentWindowRect.width);
1794touchAxisValuePending = true;
1795break;
1796case ABS_MT_POSITION_Y:
1797mtp[touchEvent.id].y = amount * (float)currentWindowRect.height / (float)mouseAbsYMax;
1798mtp[touchEvent.id].y = ofClamp(mtp[touchEvent.id].y, 0, currentWindowRect.height);
1799if(!pushTouchEvent){
1800touchEvent.type = ofTouchEventArgs::move;
1801pushTouchEvent = true;
1802}
1803touchAxisValuePending = true;
1804break;
1805default:
1806ofLogNotice("ofAppEGLWindow") << "EV_ABS unknown axis: axis " << axis << " amount " << amount << endl;
1807break;
1808}
1809}else if(ev.type == EV_MSC){
1810}else if(ev.type == EV_SYN){
1811// EV_SYN Used as markers to separate events. Events may be
1812// separated in time or in space, suc8h as with the multitouch protocol.
1813// EV_SYN events are sent when axis value (one or a pair) are changed
1814if(axisValuePending){
1815// TODO, this state doesn't make as much sense when the mouse is not dragging
1816if(mb.mouseButtonState > 0){
1817// dragging (what if dragging w/ more than one button?)
1818mouseEvent.type = ofMouseEventArgs::Dragged;
1819}else{
1820// just moving
1821mouseEvent.type = ofMouseEventArgs::Moved;
1822}
1823
1824mouseEvent.button = mb.mouseButtonState;
1825
1826pushMouseEvent = true;
1827axisValuePending = false;
1828}
1829
1830if(touchAxisValuePending){
1831if(!pushTouchEvent){
1832touchEvent.type = ofTouchEventArgs::move;
1833pushTouchEvent = true;
1834}
1835touchAxisValuePending = false;
1836}
1837}
1838
1839
1840
1841
1842if(pushKeyEvent){
1843lock();
1844keyEvents.push(keyEvent);
1845unlock();
1846pushKeyEvent = false;
1847}
1848
1849if(pushMouseEvent){
1850// lock the thread for a moment while we copy the data
1851lock();
1852mouseEvents.push(mouseEvent);
1853unlock();
1854pushMouseEvent = false;
1855}
1856
1857if(pushTouchEvent){
1858touchEvent.x = mtp[touchEvent.id].x;
1859touchEvent.y = mtp[touchEvent.id].y;
1860lock();
1861touchEvents.push(touchEvent);
1862unlock();
1863pushTouchEvent = false;
1864}
1865nBytesRead = read(fd, &ev,sizeof(struct input_event));
1866}
1867}
1868
1869//------------------------------------------------------------
1870void ofAppEGLWindow::readNativeUDevEvents() {
1871// look for devices being attatched / detatched
1872fd_set fds;
1873struct timeval tv;
1874int ret;
1875struct udev_device *dev;
1876bool is_mouse = false;
1877
1878FD_ZERO(&fds);
1879FD_SET(udev_fd, &fds);
1880tv.tv_sec = 0;
1881tv.tv_usec = 0;
1882
1883ret = select(udev_fd+1, &fds, NULL, NULL, &tv);
1884
1885/* Check if our file descriptor has received data. */
1886if(ret > 0 && FD_ISSET(udev_fd, &fds)){
1887/* Make the call to receive the device.
1888select() ensured that this will not block. */
1889dev = udev_monitor_receive_device(mon);
1890if(dev){
1891const char * devnode = udev_device_get_devnode(dev);
1892const char * devpath = udev_device_get_devpath(dev);
1893const char * sysname = udev_device_get_sysname(dev);
1894const char * subsystem = udev_device_get_subsystem(dev);
1895const char * devtype = udev_device_get_devtype(dev);
1896dev_t devnum = udev_device_get_devnum(dev);
1897const char * driver = udev_device_get_driver(dev);
1898const char * action = udev_device_get_action(dev);
1899const char * prop_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
1900const char * prop_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
1901
1902ofLogNotice("readNativeUDevEvents") << "udev monitor receive devixe";
1903ofLogNotice() << " - node: " << devnode;
1904ofLogNotice() << " - devpath: " << devpath;
1905ofLogNotice() << " - sysname: " << sysname;
1906ofLogNotice() << " - subsystem: " << subsystem;
1907ofLogNotice() << " - devtype: " << devtype;
1908ofLogNotice() << " - devnum: " << devnum;
1909ofLogNotice() << " - driver: " << driver;
1910ofLogNotice() << " - action: " << action;
1911ofLogNotice() << " - ID_INPUT_KEYBOARD: " << prop_keyboard;
1912ofLogNotice() << " - ID_INPUT_MOUSE: " << prop_mouse;
1913
1914if(prop_mouse){
1915is_mouse = true;
1916}
1917if(devnode && (prop_keyboard || prop_mouse) && string_begins_with(sysname, "event")){
1918if(strcmp(action, "add") == 0){
1919addInput(devnode, is_mouse);
1920}else if(strcmp(action, "remove") == 0){
1921removeInput(devnode);
1922}
1923}
1924
1925udev_device_unref(dev);
1926}else{
1927ofLogNotice("ofAppEGLWindow") << "readNativeUDevEvents(): device returned by receive_device() is NULL";
1928}
1929}
1930}
1931
1932void ofAppEGLWindow::readNativeInputEvents(){
1933for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
1934processInput(iter->second, iter->first.c_str());
1935}
1936}
1937
1938#ifdef TARGET_RASPBERRY_PI_LEGACY
1939//------------------------------------------------------------
1940void ofAppEGLWindow::initRPiNative() {
1941bcm_host_init();
1942
1943memset(&dispman_native_window, 0x0, sizeof(EGL_DISPMANX_WINDOW_T));
1944dispman_element = DISPMANX_NO_HANDLE;
1945dispman_display = DISPMANX_NO_HANDLE;
1946dispman_update = DISPMANX_NO_HANDLE;
1947memset(&dispman_clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
1948dispman_transform = DISPMANX_NO_ROTATE;
1949memset(&dispman_alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); // zero dispman_alpha
1950
1951}
1952
1953//------------------------------------------------------------
1954void ofAppEGLWindow::exitRPiNative() {
1955bcm_host_deinit();
1956}
1957
1958//------------------------------------------------------------
1959bool ofAppEGLWindow::createRPiNativeWindow(const ofRectangle& requestedWindowRect){
1960
1961ofRectangle screenRect = getScreenRect();
1962
1963// make sure our requested window rectangle does not exceed the native
1964// screen size, or start outside of it.
1965ofRectangle windowRect = screenRect.getIntersection(requestedWindowRect);
1966
1967ofLogNotice("ofAppEGLWindow") << "setupRPiNativeWindow(): screenRect: " << screenRect.width << "x" << screenRect.height;
1968ofLogNotice("ofAppEGLWindow") << "setupRPiNativeWindow(): windowRect: " << windowRect.width << "x" << windowRect.height;
1969
1970//////////////////////////
1971VC_RECT_T dst_rect;
1972
1973dst_rect.x = (int32_t)windowRect.x;
1974dst_rect.y = (int32_t)windowRect.y;
1975dst_rect.width = (int32_t)windowRect.width;
1976dst_rect.height = (int32_t)windowRect.height;
1977
1978VC_RECT_T src_rect;
1979
1980src_rect.x = 0;
1981src_rect.y = 0;
1982src_rect.width = dst_rect.width << 16;
1983src_rect.height = dst_rect.height << 16;
1984
1985memset(&dispman_alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); // zero dispman_alpha
1986dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
1987dispman_alpha.opacity = ofClamp(settings.eglWindowOpacity,0,255);
1988dispman_alpha.mask = 0;
1989
1990memset(&dispman_clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
1991
1992// there are other values for dispman_transform, but they do not seem to have an effect
1993dispman_transform = DISPMANX_NO_ROTATE;
1994
1995// get the zero display
1996dispman_display = vc_dispmanx_display_open(settings.screenNum);
1997
1998// begin the display manager interaction
1999dispman_update = vc_dispmanx_update_start( 0 );
2000
2001// add a "display manager element" with our parameters so
2002// that it can fill in the structures. we will pass this
2003// filled dispman_element to our native window, which will
2004// be used to construct the EGL surface, etc.
2005dispman_element = vc_dispmanx_element_add ( dispman_update,
2006dispman_display,
2007settings.layer, // layer
2008&dst_rect, // dst rect
2009(DISPMANX_RESOURCE_HANDLE_T)0, // src
2010&src_rect, // src rect
2011DISPMANX_PROTECTION_NONE, // ?
2012&dispman_alpha, // alpha
2013&dispman_clamp, // clamp
2014dispman_transform // transform
2015);
2016
2017if(dispman_element == DISPMANX_NO_HANDLE) {
2018ofLogError("ofAppEGLWindow") << "setupRPiNativeWindow(): dispman_element == DISPMANX_NO_HANDLE";
2019return false;
2020} else if(dispman_element == (unsigned)DISPMANX_INVALID) {
2021ofLogError("ofAppEGLWindow") << "setupRPiNativeWindow(): dispman_element == DISPMANX_INVALID";
2022return false;
2023}
2024
2025// set dispman_native_window to zero
2026memset(&dispman_native_window, 0x0, sizeof(EGL_DISPMANX_WINDOW_T));
2027dispman_native_window.element = dispman_element;
2028dispman_native_window.width = (int32_t)windowRect.width;
2029dispman_native_window.height = (int32_t)windowRect.height;
2030
2031// set background to black (not required)
2032vc_dispmanx_display_set_background(dispman_update, dispman_display, 0x00, 0x00, 0x00);
2033
2034// finished with display manager update, so sync
2035vc_dispmanx_update_submit_sync( dispman_update );
2036
2037currentWindowRect = windowRect;
2038
2039return true;
2040}
2041#endif
2042
2043//------------------------------------------------------------
2044// X11 BELOW
2045//------------------------------------------------------------
2046bool ofAppEGLWindow::createX11NativeWindow(const ofRectangle& requestedWindowRect){
2047
2048// X11 variables
2049x11Window = 0;
2050x11Display = 0;
2051x11ScreenNum = 0; // TODO: settings.screenNum?
2052x11Screen = 0;
2053XVisualInfo* x11Visual = 0; // TODO does this need to be deleted?
2054Colormap x11Colormap = 0;
2055
2056/*
2057Step 0 - Create a NativeWindowType that we can use it for OpenGL ES output
2058*/
2059Window sRootWindow;
2060XSetWindowAttributes sWA;
2061unsigned int ui32Mask;
2062int i32Depth;
2063
2064//ofRectangle screenRect = getScreenRect();
2065
2066// make sure our requested window rectangle does not exceed the native
2067// screen size, or start outside of it.
2068ofRectangle windowRect = requestedWindowRect.getStandardized();//screenRect.getIntersection(requestedWindowRect);
2069
2070// Initializes the display and screen
2071x11Display = XOpenDisplay( 0 );
2072if (!x11Display) {
2073ofLogError("ofAppEGLWindow") << "unable to open X display";
2074return false;
2075}
2076
2077x11ScreenNum = XDefaultScreen( x11Display );
2078
2079x11Screen = XDefaultScreenOfDisplay(x11Display);
2080
2081// Gets the window parameters
2082sRootWindow = RootWindow(x11Display, x11ScreenNum);
2083i32Depth = DefaultDepth(x11Display, x11ScreenNum);
2084x11Visual = new XVisualInfo();
2085
2086XMatchVisualInfo( x11Display,
2087x11ScreenNum,
2088i32Depth,
2089TrueColor,
2090x11Visual);
2091
2092if (!x11Visual) {
2093ofLogError("ofAppEGLWindow") << "unable to acquire XVisualInfo";
2094return false;
2095}
2096
2097x11Colormap = XCreateColormap( x11Display, sRootWindow, x11Visual->visual, AllocNone );
2098
2099delete x11Visual;
2100
2101// set the colormap window attribuet
2102sWA.colormap = x11Colormap;
2103
2104// Add to these for handling other events
2105sWA.event_mask = 0;
2106sWA.event_mask |= StructureNotifyMask;
2107sWA.event_mask |= ExposureMask;
2108sWA.event_mask |= ButtonPressMask;
2109sWA.event_mask |= ButtonReleaseMask;
2110sWA.event_mask |= PointerMotionMask;
2111sWA.event_mask |= KeyPressMask;
2112sWA.event_mask |= KeyReleaseMask;
2113
2114// setup background pixel attributes
2115ui32Mask = 0;
2116ui32Mask |= CWBackPixel;
2117ui32Mask |= CWBorderPixel;
2118ui32Mask |= CWEventMask;
2119ui32Mask |= CWColormap;
2120
2121// Creates the X11 window
2122x11Window = XCreateWindow(x11Display, // Specifies the connection to the X server.
2123sRootWindow, // Specifies the parent window.
2124(int)windowRect.x, (int)windowRect.y, // Specify the x and y coordinates,
2125// which are the top-left outside corner
2126// of the window's borders and are relative
2127// to the inside of the parent window's borders.
2128(unsigned int)windowRect.width, (unsigned int)windowRect.height, // Specify the width and height, which are the
2129// created window's inside dimensions and do
2130// not include the created window's borders.
21310, // Specifies the width of the created
2132// window's border in pixels.
2133CopyFromParent, // Specifies the window's depth.
2134// A depth of CopyFromParent means
2135// the depth is taken from the parent.
2136InputOutput, // Specifies the created window's class.
2137// You can pass InputOutput, InputOnly,
2138// or CopyFromParent. A class of CopyFromParent
2139// means the class is taken from the parent.
2140CopyFromParent, // Specifies the visual type.
2141// A visual of CopyFromParent means the visual type
2142// is taken from the parent.
2143ui32Mask, // Specifies which window attributes are
2144// defined in the attributes argument. This mask is
2145// the bitwise inclusive OR of the valid attribute
2146// mask bits. If valuemask is zero, the attributes
2147// are ignored and are not referenced.
2148&sWA //Specifies the background pixel value of the window.
2149);
2150
2151XMapWindow(x11Display, x11Window);
2152XFlush(x11Display);
2153
2154// check success?
2155currentWindowRect = windowRect;
2156
2157return true;
2158}
2159
2160//------------------------------------------------------------
2161static KeySym KeyCodeToKeySym(Display * display, KeyCode keycode, unsigned int event_mask) {
2162KeySym keysym = NoSymbol;
2163
2164//Get the map
2165XkbDescPtr keyboard_map = XkbGetMap(display, XkbAllClientInfoMask, XkbUseCoreKbd);
2166if (keyboard_map) {
2167//What is diff between XkbKeyGroupInfo and XkbKeyNumGroups?
2168unsigned char info = XkbKeyGroupInfo(keyboard_map, keycode);
2169unsigned int num_groups = XkbKeyNumGroups(keyboard_map, keycode);
2170
2171//Get the group
2172unsigned int group = 0x00;
2173switch (XkbOutOfRangeGroupAction(info)) {
2174case XkbRedirectIntoRange:
2175/* If the RedirectIntoRange flag is set, the four least significant
2176* bits of the groups wrap control specify the index of a group to
2177* which all illegal groups correspond. If the specified group is
2178* also out of range, all illegal groups map to Group1.
2179*/
2180group = XkbOutOfRangeGroupInfo(info);
2181if (group >= num_groups) {
2182group = 0;
2183}
2184break;
2185
2186case XkbClampIntoRange:
2187/* If the ClampIntoRange flag is set, out-of-range groups correspond
2188* to the nearest legal group. Effective groups larger than the
2189* highest supported group are mapped to the highest supported group;
2190* effective groups less than Group1 are mapped to Group1 . For
2191* example, a key with two groups of symbols uses Group2 type and
2192* symbols if the global effective group is either Group3 or Group4.
2193*/
2194group = num_groups - 1;
2195break;
2196
2197case XkbWrapIntoRange:
2198/* If neither flag is set, group is wrapped into range using integer
2199* modulus. For example, a key with two groups of symbols for which
2200* groups wrap uses Group1 symbols if the global effective group is
2201* Group3 or Group2 symbols if the global effective group is Group4.
2202*/
2203default:
2204if (num_groups != 0) {
2205group %= num_groups;
2206}
2207break;
2208}
2209
2210XkbKeyTypePtr key_type = XkbKeyKeyType(keyboard_map, keycode, group);
2211unsigned int active_mods = event_mask & key_type->mods.mask;
2212
2213int i, level = 0;
2214for (i = 0; i < key_type->map_count; i++) {
2215if (key_type->map[i].active && key_type->map[i].mods.mask == active_mods) {
2216level = key_type->map[i].level;
2217}
2218}
2219
2220keysym = XkbKeySymEntry(keyboard_map, keycode, level, group);
2221XkbFreeClientMap(keyboard_map, XkbAllClientInfoMask, true);
2222}
2223
2224return keysym;
2225}
2226
2227//------------------------------------------------------------
2228void ofAppEGLWindow::handleX11Event(const XEvent& event){
2229ofMouseEventArgs mouseEvent;
2230ofKeyEventArgs keyEvent;
2231
2232switch (event.type){
2233case KeyPress:
2234case KeyRelease:
2235{
2236KeySym key = KeyCodeToKeySym(instance->x11Display,event.xkey.keycode,event.xkey.state);
2237keyEvent.key = key;
2238if (event.type == KeyPress) {
2239keyEvent.type = ofKeyEventArgs::Pressed;
2240if(key == 65307){
2241keyEvent.key = OF_KEY_ESC;
2242}
2243} else if (event.type == KeyRelease){
2244keyEvent.type = ofKeyEventArgs::Released;
2245}
2246
2247instance->coreEvents.notifyKeyEvent(keyEvent);
2248}
2249break;
2250case ButtonPress:
2251case ButtonRelease:
2252mouseEvent.x = static_cast<float>(event.xbutton.x);
2253mouseEvent.y = static_cast<float>(event.xbutton.y);
2254mouseEvent.button = event.xbutton.button;
2255if (event.type == ButtonPress){
2256mouseEvent.type = ofMouseEventArgs::Pressed;
2257} else {
2258mouseEvent.type = ofMouseEventArgs::Released;
2259}
2260
2261instance->coreEvents.notifyMouseEvent(mouseEvent);
2262break;
2263case MotionNotify:
2264//cout << "motion notify" << endl;
2265mouseEvent.x = static_cast<float>(event.xmotion.x);
2266mouseEvent.y = static_cast<float>(event.xmotion.y);
2267mouseEvent.button = event.xbutton.button;
2268if(ofGetMousePressed()) {
2269mouseEvent.type = ofMouseEventArgs::Dragged;
2270} else {
2271mouseEvent.type = ofMouseEventArgs::Moved;
2272}
2273
2274instance->coreEvents.notifyMouseEvent(mouseEvent);
2275break;
2276case ConfigureNotify:
2277instance->currentWindowRect.x = event.xconfigure.x;
2278instance->currentWindowRect.y = event.xconfigure.y;
2279instance->currentWindowRect.width = event.xconfigure.width;
2280instance->currentWindowRect.height = event.xconfigure.height;
2281instance->nonFullscreenWindowRect = instance->currentWindowRect;
2282instance->coreEvents.notifyWindowResized(event.xconfigure.width,event.xconfigure.height);
2283break;
2284/*case ClientMessage:{
2285if (event.xclient.message_type == wmProtocols_ &&
2286event.xclient.format == 32 &&
2287event.xclient.data.l[0] == (long) wmDeleteWindow_)
2288{
2289if (listener())
2290{
2291if (listener()->onClose(wrapper() ? *wrapper() : *(WindowInterface*)this))
2292isShuttingDown_ = true;
2293}
2294else
2295{
2296isShuttingDown_ = true;
2297}
2298}
2299break;
2300}*/
2301}
2302}
2303