1 /*
2                                     __
3                                    / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2018-2019 Aurora Free Open Source Software.
10 
11 This file is part of the Aurora Free Open Source Software. This
12 organization promote free and open source software that you can
13 redistribute and/or modify under the terms of the GNU Lesser General
14 Public License Version 3 as published by the Free Software Foundation or
15 (at your option) any later version approved by the Aurora Free Open Source
16 Software Organization. The license is available in the package root path
17 as 'LICENSE' file. Please review the following information to ensure the
18 GNU Lesser General Public License version 3 requirements will be met:
19 https://www.gnu.org/licenses/lgpl.html .
20 
21 Alternatively, this file may be used under the terms of the GNU General
22 Public License version 3 or later as published by the Free Software
23 Foundation. Please review the following information to ensure the GNU
24 General Public License requirements will be met:
25 http://www.gnu.org/licenses/gpl-3.0.html.
26 
27 NOTE: All products, services or anything associated to trademarks and
28 service marks used or referenced on this file are the property of their
29 respective companies/owners or its subsidiaries. Other names and brands
30 may be claimed as the property of others.
31 
32 For more info about intellectual property visit: aurorafoss.org or
33 directly send an email to: contact (at) aurorafoss.org .
34 */
35 
36 module aurorafw.gui.api.x11.backend;
37 
38 import X = aurorafw.gui.platform.x11;
39 import std.conv : emplace;
40 import core.stdc.stdlib : malloc, free;
41 import aurorafw.math.vector : Vector2f;
42 import std.process : environment;
43 import aurorafw.core.logger : error;
44 import core.stdc.stdlib : exit;
45 import core.stdc.stdlib : atof;
46 import core.stdc.string : strcmp;
47 
48 import aurorafw.gui.api.backend;
49 
50 pure class X11Backend : Backend {
51 	this()
52 	{
53 		super._type = BackendType.X11;
54 
55 		X.XInitThreads();
56 		X.XrmInitialize();
57 		_display = X.XOpenDisplay(null);
58 
59 		if(_display is null)
60 		{
61 			auto env_display = environment.get("DISPLAY");
62 			if(env_display)
63 				error("X11: Cannot open display ", env_display);
64 			else
65 				error("X11: DISPLAY variable is missing");
66 
67 			exit(1); //TODO: add error to Error Database
68 		}
69 
70 		_screen = X.XDefaultScreen(_display);
71 		_root = X.XRootWindow(_display, _screen);
72 		_context = X.XUniqueContext();
73 
74 		// get system content scale
75 		float xdpi = X.XDisplayWidth(_display, _screen) * 25.4f / X.XDisplayWidthMM(_display, _screen);
76 		float ydpi = X.XDisplayHeight(_display, _screen) * 25.4f / X.XDisplayHeightMM(_display, _screen);
77 
78 		char* rms = X.XResourceManagerString(_display);
79 		if (rms)
80 		{
81 			X.XrmDatabase db = X.XrmGetStringDatabase(rms);
82 			if (db)
83 			{
84 				X.XrmValue value;
85 				char* type = null;
86 
87 				if (X.XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value))
88 				{
89 					if (type && strcmp(type, "String") == 0)
90 						xdpi = ydpi = atof(cast(char*)value.addr);
91 				}
92 
93 				X.XrmDestroyDatabase(db);
94 			}
95 		}
96 
97 		_contentScale.x = xdpi / 96.0f;
98 		_contentScale.y = ydpi / 96.0f;
99 
100 		//init x11 extensions
101 		// TODO
102 		initX11Extensions();
103 
104 		X.XSetWindowAttributes wa;
105 		wa.event_mask = X.PropertyChangeMask;
106 
107 		_helperWindowHandle = X.XCreateWindow(_display, _root,
108 			0, 0, 1, 1, 0, 0,
109 			X.InputOnly,
110 			X.XDefaultVisual(_display, _screen),
111 			X.CWEventMask, &wa);
112 
113 		immutable byte[16 * 16 * 4] pixels;
114 		_hiddenCursorHandle = createXCursor(16, 16, pixels, 0, 0);
115 
116 		if (X.XSupportsLocale())
117 		{
118 			X.XSetLocaleModifiers("");
119 
120 			this._im = X.XOpenIM(_display, null, null, null);
121 			if (this._im)
122 			{
123 				bool found = false;
124 				X.XIMStyles* styles = null;
125 
126 				if(X.XGetIMValues(_im, X.XNQueryInputStyle, &styles, null) == null)
127 				{
128 					for (uint i = 0; i < styles.count_styles; i++)
129 					{
130 						if (styles.supported_styles[i] == (X.XIMPreeditNothing | X.XIMStatusNothing))
131 						{
132 							found = true;
133 							break;
134 						}
135 					}
136 
137 					X.XFree(styles);
138 				}
139 
140 				if (!found)
141 				{
142 					X.XCloseIM(_im);
143 					this._im = null;
144 				}
145 			}
146 		}
147 	}
148 
149 	~this()
150 	{
151 		if(_display)
152 		{
153 			X.XCloseDisplay(_display);
154 			_display = null;
155 		}
156 	}
157 
158 	X.Cursor createXCursor(immutable uint width, immutable uint height, immutable byte[] pixels, int xhot, int yhot)
159 	{
160 		X.Cursor cursor;
161 
162 		if (!_xcursor_handle.dylib.isLoaded)
163 			return X.None;
164 
165 		X.XcursorImage* native = X.XcursorImageCreate(width, height);
166 		if (native == null)
167 			return X.None;
168 
169 		native.xhot = xhot;
170 		native.yhot = yhot;
171 
172 		byte* source = cast(byte*) pixels;
173 		X.XcursorPixel* target = native.pixels;
174 
175 		for (int i = 0; i < width * height; i++, target++, source += 4)
176 		{
177 			uint alpha = source[3];
178 
179 			*target = (alpha << 24) |
180 					(cast(byte) ((source[0] * alpha) / 255) << 16) |
181 					(cast(byte) ((source[1] * alpha) / 255) <<  8) |
182 					(cast(byte) ((source[2] * alpha) / 255) <<  0);
183 		}
184 
185 		cursor = X.XcursorImageLoadCursor(_display, native);
186 		X.XcursorImageDestroy(native);
187 
188 		return cursor;
189 	}
190 
191 	X.Display* display() @property { return _display; }
192 	int screen() @property { return _screen; }
193 	X.Window root() @property { return _root; }
194 
195 	void initX11Extensions()
196 	{
197 		import aurorafw.gui.platform.x11.xcursor;
198 		try _xf86vmode.handle = new X.XF86VModeDylibLoader(); catch(Exception e) debug trace(afwDebugFlag, e.msg);
199 
200 		if(_xf86vmode.handle.dylib.isLoaded)
201 		{
202 			_xf86vmode.available = X.XF86VidModeQueryExtension(_display,_xf86vmode.eventBase, _xf86vmode.errorBase);
203 		}
204 
205 		try _xcursor.handle = new X.XCursorDylibLoader(); catch(Exception e) debug trace(afwDebugFlag, e.msg);
206 	}
207 
208 private:
209 	X.Display* _display;
210 	X.Window _root;
211 	int _screen;
212 	Vector2f _contentScale;
213 	X.XContext _context;
214 	X.XIM _im;
215 	X.Window _helperWindowHandle;
216 	X.Cursor _hiddenCursorHandle;
217 	XCursorExtension _xcursor;
218 	XF86VModeExtension _xf86vmode;
219 }
220 
221 struct XCursorExtension {
222 	X.XCursorDylibLoader handle;
223 }
224 
225 struct XF86VModeExtension {
226 	X.XF86VModeDylibLoader handle;
227 	bool available;
228 	int eventBase;
229 	int errorBase;
230 }