framework2

Форк
0
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
32
struct udev* udev;
33
struct udev_monitor* mon;
34
static int udev_fd = -1;
35

36
typedef map<string, int> device;
37
static device inputDevices;
38

39
// minimal map
40
const int lowercase_map[] = {
41
		0,  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,
46
		0,  0,  0,   0,   0,   0,   0,  0,   0,  0,  0,  0, 0,  0, 0,
47
		0,  0,  0,   0,   0,   0,   0,  0,   0,  0,  0,  0, 0,  0, 0,
48
		0,  0,  0,   0,   0,   0,   0,  0,   0,  0,  0,  '\r'
49

50
};
51

52
// minimal keyboard map
53
const int uppercase_map[] = {
54
		0,  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,
59
		0,  0,  0,   0,   0,   0,   0,  0,   0,  0,  0,  0, 0,  0, 0,
60
		0,  0,  0,   0,   0,   0,   0,  0,   0,  0,  0,  0, 0,  0, 0,
61
		0,  0,  0,   0,   0,   0,   0,  0,   0,  0,  0,  '\r'
62
};
63

64
// keep track of a few things ...
65
typedef struct {
66
	bool shiftPressed;
67
	bool capsLocked;
68
} KeyboardState;
69

70
static KeyboardState kb;
71

72
static struct termios tc;
73
static struct termios ots;
74

75
typedef struct {
76
	int mouseButtonState;
77
} MouseState;
78

79
typedef map<int, int> TouchState;
80
typedef 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

87
static MouseState mb;
88
static TouchState mt;
89
static TouchPosition mtp;
90
ofAppEGLWindow* ofAppEGLWindow::instance = NULL;
91

92
static int string_ends_with(const char *str, const char *suffix) {
93
	if (!str || !suffix)
94
		return 0;
95
	size_t lenstr = strlen(str);
96
	size_t lensuffix = strlen(suffix);
97
	if (lensuffix > lenstr)
98
		return 0;
99
	return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
100
}
101

102
static int string_begins_with(const char *str, const char *prefix) {
103
	if (!str || !prefix)
104
		return 0;
105
	size_t lenstr = strlen(str);
106
	size_t lenprefix = strlen(prefix);
107
	if (lenprefix > lenstr)
108
		return 0;
109
	return 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); \
