Browse Source

Experimental support for windows with per-pixel transparency (macOS, X11 and Windows).

bruvzg 7 years ago
parent
commit
ddae098dee

+ 12 - 0
core/bind/core_bind.cpp

@@ -332,6 +332,14 @@ void _OS::set_borderless_window(bool p_borderless) {
 	OS::get_singleton()->set_borderless_window(p_borderless);
 }
 
+bool _OS::get_window_per_pixel_transparency_enabled() const {
+	return OS::get_singleton()->get_window_per_pixel_transparency_enabled();
+}
+
+void _OS::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+	OS::get_singleton()->set_window_per_pixel_transparency_enabled(p_enabled);
+}
+
 bool _OS::get_borderless_window() const {
 	return OS::get_singleton()->get_borderless_window();
 }
@@ -1063,6 +1071,9 @@ void _OS::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_borderless_window", "borderless"), &_OS::set_borderless_window);
 	ClassDB::bind_method(D_METHOD("get_borderless_window"), &_OS::get_borderless_window);
 
+	ClassDB::bind_method(D_METHOD("get_window_per_pixel_transparency_enabled"), &_OS::get_window_per_pixel_transparency_enabled);
+	ClassDB::bind_method(D_METHOD("set_window_per_pixel_transparency_enabled", "enabled"), &_OS::set_window_per_pixel_transparency_enabled);
+
 	ClassDB::bind_method(D_METHOD("set_ime_position", "position"), &_OS::set_ime_position);
 
 	ClassDB::bind_method(D_METHOD("set_screen_orientation", "orientation"), &_OS::set_screen_orientation);
@@ -1180,6 +1191,7 @@ void _OS::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"), "set_screen_orientation", "get_screen_orientation");
 	ADD_GROUP("Window", "window_");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_borderless"), "set_borderless_window", "get_borderless_window");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_per_pixel_transparency_enabled"), "set_window_per_pixel_transparency_enabled", "get_window_per_pixel_transparency_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_fullscreen"), "set_window_fullscreen", "is_window_fullscreen");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_maximized"), "set_window_maximized", "is_window_maximized");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "window_minimized"), "set_window_minimized", "is_window_minimized");

+ 3 - 0
core/bind/core_bind.h

@@ -179,6 +179,9 @@ public:
 	virtual void set_borderless_window(bool p_borderless);
 	virtual bool get_borderless_window() const;
 
+	virtual bool get_window_per_pixel_transparency_enabled() const;
+	virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
 	virtual void set_ime_position(const Point2 &p_pos);
 
 	Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track);

+ 1 - 0
core/os/os.cpp

@@ -672,6 +672,7 @@ OS::OS() {
 	_render_thread_mode = RENDER_THREAD_SAFE;
 
 	_allow_hidpi = false;
+	_allow_layered = false;
 	_stack_bottom = (void *)(&stack_bottom);
 
 	_logger = NULL;

+ 13 - 0
core/os/os.h

@@ -65,6 +65,7 @@ class OS {
 	int _exit_code;
 	int _orientation;
 	bool _allow_hidpi;
+	bool _allow_layered;
 	bool _use_vsync;
 
 	char *last_error;
@@ -102,6 +103,8 @@ public:
 		bool maximized;
 		bool always_on_top;
 		bool use_vsync;
+		bool layered_splash;
+		bool layered;
 		float get_aspect() const { return (float)width / (float)height; }
 		VideoMode(int p_width = 1024, int p_height = 600, bool p_fullscreen = false, bool p_resizable = true, bool p_borderless_window = false, bool p_maximized = false, bool p_always_on_top = false, bool p_use_vsync = false) {
 			width = p_width;
@@ -112,6 +115,8 @@ public:
 			maximized = p_maximized;
 			always_on_top = p_always_on_top;
 			use_vsync = p_use_vsync;
+			layered = false;
+			layered_splash = false;
 		}
 	};
 
@@ -208,6 +213,13 @@ public:
 	virtual void set_borderless_window(bool p_borderless) {}
 	virtual bool get_borderless_window() { return 0; }
 
+	virtual bool get_window_per_pixel_transparency_enabled() const { return false; }
+	virtual void set_window_per_pixel_transparency_enabled(bool p_enabled) {}
+
+	virtual uint8_t *get_layered_buffer_data() { return NULL; }
+	virtual Size2 get_layered_buffer_size() { return Size2(0, 0); }
+	virtual void swap_layered_buffer() {}
+
 	virtual void set_ime_position(const Point2 &p_pos) {}
 	virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp) {}
 
