tdesktop/Telegram/SourceFiles/platform/mac/specific_mac.mm

471 lines
12 KiB
Plaintext

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "platform/mac/specific_mac.h"
#include "lang/lang_keys.h"
#include "application.h"
#include "mainwidget.h"
#include "history/history_widget.h"
#include "core/crash_reports.h"
#include "storage/localstorage.h"
#include "passcodewidget.h"
#include "mainwindow.h"
#include "history/history_location_manager.h"
#include "platform/mac/mac_utilities.h"
#include <cstdlib>
#include <execinfo.h>
#include <Cocoa/Cocoa.h>
#include <CoreFoundation/CFURL.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/hidsystem/ev_keymap.h>
#include <SPMediaKeyTap.h>
#include <mach-o/dyld.h>
namespace {
QStringList _initLogs;
class _PsEventFilter : public QAbstractNativeEventFilter {
public:
_PsEventFilter() {
}
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
auto wnd = App::wnd();
if (!wnd) return false;
return wnd->psFilterNativeEvent(message);
}
};
_PsEventFilter *_psEventFilter = nullptr;
};
namespace {
QRect _monitorRect;
TimeMs _monitorLastGot = 0;
} // namespace
QRect psDesktopRect() {
auto tnow = getms(true);
if (tnow > _monitorLastGot + 1000 || tnow < _monitorLastGot) {
_monitorLastGot = tnow;
_monitorRect = QApplication::desktop()->availableGeometry(App::wnd());
}
return _monitorRect;
}
void psShowOverAll(QWidget *w, bool canFocus) {
objc_showOverAll(w->winId(), canFocus);
}
void psBringToBack(QWidget *w) {
objc_bringToBack(w->winId());
}
QAbstractNativeEventFilter *psNativeEventFilter() {
delete _psEventFilter;
_psEventFilter = new _PsEventFilter();
return _psEventFilter;
}
void psWriteDump() {
#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
double v = objc_appkitVersion();
CrashReports::dump() << "OS-Version: " << v;
#endif // TDESKTOP_DISABLE_CRASH_REPORTS
}
QString demanglestr(const QString &mangled) {
QByteArray cmd = ("c++filt -n " + mangled).toUtf8();
FILE *f = popen(cmd.constData(), "r");
if (!f) return "BAD_SYMBOL_" + mangled;
QString result;
char buffer[4096] = {0};
while (!feof(f)) {
if (fgets(buffer, 4096, f) != NULL) {
result += buffer;
}
}
pclose(f);
return result.trimmed();
}
QString escapeShell(const QString &str) {
QString result;
const QChar *b = str.constData(), *e = str.constEnd();
for (const QChar *ch = b; ch != e; ++ch) {
if (*ch == ' ' || *ch == '"' || *ch == '\'' || *ch == '\\') {
if (result.isEmpty()) {
result.reserve(str.size() * 2);
}
if (ch > b) {
result.append(b, ch - b);
}
result.append('\\');
b = ch;
}
}
if (result.isEmpty()) return str;
if (e > b) {
result.append(b, e - b);
}
return result;
}
QStringList atosstr(uint64 *addresses, int count, uint64 base) {
QStringList result;
if (!count || cExeName().isEmpty()) return result;
result.reserve(count);
QString cmdstr = "atos -o " + escapeShell(cExeDir() + cExeName()) + qsl("/Contents/MacOS/Telegram -l 0x%1").arg(base, 0, 16);
for (int i = 0; i < count; ++i) {
if (addresses[i]) {
cmdstr += qsl(" 0x%1").arg(addresses[i], 0, 16);
}
}
QByteArray cmd = cmdstr.toUtf8();
FILE *f = popen(cmd.constData(), "r");
QStringList atosResult;
if (f) {
char buffer[4096] = {0};
while (!feof(f)) {
if (fgets(buffer, 4096, f) != NULL) {
atosResult.push_back(QString::fromUtf8(buffer));
}
}
pclose(f);
}
for (int i = 0, j = 0; i < count; ++i) {
if (addresses[i]) {
if (j < atosResult.size() && !atosResult.at(j).isEmpty() && !atosResult.at(j).startsWith(qstr("0x"))) {
result.push_back(atosResult.at(j).trimmed());
} else {
result.push_back(QString());
}
++j;
} else {
result.push_back(QString());
}
}
return result;
}
QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile) {
QString initial = QString::fromUtf8(crashdump), result;
QStringList lines = initial.split('\n');
result.reserve(initial.size());
int32 i = 0, l = lines.size();
while (i < l) {
uint64 addresses[1024] = { 0 };
for (; i < l; ++i) {
result.append(lines.at(i)).append('\n');
QString line = lines.at(i).trimmed();
if (line == qstr("Base image addresses:")) {
++i;
break;
}
}
uint64 base = 0;
for (int32 start = i; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
if (!base) {
QRegularExpressionMatch m = QRegularExpression(qsl("^\\d+ (\\d+) \\((.+)\\)")).match(line);
if (m.hasMatch()) {
if (uint64 address = m.captured(1).toULongLong()) {
if (m.captured(2).endsWith(qstr("Contents/MacOS/Telegram"))) {
base = address;
}
}
}
}
}
if (base) {
result.append(qsl("(base address read: 0x%1)\n").arg(base, 0, 16));
} else {
result.append(qsl("ERROR: base address not read!\n"));
}
for (; i < l; ++i) {
result.append(lines.at(i)).append('\n');
QString line = lines.at(i).trimmed();
if (line == qstr("Backtrace:")) {
++i;
break;
}
}
int32 start = i;
for (; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
if (QRegularExpression(qsl("^\\d+")).match(line).hasMatch()) {
QStringList lst = line.split(' ', QString::SkipEmptyParts);
if (lst.size() > 2) {
uint64 addr = lst.at(2).startsWith(qstr("0x")) ? lst.at(2).mid(2).toULongLong(0, 16) : lst.at(2).toULongLong();
addresses[i - start] = addr;
}
}
}
QStringList atos = atosstr(addresses, i - start, base);
for (i = start; i < l; ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) break;
if (!QRegularExpression(qsl("^\\d+")).match(line).hasMatch()) {
if (!lines.at(i).startsWith(qstr("ERROR: "))) {
result.append(qstr("BAD LINE: "));
}
result.append(line).append('\n');
continue;
}
QStringList lst = line.split(' ', QString::SkipEmptyParts);
result.append('\n').append(lst.at(0)).append(qsl(". "));
if (lst.size() < 3) {
result.append(qstr("BAD LINE: ")).append(line).append('\n');
continue;
}
if (lst.size() > 5 && lst.at(3) == qsl("0x0") && lst.at(4) == qsl("+") && lst.at(5) == qsl("1")) {
result.append(qsl("(0x1 separator)\n"));
continue;
}
if (i - start < atos.size()) {
if (!atos.at(i - start).isEmpty()) {
result.append(atos.at(i - start)).append('\n');
continue;
}
}
for (int j = 1, s = lst.size();;) {
if (lst.at(j).startsWith('_')) {
result.append(demanglestr(lst.at(j)));
if (++j < s) {
result.append(' ');
for (;;) {
result.append(lst.at(j));
if (++j < s) {
result.append(' ');
} else {
break;
}
}
}
break;
} else if (j > 2) {
result.append(lst.at(j));
}
if (++j < s) {
result.append(' ');
} else {
break;
}
}
result.append(qsl(" [demangled]")).append('\n');
}
}
return result;
}
void psDeleteDir(const QString &dir) {
objc_deleteDir(dir);
}
namespace {
auto _lastUserAction = 0LL;
} // namespace
void psUserActionDone() {
_lastUserAction = getms(true);
}
bool psIdleSupported() {
return objc_idleSupported();
}
TimeMs psIdleTime() {
auto idleTime = 0LL;
return objc_idleTime(idleTime) ? idleTime : (getms(true) - _lastUserAction);
}
QStringList psInitLogs() {
return _initLogs;
}
void psClearInitLogs() {
_initLogs = QStringList();
}
void psActivateProcess(uint64 pid) {
if (!pid) {
objc_activateProgram(App::wnd() ? App::wnd()->winId() : 0);
}
}
QString psAppDataPath() {
return objc_appDataPath();
}
QString psDownloadPath() {
return objc_downloadPath();
}
void psDoCleanup() {
try {
psAutoStart(false, true);
psSendToMenu(false, true);
} catch (...) {
}
}
int psCleanup() {
psDoCleanup();
return 0;
}
void psDoFixPrevious() {
}
int psFixPrevious() {
psDoFixPrevious();
return 0;
}
namespace Platform {
void start() {
objc_start();
}
void finish() {
delete _psEventFilter;
_psEventFilter = nullptr;
objc_finish();
}
void StartTranslucentPaint(QPainter &p, QPaintEvent *e) {
#ifdef OS_MAC_OLD
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(e->rect(), Qt::transparent);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
#endif // OS_MAC_OLD
}
QString SystemCountry() {
NSLocale *currentLocale = [NSLocale currentLocale]; // get the current locale.
NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
return countryCode ? NS2QString(countryCode) : QString();
}
QString SystemLanguage() {
if (auto currentLocale = [NSLocale currentLocale]) { // get the current locale.
if (NSString *collator = [currentLocale objectForKey:NSLocaleCollatorIdentifier]) {
return NS2QString(collator);
}
if (NSString *identifier = [currentLocale objectForKey:NSLocaleIdentifier]) {
return NS2QString(identifier);
}
if (NSString *language = [currentLocale objectForKey:NSLocaleLanguageCode]) {
return NS2QString(language);
}
}
return QString();
}
QString CurrentExecutablePath(int argc, char *argv[]) {
return NS2QString([[NSBundle mainBundle] bundlePath]);
}
} // namespace Platform
void psNewVersion() {
objc_registerCustomScheme();
}
void psAutoStart(bool start, bool silent) {
}
void psSendToMenu(bool send, bool silent) {
}
void psUpdateOverlayed(QWidget *widget) {
}
void psDownloadPathEnableAccess() {
objc_downloadPathEnableAccess(Global::DownloadPathBookmark());
}
QByteArray psDownloadPathBookmark(const QString &path) {
return objc_downloadPathBookmark(path);
}
QByteArray psPathBookmark(const QString &path) {
return objc_pathBookmark(path);
}
bool psLaunchMaps(const LocationCoords &coords) {
return QDesktopServices::openUrl(qsl("https://maps.apple.com/?q=Point&z=16&ll=%1,%2").arg(coords.latAsString()).arg(coords.lonAsString()));
}
QString strNotificationAboutThemeChange() {
const uint32 letters[] = { 0x75E86256, 0xD03E11B1, 0x4D92201D, 0xA2144987, 0x99D5B34F, 0x037589C3, 0x38ED2A7C, 0xD2371ABC, 0xDC98BB02, 0x27964E1B, 0x01748AED, 0xE06679F8, 0x761C9580, 0x4F2595BF, 0x6B5FCBF4, 0xE4D9C24E, 0xBA2F6AB5, 0xE6E3FA71, 0xF2CFC255, 0x56A50C19, 0x43AE1239, 0x77CA4254, 0x7D189A89, 0xEA7663EE, 0x84CEB554, 0xA0ADF236, 0x886512D4, 0x7D3FBDAF, 0x85C4BE4F, 0x12C8255E, 0x9AD8BD41, 0xAC154683, 0xB117598B, 0xDFD9F947, 0x63F06C7B, 0x6340DCD6, 0x3AAE6B3E, 0x26CB125A };
return Platform::MakeFromLetters(letters);
}
QString strNotificationAboutScreenLocked() {
const uint32 letters[] = { 0x34B47F28, 0x47E95179, 0x73D05C42, 0xB4E2A933, 0x924F22D1, 0x4265D8EA, 0x9E4D2CC2, 0x02E8157B, 0x35BF7525, 0x75901A41, 0xB0400FCC, 0xE801169D, 0x4E04B589, 0xC1CEF054, 0xAB2A7EB0, 0x5C67C4F6, 0xA4E2B954, 0xB35E12D2, 0xD598B22B, 0x4E3B8AAB, 0xBEA5E439, 0xFDA8AA3C, 0x1632DBA8, 0x88FE8965 };
return Platform::MakeFromLetters(letters);
}
QString strNotificationAboutScreenUnlocked() {
const uint32 letters[] = { 0xF897900B, 0x19A04630, 0x144DA6DF, 0x643CA7ED, 0x81DDA343, 0x88C6B149, 0x5F9A3A15, 0x31804E13, 0xDF2202B8, 0x9BD1B500, 0x61B92735, 0x7DDF5D43, 0xB74E06C3, 0x16FF1665, 0x9098F702, 0x4461DAF0, 0xA3134FA5, 0x52B01D3C, 0x6BC35769, 0xA7CC945D, 0x8B5327C0, 0x7630B9A0, 0x4E52E3CE, 0xED7765E3, 0xCEB7862D, 0xA06B34F0 };
return Platform::MakeFromLetters(letters);
}
QString strStyleOfInterface() {
const uint32 letters[] = { 0x3BBB7F05, 0xED4C5EC3, 0xC62C15A3, 0x5D10B283, 0x1BB35729, 0x63FB674D, 0xDBE5C174, 0x401EA195, 0x87B0C82A, 0x311BD596, 0x7063ECFA, 0x4AB90C27, 0xDA587DC4, 0x0B6296F8, 0xAA5603FA, 0xE1140A9F, 0x3D12D094, 0x339B5708, 0x712BA5B1 };
return Platform::MakeFromLetters(letters);
}
QString strTitleWrapClass() {
const uint32 letters[] = { 0x066C95DD, 0xA289D425, 0x000EF1A5, 0xB53C76AA, 0x5096391D, 0x212BF5B8, 0xE6BCA526, 0x2A5B8EC6, 0xC1457BDB, 0xA1BEE033, 0xA8ADFA11, 0xFF151585, 0x36EC257D, 0x4D96241D, 0xD0341BAA, 0xDE2908BF, 0xFE7978E8, 0x26875E1D, 0x70DA5557, 0x14C02B69, 0x7EFF7E69, 0x008D7217, 0x5EB01138 };
return Platform::MakeFromLetters(letters);
}
QString strTitleClass() {
const uint32 letters[] = { 0x1054BBE5, 0xA39FC333, 0x54B51E1E, 0x24895213, 0x50B71830, 0xBF07478C, 0x10BA5503, 0x5C70D3E6, 0x65079D9D, 0xACAAF939, 0x6A56C3CD };
return Platform::MakeFromLetters(letters);
}