117
		while (__ip < __il) { unsigned int __l = *(__rd++); \
118
		if (__l & 128) { __l = __l - 128; \
119
		do { 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)
123
static const struct {
124
	unsigned int width;
125
	unsigned int height;
126
	unsigned int bpp; /* 2:RGB16, 3:RGB, 4:RGBA */
127
	unsigned char rle_pixel_data[382 + 1];
128
} mouse_cursor_data = {
129
		12, 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

152
static const char* eglErrorString(EGLint err) {
153
	string str;
154
	switch (err) {
155
	CASE_STR(EGL_SUCCESS, "no error");
156
	CASE_STR(EGL_NOT_INITIALIZED, "EGL not, or could not be, initialized");
157
	CASE_STR(EGL_BAD_ACCESS, "access violation");
158
	CASE_STR(EGL_BAD_ALLOC, "could not allocate resources");
159
	CASE_STR(EGL_BAD_ATTRIBUTE, "invalid attribute");
160
	CASE_STR(EGL_BAD_CONTEXT, "invalid context specified");
161
	CASE_STR(EGL_BAD_CONFIG, "invald frame buffer configuration specified");
162
	CASE_STR(EGL_BAD_CURRENT_SURFACE, "current window, pbuffer or pixmap surface is no longer valid");
163
	CASE_STR(EGL_BAD_DISPLAY, "invalid display specified");
164
	CASE_STR(EGL_BAD_SURFACE, "invalid surface specified");
165
	CASE_STR(EGL_BAD_MATCH, "bad argument match");
166
	CASE_STR(EGL_BAD_PARAMETER, "invalid paramater");
167
	CASE_STR(EGL_BAD_NATIVE_PIXMAP, "invalid NativePixmap");
168
	CASE_STR(EGL_BAD_NATIVE_WINDOW, "invalid NativeWindow");
169
	CASE_STR(EGL_CONTEXT_LOST, "APM event caused context loss");
170
	default: str = "unknown error " + err; break;
171
	}
172
	return 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
//-------------------------------------------------------------------------------------
205
ofAppEGLWindowSettings::ofAppEGLWindowSettings()
206
:ofGLESWindowSettings(){
207
	eglWindowPreference = OF_APP_WINDOW_AUTO;
208
	eglWindowOpacity = 255;
209

210
	// these are usually set as default, but set them here just to be sure
211
	frameBufferAttributes[EGL_RED_SIZE]	 = 8; // 8 bits for red
212
	frameBufferAttributes[EGL_GREEN_SIZE]   = 8; // 8 bits for green
213
	frameBufferAttributes[EGL_BLUE_SIZE]	= 8; // 8 bits for blue
214
	frameBufferAttributes[EGL_ALPHA_SIZE]   = 8; // 8 bits for alpha
215
	frameBufferAttributes[EGL_LUMINANCE_SIZE] = EGL_DONT_CARE; // 8 bits for alpha
216
	frameBufferAttributes[EGL_DEPTH_SIZE]   = 24; // 24 bits for depth
217
	frameBufferAttributes[EGL_STENCIL_SIZE] = 8; // 8 bits for stencil
218
	frameBufferAttributes[EGL_SAMPLES]	  = 1;
219

220
	initialClearColor = ofColor(0.15 * 255, 0.15 * 255, 0.15 * 255, 255);
221

222
	screenNum = 0; /* 0 = LCD on the raspberry pi */
223
	layer = 0;
224
}
225

226
ofAppEGLWindowSettings::ofAppEGLWindowSettings(const ofGLESWindowSettings & settings)
227
:ofGLESWindowSettings(settings){
228
	eglWindowPreference = OF_APP_WINDOW_AUTO;
229
	eglWindowOpacity = 255;
230

231
	// these are usually set as default, but set them here just to be sure
232
	frameBufferAttributes[EGL_RED_SIZE]	 = 8; // 8 bits for red
233
	frameBufferAttributes[EGL_GREEN_SIZE]   = 8; // 8 bits for green
234
	frameBufferAttributes[EGL_BLUE_SIZE]	= 8; // 8 bits for blue
235
	frameBufferAttributes[EGL_ALPHA_SIZE]   = 8; // 8 bits for alpha
236
	frameBufferAttributes[EGL_LUMINANCE_SIZE] = EGL_DONT_CARE; // 8 bits for alpha
237
	frameBufferAttributes[EGL_DEPTH_SIZE]   = 24; // 24 bits for depth
238
	frameBufferAttributes[EGL_STENCIL_SIZE] = 8; // 8 bits for stencil
239
	frameBufferAttributes[EGL_SAMPLES]	  = 1;
240

241
	initialClearColor = ofColor(0.15 * 255, 0.15 * 255, 0.15 * 255, 255);
242

243
	screenNum = 0; /* 0 = LCD on the raspberry pi */
244
	layer = 0;
245
}
246

247
//------------------------------------------------------------
248
ofAppEGLWindow::ofAppEGLWindow() {
249
	keyboardDetected = false;
250
	mouseDetected = false;
251
	threadTimeout = ofThread::INFINITE_JOIN_TIMEOUT;
252
	bNewScreenMode = false;
253
	buttonInUse = -1;
254
	bEnableSetupScreen = false;
255
	bShowCursor = true;
256
	nFramesSinceWindowResized = 0;
257
	mouseScaleX = 2.0f;
258
	mouseScaleY = 2.0f;
259
	isUsingX11 = false;
260
	isWindowInited = false;
261
	isSurfaceInited = false;
262
	x11Display = NULL;
263
	x11Screen = NULL;
264
	x11ScreenNum = 0l;
265
	glesVersion = 1;
266

267
	if(instance!=NULL){
268
		ofLogError("ofAppEGLWindow") << "trying to create more than one instance";
269
	}
270
	instance = this;
271
}
272

273
//------------------------------------------------------------
274
ofAppEGLWindow::~ofAppEGLWindow() {
275
	close();
276
}
277

278
//------------------------------------------------------------
279
EGLDisplay ofAppEGLWindow::getEglDisplay() const {
280
	return eglDisplay;
281
}
282

283
//------------------------------------------------------------
284
EGLSurface ofAppEGLWindow::getEglSurface() const {
285
	return eglSurface;
286
}
287

288
//------------------------------------------------------------
289
EGLContext ofAppEGLWindow::getEglContext() const {
290
	return eglContext;
291
}
292

293
#ifndef TARGET_RASPBERRY_PI_LEGACY
294
//------------------------------------------------------------
295
Display* ofAppEGLWindow::getX11Display(){
296
	return x11Display;
297
}
298

299
//------------------------------------------------------------
300
Window ofAppEGLWindow::getX11Window(){
301
	return x11Window;
302
}
303
#endif
304
//------------------------------------------------------------
305
EGLConfig ofAppEGLWindow::getEglConfig() const {
306
	return eglConfig;
307
}
308

309
//------------------------------------------------------------
310
EGLint ofAppEGLWindow::getEglVersionMajor () const {
311
	return eglVersionMajor;
312
}
313

314
//------------------------------------------------------------
315
EGLint ofAppEGLWindow::getEglVersionMinor() const {
316
	return eglVersionMinor;
317
}
318

319
//------------------------------------------------------------
320
void ofAppEGLWindow::initNative() {
321
#ifdef TARGET_RASPBERRY_PI_LEGACY
322
	initRPiNative();
323
#endif
324
}
325

326
//------------------------------------------------------------
327
void ofAppEGLWindow::exitNative() {
328
#ifdef TARGET_RASPBERRY_PI_LEGACY
329
	exitRPiNative();
330
#endif
331
}
332

333
//------------------------------------------------------------
334
EGLNativeWindowType ofAppEGLWindow::getNativeWindow()  {
335
	if(!isWindowInited) {
336
		ofLogWarning("ofAppEGLWindow") << "getNativeDisplay(): window not initialized, returning NULL";
337
		return NULL;
338
	}
339

340
	if(isUsingX11) {
341
		return (EGLNativeWindowType)x11Window;
342
	} else {
343
#ifdef TARGET_RASPBERRY_PI_LEGACY
344
		return (EGLNativeWindowType)&dispman_native_window;
345
#else
346
		ofLogNotice("ofAppEGLWindow") << "getNativeWindow(): no native window type for this system, perhaps try X11?";
347
		return NULL;
348
#endif
349
	}
350
}
351

352
//------------------------------------------------------------
353
EGLNativeDisplayType ofAppEGLWindow::getNativeDisplay() {
354
	if(!isWindowInited) {
355
		ofLogWarning("ofAppEGLWindow") << "getNativeDisplay(): window not initialized, returning NULL";
356
		return 0;
357
	}
358

359
	if(isUsingX11) {
360
		return (EGLNativeDisplayType)x11Display;
361
	} else {
362
#ifdef TARGET_RASPBERRY_PI_LEGACY
363
		return (EGLNativeDisplayType)NULL;
364
#else
365
		ofLogNotice("ofAppEGLWindow") << "getNativeDisplay(): no native window type for this system, perhaps try X11?";
366
		return 0;
367
#endif
368
	}
369
}
370

371
//------------------------------------------------------------
372
void ofAppEGLWindow::setup(const ofGLESWindowSettings & settings){
373
	const Settings * glSettings = dynamic_cast<const Settings*>(&settings);
374
	if(glSettings){
375
		setup(*glSettings);
376
	}else{
377
		setup(Settings(settings));
378
	}
379
}
380

381
//------------------------------------------------------------
382
void ofAppEGLWindow::setup(const ofAppEGLWindowSettings & _settings) {
383
	settings = _settings;
384
	windowMode = OF_WINDOW;
385
	bNewScreenMode = true;
386
	nFramesSinceWindowResized = 0;
387
	buttonInUse	= 0;
388
	bEnableSetupScreen = true;
389
	eglDisplayString = "";
390
	orientation = 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
395
	mouseScaleX = 2.0f;
396
	mouseScaleY = 2.0f;
397

398
	isUsingX11 = false;
399
	isWindowInited  = false;
400
	isSurfaceInited = false;
401

402
	eglDisplay = NULL;
403
	eglSurface = NULL;
404
	eglContext = NULL;
405
	eglConfig  = NULL;
406
	eglVersionMajor = -1;
407
	eglVersionMinor = -1;
408
	glesVersion = 1;
409

410
	// X11 check
411
	// char * pDisplay;
412
	// pDisplay = getenv ("DISPLAY");
413
	// bool bIsX11Available = (pDisplay != NULL);
414

415
	bool bIsX11Available = getenv("DISPLAY") != NULL;
416

417
	if(settings.eglWindowPreference == OF_APP_WINDOW_AUTO) {
418
		if(bIsX11Available) {
419
			isUsingX11 = true;
420
		} else {
421
			isUsingX11 = false;
422
		}
423
	} else if(settings.eglWindowPreference == OF_APP_WINDOW_NATIVE) {
424
		isUsingX11 = false;
425
	} else if(settings.eglWindowPreference == OF_APP_WINDOW_X11) {
426
		isUsingX11 = true;
427
		if(!bIsX11Available) {
428
			isUsingX11 = false;
429
			ofLogError("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
436
	if(isUsingX11) {
437
		isUsingX11 = false;
438
		ofLogWarning("ofAppEGLWindow") << "init(): X11 not availble on RPI yet, using a native window instead";
439
	}
440
#endif
441
	////////////////
442

443
	initNative();
444

445
	glesVersion = settings.glesVersion;
446
	// we set this here, and if we need to make a fullscreen
447
	// app, we do it during the first loop.
448
	windowMode = settings.windowMode;
449
	bShowCursor = true;
450

451
	nonFullscreenWindowRect.set(0,0,settings.getWidth(),settings.getHeight());
452
	nonFullscreenWindowRect.standardize();
453

454
	ofRectangle startRect = nonFullscreenWindowRect;
455
	bNewScreenMode = false;
456

457
	if(windowMode == OF_GAME_MODE) {
458
		ofLogWarning("ofAppEGLWindow") << "setupOpenGL(): OF_GAME_MODE not supported, using OF_WINDOW";
459
		startRect = nonFullscreenWindowRect;
460
	} else if(windowMode == OF_FULLSCREEN) {
461
		startRect = getScreenRect();
462
	}
463

464
	isWindowInited = createWindow(startRect);
465
	isSurfaceInited = createSurface();
466

467
	if(!isWindowInited) {
468
		ofLogError("ofAppEGLWindow")  << "setupOpenGL(): screen creation failed, window not inited";
469
	}
470

471
	setupPeripherals();
472

473
	nFramesSinceWindowResized = 0;
474

475
	if(settings.glesVersion>1){
476
		currentRenderer = make_shared<ofGLProgrammableRenderer>(this);
477
	}else{
478
		currentRenderer = make_shared<ofGLRenderer>(this);
479
	}
480

481
	makeCurrent();
482
	if(currentRenderer->getType()==ofGLProgrammableRenderer::TYPE){
483
		static_cast<ofGLProgrammableRenderer*>(currentRenderer.get())->setup(settings.glesVersion,0);
484
	}else{
485
		static_cast<ofGLRenderer*>(currentRenderer.get())->setup();
486
	}
487
}
488

489
//------------------------------------------------------------
490
void ofAppEGLWindow::setupPeripherals() {
491
	if(!isUsingX11) {
492
		// roll our own cursor!
493
		mouseCursor.allocate(mouse_cursor_data.width,mouse_cursor_data.height,OF_IMAGE_COLOR_ALPHA);
494
		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);
495
		mouseCursor.update();
496
		ofLogNotice("ofAppEGLWindow") << "setupPeripherals(): peripheral setup complete";
497
		setupNativeEvents();
498
		ofLogNotice("ofAppEGLWindow") << "setupPeripherals(): native event setup complete";
499

500
	} else {
501
		ofLogError("ofAppEGLWindow") << "setupPeripherals(): peripherals not supported on X11";
502
	}
503
}
504

505
//------------------------------------------------------------
506
bool ofAppEGLWindow::createSurface() {
507

508
	EGLNativeWindowType nativeWindow = getNativeWindow();
509
	EGLNativeDisplayType display = getNativeDisplay();
510

511
	ofLogNotice("ofAppEGLWindow") << "createSurface(): setting up EGL Display";
512
	// get an EGL eglDisplay connection
513

514
	isSurfaceInited = false;
515

516
	EGLint result;
517

518
	if(display==0){
519
		eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
520
	}else{
521
		eglDisplay = eglGetDisplay(display);
522
	}
523

524
	if(eglDisplay == EGL_NO_DISPLAY) {
525
		ofLogNotice("ofAppEGLWindow") << "createSurface(): eglGetDisplay returned: " << eglDisplay;
526
		return false;
527
	}else{
528
		ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL Display correctly set " << eglDisplay;
529
	}
530

531
	// initialize the EGL eglDisplay connection
532
	result = eglInitialize(eglDisplay,
533
			&eglVersionMajor,
534
			&eglVersionMinor);
535

536
	if(result == EGL_BAD_DISPLAY) {
537
		//  eglDisplay is not an EGL connection
538
		ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_BAD_DISPLAY";
539
		return false;
540
	} else if(result == EGL_NOT_INITIALIZED) {
541
		// eglDisplay cannot be intitialized
542
		ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_NOT_INITIALIZED";
543
		return false;
544
	} else if(result == EGL_FALSE) {
545
		// eglinitialize was not initialiezd
546
		ofLogError("ofAppEGLWindow") << "createSurface(): eglInitialize returned EGL_FALSE";
547
		return false;
548
	} else {
549
		// result == EGL_TRUE
550
		// success!
551
	}
552

553
	EGLint glesVersion;
554
	int glesVersionForContext;
555

556
	if(ofGetCurrentRenderer()) {
557
		ofLogNotice("ofAppEGLWindow") << "createSurface(): current renderer type: " << ofGetCurrentRenderer()->getType();
558
	} else {
559
		ofLogNotice("ofAppEGLWindow") << "createSurface(): no current renderer selected";
560
	}
561

562
	if(this->glesVersion==2){
563
		glesVersion = EGL_OPENGL_ES2_BIT;
564
		glesVersionForContext = 2;
565
		ofLogNotice("ofAppEGLWindow") << "createSurface(): GLES2 renderer detected";
566
	}else{
567
		glesVersion = EGL_OPENGL_ES_BIT;
568
		glesVersionForContext = 1;
569
		ofLogNotice("ofAppEGLWindow") << "createSurface(): default renderer detected";
570
	}
571

572
	ofEGLAttributeListIterator iter, iterEnd;
573
	int i;
574

575
	// each attribute has 2 values, and we need one extra for the EGL_NONE terminator
576
	EGLint attribute_list_framebuffer_config[settings.frameBufferAttributes.size() * 2 + 3];
577

578
	iter = settings.frameBufferAttributes.begin();
579
	iterEnd = settings.frameBufferAttributes.end();
580
	i = 0;
581
	for(; iter != iterEnd; iter++) {
582
		attribute_list_framebuffer_config[i++] = iter->first;
583
		attribute_list_framebuffer_config[i++] = iter->second;
584
	}
585
	attribute_list_framebuffer_config[i++] = EGL_RENDERABLE_TYPE;
586
	attribute_list_framebuffer_config[i++] = glesVersion; //openGL ES version
587
	attribute_list_framebuffer_config[i] = EGL_NONE; // add the terminator
588

589
	EGLint num_configs;
590

591
	// get an appropriate EGL frame buffer configuration
592
	// http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/eglChooseConfig.html
593
	result = eglChooseConfig(eglDisplay,
594
			attribute_list_framebuffer_config,
595
			&eglConfig,
596
			1, // 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

602
	if(result == EGL_FALSE) {
603
		EGLint error = eglGetError();
604
		ofLogError("ofAppEGLWindow") << "createSurface(): error finding valid configuration based on settings: " << eglErrorString(error);
605
		return false;
606
	}
607

608
	if(num_configs <= 0 || eglConfig == NULL) {
609
		ofLogError("ofAppEGLWindow") << "createSurface(): no matching configs were found, num_configs: " << num_configs;
610
		return false;
611
	}
612

613

614
	// each attribute has 2 values, and we need one extra for the EGL_NONE terminator
615
	EGLint attribute_list_window_surface[settings.windowSurfaceAttributes.size() * 2 + 1];
616

617
	iter = settings.windowSurfaceAttributes.begin();
618
	iterEnd = settings.windowSurfaceAttributes.end();
619

620
	i = 0;
621
	for(; iter != iterEnd; iter++) {
622
		attribute_list_window_surface[i++] = iter->first;
623
		attribute_list_window_surface[i++] = iter->second;
624
	}
625
	attribute_list_window_surface[i] = EGL_NONE; // add the terminator
626

627
	// create a surface
628
	eglSurface = eglCreateWindowSurface( eglDisplay, // our display handle
629
			eglConfig,	// our first config
630
			nativeWindow, // our native window
631
			attribute_list_window_surface); // surface attribute list
632

633
	if(eglSurface == EGL_NO_SURFACE) {
634
		EGLint error = eglGetError();
635
		switch(error) {
636
		case EGL_BAD_MATCH:
637
			ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_MATCH " << eglErrorString(error);
638
			ofLogError("ofAppEGLWindow") << "createSurface(): check window and EGLConfig attributes to determine compatibility, ";
639
			ofLogError("ofAppEGLWindow") << "createSurface(): or verify that the EGLConfig supports rendering to a window";
640
			break;
641
		case EGL_BAD_CONFIG:
642
			ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_CONFIG " << eglErrorString(error);
643
			ofLogError("ofAppEGLWindow") << "createSurface(): verify that provided EGLConfig is valid";
644
			break;
645
		case EGL_BAD_NATIVE_WINDOW:
646
			ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_NATIVE_WINDOW " << eglErrorString(error);
647
			ofLogError("ofAppEGLWindow") << "createSurface(): verify that provided EGLNativeWindow is valid";
648
			break;
649
		case EGL_BAD_ALLOC:
650
			ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: EGL_BAD_ALLOC " << eglErrorString(error);
651
			ofLogError("ofAppEGLWindow") << "createSurface(): not enough resources available";
652
			break;
653
		default:
654
			ofLogError("ofAppEGLWindow") << "createSurface(): error creating surface: << " << error << eglErrorString(error);
655
		}
656

657
		return false;
658
	}else{
659
		ofLogNotice("ofAppEGLWindow") << "createSurface(): surface created correctly";
660
	}
661

662
	// get an appropriate EGL frame buffer configuration
663
	result = eglBindAPI(EGL_OPENGL_ES_API);
664

665
	if(result == EGL_FALSE) {
666
		ofLogError("ofAppEGLWindow") << "createSurface(): error binding API: " << eglErrorString(eglGetError());
667
		return false;
668
	}else{
669
		ofLogNotice("ofAppEGLWindow") << "createSurface(): API bound correctly";
670
	}
671

672
	// create an EGL rendering eglContext
673
	EGLint attribute_list_surface_context[] = {
674
			EGL_CONTEXT_CLIENT_VERSION, glesVersionForContext,
675
			EGL_NONE
676
	};
677

678
	eglContext = eglCreateContext(eglDisplay,
679
			eglConfig,
680
			EGL_NO_CONTEXT,
681
			attribute_list_surface_context);
682

683
	if(eglContext == EGL_NO_CONTEXT) {
684
		EGLint error = eglGetError();
685
		if(error == EGL_BAD_CONFIG) {
686
			ofLogError("ofAppEGLWindow") << "createSurface(): error creating context: EGL_BAD_CONFIG " << eglErrorString(error);
687
			return false;
688
		} else {
689
			ofLogError("ofAppEGLWindow") << "createSurface(): error creating context: " << error << " " << eglErrorString(error);
690
			return false;
691
		}
692
	}
693

694
	// connect the eglContext to the eglSurface
695
	result = eglMakeCurrent(eglDisplay,
696
			eglSurface, // draw surface
697
			eglSurface, // read surface
698
			eglContext);
699

700
	if(eglContext == nullptr) {
701
		EGLint error = eglGetError();
702
		ofLogError("ofAppEGLWindow") << "createSurface(): couldn't making current surface: " << eglErrorString(error);
703
		return false;
704
	}
705

706
	// Set background color and clear buffers
707
	glClearColor(settings.initialClearColor.r / 255.0f,
708
			settings.initialClearColor.g / 255.0f,
709
			settings.initialClearColor.b / 255.0f,
710
			settings.initialClearColor.a / 255.0f);
711
	glClear( GL_COLOR_BUFFER_BIT );
712
	glClear( GL_DEPTH_BUFFER_BIT );
713

714
	ofLogNotice("ofAppEGLWindow") << "createSurface(): -----EGL-----";
715
	ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION_MAJOR = " << eglVersionMajor;
716
	ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION_MINOR = " << eglVersionMinor;
717
	ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_CLIENT_APIS = " << eglQueryString(eglDisplay, EGL_CLIENT_APIS);
718
	ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VENDOR = "  << eglQueryString(eglDisplay, EGL_VENDOR);
719
	ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_VERSION = " << eglQueryString(eglDisplay, EGL_VERSION);
720
	ofLogNotice("ofAppEGLWindow") << "createSurface(): EGL_EXTENSIONS = " << eglQueryString(eglDisplay, EGL_EXTENSIONS);
721
	ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_RENDERER = " << glGetString(GL_RENDERER);
722
	ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_VERSION  = " << glGetString(GL_VERSION);
723
	ofLogNotice("ofAppEGLWindow") << "createSurface(): GL_VENDOR   = " << glGetString(GL_VENDOR);
724
	ofLogNotice("ofAppEGLWindow") << "createSurface(): -------------";
725

726
	isSurfaceInited = true;
727

728
	return true;
729
}
730

731
//------------------------------------------------------------
732
bool ofAppEGLWindow::destroySurface() {
733
	if(isSurfaceInited) {
734
		ofLogNotice("ofAppEGLWindow") << "destroySurface(): destroying EGL surface";
735
		eglSwapBuffers(eglDisplay, eglSurface);
736
		eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
737
		eglDestroySurface(eglDisplay, eglSurface);
738
		eglDestroyContext(eglDisplay, eglContext);
739
		eglTerminate(eglDisplay);
740
		isSurfaceInited = false;
741

742
		eglDisplay = NULL;
743
		eglSurface = NULL;
744
		eglContext = NULL;
745
		eglConfig  = NULL;
746
		eglVersionMinor = -1;
747
		eglVersionMinor = -1;
748

749
		return true;
750
	} else {
751
		ofLogError("ofAppEGLWindow") << "destroySurface(): attempted to destroy uninitialized window";
752
		return false;
753
	}
754
}
755

756
//------------------------------------------------------------
757
bool ofAppEGLWindow::destroyWindow() {
758
	if(isWindowInited) {
759
		if(isUsingX11) {
760
			// TODO: double check
761
			XDestroyWindow(x11Display,x11Window); // or XCloseWindow?
762
			XFree(x11Screen);
763
		} else {
764
#ifdef TARGET_RASPBERRY_PI_LEGACY
765
			dispman_update = vc_dispmanx_update_start(0);
766
			if (dispman_element != DISPMANX_NO_HANDLE) {
767
				vc_dispmanx_element_remove(dispman_update, dispman_element);
768
				dispman_element = DISPMANX_NO_HANDLE;
769
			}
770

771
			vc_dispmanx_update_submit_sync(dispman_update);
772

773
			if (dispman_display != DISPMANX_NO_HANDLE) {
774
				vc_dispmanx_display_close(dispman_display);
775
				dispman_display = DISPMANX_NO_HANDLE;
776
			}
777
#else
778
	ofLogNotice("ofAppEGLWindow") << "destroyWindow(): no native window type for this system, perhaps try X11?";
779
#endif
780
		}
781

782
	} else {
783
		ofLogNotice("ofAppEGLWindow") << "destroyWindow(): destroying (uninited) native window (not implemented yet)";
784
	}
785

786
	return true;
787
}
788

789

790
void ofAppEGLWindow::close(){
791
	if(!isUsingX11) {
792
		destroyNativeEvents();
793
	}
794

795
	// we got a terminate ... so clean up.
796
	destroySurface();
797
	destroyWindow();
798

799
	exitNative();
800
	events().notifyExit();
801
	events().disable();
802
}
803

804
//------------------------------------------------------------
805
void ofAppEGLWindow::makeCurrent(){
806
	eglMakeCurrent(eglDisplay,
807
			eglSurface, // draw surface
808
			eglSurface, // read surface
809
			eglContext);
810
}
811

812
//------------------------------------------------------------
813
void ofAppEGLWindow::swapBuffers(){
814
	EGLBoolean success = eglSwapBuffers(eglDisplay, eglSurface);
815
	if(!success) {
816
		GLint error = eglGetError();
817
		ofLogNotice("ofAppEGLWindow") << "display(): eglSwapBuffers failed: " << eglErrorString(error);
818
	}
819
}
820

821
//--------------------------------------------
822
void ofAppEGLWindow::startRender() {
823
	renderer()->startRender();
824
}
825

826
//--------------------------------------------
827
void ofAppEGLWindow::finishRender() {
828
	renderer()->finishRender();
829
}
830

831
//------------------------------------------------------------
832
void ofAppEGLWindow::update() {
833
	coreEvents.notifyUpdate();
834
}
835

836

837
//------------------------------------------------------------
838
void ofAppEGLWindow::draw() {
839
	// take care of any requests for a new screen mode
840
	if (windowMode != OF_GAME_MODE && bNewScreenMode){
841
		if( windowMode == OF_FULLSCREEN){
842
			setWindowRect(getScreenRect());
843
		} else if( windowMode == OF_WINDOW ){
844
			setWindowRect(nonFullscreenWindowRect);
845
		}
846
		bNewScreenMode = false;
847
	}
848

849
	currentRenderer->startRender();
850
	if( bEnableSetupScreen ) currentRenderer->setupScreen();
851

852
	coreEvents.notifyDraw();
853

854
	if(!isUsingX11) {
855
		if(bShowCursor){
856
			GLboolean bIsDepthTestEnabled = GL_FALSE;
857
			glGetBooleanv(GL_DEPTH_TEST, &bIsDepthTestEnabled);
858

859
			if(bIsDepthTestEnabled == GL_TRUE) {
860
				glDisable(GL_DEPTH_TEST);
861
			}
862

863
			bool isUsingNormalizedTexCoords = ofGetUsingNormalizedTexCoords();
864
			if(isUsingNormalizedTexCoords) {
865
				ofDisableNormalizedTexCoords();
866
			}
867

868
			currentRenderer->pushStyle();
869
			currentRenderer->setBlendMode(OF_BLENDMODE_ADD);
870
			currentRenderer->setColor(255);
871
			mouseCursor.draw(ofGetMouseX(),ofGetMouseY());
872

873
			currentRenderer->popStyle();
874

875
			if(bIsDepthTestEnabled == GL_TRUE) {
876
				glEnable(GL_DEPTH_TEST);
877
			}
878

879
			if(isUsingNormalizedTexCoords) {
880
				ofEnableNormalizedTexCoords();
881
			}
882
		}
883
	}
884
	currentRenderer->finishRender();
885

886
	EGLBoolean success = eglSwapBuffers(eglDisplay, eglSurface);
887
	if(!success) {
888
		GLint error = eglGetError();
889
		ofLogNotice("ofAppEGLWindow") << "display(): eglSwapBuffers failed: " << eglErrorString(error);
890
	}
891

892
	nFramesSinceWindowResized++;
893

894
}
895

896
//------------------------------------------------------------
897
ofCoreEvents & ofAppEGLWindow::events(){
898
	return coreEvents;
899
}
900

901
//------------------------------------------------------------
902
shared_ptr<ofBaseRenderer> & ofAppEGLWindow::renderer(){
903
	return currentRenderer;
904
}
905

906
//------------------------------------------------------------
907
void ofAppEGLWindow::setupNativeEvents() {
908
	setupNativeUDev();
909
	setupNativeInput();
910
	startThread();
911
}
912

913
//------------------------------------------------------------
914
void ofAppEGLWindow::destroyNativeEvents() {
915
	destroyNativeUDev();
916
	destroyNativeInput();
917
	waitForThread(true, threadTimeout);
918
}
919

920
//------------------------------------------------------------
921
void ofAppEGLWindow::setWindowRect(const ofRectangle& requestedWindowRect) {
922
	if(!isWindowInited) {
923
		ofLogError("ofAppEGLWindow") << "setWindowRect(): window not inited";
924
		return;
925
	}
926

927
	ofRectangle newRect = requestedWindowRect.getStandardized();
928

929
	if(newRect != currentWindowRect) {
930
		ofRectangle oldWindowRect = currentWindowRect;
931

932
		if(isUsingX11) {
933
			int ret = XMoveResizeWindow(x11Display,
934
					x11Window,
935
					(int)newRect.x,
936
					(int)newRect.y,
937
					(unsigned int)newRect.width,
938
					(unsigned int)newRect.height);
939
			if(ret == BadValue) {
940
				ofLogError("ofAppEGLWindow") << "setWindowRect(): XMoveResizeWindow returned BadValue";
941
			} else if(ret == BadWindow) {
942
				ofLogError("ofAppEGLWindow") << "setWindowRect(): XMoveResizeWindow returned BadWindow";
943
			} else {
944
				// all is good
945
				currentWindowRect = newRect;
946
			}
947
		} else {
948
#ifdef TARGET_RASPBERRY_PI_LEGACY
949

950
			VC_RECT_T dst_rect;
951
			dst_rect.x = (int32_t)newRect.x;
952
			dst_rect.y = (int32_t)newRect.y;
953
			dst_rect.width = (int32_t)newRect.width;
954
			dst_rect.height = (int32_t)newRect.height;
955

956
			VC_RECT_T src_rect;
957
			src_rect.x = 0;
958
			src_rect.y = 0;
959
			src_rect.width = (int32_t)newRect.width << 16;
960
			src_rect.height = (int32_t)newRect.height << 16;
961

962
			DISPMANX_UPDATE_HANDLE_T dispman_update = vc_dispmanx_update_start(0);
963

964
			vc_dispmanx_element_change_attributes(dispman_update,
965
					dispman_element,
966
					ELEMENT_CHANGE_SRC_RECT|ELEMENT_CHANGE_DEST_RECT, // we do both when resizing
967
					0, // layer (we aren't changing it here)
968
					0, // opactiy (we aren't changing it here)
969
					&dst_rect,
970
					&src_rect,
971
					0, // mask (we aren't changing it here)
972
					(DISPMANX_TRANSFORM_T)0);
973

974

975
			vc_dispmanx_update_submit_sync(dispman_update);
976

977
			// next time swapBuffers is called, it will be resized based on this eglwindow size data
978
			dispman_native_window.element = dispman_element;
979
			dispman_native_window.width = (int32_t)newRect.width;
980
			dispman_native_window.height = (int32_t)newRect.height; // don't forget!
981

982
			currentWindowRect = newRect;
983

984
#else
985
			ofLogError("ofAppEGLWindow") << "createEGLWindow(): no native window type for this system, perhaps try X11?";
986
#endif
987
		}
988

989
		if(oldWindowRect.width  != currentWindowRect.width || oldWindowRect.height != currentWindowRect.height) {
990
			coreEvents.notifyWindowResized(currentWindowRect.width,	currentWindowRect.height);
991
			nFramesSinceWindowResized = 0;
992
		}
993
	}
994
}
995

996

997
//------------------------------------------------------------
998
bool ofAppEGLWindow::createWindow(const ofRectangle& requestedWindowRect) {
999
	if(isUsingX11) {
1000
		return createX11NativeWindow(requestedWindowRect);
1001
	} else {
1002
#ifdef TARGET_RASPBERRY_PI_LEGACY
1003
		return createRPiNativeWindow(requestedWindowRect);
1004
#else
1005
		ofLogError("ofAppEGLWindow") << "createEGLWindow(): no native window type for this system, perhaps try X11?";
1006
		return false;
1007
#endif
1008
	}
1009
}
1010

1011
//------------------------------------------------------------
1012
int ofAppEGLWindow::getWindowWidth() {
1013
	return currentWindowRect.width;
1014
}
1015

1016
//------------------------------------------------------------
1017
int ofAppEGLWindow::getWindowHeight() {
1018
	return currentWindowRect.height;
1019
}
1020

1021
//------------------------------------------------------------
1022
void ofAppEGLWindow::pollEvents(){
1023
	if(!instance) return;
1024
	if(instance->isUsingX11) {
1025
		while(1){
1026
			XEvent event;
1027
			if (::XCheckWindowEvent(instance->x11Display, instance->x11Window, -1, &event)){
1028
				handleX11Event(event);
1029
			}else if (::XCheckTypedEvent(instance->x11Display, ClientMessage, &event)){
1030
				handleX11Event(event);
1031
			}else{
1032
				break;
1033
			}
1034
		}
1035
	} else {
1036
		queue<ofMouseEventArgs> mouseEventsCopy;
1037
		instance->lock();
1038
		mouseEventsCopy = instance->mouseEvents;
1039
		while(!instance->mouseEvents.empty()){
1040
			instance->mouseEvents.pop();
1041
		}
1042
		instance->unlock();
1043
		while(!mouseEventsCopy.empty()){
1044
			instance->coreEvents.notifyMouseEvent(mouseEventsCopy.front());
1045
			mouseEventsCopy.pop();
1046
		}
1047

1048
		// KEYBOARD EVENTS
1049
		queue<ofKeyEventArgs> keyEventsCopy;
1050
		instance->lock();
1051
		keyEventsCopy = instance->keyEvents;
1052
		while(!instance->keyEvents.empty()){
1053
			instance->keyEvents.pop();
1054
		}
1055
		instance->unlock();
1056
		while(!keyEventsCopy.empty()){
1057
			instance->coreEvents.notifyKeyEvent(keyEventsCopy.front());
1058
			keyEventsCopy.pop();
1059
		}
1060

1061
		queue<ofTouchEventArgs> touchEventsCopy;
1062
		instance->lock();
1063
		touchEventsCopy = instance->touchEvents;
1064
		while(!instance->touchEvents.empty()){
1065
			instance->touchEvents.pop();
1066
		}
1067
		instance->unlock();
1068
		while(!touchEventsCopy.empty()){
1069
			instance->coreEvents.notifyTouchEvent(touchEventsCopy.front());
1070
			touchEventsCopy.pop();
1071
		}
1072
	}
1073
}
1074

1075
//------------------------------------------------------------
1076
void ofAppEGLWindow::hideCursor(){
1077
	bShowCursor = false;
1078
}
1079

1080
//------------------------------------------------------------
1081
void ofAppEGLWindow::showCursor(){
1082
	bShowCursor = true;
1083
}
1084

1085
//------------------------------------------------------------
1086
void ofAppEGLWindow::setWindowTitle(string title) {
1087
	ofLogNotice("ofAppEGLWindow") << "setWindowTitle(): not implemented";
1088
}
1089

1090
//------------------------------------------------------------
1091
glm::vec2 ofAppEGLWindow::getWindowSize(){
1092
	return {currentWindowRect.width, currentWindowRect.height};
1093
}
1094

1095
//------------------------------------------------------------
1096
glm::vec2 ofAppEGLWindow::getWindowPosition(){
1097
	return glm::vec2(currentWindowRect.getPosition());
1098
}
1099

1100
//------------------------------------------------------------
1101
glm::vec2 ofAppEGLWindow::getScreenSize(){
1102
	unsigned int screenWidth = 0;
1103
	unsigned int screenHeight = 0;
1104

1105
	if(isUsingX11) {
1106
		// TODO, there must be a way to get screensize if the window is not inited
1107
		if(isWindowInited && x11Screen) {
1108
			screenWidth  = XWidthOfScreen(x11Screen);
1109
			screenHeight = XHeightOfScreen(x11Screen);
1110
		} else {
1111
			ofLogError("ofAppEGLWindow") << "getScreenSize(): tried to get display size but failed, x11Screen is not inited";
1112
		}
1113

1114
	} else {
1115
#ifdef TARGET_RASPBERRY_PI_LEGACY
1116
		int success = graphics_get_display_size(settings.screenNum, &screenWidth, &screenHeight);
1117
		if(success < 0) {
1118
			ofLogError("ofAppEGLWindow") << "getScreenSize(): tried to get display size but failed";
1119
		}
1120

1121
#else
1122
		ofLogError("ofAppEGLWindow") << "getScreenSize(): no native window type for this system, perhaps try X11?";
1123
#endif
1124

1125
	}
1126

1127
	return {screenWidth, screenHeight};
1128
}
1129

1130
//------------------------------------------------------------
1131
int ofAppEGLWindow::getWidth(){
1132
	if( orientation == OF_ORIENTATION_DEFAULT || orientation == OF_ORIENTATION_180 ){
1133
		return currentWindowRect.width;
1134
	}
1135
	return currentWindowRect.height;
1136
}
1137

1138
//------------------------------------------------------------
1139
int ofAppEGLWindow::getHeight(){
1140
	if( orientation == OF_ORIENTATION_DEFAULT || orientation == OF_ORIENTATION_180 ){
1141
		return currentWindowRect.height;
1142
	}
1143
	return currentWindowRect.width;
1144
}
1145

1146
//------------------------------------------------------------
1147
void ofAppEGLWindow::setOrientation(ofOrientation orientationIn){
1148
	orientation = orientationIn;
1149
}
1150

1151
//------------------------------------------------------------
1152
ofOrientation ofAppEGLWindow::getOrientation(){
1153
	return orientation;
1154
}
1155

1156
//------------------------------------------------------------
1157
bool ofAppEGLWindow::doesHWOrientation() {
1158
	return false;
1159
}
1160

1161
//------------------------------------------------------------
1162
void ofAppEGLWindow::setWindowPosition(int x, int y){
1163
	if(!isWindowInited) {
1164
		ofLogError("ofAppEGLWindow") << "setWindowPosition(): window not inited";
1165
		return;
1166
	}
1167

1168
	if(isUsingX11) {
1169
		int ret = XMoveWindow(x11Display,
1170
				x11Window,
1171
				x,
1172
				y);
1173
		if(ret == BadValue) {
1174
			ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadValue";
1175
		} else if(ret == BadWindow) {
1176
			ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadWindow";
1177
		} else {
1178
			currentWindowRect.x = x;
1179
			currentWindowRect.y = y;
1180
			nonFullscreenWindowRect = currentWindowRect;
1181
		}
1182
	} else {
1183
#ifdef TARGET_RASPBERRY_PI_LEGACY
1184

1185
		// keep it in bounds
1186
		auto screenSize = getScreenSize();
1187
		x = ofClamp(x, 0, screenSize.x - currentWindowRect.width);
1188
		y = ofClamp(y, 0, screenSize.y - currentWindowRect.height);
1189

1190
		VC_RECT_T dst_rect;
1191
		dst_rect.x = (int32_t)x;
1192
		dst_rect.y = (int32_t)y;
1193
		dst_rect.width = (int32_t)currentWindowRect.width;
1194
		dst_rect.height = (int32_t)currentWindowRect.height;
1195

1196
		dispman_update = vc_dispmanx_update_start(0);
1197

1198
		vc_dispmanx_element_change_attributes(dispman_update,
1199
				dispman_native_window.element,
1200
				ELEMENT_CHANGE_DEST_RECT,
1201
				0,
1202
				0,
1203
				&dst_rect,
1204
				NULL,
1205
				0,
1206
				(DISPMANX_TRANSFORM_T)0);
1207

1208

1209
vc_dispmanx_update_submit_sync(dispman_update);
1210

1211
currentWindowRect.x = x;
1212
currentWindowRect.y = y;
1213
nonFullscreenWindowRect = currentWindowRect;
1214

1215
#else
1216
	ofLogError("ofAppEGLWindow") << "setWindowPosition(): no native window type for this system, perhaps try X11?";
1217
#endif
1218
	}
1219

1220
}
1221

1222
//------------------------------------------------------------
1223
void ofAppEGLWindow::setWindowShape(int w, int h){
1224
	if(!isWindowInited) {
1225
		ofLogError("ofAppEGLWindow") << "setWindowPosition(): window not inited";
1226
		return;
1227
	}
1228

1229
	if(isUsingX11) {
1230
		int ret = XResizeWindow(x11Display,
1231
				x11Window,
1232
				(unsigned int)w,
1233
				(unsigned int)h);
1234
		if(ret == BadValue) {
1235
			ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadValue";
1236
		} else if(ret == BadWindow) {
1237
			ofLogError("ofAppEGLWindow") << "setWindowPosition(): XMoveWindow returned BadWindow";
1238
		} else {
1239
			currentWindowRect.width = w;
1240
			currentWindowRect.height = h;
1241
			nonFullscreenWindowRect = currentWindowRect;
1242
		}
1243
	} else {
1244
#ifdef TARGET_RASPBERRY_PI_LEGACY
1245
		setWindowRect(ofRectangle(currentWindowRect.x,currentWindowRect.y,w,h));
1246
		nonFullscreenWindowRect = currentWindowRect;
1247
#else
1248
		ofLogError("ofAppEGLWindow") << "setWindowPosition(): no native window type for this system, perhaps try X11?";
1249
#endif
1250
	}
1251
}
1252

1253
//------------------------------------------------------------
1254
ofWindowMode ofAppEGLWindow::getWindowMode(){
1255
	return windowMode;
1256
}
1257

1258
//------------------------------------------------------------
1259
void ofAppEGLWindow::toggleFullscreen(){
1260
	if( windowMode == OF_GAME_MODE) return;
1261

1262
	if( windowMode == OF_WINDOW ){
1263
		setFullscreen(true);
1264
	}else{
1265
		setFullscreen(false);
1266
	}
1267

1268
}
1269

1270
//------------------------------------------------------------
1271
void ofAppEGLWindow::setFullscreen(bool fullscreen){
1272
	if( windowMode == OF_GAME_MODE) return;
1273

1274
	if(fullscreen && windowMode != OF_FULLSCREEN){
1275
		bNewScreenMode = true;
1276
		windowMode = OF_FULLSCREEN;
1277
	}else if(!fullscreen && windowMode != OF_WINDOW) {
1278
		bNewScreenMode = true;
1279
		windowMode = OF_WINDOW;
1280
	}
1281
}
1282

1283
//------------------------------------------------------------
1284
void ofAppEGLWindow::enableSetupScreen(){
1285
	bEnableSetupScreen = true;
1286
}
1287

1288
//------------------------------------------------------------
1289
void ofAppEGLWindow::disableSetupScreen(){
1290
	bEnableSetupScreen = false;
1291
}
1292

1293
//------------------------------------------------------------
1294
ofRectangle ofAppEGLWindow::getScreenRect(){
1295
	auto screenSize = getScreenSize();
1296
	return ofRectangle(0,0,screenSize.x,screenSize.y);
1297
}
1298

1299
//------------------------------------------------------------
1300
void ofAppEGLWindow::setVerticalSync(bool enabled){
1301
	eglSwapInterval(eglDisplay, enabled ? 1 : 0);
1302
}
1303

1304
//------------------------------------------------------------
1305
void 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

1310
	while(isThreadRunning()) {
1311
		readNativeUDevEvents();
1312
		readNativeInputEvents();
1313
		// sleep briefly
1314
		ofSleepMillis(20);
1315
	}
1316
}
1317

1318
//------------------------------------------------------------
1319
// PLATFORM SPECIFIC RPI
1320
//------------------------------------------------------------
1321

1322
//------------------------------------------------------------
1323
void ofAppEGLWindow::setupNativeUDev() {
1324

1325
	udev = udev_new(); // create new udev object
1326
	if(!udev) {
1327
		ofLogError("ofAppEGLWindow") << "setupNativeUDev(): couldn't create udev object";
1328
	} else {
1329
		ofLogNotice("ofAppEGLWindow") << "setupNativeUDev(): created udev object";
1330
		// setup udev to monitor for input devices
1331
		mon = udev_monitor_new_from_netlink(udev, "udev");
1332
		// just listen for input devices
1333
		udev_monitor_filter_add_match_subsystem_devtype(mon, "input", NULL);
1334
		udev_monitor_enable_receiving(mon);
1335
		// get the file descriptor for the mon (used w/ select);
1336
		udev_fd = udev_monitor_get_fd(mon);
1337
	}
1338

1339
	if(udev_fd < 0) {
1340
		ofLogError("ofAppEGLWindow") << "setupNativeUDev(): did not create udev object, udev_fd < 0";
1341
	}
1342

1343
}
1344

1345
//------------------------------------------------------------
1346
void ofAppEGLWindow::destroyNativeUDev() {
1347
	udev_unref(udev); // clean up
1348
}
1349

1350
void ofAppEGLWindow::setupNativeInput(){
1351
	struct udev_enumerate *enumerate;
1352
	struct udev_list_entry *devices, *entry;
1353
	struct udev_device *dev;
1354
	bool isMouse;
1355

1356
	ofLogNotice("ofAppEGLWindow") << "setupNativeInput()";
1357

1358
	/* Create a list of the devices in the 'input' subsystem. */
1359
	enumerate = udev_enumerate_new(udev);
1360
	udev_enumerate_add_match_subsystem(enumerate, "input");
1361

1362
	udev_enumerate_scan_devices(enumerate);
1363

1364
	devices = udev_enumerate_get_list_entry(enumerate);
1365

1366
	udev_list_entry_foreach(entry, devices)
1367
	{
1368
		/* Get the filename of the /sys entry for the device
1369
			and create a udev_device object (dev) representing it */
1370
		const char * name = udev_list_entry_get_name(entry);
1371

1372
		dev = udev_device_new_from_syspath(udev, name);
1373

1374
		const char * sysname = udev_device_get_sysname(dev);
1375
		const char * devnode = udev_device_get_devnode(dev);
1376
		const char * devpath = udev_device_get_devpath(dev);
1377
		const char * devtype = udev_device_get_devtype(dev);
1378
		dev_t devnum = udev_device_get_devnum(dev);
1379
		const char * driver = udev_device_get_driver(dev);
1380
		const char * prop_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
1381
		const char * prop_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
1382
		const char * prop_touch = udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
1383

1384
		ofLogNotice() << "Got device";
1385
		ofLogNotice() << " - node: " << devnode;
1386
		ofLogNotice() << " - sysname: " << sysname;
1387
		ofLogNotice() << " - devpath: " << devpath;
1388
		ofLogNotice() << " - devtype: " << devtype;
1389
		ofLogNotice() << " - driver: " << driver;
1390
		ofLogNotice() << " - devnum: " << devnum;
1391
		ofLogNotice() << " - ID_INPUT_KEYBOARD: " << prop_keyboard;
1392
		ofLogNotice() << " - ID_INPUT_MOUSE: " << prop_mouse;
1393
		ofLogNotice() << " - ID_INPUT_TOUCHSCREEN: " << prop_touch;
1394

1395
		if(prop_mouse || prop_touch){
1396
			isMouse = true;
1397
		}else{
1398
			isMouse = false;
1399
		}
1400

1401
		if(devnode && (prop_keyboard || prop_mouse || prop_touch) && string_begins_with(sysname, "event")){
1402
			addInput(devnode, isMouse);
1403
		}
1404
		if(prop_keyboard){
1405
			keyboardDetected = true;
1406
		}
1407
		if(prop_mouse || prop_touch){
1408
			mouseDetected = true;
1409
		}
1410

1411
		udev_device_unref(dev);
1412
    }
1413
	/* Free the enumerator object */
1414
	udev_enumerate_unref(enumerate);
1415

1416
	if(!mouseDetected){
1417
		ofLogError("ofAppEGLWindow") << "setupNativeInput(): did not open mouse";
1418
	}
1419
	if(!keyboardDetected){
1420
		ofLogError("ofAppEGLWindow") << "setupKeyboard(): did not open keyboard";
1421
	}
1422

1423
	// save current terminal settings
1424
	tcgetattr (STDIN_FILENO, &tc);
1425
	ots = tc;
1426
	// disable echo on our temporary settings
1427
	tc.c_lflag &= ~ECHO;
1428
	tc.c_lflag |= ECHONL;
1429
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc);
1430
	
1431
	mb.mouseButtonState = 0;
1432

1433
	kb.shiftPressed = false;
1434
	kb.capsLocked = false;
1435

1436
	printInput();
1437
}
1438

1439
void ofAppEGLWindow::addInput(const char * node, bool isMouse){
1440
	if(node == NULL){
1441
		return;
1442
	}
1443

1444
	removeInput(node);
1445

1446
	int fd = open(node, O_RDONLY | O_NONBLOCK);
1447
	if(fd >= 0){
1448
		char deviceNameBuffer[256] = "Unknown Device";
1449
		ioctl(fd, EVIOCGNAME(sizeof(deviceNameBuffer)), deviceNameBuffer);
1450
		ofLogNotice("ofAppEGLWindow") << "addInput(): input device name = " << deviceNameBuffer;
1451

1452
		if(isMouse){
1453
			struct input_absinfo mabsx;
1454
			if(ioctl(fd, EVIOCGABS(0), &mabsx) < 0){
1455
				ofLogError("ofAppEGLWindow") << "ioctl GABS failed";
1456
			} else {
1457
				mouseAbsXMin = mabsx.minimum;
1458
				mouseAbsXMax = mabsx.maximum;
1459
				ofLogNotice("ofAppEGLWindow") << "mouse x axis min, max: " << mouseAbsXMin << ", " << mouseAbsXMax;
1460
			}
1461

1462
			// Do that for the y axis. EVIOCGABS(1): 1 stands for y axis.
1463
			struct input_absinfo mabsy;
1464
			if(ioctl(fd, EVIOCGABS(1), &mabsy) < 0){
1465
				ofLogError("ofAppEGLWindow") << "ioctl GABS failed";
1466
			}else{
1467
				mouseAbsYMin = mabsy.minimum;
1468
				mouseAbsYMax = mabsy.maximum;
1469
				ofLogNotice("ofAppEGLWindow") << "mouse y axis min, max: " << mouseAbsYMin << ", " << mouseAbsYMax;
1470
			}
1471
		}
1472

1473
		inputDevices[node] = fd;
1474
    }
1475
}
1476

1477
void ofAppEGLWindow::removeInput(const char * node){
1478
	if(node == NULL)
1479
        return;
1480

1481
	device::iterator iter = inputDevices.find(node);
1482
	if(iter != inputDevices.end()){
1483
		::close(iter->second);
1484
		inputDevices.erase(iter);
1485
	}
1486
}
1487

1488
void ofAppEGLWindow::printInput(){
1489
	ofLogNotice("--- Input Device List ---");
1490
	for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
1491
		ofLogNotice() << " - " << iter->first;
1492
	}
1493
	ofLogNotice("-------------------------");
1494
}
1495

1496
void ofAppEGLWindow::destroyNativeInput(){
1497
	ofLogNotice("ofAppEGLWindow") << "destroyNativeInput()";
1498
	
1499
	for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
1500
		if(iter->second >= 0){
1501
            ::close(iter->second);
1502
        }
1503
	}
1504

1505
	inputDevices.clear();
1506

1507
	tcsetattr (STDIN_FILENO, TCSAFLUSH, &ots);
1508
}
1509

1510
//------------------------------------------------------------
1511
void 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
1515
	static ofTouchEventArgs touchEvent;
1516
	static ofKeyEventArgs keyEvent;
1517
	static ofMouseEventArgs mouseEvent;
1518
	struct input_event ev;
1519
	char key = 0;
1520
	
1521
	bool pushKeyEvent = false;
1522
	bool pushMouseEvent = false;
1523
	bool pushTouchEvent = false;
1524
	bool axisValuePending = false;
1525
	bool touchAxisValuePending = false;
1526

1527
	int nBytesRead = read(fd, &ev, sizeof(struct input_event));
1528
	while(nBytesRead >= 0){
1529
		if(ev.type == EV_KEY){
1530
			if(ev.code == BTN_LEFT){
1531
				ofLogNotice("ofAppEGLWindow") << "BTN_LEFT" << endl;
1532
				if(ev.value == 0){ // release
1533
					mouseEvent.button = OF_MOUSE_BUTTON_LEFT;
1534
					mouseEvent.type = ofMouseEventArgs::Released;
1535
					mb.mouseButtonState &= ~MOUSE_BUTTON_LEFT_MASK;
1536
					pushMouseEvent = true;
1537
				}else if(ev.value == 1){ // press
1538
					mb.mouseButtonState |= MOUSE_BUTTON_LEFT_MASK;
1539
					mouseEvent.type = ofMouseEventArgs::Pressed;
1540
					mouseEvent.button = OF_MOUSE_BUTTON_LEFT;
1541
					pushMouseEvent = true;
1542
				}
1543
			}else if(ev.code == BTN_MIDDLE){
1544
				ofLogNotice("ofAppEGLWindow") << "BTN_MIDDLE" << endl;
1545
				if(ev.value == 0){ // release
1546
					mouseEvent.button = OF_MOUSE_BUTTON_MIDDLE;
1547
					mouseEvent.type = ofMouseEventArgs::Released;
1548
					mb.mouseButtonState &= ~MOUSE_BUTTON_MIDDLE_MASK;
1549
					pushMouseEvent = true;
1550
				}else if(ev.value == 1){ // press
1551
					mb.mouseButtonState |= MOUSE_BUTTON_MIDDLE_MASK;
1552
					mouseEvent.type = ofMouseEventArgs::Pressed;
1553
					mouseEvent.button = OF_MOUSE_BUTTON_MIDDLE;
1554
					pushMouseEvent = true;
1555
				}
1556
			}else if(ev.code == BTN_RIGHT){
1557
				ofLogNotice("ofAppEGLWindow") << "BTN_RIGHT" << endl;
1558
				if(ev.value == 0){ // release
1559
					mouseEvent.button = OF_MOUSE_BUTTON_RIGHT;
1560
					mouseEvent.type = ofMouseEventArgs::Released;
1561
					mb.mouseButtonState &= ~MOUSE_BUTTON_RIGHT_MASK;
1562
					pushMouseEvent = true;
1563
				}else if(ev.value == 1){ // press
1564
					mb.mouseButtonState |= MOUSE_BUTTON_RIGHT_MASK;
1565
					mouseEvent.type = ofMouseEventArgs::Pressed;
1566
					mouseEvent.button = OF_MOUSE_BUTTON_RIGHT;
1567
					pushMouseEvent = true;
1568
				}
1569
			}else if(ev.code == BTN_TOUCH){
1570
				if(ev.value == 0){ // release	
1571
					touchEvent.type = ofTouchEventArgs::up;
1572
					touchEvent.id = 0;
1573
					mt[touchEvent.id] = 0;
1574
					pushTouchEvent = true;
1575

1576
				}else if(ev.value == 1){ // press
1577
					touchEvent.type = ofTouchEventArgs::down;
1578
					touchEvent.id = 0;
1579
					mt[touchEvent.id] = 1;
1580
					pushTouchEvent = true;
1581
				}
1582
			}else{
1583
				if(ev.value == 0){
1584
					// key released
1585
					keyEvent.type = ofKeyEventArgs::Released;
1586
				}else if(ev.value == 1){
1587
					// key pressed
1588
					keyEvent.type = ofKeyEventArgs::Pressed;
1589
				}else if(ev.value == 2){
1590
					// key repeated
1591
					keyEvent.type = ofKeyEventArgs::Pressed;
1592
				}else{
1593
					// unknown ev.value
1594
				}
1595
				switch (ev.code) {
1596
					case KEY_RIGHTSHIFT:
1597
					case KEY_LEFTSHIFT:
1598
						kb.shiftPressed = ev.value;
1599
						break;
1600
					case KEY_RIGHTCTRL:
1601
					case KEY_LEFTCTRL:
1602
						break;
1603
					case KEY_CAPSLOCK:
1604
						if (ev.value == 1) {
1605
							if (kb.capsLocked) {
1606
								kb.capsLocked = 0;
1607
							} else {
1608
								kb.capsLocked = 1;
1609
							}
1610
						}
1611
						break;
1612
					case KEY_ESC:
1613
						pushKeyEvent = true;
1614
						keyEvent.key = OF_KEY_ESC;
1615
						break;
1616
					case KEY_BACKSPACE:
1617
						pushKeyEvent = true;
1618
						keyEvent.key = OF_KEY_BACKSPACE;
1619
						break;
1620
					case KEY_DELETE:
1621
						pushKeyEvent = true;
1622
						keyEvent.key = OF_KEY_DEL;
1623
						break;
1624
					case KEY_F1:
1625
						pushKeyEvent = true;
1626
						keyEvent.key = OF_KEY_F1;
1627
						break;
1628
					case KEY_F2:
1629
						pushKeyEvent = true;
1630
						keyEvent.key = OF_KEY_F2;
1631
						break;
1632
					case KEY_F3:
1633
						pushKeyEvent = true;
1634
						keyEvent.key = OF_KEY_F3;
1635
						break;
1636
					case KEY_F4:
1637
						pushKeyEvent = true;
1638
						keyEvent.key = OF_KEY_F4;
1639
						break;
1640
					case KEY_F5:
1641
						pushKeyEvent = true;
1642
						keyEvent.key = OF_KEY_F5;
1643
						break;
1644
					case KEY_F6:
1645
						pushKeyEvent = true;
1646
						keyEvent.key = OF_KEY_F6;
1647
						break;
1648
					case KEY_F7:
1649
						pushKeyEvent = true;
1650
						keyEvent.key = OF_KEY_F7;
1651
						break;
1652
					case KEY_F8:
1653
						pushKeyEvent = true;
1654
						keyEvent.key = OF_KEY_F8;
1655
						break;
1656
					case KEY_F9:
1657
						pushKeyEvent = true;
1658
						keyEvent.key = OF_KEY_F9;
1659
						break;
1660
					case KEY_F10:
1661
						pushKeyEvent = true;
1662
						keyEvent.key = OF_KEY_F10;
1663
						break;
1664
					case KEY_F11:
1665
						pushKeyEvent = true;
1666
						keyEvent.key = OF_KEY_F11;
1667
						break;
1668
					case KEY_F12:
1669
						pushKeyEvent = true;
1670
						keyEvent.key = OF_KEY_F12;
1671
						break;
1672
					case KEY_LEFT:
1673
						pushKeyEvent = true;
1674
						keyEvent.key = OF_KEY_LEFT;
1675
						break;
1676
					case KEY_UP:
1677
						pushKeyEvent = true;
1678
						keyEvent.key = OF_KEY_UP;
1679
						break;
1680
					case KEY_RIGHT:
1681
						pushKeyEvent = true;
1682
						keyEvent.key = OF_KEY_RIGHT;
1683
						break;
1684
					case KEY_DOWN:
1685
						pushKeyEvent = true;
1686
						keyEvent.key = OF_KEY_DOWN;
1687
						break;
1688
					case KEY_PAGEUP:
1689
						pushKeyEvent = true;
1690
						keyEvent.key = OF_KEY_PAGE_UP;
1691
						break;
1692
					case KEY_PAGEDOWN:
1693
						pushKeyEvent = true;
1694
						keyEvent.key = OF_KEY_PAGE_DOWN;
1695
						break;
1696
					case KEY_HOME:
1697
						pushKeyEvent = true;
1698
						keyEvent.key = OF_KEY_HOME;
1699
						break;
1700
					case KEY_END:
1701
						pushKeyEvent = true;
1702
						keyEvent.key = OF_KEY_END;
1703
						break;
1704
					case KEY_INSERT:
1705
						pushKeyEvent = true;
1706
						keyEvent.key = OF_KEY_INSERT;
1707
						break;
1708
					case KEY_ENTER:
1709
					case KEY_KPENTER:
1710
						pushKeyEvent = true;
1711
						keyEvent.key = OF_KEY_RETURN;
1712
						break;
1713
					default:
1714
						// VERY RUDIMENTARY KEY MAPPING WITH MAPS ABOVE
1715
						if(ev.code < sizeof(lowercase_map)){
1716
							if(kb.shiftPressed){
1717
								key = uppercase_map[ev.code];
1718
								if(kb.capsLocked) keyEvent.key = tolower(key);
1719
								keyEvent.key = key;
1720
								pushKeyEvent = true;
1721
							}else{
1722
								key = lowercase_map[ev.code];
1723
								if(kb.capsLocked) key = toupper(key);
1724
								keyEvent.key = key;
1725
								pushKeyEvent = true;
1726
							}
1727
						}else{
1728
							ofLogNotice("ofAppEGLWindow") << "readKeyboardEvents(): input_event.code is outside of our small range";
1729
						}
1730
				}	
1731
			}
1732
		}else if (ev.type == EV_REL){
1733
			int axis = ev.code;
1734
			int amount = ev.value;
1735
			switch (axis)
1736
			{
1737
			case REL_X:
1738
				mouseEvent.x += amount * mouseScaleX;
1739
				mouseEvent.x = ofClamp(mouseEvent.x, 0, currentWindowRect.width);
1740
				axisValuePending = true;
1741
				break;
1742
			case REL_Y:
1743
				mouseEvent.y += amount * mouseScaleY;
1744
				mouseEvent.y = ofClamp(mouseEvent.y, 0, currentWindowRect.height);
1745
				axisValuePending = true;
1746
				break;
1747
			default:
1748
				ofLogNotice("ofAppEGLWindow") << "readMouseEvents(): unknown mouse axis (perhaps it's the scroll wheel?): axis " << axis << " amount " << amount << endl;
1749
				break;
1750
			}
1751
		}else if (ev.type == EV_ABS){
1752
			int axis = ev.code;
1753
			int amount = ev.value;
1754
			switch (axis)
1755
			{
1756
				// do not need this mouse returns REL_X/REL_Y
1757
				case ABS_X:
1758
					// mouseEvent.x = amount * (float)currentWindowRect.width / (float)mouseAbsXMax;
1759
					// mouseEvent.x = ofClamp(mouseEvent.x, 0, currentWindowRect.width);
1760
					// axisValuePending = true;
1761
					break;
1762
				case ABS_Y:
1763
					// mouseEvent.y = amount * (float)currentWindowRect.height / (float)mouseAbsYMax;
1764
					// mouseEvent.y = ofClamp(mouseEvent.y, 0, currentWindowRect.height);
1765
					// axisValuePending = true;
1766
					break;
1767
				case ABS_MT_TOOL_TYPE:
1768
					break;
1769
				case ABS_MT_SLOT:
1770
					touchEvent.id = amount;
1771
					break;
1772
				case ABS_MT_TRACKING_ID:
1773
					if (amount == -1)
1774
					{
1775
						if( mt[touchEvent.id] == 1){
1776
							touchEvent.type = ofTouchEventArgs::up;
1777
							mt[touchEvent.id] = 0;
1778
							pushTouchEvent = true;
1779
						}
1780
					}
1781
					else 
1782
					{
1783
						if (mt[touchEvent.id] == 0){
1784
							touchEvent.type = ofTouchEventArgs::down;
1785
							mt[touchEvent.id] = 1;
1786
							pushTouchEvent = true;
1787
						}
1788
						touchAxisValuePending = true;
1789
					}
1790
					break;
1791
				case ABS_MT_POSITION_X:
1792
					mtp[touchEvent.id].x = amount * (float)currentWindowRect.width / (float)mouseAbsXMax;
1793
					mtp[touchEvent.id].x = ofClamp(mtp[touchEvent.id].x, 0, currentWindowRect.width);
1794
					touchAxisValuePending = true;
1795
					break;
1796
				case ABS_MT_POSITION_Y:
1797
					mtp[touchEvent.id].y = amount * (float)currentWindowRect.height / (float)mouseAbsYMax;
1798
					mtp[touchEvent.id].y = ofClamp(mtp[touchEvent.id].y, 0, currentWindowRect.height);
1799
					if(!pushTouchEvent){
1800
						touchEvent.type = ofTouchEventArgs::move;
1801
						pushTouchEvent = true;
1802
					}
1803
					touchAxisValuePending = true;
1804
					break;
1805
				default:
1806
					ofLogNotice("ofAppEGLWindow") << "EV_ABS unknown axis: axis " << axis << " amount " << amount << endl;
1807
					break;
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
1814
			if(axisValuePending){
1815
				// TODO, this state doesn't make as much sense when the mouse is not dragging
1816
				if(mb.mouseButtonState > 0){
1817
					// dragging (what if dragging w/ more than one button?)
1818
					mouseEvent.type = ofMouseEventArgs::Dragged;
1819
				}else{
1820
					// just moving
1821
					mouseEvent.type = ofMouseEventArgs::Moved;
1822
				}
1823

1824
				mouseEvent.button = mb.mouseButtonState;
1825

1826
				pushMouseEvent = true;
1827
				axisValuePending = false;
1828
			}
1829

1830
			if(touchAxisValuePending){
1831
				if(!pushTouchEvent){
1832
					touchEvent.type = ofTouchEventArgs::move;
1833
					pushTouchEvent = true;
1834
				}
1835
				touchAxisValuePending = false;
1836
			}
1837
		}
1838

1839
		
1840
		
1841

1842
		if(pushKeyEvent){
1843
			lock();
1844
			keyEvents.push(keyEvent);
1845
			unlock();
1846
			pushKeyEvent = false;
1847
		}
1848
		
1849
		if(pushMouseEvent){
1850
			// lock the thread for a moment while we copy the data
1851
			lock();
1852
			mouseEvents.push(mouseEvent);
1853
			unlock();
1854
			pushMouseEvent = false;
1855
		}
1856

1857
		if(pushTouchEvent){
1858
			touchEvent.x = mtp[touchEvent.id].x;
1859
			touchEvent.y = mtp[touchEvent.id].y;
1860
			lock();
1861
			touchEvents.push(touchEvent);
1862
			unlock();
1863
			pushTouchEvent = false;
1864
		}
1865
		nBytesRead = read(fd, &ev,sizeof(struct input_event));
1866
	}
1867
}
1868

1869
//------------------------------------------------------------
1870
void ofAppEGLWindow::readNativeUDevEvents() {
1871
	// look for devices being attatched / detatched
1872
	fd_set fds;
1873
	struct timeval tv;
1874
	int ret;
1875
	struct udev_device *dev;
1876
	bool is_mouse = false;
1877

1878
	FD_ZERO(&fds);
1879
	FD_SET(udev_fd, &fds);
1880
	tv.tv_sec = 0;
1881
	tv.tv_usec = 0;
1882

1883
	ret = select(udev_fd+1, &fds, NULL, NULL, &tv);
1884

1885
	/* Check if our file descriptor has received data. */
1886
	if(ret > 0 && FD_ISSET(udev_fd, &fds)){
1887
		/* Make the call to receive the device.
1888
		   select() ensured that this will not block. */
1889
		dev = udev_monitor_receive_device(mon);
1890
		if(dev){
1891
			const char * devnode = udev_device_get_devnode(dev);
1892
			const char * devpath = udev_device_get_devpath(dev);
1893
			const char * sysname = udev_device_get_sysname(dev);
1894
			const char * subsystem = udev_device_get_subsystem(dev);
1895
			const char * devtype = udev_device_get_devtype(dev);
1896
			dev_t devnum = udev_device_get_devnum(dev);
1897
			const char * driver = udev_device_get_driver(dev);
1898
			const char * action = udev_device_get_action(dev);
1899
			const char * prop_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
1900
			const char * prop_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
1901

1902
			ofLogNotice("readNativeUDevEvents") << "udev monitor receive devixe";
1903
			ofLogNotice() << " - node: " << devnode;
1904
			ofLogNotice() << " - devpath: " << devpath;
1905
			ofLogNotice() << " - sysname: " << sysname;
1906
			ofLogNotice() << " - subsystem: " << subsystem;
1907
			ofLogNotice() << " - devtype: " << devtype;
1908
			ofLogNotice() << " - devnum: " << devnum;
1909
			ofLogNotice() << " - driver: " << driver;
1910
			ofLogNotice() << " - action: " << action;
1911
			ofLogNotice() << " - ID_INPUT_KEYBOARD: " << prop_keyboard;
1912
			ofLogNotice() << " - ID_INPUT_MOUSE: " << prop_mouse;
1913

1914
			if(prop_mouse){
1915
				is_mouse = true;
1916
			}
1917
            if(devnode && (prop_keyboard || prop_mouse) && string_begins_with(sysname, "event")){
1918
                if(strcmp(action, "add") == 0){
1919
					addInput(devnode, is_mouse);
1920
                }else if(strcmp(action, "remove") == 0){
1921
					removeInput(devnode);
1922
                }
1923
            }
1924
			
1925
			udev_device_unref(dev);
1926
		}else{
1927
			ofLogNotice("ofAppEGLWindow") << "readNativeUDevEvents(): device returned by receive_device() is NULL";
1928
		}
1929
	}
1930
}
1931

1932
void ofAppEGLWindow::readNativeInputEvents(){
1933
	for(device::iterator iter = inputDevices.begin(); iter != inputDevices.end(); iter++){
1934
		processInput(iter->second, iter->first.c_str());
1935
	}
1936
}
1937

1938
#ifdef TARGET_RASPBERRY_PI_LEGACY
1939
//------------------------------------------------------------
1940
void ofAppEGLWindow::initRPiNative() {
1941
	bcm_host_init();
1942

1943
	memset(&dispman_native_window, 0x0, sizeof(EGL_DISPMANX_WINDOW_T));
1944
	dispman_element = DISPMANX_NO_HANDLE;
1945
	dispman_display = DISPMANX_NO_HANDLE;
1946
	dispman_update  = DISPMANX_NO_HANDLE;
1947
	memset(&dispman_clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
1948
	dispman_transform = DISPMANX_NO_ROTATE;
1949
	memset(&dispman_alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); // zero dispman_alpha
1950

1951
}
1952

1953
//------------------------------------------------------------
1954
void ofAppEGLWindow::exitRPiNative() {
1955
	bcm_host_deinit();
1956
}
1957

1958
//------------------------------------------------------------
1959
bool ofAppEGLWindow::createRPiNativeWindow(const ofRectangle& requestedWindowRect){
1960

1961
	ofRectangle screenRect = getScreenRect();
1962

1963
	// make sure our requested window rectangle does not exceed the native
1964
	// screen size, or start outside of it.
1965
	ofRectangle windowRect = screenRect.getIntersection(requestedWindowRect);
1966

1967
	ofLogNotice("ofAppEGLWindow") << "setupRPiNativeWindow(): screenRect: " << screenRect.width << "x" << screenRect.height;
1968
	ofLogNotice("ofAppEGLWindow") << "setupRPiNativeWindow(): windowRect: " << windowRect.width << "x" << windowRect.height;
1969

1970
	//////////////////////////
1971
	VC_RECT_T dst_rect;
1972

1973
	dst_rect.x = (int32_t)windowRect.x;
1974
	dst_rect.y = (int32_t)windowRect.y;
1975
	dst_rect.width = (int32_t)windowRect.width;
1976
	dst_rect.height = (int32_t)windowRect.height;
1977

1978
	VC_RECT_T src_rect;
1979

1980
	src_rect.x = 0;
1981
	src_rect.y = 0;
1982
	src_rect.width  = dst_rect.width << 16;
1983
	src_rect.height = dst_rect.height << 16;
1984

1985
	memset(&dispman_alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T)); // zero dispman_alpha
1986
	dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
1987
	dispman_alpha.opacity = ofClamp(settings.eglWindowOpacity,0,255);
1988
	dispman_alpha.mask = 0;
1989

1990
	memset(&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
1993
	dispman_transform = DISPMANX_NO_ROTATE;
1994

1995
	// get the zero display
1996
	dispman_display = vc_dispmanx_display_open(settings.screenNum);
1997

1998
	// begin the display manager interaction
1999
	dispman_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.
2005
	dispman_element = vc_dispmanx_element_add ( dispman_update,
2006
			dispman_display,
2007
			settings.layer, // layer
2008
			&dst_rect, // dst rect
2009
			(DISPMANX_RESOURCE_HANDLE_T)0, // src
2010
			&src_rect, // src rect
2011
			DISPMANX_PROTECTION_NONE, // ?
2012
			&dispman_alpha, // alpha
2013
			&dispman_clamp, // clamp
2014
			dispman_transform // transform
2015
	);
2016

2017
	if(dispman_element == DISPMANX_NO_HANDLE) {
2018
		ofLogError("ofAppEGLWindow") << "setupRPiNativeWindow(): dispman_element == DISPMANX_NO_HANDLE";
2019
		return false;
2020
	} else if(dispman_element == (unsigned)DISPMANX_INVALID) {
2021
		ofLogError("ofAppEGLWindow") << "setupRPiNativeWindow(): dispman_element == DISPMANX_INVALID";
2022
		return false;
2023
	}
2024

2025
	// set dispman_native_window to zero
2026
	memset(&dispman_native_window, 0x0, sizeof(EGL_DISPMANX_WINDOW_T));
2027
	dispman_native_window.element = dispman_element;
2028
	dispman_native_window.width = (int32_t)windowRect.width;
2029
	dispman_native_window.height = (int32_t)windowRect.height;
2030

2031
	// set background to black (not required)
2032
	vc_dispmanx_display_set_background(dispman_update, dispman_display, 0x00, 0x00, 0x00);
2033

2034
	// finished with display manager update, so sync
2035
	vc_dispmanx_update_submit_sync( dispman_update );
2036

2037
	currentWindowRect = windowRect;
2038

2039
	return true;
2040
}
2041
#endif
2042

2043
//------------------------------------------------------------
2044
// X11 BELOW
2045
//------------------------------------------------------------
2046
bool ofAppEGLWindow::createX11NativeWindow(const ofRectangle& requestedWindowRect){
2047

2048
	// X11 variables
2049
	x11Window = 0;
2050
	x11Display = 0;
2051
	x11ScreenNum = 0; // TODO: settings.screenNum?
2052
	x11Screen = 0;
2053
	XVisualInfo* x11Visual = 0; // TODO does this need to be deleted?
2054
	Colormap x11Colormap = 0;
2055

2056
	/*
2057
	Step 0 - Create a NativeWindowType that we can use it for OpenGL ES output
2058
	 */
2059
	Window sRootWindow;
2060
	XSetWindowAttributes sWA;
2061
	unsigned int ui32Mask;
2062
	int 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.
2068
	ofRectangle windowRect = requestedWindowRect.getStandardized();//screenRect.getIntersection(requestedWindowRect);
2069

2070
	// Initializes the display and screen
2071
	x11Display = XOpenDisplay( 0 );
2072
	if (!x11Display) {
2073
		ofLogError("ofAppEGLWindow") << "unable to open X display";
2074
		return false;
2075
	}
2076

2077
	x11ScreenNum = XDefaultScreen( x11Display );
2078

2079
	x11Screen = XDefaultScreenOfDisplay(x11Display);
2080

2081
	// Gets the window parameters
2082
	sRootWindow = RootWindow(x11Display, x11ScreenNum);
2083
	i32Depth = DefaultDepth(x11Display, x11ScreenNum);
2084
	x11Visual = new XVisualInfo();
2085

2086
	XMatchVisualInfo( x11Display,
2087
			x11ScreenNum,
2088
			i32Depth,
2089
			TrueColor,
2090
			x11Visual);
2091

2092
	if (!x11Visual) {
2093
		ofLogError("ofAppEGLWindow") << "unable to acquire XVisualInfo";
2094
		return false;
2095
	}
2096

2097
	x11Colormap = XCreateColormap( x11Display, sRootWindow, x11Visual->visual, AllocNone );
2098

2099
	delete x11Visual;
2100

2101
	// set the colormap window attribuet
2102
	sWA.colormap = x11Colormap;
2103

2104
	// Add to these for handling other events
2105
	sWA.event_mask = 0;
2106
	sWA.event_mask |= StructureNotifyMask;
2107
	sWA.event_mask |= ExposureMask;
2108
	sWA.event_mask |= ButtonPressMask;
2109
	sWA.event_mask |= ButtonReleaseMask;
2110
	sWA.event_mask |= PointerMotionMask;
2111
	sWA.event_mask |= KeyPressMask;
2112
	sWA.event_mask |= KeyReleaseMask;
2113

2114
	// setup background pixel attributes
2115
	ui32Mask = 0;
2116
	ui32Mask |= CWBackPixel;
2117
	ui32Mask |= CWBorderPixel;
2118
	ui32Mask |= CWEventMask;
2119
	ui32Mask |= CWColormap;
2120

2121
	// Creates the X11 window
2122
	x11Window = XCreateWindow(x11Display, // Specifies the connection to the X server.
2123
			sRootWindow, // 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.
2131
			0, // Specifies the width of the created
2132
			// window's border in pixels.
2133
			CopyFromParent, // Specifies the window's depth.
2134
			// A depth of CopyFromParent means
2135
			// the depth is taken from the parent.
2136
			InputOutput, // 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.
2140
			CopyFromParent, // Specifies the visual type.
2141
			// A visual of CopyFromParent means the visual type
2142
			// is taken from the parent.
2143
			ui32Mask, // 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

2151
	XMapWindow(x11Display, x11Window);
2152
	XFlush(x11Display);
2153

2154
	// check success?
2155
	currentWindowRect = windowRect;
2156

2157
	return true;
2158
}
2159

2160
//------------------------------------------------------------
2161
static KeySym KeyCodeToKeySym(Display * display, KeyCode keycode, unsigned int event_mask) {
2162
	KeySym keysym = NoSymbol;
2163

2164
	//Get the map
2165
	XkbDescPtr keyboard_map = XkbGetMap(display, XkbAllClientInfoMask, XkbUseCoreKbd);
2166
	if (keyboard_map) {
2167
		//What is diff between XkbKeyGroupInfo and XkbKeyNumGroups?
2168
		unsigned char info = XkbKeyGroupInfo(keyboard_map, keycode);
2169
		unsigned int num_groups = XkbKeyNumGroups(keyboard_map, keycode);
2170

2171
		//Get the group
2172
		unsigned int group = 0x00;
2173
		switch (XkbOutOfRangeGroupAction(info)) {
2174
		case 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
			 */
2180
			group = XkbOutOfRangeGroupInfo(info);
2181
			if (group >= num_groups) {
2182
				group = 0;
2183
			}
2184
			break;
2185

2186
		case 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
			 */
2194
			group = num_groups - 1;
2195
			break;
2196

2197
		case 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
			 */
2203
		default:
2204
			if (num_groups != 0) {
2205
				group %= num_groups;
2206
			}
2207
			break;
2208
		}
2209

2210
		XkbKeyTypePtr key_type = XkbKeyKeyType(keyboard_map, keycode, group);
2211
		unsigned int active_mods = event_mask & key_type->mods.mask;
2212

2213
		int i, level = 0;
2214
		for (i = 0; i < key_type->map_count; i++) {
2215
			if (key_type->map[i].active && key_type->map[i].mods.mask == active_mods) {
2216
				level = key_type->map[i].level;
2217
			}
2218
		}
2219

2220
		keysym = XkbKeySymEntry(keyboard_map, keycode, level, group);
2221
		XkbFreeClientMap(keyboard_map, XkbAllClientInfoMask, true);
2222
	}
2223

2224
	return keysym;
2225
}
2226

2227
//------------------------------------------------------------
2228
void ofAppEGLWindow::handleX11Event(const XEvent& event){
2229
	ofMouseEventArgs mouseEvent;
2230
	ofKeyEventArgs keyEvent;
2231

2232
	switch (event.type){
2233
	case KeyPress:
2234
	case KeyRelease:
2235
	{
2236
		KeySym key = KeyCodeToKeySym(instance->x11Display,event.xkey.keycode,event.xkey.state);
2237
		keyEvent.key = key;
2238
		if (event.type == KeyPress) {
2239
			keyEvent.type = ofKeyEventArgs::Pressed;
2240
			if(key == 65307){
2241
				keyEvent.key = OF_KEY_ESC;
2242
			}
2243
		} else if (event.type == KeyRelease){
2244
			keyEvent.type = ofKeyEventArgs::Released;
2245
		}
2246

2247
		instance->coreEvents.notifyKeyEvent(keyEvent);
2248
	}
2249
	break;
2250
	case ButtonPress:
2251
	case ButtonRelease:
2252
		mouseEvent.x = static_cast<float>(event.xbutton.x);
2253
		mouseEvent.y = static_cast<float>(event.xbutton.y);
2254
		mouseEvent.button = event.xbutton.button;
2255
		if (event.type == ButtonPress){
2256
			mouseEvent.type = ofMouseEventArgs::Pressed;
2257
		} else {
2258
			mouseEvent.type = ofMouseEventArgs::Released;
2259
		}
2260

2261
		instance->coreEvents.notifyMouseEvent(mouseEvent);
2262
		break;
2263
	case MotionNotify:
2264
		//cout << "motion notify" << endl;
2265
		mouseEvent.x = static_cast<float>(event.xmotion.x);
2266
		mouseEvent.y = static_cast<float>(event.xmotion.y);
2267
		mouseEvent.button = event.xbutton.button;
2268
		if(ofGetMousePressed()) {
2269
			mouseEvent.type = ofMouseEventArgs::Dragged;
2270
		} else {
2271
			mouseEvent.type = ofMouseEventArgs::Moved;
2272
		}
2273

2274
		instance->coreEvents.notifyMouseEvent(mouseEvent);
2275
		break;
2276
	case ConfigureNotify:
2277
		instance->currentWindowRect.x = event.xconfigure.x;
2278
		instance->currentWindowRect.y = event.xconfigure.y;
2279
		instance->currentWindowRect.width = event.xconfigure.width;
2280
		instance->currentWindowRect.height = event.xconfigure.height;
2281
		instance->nonFullscreenWindowRect = instance->currentWindowRect;
2282
		instance->coreEvents.notifyWindowResized(event.xconfigure.width,event.xconfigure.height);
2283
		break;
2284
	/*case ClientMessage:{
2285
		if (event.xclient.message_type == wmProtocols_ &&
2286
		event.xclient.format == 32 &&
2287
		event.xclient.data.l[0] == (long) wmDeleteWindow_)
2288
		{
2289
		if (listener())
2290
		{
2291
		  if (listener()->onClose(wrapper() ? *wrapper() : *(WindowInterface*)this))
2292
			isShuttingDown_ = true;
2293
		}
2294
		else
2295
		{
2296
		  isShuttingDown_ = true;
2297
		}
2298
		}
2299
		break;
2300
	  }*/
2301
	}
2302
}
2303

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.