@@ -468,6 +480,7 @@ public:
 	virtual void force_process_input(){};
 	bool has_feature(const String &p_feature);
 
+	bool is_layered_allowed() const { return _allow_layered; }
 	bool is_hidpi_allowed() const { return _allow_hidpi; }
 	OS();
 	virtual ~OS();

+ 48 - 1
drivers/gles2/rasterizer_gles2.cpp

@@ -316,7 +316,11 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c
 	glViewport(0, 0, window_w, window_h);
 	glDisable(GL_BLEND);
 	glDepthMask(GL_FALSE);
-	glClearColor(p_color.r, p_color.g, p_color.b, p_color.a);
+	if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+		glClearColor(0.0, 0.0, 0.0, 0.0);
+	} else {
+		glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
+	}
 	glClear(GL_COLOR_BUFFER_BIT);
 
 	canvas->canvas_begin();
@@ -340,6 +344,27 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c
 
 	storage->free(texture);
 
+	if (OS::get_singleton()->is_layered_allowed()) {
+		if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#ifdef WINDOWS_ENABLED
+			Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+			uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+			if (data) {
+				glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+				OS::get_singleton()->swap_layered_buffer();
+
+				return;
+			}
+#endif
+		} else {
+			//clear alpha
+			glColorMask(false, false, false, true);
+			glClearColor(0, 0, 0, 1);
+			glClear(GL_COLOR_BUFFER_BIT);
+			glColorMask(true, true, true, true);
+		}
+	}
+
 	OS::get_singleton()->swap_buffers();
 }
 
@@ -373,6 +398,28 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re
 }
 
 void RasterizerGLES2::end_frame(bool p_swap_buffers) {
+
+	if (OS::get_singleton()->is_layered_allowed()) {
+		if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#ifdef WINDOWS_ENABLED
+			Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+			uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+			if (data) {
+				glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+				OS::get_singleton()->swap_layered_buffer();
+
+				return;
+			}
+#endif
+		} else {
+			//clear alpha
+			glColorMask(false, false, false, true);
+			glClearColor(0, 0, 0, 1);
+			glClear(GL_COLOR_BUFFER_BIT);
+			glColorMask(true, true, true, true);
+		}
+	}
+
 	if (p_swap_buffers)
 		OS::get_singleton()->swap_buffers();
 	else

+ 47 - 1
drivers/gles3/rasterizer_gles3.cpp

@@ -292,7 +292,11 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
 	glViewport(0, 0, window_w, window_h);
 	glDisable(GL_BLEND);
 	glDepthMask(GL_FALSE);
-	glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
+	if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+		glClearColor(0.0, 0.0, 0.0, 0.0);
+	} else {
+		glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
+	}
 	glClear(GL_COLOR_BUFFER_BIT);
 	canvas->canvas_begin();
 
@@ -331,6 +335,27 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c
 
 	storage->free(texture); // free since it's only one frame that stays there
 
+	if (OS::get_singleton()->is_layered_allowed()) {
+		if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#ifdef WINDOWS_ENABLED
+			Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+			uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+			if (data) {
+				glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+				OS::get_singleton()->swap_layered_buffer();
+
+				return;
+			}
+#endif
+		} else {
+			//clear alpha
+			glColorMask(false, false, false, true);
+			glClearColor(0, 0, 0, 1);
+			glClear(GL_COLOR_BUFFER_BIT);
+			glColorMask(true, true, true, true);
+		}
+	}
+
 	OS::get_singleton()->swap_buffers();
 }
 
