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.x;
39 import riverd.xcursor;
40 
41 import std.conv : emplace;
42 import core.stdc.stdlib : malloc, free;
43 import aurorafw.math.vector : Vector2f;
44 import std.process : environment;
45 import aurorafw.core.logger : error;
46 import core.stdc.stdlib : exit;
47 import core.stdc.stdlib : atof;
48 import core.stdc..string : strcmp;
49 
50 import aurorafw.gui.api.backend;
51 
52 pure class X11Backend : Backend {
53 	this()
54 	{
55 		super._type = BackendType.X11;
56 
57 		X.XInitThreads();
58 		X.XrmInitialize();
59 		_display = X.XOpenDisplay(null);
60 
61 		if(_display is null)
62 		{
63 			auto env_display = environment.get("DISPLAY");
64 			if(env_display)
65 				error("X11: Cannot open display ", env_display);
66 			else
67 				error("X11: DISPLAY variable is missing");
68 
69 			exit(1);
70 		}
71 
72 		_screen = X.XDefaultScreen(_display);
73 		_root = X.XRootWindow(_display, _screen);
74 		_context = X.XUniqueContext();
75 
76 		// get system content scale
77 		float xdpi = X.XDisplayWidth(_display, _screen) * 25.4f / X.XDisplayWidthMM(_display, _screen);
78 		float ydpi = X.XDisplayHeight(_display, _screen) * 25.4f / X.XDisplayHeightMM(_display, _screen);
79 
80 		char* rms = X.XResourceManagerString(_display);
81 		if (rms)
82 		{
83 			X.XrmDatabase db = X.XrmGetStringDatabase(rms);
84 			if (db)
85 			{
86 				X.XrmValue value;
87 				char* type = null;
88 
89 				if (X.XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value))
90 				{
91 					if (type && strcmp(type, "String") == 0)
92 						xdpi = ydpi = atof(cast(char*)value.addr);
93 				}
94 
95 				X.XrmDestroyDatabase(db);
96 			}
97 		}
98 
99 		_contentScale.x = xdpi / 96.0f;
100 		_contentScale.y = ydpi / 96.0f;
101 
102 		//init x11 extensions
103 		// TODO
104 		initX11Extensions();
105 
106 		X.XSetWindowAttributes wa;
107 		wa.event_mask = X.PropertyChangeMask;
108 
109 		_helperWindowHandle = X.XCreateWindow(_display, _root,
110 			0, 0, 1, 1, 0, 0,
111 			X.InputOnly,
112 			X.XDefaultVisual(_display, _screen),
113 			X.CWEventMask, &wa);
114 
115 		immutable byte[16 * 16 * 4] pixels;
116 		_hiddenCursorHandle = createXCursor(16, 16, pixels, 0, 0);
117 
118 		if (X.XSupportsLocale())
119 		{
120 			X.XSetLocaleModifiers("");
121 
122 			this._im = X.XOpenIM(_display, null, null, null);
123 			if (this._im)
124 			{
125 				bool found = false;
126 				X.XIMStyles* styles = null;
127 
128 				if(X.XGetIMValues(_im, X.XNQueryInputStyle, &styles, null) == null)
129 				{
130 					for (uint i = 0; i < styles.count_styles; i++)
131 					{
132 						if (styles.supported_styles[i] == (X.XIMPreeditNothing | X.XIMStatusNothing))
133 						{
134 							found = true;
135 							break;
136 						}
137 					}
138 
139 					X.XFree(styles);
140 				}
141 
142 				if (!found)
143 				{
144 					X.XCloseIM(_im);
145 					this._im = null;
146 				}
147 			}
148 		}
149 	}
150 
151 	~this()
152 	{
153 		if(_display)
154 		{
155 			X.XCloseDisplay(_display);
156 			_display = null;
157 		}
158 	}
159 
160 	X.Cursor createXCursor(immutable uint width, immutable uint height, immutable byte[] pixels, int xhot, int yhot)
161 	{
162 		X.Cursor cursor;
163 
164 		if (!X.dylib_is_loaded(_xcursor.handle))
165 			return X.None;
166 
167 		X.XcursorImage* native = X.XcursorImageCreate(width, height);
168 		if (native == null)
169 			return X.None;
170 
171 		native.xhot = xhot;
172 		native.yhot = yhot;
173 
174 		byte* source = cast(byte*) pixels;
175 		X.XcursorPixel* target = native.pixels;
176 
177 		for (int i = 0; i < width * height; i++, target++, source += 4)
178 		{
179 			uint alpha = source[3];
180 
181 			*target = (alpha << 24) |
182 					(cast(byte) ((source[0] * alpha) / 255) << 16) |
183 					(cast(byte) ((source[1] * alpha) / 255) <<  8) |
184 					(cast(byte) ((source[2] * alpha) / 255) <<  0);
185 		}
186 
187 		cursor = X.XcursorImageLoadCursor(_display, native);
188 		X.XcursorImageDestroy(native);
189 
190 		return cursor;
191 	}
192 
193 	X.Display* display() @property { return _display; }
194 	int screen() @property { return _screen; }
195 	X.Window root() @property { return _root; }
196 
197 	private void initX11Extensions()
198 	{
199 		//TODO: Remove/improve logger
200 		import std.experimental.logger;
201 		try _xf86vmode.handle = X.dylib_load_xxf86vm(); catch(Exception e) debug trace(e.msg);
202 
203 		if(X.dylib_is_loaded(_xf86vmode.handle))
204 		{
205 			_xf86vmode.available = cast(bool)X.XF86VidModeQueryExtension(_display, &_xf86vmode.eventBase, &_xf86vmode.errorBase);
206 		}
207 
208 		try _xcursor.handle = X.dylib_load_xcursor(); catch(Exception e) debug trace(e.msg);
209 	}
210 
211 private:
212 	X.Display* _display;
213 	X.Window _root;
214 	int _screen;
215 	Vector2f _contentScale;
216 	X.XContext _context;
217 	X.XIM _im;
218 	X.Window _helperWindowHandle;
219 	X.Cursor _hiddenCursorHandle;
220 	XCursorExtension _xcursor;
221 	XF86VModeExtension _xf86vmode;
222 }
223 
224 struct XCursorExtension {
225 	void* handle;
226 }
227 
228 struct XF86VModeExtension {
229 	void* handle;
230 	bool available;
231 	int eventBase;
232 	int errorBase;
233 }