1 /******************************************************************************
2     Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
3     Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 ******************************************************************************/
18 
19 #include "obs-internal.h"
20 #include "obs-nix.h"
21 #include "obs-nix-platform.h"
22 #include "obs-nix-x11.h"
23 
24 #ifdef ENABLE_WAYLAND
25 #include "obs-nix-wayland.h"
26 #endif
27 
28 #if defined(__FreeBSD__)
29 #define _GNU_SOURCE
30 #endif
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #if defined(__FreeBSD__) || defined(__OpenBSD__)
35 #include <sys/sysctl.h>
36 #endif
37 #if !defined(__OpenBSD__)
38 #include <sys/sysinfo.h>
39 #endif
40 #include <sys/utsname.h>
41 #include <inttypes.h>
42 
get_module_extension(void)43 const char *get_module_extension(void)
44 {
45 	return ".so";
46 }
47 
48 #ifdef __LP64__
49 #define BIT_STRING "64bit"
50 #else
51 #define BIT_STRING "32bit"
52 #endif
53 
54 #define FLATPAK_PLUGIN_PATH "/app/plugins"
55 
56 static const char *module_bin[] = {
57 	"../../obs-plugins/" BIT_STRING,
58 	OBS_INSTALL_PREFIX "/" OBS_PLUGIN_DESTINATION,
59 	FLATPAK_PLUGIN_PATH "/" OBS_PLUGIN_DESTINATION,
60 };
61 
62 static const char *module_data[] = {
63 	OBS_DATA_PATH "/obs-plugins/%module%",
64 	OBS_INSTALL_DATA_PATH "/obs-plugins/%module%",
65 	FLATPAK_PLUGIN_PATH "/share/obs/obs-plugins/%module%",
66 };
67 
68 static const int module_patterns_size =
69 	sizeof(module_bin) / sizeof(module_bin[0]);
70 
71 static const struct obs_nix_hotkeys_vtable *hotkeys_vtable = NULL;
72 
add_default_module_paths(void)73 void add_default_module_paths(void)
74 {
75 	for (int i = 0; i < module_patterns_size; i++)
76 		obs_add_module_path(module_bin[i], module_data[i]);
77 }
78 
79 /*
80  *   /usr/local/share/libobs
81  *   /usr/share/libobs
82  */
find_libobs_data_file(const char * file)83 char *find_libobs_data_file(const char *file)
84 {
85 	struct dstr output;
86 	dstr_init(&output);
87 
88 	if (check_path(file, OBS_DATA_PATH "/libobs/", &output))
89 		return output.array;
90 
91 	if (OBS_INSTALL_PREFIX[0] != 0) {
92 		if (check_path(file, OBS_INSTALL_DATA_PATH "/libobs/", &output))
93 			return output.array;
94 	}
95 
96 	dstr_free(&output);
97 	return NULL;
98 }
99 
log_processor_cores(void)100 static void log_processor_cores(void)
101 {
102 	blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
103 	     os_get_physical_cores(), os_get_logical_cores());
104 }
105 
106 #if defined(__linux__)
log_processor_info(void)107 static void log_processor_info(void)
108 {
109 	int physical_id = -1;
110 	int last_physical_id = -1;
111 	char *line = NULL;
112 	size_t linecap = 0;
113 
114 	FILE *fp;
115 	struct dstr proc_name;
116 	struct dstr proc_speed;
117 
118 	fp = fopen("/proc/cpuinfo", "r");
119 	if (!fp)
120 		return;
121 
122 	dstr_init(&proc_name);
123 	dstr_init(&proc_speed);
124 
125 	while (getline(&line, &linecap, fp) != -1) {
126 		if (!strncmp(line, "model name", 10)) {
127 			char *start = strchr(line, ':');
128 			if (!start || *(++start) == '\0')
129 				continue;
130 
131 			dstr_copy(&proc_name, start);
132 			dstr_resize(&proc_name, proc_name.len - 1);
133 			dstr_depad(&proc_name);
134 		}
135 
136 		if (!strncmp(line, "physical id", 11)) {
137 			char *start = strchr(line, ':');
138 			if (!start || *(++start) == '\0')
139 				continue;
140 
141 			physical_id = atoi(start);
142 		}
143 
144 		if (!strncmp(line, "cpu MHz", 7)) {
145 			char *start = strchr(line, ':');
146 			if (!start || *(++start) == '\0')
147 				continue;
148 
149 			dstr_copy(&proc_speed, start);
150 			dstr_resize(&proc_speed, proc_speed.len - 1);
151 			dstr_depad(&proc_speed);
152 		}
153 
154 		if (*line == '\n' && physical_id != last_physical_id) {
155 			last_physical_id = physical_id;
156 			blog(LOG_INFO, "CPU Name: %s", proc_name.array);
157 			blog(LOG_INFO, "CPU Speed: %sMHz", proc_speed.array);
158 		}
159 	}
160 
161 	fclose(fp);
162 	dstr_free(&proc_name);
163 	dstr_free(&proc_speed);
164 	free(line);
165 }
166 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
log_processor_speed(void)167 static void log_processor_speed(void)
168 {
169 #ifndef __OpenBSD__
170 	char *line = NULL;
171 	size_t linecap = 0;
172 	FILE *fp;
173 	struct dstr proc_speed;
174 
175 	fp = fopen("/var/run/dmesg.boot", "r");
176 	if (!fp) {
177 		blog(LOG_INFO, "CPU: Missing /var/run/dmesg.boot !");
178 		return;
179 	}
180 
181 	dstr_init(&proc_speed);
182 
183 	while (getline(&line, &linecap, fp) != -1) {
184 		if (!strncmp(line, "CPU: ", 5)) {
185 			char *start = strrchr(line, '(');
186 			if (!start || *(++start) == '\0')
187 				continue;
188 
189 			size_t len = strcspn(start, "-");
190 			dstr_ncopy(&proc_speed, start, len);
191 		}
192 	}
193 
194 	blog(LOG_INFO, "CPU Speed: %sMHz", proc_speed.array);
195 
196 	fclose(fp);
197 	dstr_free(&proc_speed);
198 	free(line);
199 #endif
200 }
201 
log_processor_name(void)202 static void log_processor_name(void)
203 {
204 	int mib[2];
205 	size_t len;
206 	char *proc;
207 
208 	mib[0] = CTL_HW;
209 	mib[1] = HW_MODEL;
210 
211 	sysctl(mib, 2, NULL, &len, NULL, 0);
212 	proc = bmalloc(len);
213 	if (!proc)
214 		return;
215 
216 	sysctl(mib, 2, proc, &len, NULL, 0);
217 	blog(LOG_INFO, "CPU Name: %s", proc);
218 
219 	bfree(proc);
220 }
221 
log_processor_info(void)222 static void log_processor_info(void)
223 {
224 	log_processor_name();
225 	log_processor_speed();
226 }
227 #endif
228 
log_memory_info(void)229 static void log_memory_info(void)
230 {
231 #if defined(__OpenBSD__)
232 	int mib[2];
233 	size_t len;
234 	int64_t mem;
235 
236 	mib[0] = CTL_HW;
237 	mib[1] = HW_PHYSMEM64;
238 	len = sizeof(mem);
239 
240 	if (sysctl(mib, 2, &mem, &len, NULL, 0) >= 0)
241 		blog(LOG_INFO, "Physical Memory: %" PRIi64 "MB Total",
242 		     mem / 1024 / 1024);
243 #else
244 	struct sysinfo info;
245 	if (sysinfo(&info) < 0)
246 		return;
247 
248 	blog(LOG_INFO,
249 	     "Physical Memory: %" PRIu64 "MB Total, %" PRIu64 "MB Free",
250 	     (uint64_t)info.totalram * info.mem_unit / 1024 / 1024,
251 	     ((uint64_t)info.freeram + (uint64_t)info.bufferram) *
252 		     info.mem_unit / 1024 / 1024);
253 #endif
254 }
255 
log_kernel_version(void)256 static void log_kernel_version(void)
257 {
258 	struct utsname info;
259 	if (uname(&info) < 0)
260 		return;
261 
262 	blog(LOG_INFO, "Kernel Version: %s %s", info.sysname, info.release);
263 }
264 
265 #if defined(__linux__)
log_distribution_info(void)266 static void log_distribution_info(void)
267 {
268 	FILE *fp;
269 	char *line = NULL;
270 	size_t linecap = 0;
271 	struct dstr distro;
272 	struct dstr version;
273 
274 	fp = fopen("/etc/os-release", "r");
275 	if (!fp) {
276 		blog(LOG_INFO, "Distribution: Missing /etc/os-release !");
277 		return;
278 	}
279 
280 	dstr_init_copy(&distro, "Unknown");
281 	dstr_init_copy(&version, "Unknown");
282 
283 	while (getline(&line, &linecap, fp) != -1) {
284 		if (!strncmp(line, "NAME", 4)) {
285 			char *start = strchr(line, '=');
286 			if (!start || *(++start) == '\0')
287 				continue;
288 			dstr_copy(&distro, start);
289 			dstr_resize(&distro, distro.len - 1);
290 		}
291 
292 		if (!strncmp(line, "VERSION_ID", 10)) {
293 			char *start = strchr(line, '=');
294 			if (!start || *(++start) == '\0')
295 				continue;
296 			dstr_copy(&version, start);
297 			dstr_resize(&version, version.len - 1);
298 		}
299 	}
300 
301 	blog(LOG_INFO, "Distribution: %s %s", distro.array, version.array);
302 
303 	fclose(fp);
304 	dstr_free(&version);
305 	dstr_free(&distro);
306 	free(line);
307 }
308 
log_desktop_session_info(void)309 static void log_desktop_session_info(void)
310 {
311 	char *session_ptr = getenv("XDG_SESSION_TYPE");
312 	if (session_ptr) {
313 		blog(LOG_INFO, "Session Type: %s", session_ptr);
314 	}
315 }
316 #endif
317 
log_system_info(void)318 void log_system_info(void)
319 {
320 #if defined(__linux__) || defined(__FreeBSD__)
321 	log_processor_info();
322 #endif
323 	log_processor_cores();
324 	log_memory_info();
325 	log_kernel_version();
326 #if defined(__linux__)
327 	log_distribution_info();
328 	log_desktop_session_info();
329 #endif
330 	switch (obs_get_nix_platform()) {
331 	case OBS_NIX_PLATFORM_X11_GLX:
332 	case OBS_NIX_PLATFORM_X11_EGL:
333 		obs_nix_x11_log_info();
334 		break;
335 #ifdef ENABLE_WAYLAND
336 	case OBS_NIX_PLATFORM_WAYLAND:
337 		break;
338 #endif
339 	}
340 }
341 
obs_hotkeys_platform_init(struct obs_core_hotkeys * hotkeys)342 bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
343 {
344 	switch (obs_get_nix_platform()) {
345 	case OBS_NIX_PLATFORM_X11_GLX:
346 	case OBS_NIX_PLATFORM_X11_EGL:
347 		hotkeys_vtable = obs_nix_x11_get_hotkeys_vtable();
348 		break;
349 #ifdef ENABLE_WAYLAND
350 	case OBS_NIX_PLATFORM_WAYLAND:
351 		hotkeys_vtable = obs_nix_wayland_get_hotkeys_vtable();
352 		break;
353 #endif
354 	}
355 
356 	return hotkeys_vtable->init(hotkeys);
357 }
358 
obs_hotkeys_platform_free(struct obs_core_hotkeys * hotkeys)359 void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
360 {
361 	hotkeys_vtable->free(hotkeys);
362 	hotkeys_vtable = NULL;
363 }
364 
obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t * context,obs_key_t key)365 bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
366 				     obs_key_t key)
367 {
368 	return hotkeys_vtable->is_pressed(context, key);
369 }
370 
obs_key_to_str(obs_key_t key,struct dstr * dstr)371 void obs_key_to_str(obs_key_t key, struct dstr *dstr)
372 {
373 	return hotkeys_vtable->key_to_str(key, dstr);
374 }
375 
obs_key_from_virtual_key(int sym)376 obs_key_t obs_key_from_virtual_key(int sym)
377 {
378 	return hotkeys_vtable->key_from_virtual_key(sym);
379 }
380 
obs_key_to_virtual_key(obs_key_t key)381 int obs_key_to_virtual_key(obs_key_t key)
382 {
383 	return hotkeys_vtable->key_to_virtual_key(key);
384 }
385 
add_combo_key(obs_key_t key,struct dstr * str)386 static inline void add_combo_key(obs_key_t key, struct dstr *str)
387 {
388 	struct dstr key_str = {0};
389 
390 	obs_key_to_str(key, &key_str);
391 
392 	if (!dstr_is_empty(&key_str)) {
393 		if (!dstr_is_empty(str)) {
394 			dstr_cat(str, " + ");
395 		}
396 		dstr_cat_dstr(str, &key_str);
397 	}
398 
399 	dstr_free(&key_str);
400 }
401 
obs_key_combination_to_str(obs_key_combination_t combination,struct dstr * str)402 void obs_key_combination_to_str(obs_key_combination_t combination,
403 				struct dstr *str)
404 {
405 	if ((combination.modifiers & INTERACT_CONTROL_KEY) != 0) {
406 		add_combo_key(OBS_KEY_CONTROL, str);
407 	}
408 	if ((combination.modifiers & INTERACT_COMMAND_KEY) != 0) {
409 		add_combo_key(OBS_KEY_META, str);
410 	}
411 	if ((combination.modifiers & INTERACT_ALT_KEY) != 0) {
412 		add_combo_key(OBS_KEY_ALT, str);
413 	}
414 	if ((combination.modifiers & INTERACT_SHIFT_KEY) != 0) {
415 		add_combo_key(OBS_KEY_SHIFT, str);
416 	}
417 	if (combination.key != OBS_KEY_NONE) {
418 		add_combo_key(combination.key, str);
419 	}
420 }
421