@@ -367,6 +392,27 @@ void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Re
 
 void RasterizerGLES3::end_frame(bool p_swap_buffers) {
 
+	if (OS::get_singleton()->is_layered_allowed()) {
+		if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+#ifdef WINDOWS_ENABLED
+			Size2 wndsize = OS::get_singleton()->get_layered_buffer_size();
+			uint8_t *data = OS::get_singleton()->get_layered_buffer_data();
+			if (data) {
+				glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data);
+				OS::get_singleton()->swap_layered_buffer();
+
+				return;
+			}
+#endif
+		} else {
+			//clear alpha
+			glColorMask(false, false, false, true);
+			glClearColor(0, 0, 0, 1);
+			glClear(GL_COLOR_BUFFER_BIT);
+			glColorMask(true, true, true, true);
+		}
+	}
+
 	if (p_swap_buffers)
 		OS::get_singleton()->swap_buffers();
 	else

+ 5 - 0
main/main.cpp

@@ -874,7 +874,11 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 		OS::get_singleton()->_allow_hidpi = GLOBAL_DEF("display/window/dpi/allow_hidpi", false);
 	}
 
+	OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/allow_per_pixel_transparency", false);
+
 	video_mode.use_vsync = GLOBAL_DEF("display/window/vsync/use_vsync", true);
+	video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency", false);
+	video_mode.layered_splash = GLOBAL_DEF("display/window/per_pixel_transparency_splash", false);
 
 	GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation", 2);
 	GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_allocation.mobile", 3);
@@ -882,6 +886,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	if (editor || project_manager) {
 		// The editor and project manager always detect and use hiDPI if needed
 		OS::get_singleton()->_allow_hidpi = true;
+		OS::get_singleton()->_allow_layered = false;
 	}
 
 	Engine::get_singleton()->_pixel_snap = GLOBAL_DEF("rendering/quality/2d/use_pixel_snap", false);

+ 6 - 0
platform/osx/os_osx.h

@@ -99,6 +99,8 @@ public:
 	id pixelFormat;
 	id context;
 
+	bool layered_window;
+
 	CursorShape cursor_shape;
 	NSCursor *cursors[CURSOR_MAX];
 	MouseMode mouse_mode;
@@ -226,6 +228,10 @@ public:
 
 	virtual void set_borderless_window(bool p_borderless);
 	virtual bool get_borderless_window();
+
+	virtual bool get_window_per_pixel_transparency_enabled() const;
+	virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
 	virtual void set_ime_position(const Point2 &p_pos);
 	virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp);
 

+ 42 - 0
platform/osx/os_osx.mm

@@ -1330,6 +1330,9 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
 
 	restore_rect = Rect2(get_window_position(), get_window_size());
 
+	if (p_desired.layered_splash) {
+		set_window_per_pixel_transparency_enabled(true);
+	}
 	return OK;
 }
 
