1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "core/core_settings.h"
9 
10 #include "boxes/send_files_box.h"
11 #include "ui/widgets/input_fields.h"
12 #include "storage/serialize_common.h"
13 #include "window/section_widget.h"
14 #include "base/platform/base_platform_info.h"
15 #include "webrtc/webrtc_create_adm.h"
16 #include "ui/gl/gl_detection.h"
17 #include "calls/group/calls_group_common.h"
18 #include "facades.h"
19 
20 namespace Core {
21 namespace {
22 
23 constexpr auto kRecentEmojiLimit = 42;
24 
Deserialize(const QByteArray & data)25 [[nodiscard]] WindowPosition Deserialize(const QByteArray &data) {
26 	QDataStream stream(data);
27 	stream.setVersion(QDataStream::Qt_5_1);
28 
29 	auto result = WindowPosition();
30 	stream
31 		>> result.x
32 		>> result.y
33 		>> result.w
34 		>> result.h
35 		>> result.moncrc
36 		>> result.maximized
37 		>> result.scale;
38 	return result;
39 }
40 
Serialize(const WindowPosition & position)41 [[nodiscard]] QByteArray Serialize(const WindowPosition &position) {
42 	auto result = QByteArray();
43 	const auto size = 7 * sizeof(qint32);
44 	result.reserve(size);
45 	{
46 		QDataStream stream(&result, QIODevice::WriteOnly);
47 		stream.setVersion(QDataStream::Qt_5_1);
48 		stream
49 			<< qint32(position.x)
50 			<< qint32(position.y)
51 			<< qint32(position.w)
52 			<< qint32(position.h)
53 			<< qint32(position.moncrc)
54 			<< qint32(position.maximized)
55 			<< qint32(position.scale);
56 	}
57 	DEBUG_LOG(("Window Pos: Writing to storage %1, %2, %3, %4"
58 		" (scale %5%, maximized %6)")
59 		.arg(position.x)
60 		.arg(position.y)
61 		.arg(position.w)
62 		.arg(position.h)
63 		.arg(position.scale)
64 		.arg(Logs::b(position.maximized)));
65 	return result;
66 }
67 
68 } // namespace
69 
Settings()70 Settings::Settings()
71 : _sendSubmitWay(Ui::InputSubmitSettings::Enter)
72 , _floatPlayerColumn(Window::Column::Second)
73 , _floatPlayerCorner(RectPart::TopRight)
74 , _dialogsWidthRatio(DefaultDialogsWidthRatio()) {
75 }
76 
serialize() const77 QByteArray Settings::serialize() const {
78 	const auto themesAccentColors = _themesAccentColors.serialize();
79 	const auto windowPosition = Serialize(_windowPosition);
80 	const auto proxy = _proxy.serialize();
81 
82 	auto recentEmojiPreloadGenerated = std::vector<RecentEmojiId>();
83 	if (_recentEmojiPreload.empty()) {
84 		recentEmojiPreloadGenerated.reserve(_recentEmoji.size());
85 		for (const auto &[emoji, rating] : _recentEmoji) {
86 			recentEmojiPreloadGenerated.push_back({ emoji->id(), rating });
87 		}
88 	}
89 	const auto &recentEmojiPreloadData = _recentEmojiPreload.empty()
90 		? recentEmojiPreloadGenerated
91 		: _recentEmojiPreload;
92 
93 	auto size = Serialize::bytearraySize(themesAccentColors)
94 		+ sizeof(qint32) * 5
95 		+ Serialize::stringSize(_downloadPath.current())
96 		+ Serialize::bytearraySize(_downloadPathBookmark)
97 		+ sizeof(qint32) * 9
98 		+ Serialize::stringSize(_callOutputDeviceId)
99 		+ Serialize::stringSize(_callInputDeviceId)
100 		+ sizeof(qint32) * 5;
101 	for (const auto &[key, value] : _soundOverrides) {
102 		size += Serialize::stringSize(key) + Serialize::stringSize(value);
103 	}
104 	size += sizeof(qint32) * 13
105 		+ Serialize::bytearraySize(_videoPipGeometry)
106 		+ sizeof(qint32)
107 		+ (_dictionariesEnabled.current().size() * sizeof(quint64))
108 		+ sizeof(qint32) * 12
109 		+ Serialize::stringSize(_callVideoInputDeviceId)
110 		+ sizeof(qint32) * 2
111 		+ Serialize::bytearraySize(_groupCallPushToTalkShortcut)
112 		+ sizeof(qint64)
113 		+ sizeof(qint32) * 2
114 		+ Serialize::bytearraySize(windowPosition)
115 		+ sizeof(qint32);
116 	for (const auto &[id, rating] : recentEmojiPreloadData) {
117 		size += Serialize::stringSize(id) + sizeof(quint16);
118 	}
119 	size += sizeof(qint32);
120 	for (const auto &[id, variant] : _emojiVariants) {
121 		size += Serialize::stringSize(id) + sizeof(quint8);
122 	}
123 	size += sizeof(qint32) * 3
124 		+ Serialize::bytearraySize(proxy)
125 		+ sizeof(qint32) * 2
126 		+ Serialize::bytearraySize(_photoEditorBrush)
127 		+ sizeof(qint32);
128 
129 	auto result = QByteArray();
130 	result.reserve(size);
131 	{
132 		QDataStream stream(&result, QIODevice::WriteOnly);
133 		stream.setVersion(QDataStream::Qt_5_1);
134 		stream
135 			<< themesAccentColors
136 			<< qint32(_adaptiveForWide.current() ? 1 : 0)
137 			<< qint32(_moderateModeEnabled ? 1 : 0)
138 			<< qint32(qRound(_songVolume.current() * 1e6))
139 			<< qint32(qRound(_videoVolume.current() * 1e6))
140 			<< qint32(_askDownloadPath ? 1 : 0)
141 			<< _downloadPath.current()
142 			<< _downloadPathBookmark
143 			<< qint32(_nonDefaultVoicePlaybackSpeed ? 1 : 0)
144 			<< qint32(_soundNotify ? 1 : 0)
145 			<< qint32(_desktopNotify ? 1 : 0)
146 			<< qint32(_flashBounceNotify ? 1 : 0)
147 			<< static_cast<qint32>(_notifyView)
148 			<< qint32(_nativeNotifications ? (*_nativeNotifications ? 1 : 2) : 0)
149 			<< qint32(_notificationsCount)
150 			<< static_cast<qint32>(_notificationsCorner)
151 			<< qint32(_autoLock)
152 			<< _callOutputDeviceId
153 			<< _callInputDeviceId
154 			<< qint32(_callOutputVolume)
155 			<< qint32(_callInputVolume)
156 			<< qint32(_callAudioDuckingEnabled ? 1 : 0)
157 			<< qint32(_lastSeenWarningSeen ? 1 : 0)
158 			<< qint32(_soundOverrides.size());
159 		for (const auto &[key, value] : _soundOverrides) {
160 			stream << key << value;
161 		}
162 		stream
163 			<< qint32(_sendFilesWay.serialize())
164 			<< qint32(_sendSubmitWay)
165 			<< qint32(_includeMutedCounter ? 1 : 0)
166 			<< qint32(_countUnreadMessages ? 1 : 0)
167 			<< qint32(_exeLaunchWarning ? 1 : 0)
168 			<< qint32(_notifyAboutPinned.current() ? 1 : 0)
169 			<< qint32(_loopAnimatedStickers ? 1 : 0)
170 			<< qint32(_largeEmoji.current() ? 1 : 0)
171 			<< qint32(_replaceEmoji.current() ? 1 : 0)
172 			<< qint32(_suggestEmoji ? 1 : 0)
173 			<< qint32(_suggestStickersByEmoji ? 1 : 0)
174 			<< qint32(_spellcheckerEnabled.current() ? 1 : 0)
175 			<< qint32(SerializePlaybackSpeed(_videoPlaybackSpeed.current()))
176 			<< _videoPipGeometry
177 			<< qint32(_dictionariesEnabled.current().size());
178 		for (const auto i : _dictionariesEnabled.current()) {
179 			stream << quint64(i);
180 		}
181 		stream
182 			<< qint32(_autoDownloadDictionaries.current() ? 1 : 0)
183 			<< qint32(_mainMenuAccountsShown.current() ? 1 : 0)
184 			<< qint32(_tabbedSelectorSectionEnabled ? 1 : 0)
185 			<< qint32(_floatPlayerColumn)
186 			<< qint32(_floatPlayerCorner)
187 			<< qint32(_thirdSectionInfoEnabled ? 1 : 0)
188 			<< qint32(std::clamp(
189 				qRound(_dialogsWidthRatio.current() * 1000000),
190 				0,
191 				1000000))
192 			<< qint32(_thirdColumnWidth.current())
193 			<< qint32(_thirdSectionExtendedBy)
194 			<< qint32(_notifyFromAll ? 1 : 0)
195 			<< qint32(_nativeWindowFrame.current() ? 1 : 0)
196 			<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
197 			<< _callVideoInputDeviceId
198 			<< qint32(_ipRevealWarning ? 1 : 0)
199 			<< qint32(_groupCallPushToTalk ? 1 : 0)
200 			<< _groupCallPushToTalkShortcut
201 			<< qint64(_groupCallPushToTalkDelay)
202 			<< qint32(0) // Call audio backend
203 			<< qint32(_disableCalls ? 1 : 0)
204 			<< windowPosition
205 			<< qint32(recentEmojiPreloadData.size());
206 		for (const auto &[id, rating] : recentEmojiPreloadData) {
207 			stream << id << quint16(rating);
208 		}
209 		stream
210 			<< qint32(_emojiVariants.size());
211 		for (const auto &[id, variant] : _emojiVariants) {
212 			stream << id << quint8(variant);
213 		}
214 		stream
215 			<< qint32(0) // Old Disable OpenGL
216 			<< qint32(0) // Old Noise Suppression
217 			<< qint32(_workMode.current())
218 			<< proxy
219 			<< qint32(_hiddenGroupCallTooltips.value())
220 			<< qint32(_disableOpenGL ? 1 : 0)
221 			<< _photoEditorBrush
222 			<< qint32(_groupCallNoiseSuppression ? 1 : 0)
223 			<< qint32(_voicePlaybackSpeed * 100)
224 			<< qint32(_closeToTaskbar.current() ? 1 : 0);
225 	}
226 	return result;
227 }
228 
addFromSerialized(const QByteArray & serialized)229 void Settings::addFromSerialized(const QByteArray &serialized) {
230 	if (serialized.isEmpty()) {
231 		return;
232 	}
233 
234 	QDataStream stream(serialized);
235 	stream.setVersion(QDataStream::Qt_5_1);
236 
237 	QByteArray themesAccentColors;
238 	qint32 adaptiveForWide = _adaptiveForWide.current() ? 1 : 0;
239 	qint32 moderateModeEnabled = _moderateModeEnabled ? 1 : 0;
240 	qint32 songVolume = qint32(qRound(_songVolume.current() * 1e6));
241 	qint32 videoVolume = qint32(qRound(_videoVolume.current() * 1e6));
242 	qint32 askDownloadPath = _askDownloadPath ? 1 : 0;
243 	QString downloadPath = _downloadPath.current();
244 	QByteArray downloadPathBookmark = _downloadPathBookmark;
245 	qint32 nonDefaultVoicePlaybackSpeed = _nonDefaultVoicePlaybackSpeed ? 1 : 0;
246 	qint32 soundNotify = _soundNotify ? 1 : 0;
247 	qint32 desktopNotify = _desktopNotify ? 1 : 0;
248 	qint32 flashBounceNotify = _flashBounceNotify ? 1 : 0;
249 	qint32 notifyView = static_cast<qint32>(_notifyView);
250 	qint32 nativeNotifications = _nativeNotifications ? (*_nativeNotifications ? 1 : 2) : 0;
251 	qint32 notificationsCount = _notificationsCount;
252 	qint32 notificationsCorner = static_cast<qint32>(_notificationsCorner);
253 	qint32 autoLock = _autoLock;
254 	QString callOutputDeviceId = _callOutputDeviceId;
255 	QString callInputDeviceId = _callInputDeviceId;
256 	QString callVideoInputDeviceId = _callVideoInputDeviceId;
257 	qint32 callOutputVolume = _callOutputVolume;
258 	qint32 callInputVolume = _callInputVolume;
259 	qint32 callAudioDuckingEnabled = _callAudioDuckingEnabled ? 1 : 0;
260 	qint32 lastSeenWarningSeen = _lastSeenWarningSeen ? 1 : 0;
261 	qint32 soundOverridesCount = 0;
262 	base::flat_map<QString, QString> soundOverrides;
263 	qint32 sendFilesWay = _sendFilesWay.serialize();
264 	qint32 sendSubmitWay = static_cast<qint32>(_sendSubmitWay);
265 	qint32 includeMutedCounter = _includeMutedCounter ? 1 : 0;
266 	qint32 countUnreadMessages = _countUnreadMessages ? 1 : 0;
267 	qint32 exeLaunchWarning = _exeLaunchWarning ? 1 : 0;
268 	qint32 notifyAboutPinned = _notifyAboutPinned.current() ? 1 : 0;
269 	qint32 loopAnimatedStickers = _loopAnimatedStickers ? 1 : 0;
270 	qint32 largeEmoji = _largeEmoji.current() ? 1 : 0;
271 	qint32 replaceEmoji = _replaceEmoji.current() ? 1 : 0;
272 	qint32 suggestEmoji = _suggestEmoji ? 1 : 0;
273 	qint32 suggestStickersByEmoji = _suggestStickersByEmoji ? 1 : 0;
274 	qint32 spellcheckerEnabled = _spellcheckerEnabled.current() ? 1 : 0;
275 	qint32 videoPlaybackSpeed = Core::Settings::SerializePlaybackSpeed(_videoPlaybackSpeed.current());
276 	qint32 voicePlaybackSpeed = _voicePlaybackSpeed * 100;
277 	QByteArray videoPipGeometry = _videoPipGeometry;
278 	qint32 dictionariesEnabledCount = 0;
279 	std::vector<int> dictionariesEnabled;
280 	qint32 autoDownloadDictionaries = _autoDownloadDictionaries.current() ? 1 : 0;
281 	qint32 mainMenuAccountsShown = _mainMenuAccountsShown.current() ? 1 : 0;
282 	qint32 tabbedSelectorSectionEnabled = 1;
283 	qint32 floatPlayerColumn = static_cast<qint32>(Window::Column::Second);
284 	qint32 floatPlayerCorner = static_cast<qint32>(RectPart::TopRight);
285 	qint32 thirdSectionInfoEnabled = 0;
286 	float64 dialogsWidthRatio = _dialogsWidthRatio.current();
287 	qint32 thirdColumnWidth = _thirdColumnWidth.current();
288 	qint32 thirdSectionExtendedBy = _thirdSectionExtendedBy;
289 	qint32 notifyFromAll = _notifyFromAll ? 1 : 0;
290 	qint32 nativeWindowFrame = _nativeWindowFrame.current() ? 1 : 0;
291 	qint32 systemDarkModeEnabled = _systemDarkModeEnabled.current() ? 1 : 0;
292 	qint32 ipRevealWarning = _ipRevealWarning ? 1 : 0;
293 	qint32 groupCallPushToTalk = _groupCallPushToTalk ? 1 : 0;
294 	QByteArray groupCallPushToTalkShortcut = _groupCallPushToTalkShortcut;
295 	qint64 groupCallPushToTalkDelay = _groupCallPushToTalkDelay;
296 	qint32 callAudioBackend = 0;
297 	qint32 disableCalls = _disableCalls ? 1 : 0;
298 	QByteArray windowPosition;
299 	std::vector<RecentEmojiId> recentEmojiPreload;
300 	base::flat_map<QString, uint8> emojiVariants;
301 	qint32 disableOpenGL = _disableOpenGL ? 1 : 0;
302 	qint32 groupCallNoiseSuppression = _groupCallNoiseSuppression ? 1 : 0;
303 	qint32 workMode = static_cast<qint32>(_workMode.current());
304 	QByteArray proxy;
305 	qint32 hiddenGroupCallTooltips = qint32(_hiddenGroupCallTooltips.value());
306 	QByteArray photoEditorBrush = _photoEditorBrush;
307 	qint32 closeToTaskbar = _closeToTaskbar.current() ? 1 : 0;
308 
309 	stream >> themesAccentColors;
310 	if (!stream.atEnd()) {
311 		stream
312 			>> adaptiveForWide
313 			>> moderateModeEnabled
314 			>> songVolume
315 			>> videoVolume
316 			>> askDownloadPath
317 			>> downloadPath
318 			>> downloadPathBookmark
319 			>> nonDefaultVoicePlaybackSpeed
320 			>> soundNotify
321 			>> desktopNotify
322 			>> flashBounceNotify
323 			>> notifyView
324 			>> nativeNotifications
325 			>> notificationsCount
326 			>> notificationsCorner
327 			>> autoLock
328 			>> callOutputDeviceId
329 			>> callInputDeviceId
330 			>> callOutputVolume
331 			>> callInputVolume
332 			>> callAudioDuckingEnabled
333 			>> lastSeenWarningSeen
334 			>> soundOverridesCount;
335 		if (stream.status() == QDataStream::Ok) {
336 			for (auto i = 0; i != soundOverridesCount; ++i) {
337 				QString key, value;
338 				stream >> key >> value;
339 				soundOverrides.emplace(key, value);
340 			}
341 		}
342 		stream
343 			>> sendFilesWay
344 			>> sendSubmitWay
345 			>> includeMutedCounter
346 			>> countUnreadMessages
347 			>> exeLaunchWarning
348 			>> notifyAboutPinned
349 			>> loopAnimatedStickers
350 			>> largeEmoji
351 			>> replaceEmoji
352 			>> suggestEmoji
353 			>> suggestStickersByEmoji
354 			>> spellcheckerEnabled
355 			>> videoPlaybackSpeed
356 			>> videoPipGeometry
357 			>> dictionariesEnabledCount;
358 		if (stream.status() == QDataStream::Ok) {
359 			for (auto i = 0; i != dictionariesEnabledCount; ++i) {
360 				qint64 langId;
361 				stream >> langId;
362 				dictionariesEnabled.emplace_back(langId);
363 			}
364 		}
365 		stream
366 			>> autoDownloadDictionaries
367 			>> mainMenuAccountsShown;
368 	}
369 	if (!stream.atEnd()) {
370 		auto dialogsWidthRatioInt = qint32();
371 		stream
372 			>> tabbedSelectorSectionEnabled
373 			>> floatPlayerColumn
374 			>> floatPlayerCorner
375 			>> thirdSectionInfoEnabled
376 			>> dialogsWidthRatioInt
377 			>> thirdColumnWidth
378 			>> thirdSectionExtendedBy
379 			>> notifyFromAll;
380 		dialogsWidthRatio = std::clamp(
381 			dialogsWidthRatioInt / 1000000.,
382 			0.,
383 			1.);
384 	}
385 	if (!stream.atEnd()) {
386 		stream >> nativeWindowFrame;
387 	}
388 	if (!stream.atEnd()) {
389 		stream >> systemDarkModeEnabled;
390 	}
391 	if (!stream.atEnd()) {
392 		stream >> callVideoInputDeviceId;
393 	}
394 	if (!stream.atEnd()) {
395 		stream >> ipRevealWarning;
396 	}
397 	if (!stream.atEnd()) {
398 		stream
399 			>> groupCallPushToTalk
400 			>> groupCallPushToTalkShortcut
401 			>> groupCallPushToTalkDelay;
402 	}
403 	if (!stream.atEnd()) {
404 		stream >> callAudioBackend;
405 	}
406 	if (!stream.atEnd()) {
407 		stream >> disableCalls;
408 	}
409 	if (!stream.atEnd()) {
410 		stream >> windowPosition;
411 	}
412 	if (!stream.atEnd()) {
413 		auto recentCount = qint32(0);
414 		stream >> recentCount;
415 		if (recentCount > 0 && recentCount < 10000) {
416 			recentEmojiPreload.reserve(recentCount);
417 			for (auto i = 0; i != recentCount; ++i) {
418 				auto id = QString();
419 				auto rating = quint16();
420 				stream >> id >> rating;
421 				recentEmojiPreload.push_back({ id, rating });
422 			}
423 		}
424 		auto variantsCount = qint32(0);
425 		stream >> variantsCount;
426 		if (variantsCount > 0 && variantsCount < 10000) {
427 			emojiVariants.reserve(variantsCount);
428 			for (auto i = 0; i != variantsCount; ++i) {
429 				auto id = QString();
430 				auto variant = quint8();
431 				stream >> id >> variant;
432 				emojiVariants.emplace(id, variant);
433 			}
434 		}
435 	}
436 	if (!stream.atEnd()) {
437 		qint32 disableOpenGLOld;
438 		stream >> disableOpenGLOld;
439 	}
440 	if (!stream.atEnd()) {
441 		qint32 groupCallNoiseSuppressionOld;
442 		stream >> groupCallNoiseSuppressionOld;
443 	}
444 	if (!stream.atEnd()) {
445 		stream >> workMode;
446 	}
447 	if (!stream.atEnd()) {
448 		stream >> proxy;
449 	}
450 	if (!stream.atEnd()) {
451 		stream >> hiddenGroupCallTooltips;
452 	}
453 	if (!stream.atEnd()) {
454 		stream >> disableOpenGL;
455 	}
456 	if (!stream.atEnd()) {
457 		stream >> photoEditorBrush;
458 	}
459 	if (!stream.atEnd()) {
460 		stream >> groupCallNoiseSuppression;
461 	}
462 	if (!stream.atEnd()) {
463 		stream >> voicePlaybackSpeed;
464 	}
465 	if (!stream.atEnd()) {
466 		stream >> closeToTaskbar;
467 	}
468 	if (stream.status() != QDataStream::Ok) {
469 		LOG(("App Error: "
470 			"Bad data for Core::Settings::constructFromSerialized()"));
471 		return;
472 	} else if (!_themesAccentColors.setFromSerialized(themesAccentColors)) {
473 		return;
474 	} else if (!_proxy.setFromSerialized(proxy)) {
475 		return;
476 	}
477 	_adaptiveForWide = (adaptiveForWide == 1);
478 	_moderateModeEnabled = (moderateModeEnabled == 1);
479 	_songVolume = std::clamp(songVolume / 1e6, 0., 1.);
480 	_videoVolume = std::clamp(videoVolume / 1e6, 0., 1.);
481 	_askDownloadPath = (askDownloadPath == 1);
482 	_downloadPath = downloadPath;
483 	_downloadPathBookmark = downloadPathBookmark;
484 	_soundNotify = (soundNotify == 1);
485 	_desktopNotify = (desktopNotify == 1);
486 	_flashBounceNotify = (flashBounceNotify == 1);
487 	const auto uncheckedNotifyView = static_cast<NotifyView>(notifyView);
488 	switch (uncheckedNotifyView) {
489 	case NotifyView::ShowNothing:
490 	case NotifyView::ShowName:
491 	case NotifyView::ShowPreview: _notifyView = uncheckedNotifyView; break;
492 	}
493 	switch (nativeNotifications) {
494 	case 0: _nativeNotifications = std::nullopt; break;
495 	case 1: _nativeNotifications = true; break;
496 	case 2: _nativeNotifications = false; break;
497 	default: break;
498 	}
499 	_notificationsCount = (notificationsCount > 0) ? notificationsCount : 3;
500 	const auto uncheckedNotificationsCorner = static_cast<ScreenCorner>(notificationsCorner);
501 	switch (uncheckedNotificationsCorner) {
502 	case ScreenCorner::TopLeft:
503 	case ScreenCorner::TopRight:
504 	case ScreenCorner::BottomRight:
505 	case ScreenCorner::BottomLeft: _notificationsCorner = uncheckedNotificationsCorner; break;
506 	}
507 	_includeMutedCounter = (includeMutedCounter == 1);
508 	_countUnreadMessages = (countUnreadMessages == 1);
509 	_notifyAboutPinned = (notifyAboutPinned == 1);
510 	_autoLock = autoLock;
511 	_callOutputDeviceId = callOutputDeviceId;
512 	_callInputDeviceId = callInputDeviceId;
513 	_callVideoInputDeviceId = callVideoInputDeviceId;
514 	_callOutputVolume = callOutputVolume;
515 	_callInputVolume = callInputVolume;
516 	_callAudioDuckingEnabled = (callAudioDuckingEnabled == 1);
517 	_lastSeenWarningSeen = (lastSeenWarningSeen == 1);
518 	_soundOverrides = std::move(soundOverrides);
519 	_sendFilesWay = Ui::SendFilesWay::FromSerialized(sendFilesWay).value_or(_sendFilesWay);
520 	auto uncheckedSendSubmitWay = static_cast<Ui::InputSubmitSettings>(sendSubmitWay);
521 	switch (uncheckedSendSubmitWay) {
522 	case Ui::InputSubmitSettings::Enter:
523 	case Ui::InputSubmitSettings::CtrlEnter: _sendSubmitWay = uncheckedSendSubmitWay; break;
524 	}
525 	_includeMutedCounter = (includeMutedCounter == 1);
526 	_countUnreadMessages = (countUnreadMessages == 1);
527 	_exeLaunchWarning = (exeLaunchWarning == 1);
528 	_ipRevealWarning = (ipRevealWarning == 1);
529 	_notifyAboutPinned = (notifyAboutPinned == 1);
530 	_loopAnimatedStickers = (loopAnimatedStickers == 1);
531 	_largeEmoji = (largeEmoji == 1);
532 	_replaceEmoji = (replaceEmoji == 1);
533 	_suggestEmoji = (suggestEmoji == 1);
534 	_suggestStickersByEmoji = (suggestStickersByEmoji == 1);
535 	_spellcheckerEnabled = (spellcheckerEnabled == 1);
536 	_videoPlaybackSpeed = DeserializePlaybackSpeed(videoPlaybackSpeed);
537 	{
538 		// Restore settings from 3.0.1 version.
539 		if (voicePlaybackSpeed == 100) {
540 			_nonDefaultVoicePlaybackSpeed = false;
541 			_voicePlaybackSpeed = 2.0;
542 		} else {
543 			_nonDefaultVoicePlaybackSpeed =
544 				(nonDefaultVoicePlaybackSpeed == 1);
545 			_voicePlaybackSpeed = voicePlaybackSpeed / 100.;
546 		}
547 	}
548 	_videoPipGeometry = (videoPipGeometry);
549 	_dictionariesEnabled = std::move(dictionariesEnabled);
550 	_autoDownloadDictionaries = (autoDownloadDictionaries == 1);
551 	_mainMenuAccountsShown = (mainMenuAccountsShown == 1);
552 	_tabbedSelectorSectionEnabled = (tabbedSelectorSectionEnabled == 1);
553 	auto uncheckedColumn = static_cast<Window::Column>(floatPlayerColumn);
554 	switch (uncheckedColumn) {
555 	case Window::Column::First:
556 	case Window::Column::Second:
557 	case Window::Column::Third: _floatPlayerColumn = uncheckedColumn; break;
558 	}
559 	auto uncheckedCorner = static_cast<RectPart>(floatPlayerCorner);
560 	switch (uncheckedCorner) {
561 	case RectPart::TopLeft:
562 	case RectPart::TopRight:
563 	case RectPart::BottomLeft:
564 	case RectPart::BottomRight: _floatPlayerCorner = uncheckedCorner; break;
565 	}
566 	_thirdSectionInfoEnabled = thirdSectionInfoEnabled;
567 	_dialogsWidthRatio = dialogsWidthRatio;
568 	_thirdColumnWidth = thirdColumnWidth;
569 	_thirdSectionExtendedBy = thirdSectionExtendedBy;
570 	if (_thirdSectionInfoEnabled) {
571 		_tabbedSelectorSectionEnabled = false;
572 	}
573 	_notifyFromAll = (notifyFromAll == 1);
574 	_nativeWindowFrame = (nativeWindowFrame == 1);
575 	_systemDarkModeEnabled = (systemDarkModeEnabled == 1);
576 	_groupCallPushToTalk = (groupCallPushToTalk == 1);
577 	_groupCallPushToTalkShortcut = groupCallPushToTalkShortcut;
578 	_groupCallPushToTalkDelay = groupCallPushToTalkDelay;
579 	_disableCalls = (disableCalls == 1);
580 	if (!windowPosition.isEmpty()) {
581 		_windowPosition = Deserialize(windowPosition);
582 	}
583 	_recentEmojiPreload = std::move(recentEmojiPreload);
584 	_emojiVariants = std::move(emojiVariants);
585 	_disableOpenGL = (disableOpenGL == 1);
586 	if (!Platform::IsMac()) {
587 		Ui::GL::ForceDisable(_disableOpenGL
588 			|| Ui::GL::LastCrashCheckFailed());
589 	}
590 	_groupCallNoiseSuppression = (groupCallNoiseSuppression == 1);
591 	const auto uncheckedWorkMode = static_cast<WorkMode>(workMode);
592 	switch (uncheckedWorkMode) {
593 	case WorkMode::WindowAndTray:
594 	case WorkMode::TrayOnly:
595 	case WorkMode::WindowOnly: _workMode = uncheckedWorkMode; break;
596 	}
597 	_hiddenGroupCallTooltips = [&] {
598 		using Tooltip = Calls::Group::StickedTooltip;
599 		return Tooltip(0)
600 			| ((hiddenGroupCallTooltips & int(Tooltip::Camera))
601 				? Tooltip::Camera
602 				: Tooltip(0))
603 			| ((hiddenGroupCallTooltips & int(Tooltip::Microphone))
604 				? Tooltip::Microphone
605 				: Tooltip(0));
606 	}();
607 	_photoEditorBrush = photoEditorBrush;
608 	_closeToTaskbar = (closeToTaskbar == 1);
609 }
610 
getSoundPath(const QString & key) const611 QString Settings::getSoundPath(const QString &key) const {
612 	auto it = _soundOverrides.find(key);
613 	if (it != _soundOverrides.end()) {
614 		return it->second;
615 	}
616 	return qsl(":/sounds/") + key + qsl(".mp3");
617 }
618 
setTabbedSelectorSectionEnabled(bool enabled)619 void Settings::setTabbedSelectorSectionEnabled(bool enabled) {
620 	_tabbedSelectorSectionEnabled = enabled;
621 	if (enabled) {
622 		setThirdSectionInfoEnabled(false);
623 	}
624 	setTabbedReplacedWithInfo(false);
625 }
626 
tabbedReplacedWithInfoValue() const627 rpl::producer<bool> Settings::tabbedReplacedWithInfoValue() const {
628 	return _tabbedReplacedWithInfoValue.events_starting_with(
629 		tabbedReplacedWithInfo());
630 }
631 
setThirdSectionInfoEnabled(bool enabled)632 void Settings::setThirdSectionInfoEnabled(bool enabled) {
633 	if (_thirdSectionInfoEnabled != enabled) {
634 		_thirdSectionInfoEnabled = enabled;
635 		if (enabled) {
636 			setTabbedSelectorSectionEnabled(false);
637 		}
638 		setTabbedReplacedWithInfo(false);
639 		_thirdSectionInfoEnabledValue.fire_copy(enabled);
640 	}
641 }
642 
thirdSectionInfoEnabledValue() const643 rpl::producer<bool> Settings::thirdSectionInfoEnabledValue() const {
644 	return _thirdSectionInfoEnabledValue.events_starting_with(
645 		thirdSectionInfoEnabled());
646 }
647 
setTabbedReplacedWithInfo(bool enabled)648 void Settings::setTabbedReplacedWithInfo(bool enabled) {
649 	if (_tabbedReplacedWithInfo != enabled) {
650 		_tabbedReplacedWithInfo = enabled;
651 		_tabbedReplacedWithInfoValue.fire_copy(enabled);
652 	}
653 }
654 
callAudioBackend() const655 Webrtc::Backend Settings::callAudioBackend() const {
656 	return Webrtc::Backend::OpenAL;
657 }
658 
setDialogsWidthRatio(float64 ratio)659 void Settings::setDialogsWidthRatio(float64 ratio) {
660 	_dialogsWidthRatio = ratio;
661 }
662 
dialogsWidthRatio() const663 float64 Settings::dialogsWidthRatio() const {
664 	return _dialogsWidthRatio.current();
665 }
666 
dialogsWidthRatioChanges() const667 rpl::producer<float64> Settings::dialogsWidthRatioChanges() const {
668 	return _dialogsWidthRatio.changes();
669 }
670 
setThirdColumnWidth(int width)671 void Settings::setThirdColumnWidth(int width) {
672 	_thirdColumnWidth = width;
673 }
674 
thirdColumnWidth() const675 int Settings::thirdColumnWidth() const {
676 	return _thirdColumnWidth.current();
677 }
678 
thirdColumnWidthChanges() const679 rpl::producer<int> Settings::thirdColumnWidthChanges() const {
680 	return _thirdColumnWidth.changes();
681 }
682 
recentEmoji() const683 const std::vector<Settings::RecentEmoji> &Settings::recentEmoji() const {
684 	if (_recentEmoji.empty()) {
685 		resolveRecentEmoji();
686 	}
687 	return _recentEmoji;
688 }
689 
resolveRecentEmoji() const690 void Settings::resolveRecentEmoji() const {
691 	const auto haveAlready = [&](EmojiPtr emoji) {
692 		return ranges::contains(
693 			_recentEmoji,
694 			emoji->id(),
695 			[](const RecentEmoji &data) { return data.emoji->id(); });
696 	};
697 	if (!_recentEmojiPreload.empty()) {
698 		_recentEmoji.reserve(_recentEmojiPreload.size());
699 		for (const auto &[id, rating] : base::take(_recentEmojiPreload)) {
700 			if (const auto emoji = Ui::Emoji::Find(id)) {
701 				if (!haveAlready(emoji)) {
702 					_recentEmoji.push_back({ emoji, rating });
703 				}
704 			}
705 		}
706 		_recentEmojiPreload.clear();
707 	}
708 	for (const auto emoji : Ui::Emoji::GetDefaultRecent()) {
709 		if (_recentEmoji.size() >= kRecentEmojiLimit) {
710 			break;
711 		} else if (!haveAlready(emoji)) {
712 			_recentEmoji.push_back({ emoji, 1 });
713 		}
714 	}
715 }
716 
recentEmojiSection() const717 EmojiPack Settings::recentEmojiSection() const {
718 	const auto &recent = recentEmoji();
719 
720 	auto result = EmojiPack();
721 	result.reserve(recent.size());
722 	for (const auto &[emoji, rating] : recent) {
723 		result.push_back(emoji);
724 	}
725 	return result;
726 }
727 
incrementRecentEmoji(EmojiPtr emoji)728 void Settings::incrementRecentEmoji(EmojiPtr emoji) {
729 	resolveRecentEmoji();
730 
731 	auto i = _recentEmoji.begin(), e = _recentEmoji.end();
732 	for (; i != e; ++i) {
733 		if (i->emoji == emoji) {
734 			++i->rating;
735 			if (i->rating > 0x8000) {
736 				for (auto j = _recentEmoji.begin(); j != e; ++j) {
737 					if (j->rating > 1) {
738 						j->rating /= 2;
739 					} else {
740 						j->rating = 1;
741 					}
742 				}
743 			}
744 			for (; i != _recentEmoji.begin(); --i) {
745 				if ((i - 1)->rating > i->rating) {
746 					break;
747 				}
748 				std::swap(*i, *(i - 1));
749 			}
750 			break;
751 		}
752 	}
753 	if (i == e) {
754 		while (_recentEmoji.size() >= kRecentEmojiLimit) {
755 			_recentEmoji.pop_back();
756 		}
757 		_recentEmoji.push_back({ emoji, 1 });
758 		for (i = _recentEmoji.end() - 1; i != _recentEmoji.begin(); --i) {
759 			if ((i - 1)->rating > i->rating) {
760 				break;
761 			}
762 			std::swap(*i, *(i - 1));
763 		}
764 	}
765 	_recentEmojiUpdated.fire({});
766 	_saveDelayed.fire({});
767 }
768 
setLegacyRecentEmojiPreload(QVector<QPair<QString,ushort>> data)769 void Settings::setLegacyRecentEmojiPreload(
770 		QVector<QPair<QString, ushort>> data) {
771 	if (!_recentEmojiPreload.empty() || data.isEmpty()) {
772 		return;
773 	}
774 	_recentEmojiPreload.reserve(data.size());
775 	for (const auto &[id, rating] : data) {
776 		_recentEmojiPreload.push_back({ id, rating });
777 	}
778 }
779 
saveEmojiVariant(EmojiPtr emoji)780 void Settings::saveEmojiVariant(EmojiPtr emoji) {
781 	_emojiVariants[emoji->nonColoredId()] = emoji->variantIndex(emoji);
782 	_saveDelayed.fire({});
783 }
784 
setLegacyEmojiVariants(QMap<QString,int> data)785 void Settings::setLegacyEmojiVariants(QMap<QString, int> data) {
786 	if (!_emojiVariants.empty() || data.isEmpty()) {
787 		return;
788 	}
789 	_emojiVariants.reserve(data.size());
790 	for (auto i = data.begin(), e = data.end(); i != e; ++i) {
791 		_emojiVariants.emplace(i.key(), i.value());
792 	}
793 }
794 
resetOnLastLogout()795 void Settings::resetOnLastLogout() {
796 	_adaptiveForWide = true;
797 	_moderateModeEnabled = false;
798 
799 	_songVolume = kDefaultVolume;
800 	_videoVolume = kDefaultVolume;
801 
802 	_askDownloadPath = false;
803 	_downloadPath = QString();
804 	_downloadPathBookmark = QByteArray();
805 
806 	_nonDefaultVoicePlaybackSpeed = false;
807 	_soundNotify = true;
808 	_desktopNotify = true;
809 	_flashBounceNotify = true;
810 	_notifyView = NotifyView::ShowPreview;
811 	//_nativeNotifications = std::nullopt;
812 	//_notificationsCount = 3;
813 	//_notificationsCorner = ScreenCorner::BottomRight;
814 	_includeMutedCounter = true;
815 	_countUnreadMessages = true;
816 	_notifyAboutPinned = true;
817 	//_autoLock = 3600;
818 
819 	//_callOutputDeviceId = u"default"_q;
820 	//_callInputDeviceId = u"default"_q;
821 	//_callVideoInputDeviceId = u"default"_q;
822 	//_callOutputVolume = 100;
823 	//_callInputVolume = 100;
824 	//_callAudioDuckingEnabled = true;
825 
826 	_disableCalls = false;
827 
828 	_groupCallPushToTalk = false;
829 	_groupCallPushToTalkShortcut = QByteArray();
830 	_groupCallPushToTalkDelay = 20;
831 
832 	_groupCallNoiseSuppression = false;
833 
834 	//_themesAccentColors = Window::Theme::AccentColors();
835 
836 	_lastSeenWarningSeen = false;
837 	_sendFilesWay = Ui::SendFilesWay();
838 	//_sendSubmitWay = Ui::InputSubmitSettings::Enter;
839 	_soundOverrides = {};
840 
841 	_exeLaunchWarning = true;
842 	_ipRevealWarning = true;
843 	_loopAnimatedStickers = true;
844 	_largeEmoji = true;
845 	_replaceEmoji = true;
846 	_suggestEmoji = true;
847 	_suggestStickersByEmoji = true;
848 	_spellcheckerEnabled = true;
849 	_videoPlaybackSpeed = 1.;
850 	_voicePlaybackSpeed = 1.;
851 	//_videoPipGeometry = QByteArray();
852 	_dictionariesEnabled = std::vector<int>();
853 	_autoDownloadDictionaries = true;
854 	_mainMenuAccountsShown = true;
855 	_tabbedSelectorSectionEnabled = false; // per-window
856 	_floatPlayerColumn = Window::Column::Second; // per-window
857 	_floatPlayerCorner = RectPart::TopRight; // per-window
858 	_thirdSectionInfoEnabled = true; // per-window
859 	_thirdSectionExtendedBy = -1; // per-window
860 	_dialogsWidthRatio = DefaultDialogsWidthRatio(); // per-window
861 	_thirdColumnWidth = kDefaultThirdColumnWidth; // p-w
862 	_notifyFromAll = true;
863 	_tabbedReplacedWithInfo = false; // per-window
864 	_systemDarkModeEnabled = false;
865 	_hiddenGroupCallTooltips = 0;
866 
867 	_recentEmojiPreload.clear();
868 	_recentEmoji.clear();
869 	_emojiVariants.clear();
870 
871 	_workMode = WorkMode::WindowAndTray;
872 }
873 
ThirdColumnByDefault()874 bool Settings::ThirdColumnByDefault() {
875 	return Platform::IsMacStoreBuild();
876 }
877 
DefaultDialogsWidthRatio()878 float64 Settings::DefaultDialogsWidthRatio() {
879 	return ThirdColumnByDefault()
880 		? kDefaultBigDialogsWidthRatio
881 		: kDefaultDialogsWidthRatio;
882 }
883 
884 } // namespace Core
885