@@ -2037,6 +2040,8 @@ void OS_OSX::set_window_size(const Size2 p_size) {
 void OS_OSX::set_window_fullscreen(bool p_enabled) {
 
 	if (zoomed != p_enabled) {
+		if (layered_window)
+			set_window_per_pixel_transparency_enabled(false);
 		[window_object toggleFullScreen:nil];
 	}
 	zoomed = p_enabled;
@@ -2118,6 +2123,39 @@ void OS_OSX::request_attention() {
 	[NSApp requestUserAttention:NSCriticalRequest];
 }
 
+bool OS_OSX::get_window_per_pixel_transparency_enabled() const {
+
+	if (!is_layered_allowed()) return false;
+	return layered_window;
+}
+
+void OS_OSX::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+
+	if (!is_layered_allowed()) return;
+	if (layered_window != p_enabled) {
+		if (p_enabled) {
+			set_borderless_window(true);
+			GLint opacity = 0;
+			[window_object setBackgroundColor:[NSColor clearColor]];
+			[window_object setOpaque:NO];
+			[window_object setHasShadow:NO];
+			[context setValues:&opacity forParameter:NSOpenGLCPSurfaceOpacity];
+			layered_window = true;
+		} else {
+			GLint opacity = 1;
+			[window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
+			[window_object setOpaque:YES];
+			[window_object setHasShadow:YES];
+			[context setValues:&opacity forParameter:NSOpenGLCPSurfaceOpacity];
+			layered_window = false;
+		}
+		[context update];
+		NSRect frame = [window_object frame];
+		[window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES];
+		[window_object setFrame:frame display:YES];
+	}
+}
+
 void OS_OSX::set_borderless_window(bool p_borderless) {
 
 	// OrderOut prevents a lose focus bug with the window
@@ -2126,6 +2164,9 @@ void OS_OSX::set_borderless_window(bool p_borderless) {
 	if (p_borderless) {
 		[window_object setStyleMask:NSWindowStyleMaskBorderless];
 	} else {
+		if (layered_window)
+			set_window_per_pixel_transparency_enabled(false);
+
 		[window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable];
 
 		// Force update of the window styles
@@ -2430,6 +2471,7 @@ OS_OSX::OS_OSX() {
 	im_position = Point2();
 	im_callback = NULL;
 	im_target = NULL;
+	layered_window = false;
 	autoreleasePool = [[NSAutoreleasePool alloc] init];
 
 	eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);

+ 121 - 0
platform/windows/os_windows.cpp

@@ -623,6 +623,28 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 				maximized = false;
 				minimized = false;
 			}
+			if (is_layered_allowed() && layered_window) {
+				DeleteObject(hBitmap);
+
+				RECT r;
+				GetWindowRect(hWnd, &r);
+				dib_size = Size2(r.right - r.left, r.bottom - r.top);
+
+				BITMAPINFO bmi;
+				ZeroMemory(&bmi, sizeof(BITMAPINFO));
+				bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+				bmi.bmiHeader.biWidth = dib_size.x;
+				bmi.bmiHeader.biHeight = dib_size.y;
+				bmi.bmiHeader.biPlanes = 1;
+				bmi.bmiHeader.biBitCount = 32;
+				bmi.bmiHeader.biCompression = BI_RGB;
+				bmi.bmiHeader.biSizeImage = dib_size.x, dib_size.y * 4;
+				hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
+				SelectObject(hDC_dib, hBitmap);
+
+				ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
+			}
+			//return 0;								// Jump Back
 		} break;
 
 		case WM_ENTERSIZEMOVE: {
@@ -1137,6 +1159,9 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
 		SetFocus(hWnd); // Sets Keyboard Focus To
 	}
 
+	if (p_desired.layered_splash) {
+		set_window_per_pixel_transparency_enabled(true);
+	}
 	return OK;
 }
 
@@ -1524,6 +1549,9 @@ void OS_Windows::set_window_fullscreen(bool p_enabled) {
 	if (video_mode.fullscreen == p_enabled)
 		return;
 
+	if (layered_window)
+		set_window_per_pixel_transparency_enabled(false);
+
 	if (p_enabled) {
 
 		if (pre_fs_valid) {
@@ -1628,10 +1656,97 @@ bool OS_Windows::is_window_always_on_top() const {
 	return video_mode.always_on_top;
 }
 
+bool OS_Windows::get_window_per_pixel_transparency_enabled() const {
+
+	if (!is_layered_allowed()) return false;
+	return layered_window;
+}
+
+void OS_Windows::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+
+	if (!is_layered_allowed()) return;
+	if (layered_window != p_enabled) {
+		if (p_enabled) {
+			set_borderless_window(true);
+			//enable per-pixel alpha
+			hDC_dib = CreateCompatibleDC(GetDC(hWnd));
+
+			SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+
+			RECT r;
+			GetWindowRect(hWnd, &r);
+			dib_size = Size2(r.right - r.left, r.bottom - r.top);
+
+			BITMAPINFO bmi;
+			ZeroMemory(&bmi, sizeof(BITMAPINFO));
+			bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+			bmi.bmiHeader.biWidth = dib_size.x;
+			bmi.bmiHeader.biHeight = dib_size.y;
+			bmi.bmiHeader.biPlanes = 1;
+			bmi.bmiHeader.biBitCount = 32;
+			bmi.bmiHeader.biCompression = BI_RGB;
+			bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
+			hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
+			SelectObject(hDC_dib, hBitmap);
+
+			ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
+
+			layered_window = true;
+		} else {
+			//disable per-pixel alpha
+			layered_window = false;
+
+			SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
+
+			//cleanup
+			DeleteObject(hBitmap);
+			DeleteDC(hDC_dib);
+		}
+	}
+}
+
+uint8_t *OS_Windows::get_layered_buffer_data() {
+
+	return (is_layered_allowed() && layered_window) ? dib_data : NULL;
+}
+
+Size2 OS_Windows::get_layered_buffer_size() {
+
+	return (is_layered_allowed() && layered_window) ? dib_size : Size2();
+}
+
+void OS_Windows::swap_layered_buffer() {
+
+	if (is_layered_allowed() && layered_window) {
+
+		//premultiply alpha
+		for (int y = 0; y < dib_size.y; y++) {
+			for (int x = 0; x < dib_size.x; x++) {
+				float alpha = (float)dib_data[y * (int)dib_size.x * 4 + x * 4 + 3] / (float)0xFF;
+				dib_data[y * (int)dib_size.x * 4 + x * 4 + 0] *= alpha;
+				dib_data[y * (int)dib_size.x * 4 + x * 4 + 1] *= alpha;
+				dib_data[y * (int)dib_size.x * 4 + x * 4 + 2] *= alpha;
+			}
+		}
+		//swap layered window buffer
+		POINT ptSrc = { 0, 0 };
+		SIZE sizeWnd = { (long)dib_size.x, (long)dib_size.y };
+		BLENDFUNCTION bf;
+		bf.BlendOp = AC_SRC_OVER;
+		bf.BlendFlags = 0;
+		bf.AlphaFormat = AC_SRC_ALPHA;
+		bf.SourceConstantAlpha = 0xFF;
+		UpdateLayeredWindow(hWnd, NULL, NULL, &sizeWnd, hDC_dib, &ptSrc, 0, &bf, ULW_ALPHA);
+	}
+}
+
 void OS_Windows::set_borderless_window(bool p_borderless) {
 	if (video_mode.borderless_window == p_borderless)
 		return;
 
+	if (!p_borderless && layered_window)
+		set_window_per_pixel_transparency_enabled(false);
+
 	video_mode.borderless_window = p_borderless;
 
 	_update_window_style();
@@ -2558,6 +2673,8 @@ Error OS_Windows::move_to_trash(const String &p_path) {
 OS_Windows::OS_Windows(HINSTANCE _hInstance) {
 
 	key_event_pos = 0;
+	layered_window = false;
+	hBitmap = NULL;
 	force_quit = false;
 	alt_mem = false;
 	gr_mem = false;
@@ -2591,6 +2708,10 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
 }
 
 OS_Windows::~OS_Windows() {
+	if (is_layered_allowed() && layered_window) {
+		DeleteObject(hBitmap);
+		DeleteDC(hDC_dib);
+	}
 #ifdef STDOUT_FILE
 	fclose(stdo);
 #endif

+ 13 - 0
platform/windows/os_windows.h

@@ -93,6 +93,12 @@ class OS_Windows : public OS {
 	HINSTANCE hInstance; // Holds The Instance Of The Application
 	HWND hWnd;
 
+	HBITMAP hBitmap; //DIB section for layered window
+	uint8_t *dib_data;
+	Size2 dib_size;
+	HDC hDC_dib;
+	bool layered_window;
+
 	uint32_t move_timer_id;
 
 	HCURSOR hCursor;
@@ -212,6 +218,13 @@ public:
 	virtual void set_borderless_window(bool p_borderless);
 	virtual bool get_borderless_window();
 
+	virtual bool get_window_per_pixel_transparency_enabled() const;
+	virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
+	virtual uint8_t *get_layered_buffer_data();
+	virtual Size2 get_layered_buffer_size();
+	virtual void swap_layered_buffer();
+
 	virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
 	virtual Error close_dynamic_library(void *p_library_handle);
 	virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false);

+ 63 - 20
platform/x11/context_gl_x11.cpp

@@ -116,32 +116,76 @@ Error ContextGL_X11::initialize() {
 		None
 	};
 
+	static int visual_attribs_layered[] = {
+		GLX_RENDER_TYPE, GLX_RGBA_BIT,
+		GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
+		GLX_DOUBLEBUFFER, true,
+		GLX_RED_SIZE, 8,
+		GLX_GREEN_SIZE, 8,
+		GLX_BLUE_SIZE, 8,
+		GLX_ALPHA_SIZE, 8,
+		GLX_DEPTH_SIZE, 24,
+		None
+	};
+
 	int fbcount;
-	GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);
-	ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
+	GLXFBConfig fbconfig;
+	XVisualInfo *vi = NULL;
+
+	if (OS::get_singleton()->is_layered_allowed()) {
+		GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs_layered, &fbcount);
+		ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
+
+		for (int i = 0; i < fbcount; i++) {
+			vi = (XVisualInfo *)glXGetVisualFromFBConfig(x11_display, fbc[i]);
+			if (!vi)
+				continue;
+
+			XRenderPictFormat *pict_format = XRenderFindVisualFormat(x11_display, vi->visual);
+			if (!pict_format) {
+				XFree(vi);
+				vi = NULL;
+				continue;
+			}
+
+			fbconfig = fbc[i];
+			if (pict_format->direct.alphaMask > 0) {
+				break;
+			}
+		}
+		ERR_FAIL_COND_V(!fbconfig, ERR_UNCONFIGURED);
+
+		XSetWindowAttributes swa;
+
+		swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
+		swa.border_pixel = 0;
+		swa.background_pixmap = None;
+		swa.background_pixel = 0;
+		swa.border_pixmap = None;
+		swa.event_mask = StructureNotifyMask;
+
+		x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask | CWBackPixel, &swa);
 
-	XVisualInfo *vi = glXGetVisualFromFBConfig(x11_display, fbc[0]);
+	} else {
+		GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount);
+		ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED);
 
-	XSetWindowAttributes swa;
+		vi = glXGetVisualFromFBConfig(x11_display, fbc[0]);
 
-	swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
-	swa.border_pixel = 0;
-	swa.event_mask = StructureNotifyMask;
+		fbconfig = fbc[0];
 
-	/*
-	char* windowid = getenv("GODOT_WINDOWID");
-	if (windowid) {
+		XSetWindowAttributes swa;
+
+		swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone);
+		swa.border_pixel = 0;
+		swa.event_mask = StructureNotifyMask;
+
+		x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);
+	}
 
-		//freopen("/home/punto/stdout", "w", stdout);
-		//reopen("/home/punto/stderr", "w", stderr);
-		x11_window = atol(windowid);
-	} else {
-	*/
-	x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);
 	ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED);
 	set_class_hint(x11_display, x11_window);
 	XMapWindow(x11_display, x11_window);
-	//};
 
 	int (*oldHandler)(Display *, XErrorEvent *) =
 			XSetErrorHandler(&ctxErrorHandler);
@@ -160,7 +204,7 @@ Error ContextGL_X11::initialize() {
 				None
 			};
 
-			p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs);
+			p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs);
 			ERR_EXPLAIN("Could not obtain an OpenGL 3.0 context!");
 			ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED);
 		} break;
@@ -175,7 +219,7 @@ Error ContextGL_X11::initialize() {
 				None
 			};
 
-			p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs);
+			p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, NULL, true, context_attribs);
 			ERR_EXPLAIN("Could not obtain an OpenGL 3.3 context!");
 			ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED);
 		} break;
@@ -195,7 +239,6 @@ Error ContextGL_X11::initialize() {
 	//glXMakeCurrent(x11_display, None, NULL);
 
 	XFree(vi);
-	XFree(fbc);
 
 	return OK;
 }

+ 1 - 0
platform/x11/context_gl_x11.h

@@ -41,6 +41,7 @@
 #include "drivers/gl_context/context_gl.h"
 #include "os/os.h"
 #include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
 
 struct ContextGL_X11_Private;
 

+ 6 - 0
platform/x11/detect.py

@@ -42,6 +42,11 @@ def can_build():
         print("xrandr not found.. x11 disabled.")
         return False
 
+    x11_error = os.system("pkg-config xrender --modversion > /dev/null ")
+    if (x11_error):
+        print("xrender not found.. x11 disabled.")
+        return False
+
     return True
 
 def get_opts():
@@ -141,6 +146,7 @@ def configure(env):
     env.ParseConfig('pkg-config xcursor --cflags --libs')
     env.ParseConfig('pkg-config xinerama --cflags --libs')
     env.ParseConfig('pkg-config xrandr --cflags --libs')
+    env.ParseConfig('pkg-config xrender --cflags --libs')
 
     if (env['touch']):
         x11_error = os.system("pkg-config xi --modversion > /dev/null ")

+ 31 - 0
platform/x11/os_x11.cpp

@@ -517,6 +517,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
 
 	power_manager = memnew(PowerX11);
 
+	if (p_desired.layered_splash) {
+		set_window_per_pixel_transparency_enabled(true);
+	}
+
 	XEvent xevent;
 	while (XPending(x11_display) > 0) {
 		XNextEvent(x11_display, &xevent);
@@ -707,6 +711,25 @@ Point2 OS_X11::get_mouse_position() const {
 	return last_mouse_pos;
 }
 
+bool OS_X11::get_window_per_pixel_transparency_enabled() const {
+
+	if (!is_layered_allowed()) return false;
+	return layered_window;
+}
+
+void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+
+	if (!is_layered_allowed()) return;
+	if (layered_window != p_enabled) {
+		if (p_enabled) {
+			set_borderless_window(true);
+			layered_window = true;
+		} else {
+			layered_window = false;
+		}
+	}
+}
+
 void OS_X11::set_window_title(const String &p_title) {
 	XStoreName(x11_display, x11_window, p_title.utf8().get_data());
 
@@ -1006,9 +1029,13 @@ void OS_X11::set_window_size(const Size2 p_size) {
 }
 
 void OS_X11::set_window_fullscreen(bool p_enabled) {
+
 	if (current_videomode.fullscreen == p_enabled)
 		return;
 
+	if (layered_window)
+		set_window_per_pixel_transparency_enabled(false);
+
 	if (p_enabled && current_videomode.always_on_top) {
 		// Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
 		set_window_maximized(true);
@@ -1254,6 +1281,9 @@ void OS_X11::set_borderless_window(bool p_borderless) {
 	if (current_videomode.borderless_window == p_borderless)
 		return;
 
+	if (!p_borderless && layered_window)
+		set_window_per_pixel_transparency_enabled(false);
+
 	current_videomode.borderless_window = p_borderless;
 
 	Hints hints;
@@ -2698,6 +2728,7 @@ OS_X11::OS_X11() {
 	AudioDriverManager::add_driver(&driver_alsa);
 #endif
 
+	layered_window = false;
 	minimized = false;
 	xim_style = 0L;
 	mouse_mode = MOUSE_MODE_VISIBLE;

+ 6 - 0
platform/x11/os_x11.h

@@ -171,6 +171,8 @@ class OS_X11 : public OS_Unix {
 
 	PowerX11 *power_manager;
 
+	bool layered_window;
+
 	CrashHandler crash_handler;
 
 	int audio_driver_index;
@@ -262,6 +264,10 @@ public:
 
 	virtual void set_borderless_window(bool p_borderless);
 	virtual bool get_borderless_window();
+
+	virtual bool get_window_per_pixel_transparency_enabled() const;
+	virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
+
 	virtual void set_ime_position(const Point2 &p_pos);
 
 	virtual String get_unique_id() const;