diff --git a/.gitignore b/.gitignore index d1c2f1f14..9289e37d3 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,7 @@ ipch/ /Mac/ /Telegram/*.xcodeproj/xcuserdata/ /Telegram/*.xcodeproj/project.xcworkspace/ + +/Telegram/*.user.* +/Linux/ +/Telegram/Makefile diff --git a/Telegram/MetaEmoji.pro b/Telegram/MetaEmoji.pro index 77927fb41..8bc1723dd 100644 --- a/Telegram/MetaEmoji.pro +++ b/Telegram/MetaEmoji.pro @@ -2,14 +2,14 @@ QT += core CONFIG(debug, debug|release) { DEFINES += _DEBUG - OBJECTS_DIR = ./../Mac/DebugIntermediateEmoji + OBJECTS_DIR = ./../Linux/DebugIntermediateEmoji MOC_DIR = ./GeneratedFiles/Debug - DESTDIR = ./../Mac/DebugEmoji + DESTDIR = ./../Linux/DebugEmoji } CONFIG(release, debug|release) { - OBJECTS_DIR = ./../Mac/ReleaseIntermediateEmoji + OBJECTS_DIR = ./../Linux/ReleaseIntermediateEmoji MOC_DIR = ./GeneratedFiles/Release - DESTDIR = ./../Mac/ReleaseEmoji + DESTDIR = ./../Linux/ReleaseEmoji } macx { diff --git a/Telegram/MetaLang.pro b/Telegram/MetaLang.pro index cee803ce7..abe460d42 100644 --- a/Telegram/MetaLang.pro +++ b/Telegram/MetaLang.pro @@ -2,14 +2,14 @@ T += core CONFIG(debug, debug|release) { DEFINES += _DEBUG - OBJECTS_DIR = ./../Mac/DebugIntermediateLang + OBJECTS_DIR = ./../Linux/DebugIntermediateLang MOC_DIR = ./GeneratedFiles/Debug - DESTDIR = ./../Mac/DebugLang + DESTDIR = ./../Linux/DebugLang } CONFIG(release, debug|release) { - OBJECTS_DIR = ./../Mac/ReleaseIntermediateLang + OBJECTS_DIR = ./../Linux/ReleaseIntermediateLang MOC_DIR = ./GeneratedFiles/Release - DESTDIR = ./../Mac/ReleaseLang + DESTDIR = ./../Linux/ReleaseLang } macx { diff --git a/Telegram/MetaStyle.pro b/Telegram/MetaStyle.pro index f5ff0f7e5..f26242a0c 100644 --- a/Telegram/MetaStyle.pro +++ b/Telegram/MetaStyle.pro @@ -2,14 +2,14 @@ QT += core CONFIG(debug, debug|release) { DEFINES += _DEBUG - OBJECTS_DIR = ./../Mac/DebugIntermediateStyle + OBJECTS_DIR = ./../Linux/DebugIntermediateStyle MOC_DIR = ./GeneratedFiles/Debug - DESTDIR = ./../Mac/DebugStyle + DESTDIR = ./../Linux/DebugStyle } CONFIG(release, debug|release) { - OBJECTS_DIR = ./../Mac/ReleaseIntermediateStyle + OBJECTS_DIR = ./../Linux/ReleaseIntermediateStyle MOC_DIR = ./GeneratedFiles/Release - DESTDIR = ./../Mac/ReleaseStyle + DESTDIR = ./../Linux/ReleaseStyle } macx { diff --git a/Telegram/Packer.pro b/Telegram/Packer.pro index 4fb98d457..16082158f 100644 --- a/Telegram/Packer.pro +++ b/Telegram/Packer.pro @@ -2,14 +2,14 @@ QT += core CONFIG(debug, debug|release) { DEFINES += _DEBUG - OBJECTS_DIR = ./../Mac/DebugIntermediatePacker + OBJECTS_DIR = ./../Linux/DebugIntermediatePacker MOC_DIR = ./GeneratedFiles/Debug - DESTDIR = ./../Mac/DebugPacker + DESTDIR = ./../Linux/DebugPacker } CONFIG(release, debug|release) { - OBJECTS_DIR = ./../Mac/ReleaseIntermediatePacker + OBJECTS_DIR = ./../Linux/ReleaseIntermediatePacker MOC_DIR = ./GeneratedFiles/Release - DESTDIR = ./../Mac/ReleasePacker + DESTDIR = ./../Linux/ReleasePacker } macx { diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 7f0f2c03c..0a508deeb 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -321,7 +321,7 @@ namespace App { case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break; } - if (data->contact < 0 && !data->phone.isEmpty() && (data->id & 0xFFFFFFFF) != MTP::authedId()) { + if (data->contact < 0 && !data->phone.isEmpty() && int32(data->id & 0xFFFFFFFF) != MTP::authedId()) { data->contact = 0; } if (data->contact > 0 && !wasContact) { diff --git a/Telegram/SourceFiles/boxes/aboutbox.cpp b/Telegram/SourceFiles/boxes/aboutbox.cpp index 188e8bf15..462ce32b3 100644 --- a/Telegram/SourceFiles/boxes/aboutbox.cpp +++ b/Telegram/SourceFiles/boxes/aboutbox.cpp @@ -84,14 +84,14 @@ void AboutBox::paintEvent(QPaintEvent *e) { p.setPen(st::black->p); p.setFont(st::aboutHeaderFont->f); - p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2, st::aboutHeaderTop + st::aboutHeaderFont->ascent, qsl("Telegram")); + p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2, st::aboutHeaderTop + st::aboutHeaderFont->ascent, qsl("Telegram")); p.setFont(st::aboutSubheaderFont->f); - p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2 + _headerWidth, st::aboutHeaderTop + st::aboutSubheaderFont->ascent, qsl("Desktop")); + p.drawText((_width - (_headerWidth + _subheaderWidth)) / 2 + _headerWidth, st::aboutHeaderTop + st::aboutSubheaderFont->ascent, qsl("Desktop")); p.setFont(st::aboutVersionFont->f); p.setPen(st::aboutVersionColor->p); - p.drawText((_width - _versionWidth) / 2, st::aboutVersionTop + st::aboutVersionFont->ascent, _versionText); + p.drawText((_width - _versionWidth) / 2, st::aboutVersionTop + st::aboutVersionFont->ascent, _versionText); } } else { p.setOpacity(a_opacity.current()); diff --git a/Telegram/SourceFiles/gui/style_core.cpp b/Telegram/SourceFiles/gui/style_core.cpp index 561953587..1addc9ece 100644 --- a/Telegram/SourceFiles/gui/style_core.cpp +++ b/Telegram/SourceFiles/gui/style_core.cpp @@ -32,7 +32,7 @@ namespace style { modified[_flags] = Font(this); f.setPixelSize(size); - f.setBold(_flags & FontBold); + f.setBold(_flags & FontBold); f.setItalic(_flags & FontItalic); f.setUnderline(_flags & FontUnderline); f.setStyleStrategy(QFont::PreferQuality); diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index f7522142b..32f4e0a5e 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -1245,9 +1245,9 @@ public: break; } }/**/ - for (; _lineEnd > _lineStart + 1; --_lineEnd) { + for (; _lineEnd > _lineStart; --_lineEnd) { QChar ch = _t->_text.at(_lineEnd - 1); - if (ch != QChar::Space && ch != QChar::LineFeed) { + if ((ch != QChar::Space || _lineEnd == _lineStart + 1) && ch != QChar::LineFeed) { break; } }/**/ diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp index 8b6c30e41..2e9fbd345 100644 --- a/Telegram/SourceFiles/logs.cpp +++ b/Telegram/SourceFiles/logs.cpp @@ -16,6 +16,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014 John Preston, https://tdesktop.com */ #include "stdafx.h" +#include #include "pspecific.h" namespace { @@ -71,6 +72,8 @@ void debugLogWrite(const char *file, int32 line, const QString &v) { OutputDebugString(reinterpret_cast(msg.utf16())); #elif defined Q_OS_MAC objc_outputDebugString(msg); +#elif defined Q_OS_LINUX && defined _DEBUG + std::cout << msg.toUtf8().constData(); #endif } } diff --git a/Telegram/SourceFiles/mtproto/mtp.cpp b/Telegram/SourceFiles/mtproto/mtp.cpp index f3d5f0052..1ce00a08b 100644 --- a/Telegram/SourceFiles/mtproto/mtp.cpp +++ b/Telegram/SourceFiles/mtproto/mtp.cpp @@ -130,7 +130,7 @@ namespace { bool onErrorDefault(mtpRequestId requestId, const RPCError &error) { const QString &err(error.type()); - QRegularExpressionMatch m;; + QRegularExpressionMatch m; if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) { if (!requestId) return false; diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.cpp b/Telegram/SourceFiles/mtproto/mtpConnection.cpp index a114c0f57..a1cdd63d1 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.cpp +++ b/Telegram/SourceFiles/mtproto/mtpConnection.cpp @@ -2756,12 +2756,12 @@ void MTProtoConnectionPrivate::authKeyCreated() { void MTProtoConnectionPrivate::clearAuthKeyData() { if (authKeyData) { -#ifdef Q_OS_WIN - SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData)); +#ifdef Q_OS_WIN // TODO +// SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData)); #else - memset(authKeyData, 0, sizeof(AuthKeyCreateData)); +// memset(authKeyData, 0, sizeof(AuthKeyCreateData)); #endif - delete authKeyData; + delete authKeyData; authKeyData = 0; } } diff --git a/Telegram/SourceFiles/pspecific.h b/Telegram/SourceFiles/pspecific.h index a20715723..529960ede 100644 --- a/Telegram/SourceFiles/pspecific.h +++ b/Telegram/SourceFiles/pspecific.h @@ -26,7 +26,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com #endif #ifdef Q_OS_LINUX - +#include "pspecific_linux.h" #endif #ifdef Q_OS_WIN diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp new file mode 100644 index 000000000..0efa13350 --- /dev/null +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -0,0 +1,1332 @@ +/* +This file is part of Telegram Desktop, +an unofficial desktop 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 John Preston, https://tdesktop.com +*/ +#include "stdafx.h" +#include "pspecific.h" + +#include "lang.h" +#include "application.h" +#include "mainwidget.h" + +namespace { + bool frameless = true; + bool finished = true; + + class _PsEventFilter : public QAbstractNativeEventFilter { + public: + _PsEventFilter() { + } + + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) { + Window *wnd = Application::wnd(); + if (!wnd) return false; + + return false; + } + }; + _PsEventFilter *_psEventFilter = 0; + +}; + +PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent), +posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/iconround256.png")) { + connect(&psIdleTimer, SIGNAL(timeout()), this, SLOT(psIdleTimeout())); + psIdleTimer.setSingleShot(false); + connect(¬ifyWaitTimer, SIGNAL(timeout()), this, SLOT(psNotifyFire())); + notifyWaitTimer.setSingleShot(true); +} + +void PsMainWindow::psNotIdle() const { + psIdleTimer.stop(); + if (psIdle) { + psIdle = false; + if (App::main()) App::main()->setOnline(); + if (App::wnd()) App::wnd()->checkHistoryActivation(); + } +} + +void PsMainWindow::psIdleTimeout() { + int64 idleTime = 0;//objc_idleTime(); + if (idleTime >= 0) { + if (idleTime <= IdleMsecs) { + psNotIdle(); + } + } else { // error + psNotIdle(); + } +} + +bool PsMainWindow::psIsOnline(int state) const { + if (state < 0) state = this->windowState(); + if (state & Qt::WindowMinimized) { + return false; + } else if (!isVisible()) { + return false; + } + int64 idleTime = 0;//objc_idleTime(); + LOG(("App Info: idle time %1").arg(idleTime)); + if (idleTime >= 0) { + if (idleTime > IdleMsecs) { + if (!psIdle) { + psIdle = true; + psIdleTimer.start(900); + } + return false; + } else { + psNotIdle(); + } + } else { // error + psNotIdle(); + } + return true; +} + +bool PsMainWindow::psIsActive(int state) const { + if (state < 0) state = this->windowState(); + return isActiveWindow() && isVisible() && !(state & Qt::WindowMinimized) && !psIdle; +} + +void PsMainWindow::psRefreshTaskbarIcon() { +} + +void PsMainWindow::psUpdateWorkmode() { +} + +void PsMainWindow::psUpdateCounter() { + int32 counter = App::histories().unreadFull; + + setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); + + QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0')); + //_private.setWindowBadge(counter ? cnt : QString()); +} + +void PsMainWindow::psInitSize() { + setMinimumWidth(st::wndMinWidth); + setMinimumHeight(st::wndMinHeight); + + TWindowPos pos(cWindowPos()); + QRect avail(QDesktopWidget().availableGeometry()); + bool maximized = false; + QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); + if (pos.w && pos.h) { + QList screens = App::app()->screens(); + for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { + QByteArray name = (*i)->name().toUtf8(); + if (pos.moncrc == hashCrc32(name.constData(), name.size())) { + QRect screen((*i)->geometry()); + int32 w = screen.width(), h = screen.height(); + if (w >= st::wndMinWidth && h >= st::wndMinHeight) { + if (pos.w > w) pos.w = w; + if (pos.h > h) pos.h = h; + pos.x += screen.x(); + pos.y += screen.y(); + if (pos.x < screen.x() + screen.width() - 10 && pos.y < screen.y() + screen.height() - 10) { + geom = QRect(pos.x, pos.y, pos.w, pos.h); + } + } + break; + } + } + + if (pos.y < 0) pos.y = 0; + maximized = pos.maximized; + } + setGeometry(geom); +} + +void PsMainWindow::psInitFrameless() { + psUpdatedPositionTimer.setSingleShot(true); + connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); + + if (frameless) { + //setWindowFlags(Qt::FramelessWindowHint); + } + + connect(windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(psStateChanged(Qt::WindowState))); +} + +void PsMainWindow::psSavePosition(Qt::WindowState state) { + if (state == Qt::WindowActive) state = windowHandle()->windowState(); + if (state == Qt::WindowMinimized || !posInited) return; + + TWindowPos pos(cWindowPos()), curPos = pos; + + if (state == Qt::WindowMaximized) { + curPos.maximized = 1; + } else { + QRect r(geometry()); + curPos.x = r.x(); + curPos.y = r.y(); + curPos.w = r.width(); + curPos.h = r.height(); + curPos.maximized = 0; + } + + int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0; + QScreen *chosen = 0; + QList screens = App::app()->screens(); + for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { + int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx; + int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy; + if (!chosen || dx + dy < d) { + d = dx + dy; + chosen = *i; + } + } + if (chosen) { + curPos.x -= chosen->geometry().x(); + curPos.y -= chosen->geometry().y(); + QByteArray name = chosen->name().toUtf8(); + curPos.moncrc = hashCrc32(name.constData(), name.size()); + } + + if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { + if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { + cSetWindowPos(curPos); + App::writeConfig(); + } + } +} + +void PsMainWindow::psUpdatedPosition() { + psUpdatedPositionTimer.start(4000); +} + +void PsMainWindow::psStateChanged(Qt::WindowState state) { + psUpdateSysMenu(state); + psUpdateMargins(); +// if (state == Qt::WindowMinimized && GetWindowLong(ps_hWnd, GWL_HWNDPARENT)) { +// minimizeToTray(); +// } + psSavePosition(state); +} + +void PsMainWindow::psFirstShow() { + finished = false; + + psUpdateMargins(); + + bool showShadows = true; + + show(); + //_private.enableShadow(winId()); + if (cWindowPos().maximized) { + setWindowState(Qt::WindowMaximized); + } + + if (cFromAutoStart()) { + if (cStartMinimized()) { + setWindowState(Qt::WindowMinimized); + if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + hide(); + } else { + show(); + } + showShadows = false; + } else { + show(); + } + } else { + show(); + } + posInited = true; +} + +bool PsMainWindow::psHandleTitle() { + return false; +} + +void PsMainWindow::psInitSysMenu() { +} + +void PsMainWindow::psUpdateSysMenu(Qt::WindowState state) { +} + +void PsMainWindow::psUpdateMargins() { +} + +void PsMainWindow::psFlash() { + //_private.startBounce(); +} + +PsMainWindow::~PsMainWindow() { + finished = true; + psClearNotifyFast(); +} + +void PsMainWindow::psNotify(History *history, MsgId msgId) { + if (App::quiting() || !history->notifyFrom) return; + + bool haveSetting = (history->peer->notify != UnknownNotifySettings); + if (haveSetting) { + if (history->peer->notify != EmptyNotifySettings && history->peer->notify->mute > unixtime()) { + history->clearNotifyFrom(); + return; + } + } else { + App::wnd()->getNotifySetting(MTP_inputNotifyPeer(history->peer->input)); + } + + uint64 ms = getms() + NotifyWaitTimeout; + notifyWhenAlerts[history].insert(ms); + if (cDesktopNotify()) { + NotifyWhenMaps::iterator i = notifyWhenMaps.find(history); + if (i == notifyWhenMaps.end()) { + i = notifyWhenMaps.insert(history, NotifyWhenMap()); + } + if (i.value().constFind(msgId) == i.value().cend()) { + i.value().insert(msgId, ms); + } + NotifyWaiters *addTo = haveSetting ? ¬ifyWaiters : ¬ifySettingWaiters; + if (addTo->constFind(history) == addTo->cend()) { + addTo->insert(history, NotifyWaiter(msgId, ms)); + } + } + if (haveSetting) { + if (!notifyWaitTimer.isActive()) { + notifyWaitTimer.start(NotifyWaitTimeout); + } + } +} + +void PsMainWindow::psNotifyFire() { + psShowNextNotify(); +} + +void PsMainWindow::psNotifySettingGot() { + int32 t = unixtime(); + for (NotifyWaiters::iterator i = notifySettingWaiters.begin(); i != notifySettingWaiters.end();) { + History *history = i.key(); + if (history->peer->notify == UnknownNotifySettings) { + ++i; + } else { + if (history->peer->notify == EmptyNotifySettings || history->peer->notify->mute <= t) { + notifyWaiters.insert(i.key(), i.value()); + } + i = notifySettingWaiters.erase(i); + } + } + notifyWaitTimer.stop(); + psShowNextNotify(); +} + +void PsMainWindow::psClearNotify(History *history) { + if (!history) { + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + (*i)->unlinkHistory(); + } + //_private.clearNotifies(); + for (NotifyWhenMaps::const_iterator i = notifyWhenMaps.cbegin(), e = notifyWhenMaps.cend(); i != e; ++i) { + i.key()->clearNotifyFrom(); + } + notifyWaiters.clear(); + notifySettingWaiters.clear(); + notifyWhenMaps.clear(); + return; + } + notifyWaiters.remove(history); + notifySettingWaiters.remove(history); + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + (*i)->unlinkHistory(history); + } + //_private.clearNotifies(history->peer->id); + notifyWhenMaps.remove(history); + notifyWhenAlerts.remove(history); +} + +void PsMainWindow::psClearNotifyFast() { + notifyWaiters.clear(); + notifySettingWaiters.clear(); + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + (*i)->deleteLater(); + } + //_private.clearNotifies(); + notifyWindows.clear(); + notifyWhenMaps.clear(); + notifyWhenAlerts.clear(); +} + +void PsMainWindow::psActivateNotifies() { + if (cCustomNotifies()) { + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + //_private.activateWnd((*i)->winId()); + } + } +} + +namespace { + QRect _monitorRect; + uint64 _monitorLastGot = 0; + QRect _desktopRect() { + uint64 tnow = getms(); + if (tnow > _monitorLastGot + 1000 || tnow < _monitorLastGot) { + _monitorLastGot = tnow; + _monitorRect = QApplication::desktop()->availableGeometry(App::wnd()); + } + return _monitorRect; + } +} + +void PsMainWindow::psShowNextNotify(PsNotifyWindow *remove) { + if (App::quiting()) return; + + int32 count = NotifyWindows; + if (remove) { + for (PsNotifyWindows::iterator i = notifyWindows.begin(), e = notifyWindows.end(); i != e; ++i) { + if ((*i) == remove) { + notifyWindows.erase(i); + break; + } + } + } + + uint64 ms = getms(), nextAlert = 0; + bool alert = false; + for (NotifyWhenAlerts::iterator i = notifyWhenAlerts.begin(); i != notifyWhenAlerts.end();) { + while (!i.value().isEmpty() && *i.value().begin() <= ms) { + i.value().erase(i.value().begin()); + NotifySettingsPtr n = i.key()->peer->notify; + if (n == EmptyNotifySettings || (n != UnknownNotifySettings && n->mute <= unixtime())) { + alert = true; + } + } + if (i.value().isEmpty()) { + i = notifyWhenAlerts.erase(i); + } else { + if (!nextAlert || nextAlert > *i.value().begin()) { + nextAlert = *i.value().begin(); + } + ++i; + } + } + if (alert) { + psFlash(); + App::playSound(); + } + + if (cCustomNotifies()) { + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + int32 ind = (*i)->index(); + if (ind < 0) continue; + --count; + } + } + if (count <= 0 || !cDesktopNotify()) { + if (nextAlert) { + notifyWaitTimer.start(nextAlert - ms); + } + return; + } + + QRect r = _desktopRect(); + int32 x = r.x() + r.width() - st::notifyWidth - st::notifyDeltaX, y = r.y() + r.height() - st::notifyHeight - st::notifyDeltaY; + while (count > 0) { + uint64 next = 0; + HistoryItem *notifyItem = 0; + NotifyWaiters::iterator notifyWaiter; + for (NotifyWaiters::iterator i = notifyWaiters.begin(); i != notifyWaiters.end(); ++i) { + History *history = i.key(); + if (history->notifyFrom && history->notifyFrom->id != i.value().msg) { + NotifyWhenMaps::iterator j = notifyWhenMaps.find(history); + if (j == notifyWhenMaps.end()) { + history->clearNotifyFrom(); + i = notifyWaiters.erase(i); + continue; + } + do { + NotifyWhenMap::const_iterator k = j.value().constFind(history->notifyFrom->id); + if (k != j.value().cend()) { + i.value().msg = k.key(); + i.value().when = k.value(); + break; + } + history->getNextNotifyFrom(); + } while (history->notifyFrom); + } + if (!history->notifyFrom) { + notifyWhenMaps.remove(history); + i = notifyWaiters.erase(i); + continue; + } + uint64 when = i.value().when; + if (!notifyItem || next > when) { + next = when; + notifyItem = history->notifyFrom; + notifyWaiter = i; + } + } + if (notifyItem) { + if (next > ms) { + if (nextAlert && nextAlert < next) { + next = nextAlert; + nextAlert = 0; + } + notifyWaitTimer.start(next - ms); + break; + } else { + if (cCustomNotifies()) { + PsNotifyWindow *notify = new PsNotifyWindow(notifyItem, x, y); + notifyWindows.push_back(notify); + //notify->hide(); + //_private.holdOnTop(notify->winId()); + //notify->show(); + //_private.showOverAll(notify->winId()); + --count; + } else { + //_private.showNotify(notifyItem->history()->peer->id, notifyItem->history()->peer->name, notifyItem->notificationHeader(), notifyItem->notificationText()); + } + + uint64 ms = getms(); + History *history = notifyItem->history(); + history->getNextNotifyFrom(); + NotifyWhenMaps::iterator j = notifyWhenMaps.find(history); + if (j == notifyWhenMaps.end() || !history->notifyFrom) { + history->clearNotifyFrom(); + notifyWaiters.erase(notifyWaiter); + if (j != notifyWhenMaps.end()) notifyWhenMaps.erase(j); + continue; + } + j.value().remove(notifyItem->id); + do { + NotifyWhenMap::const_iterator k = j.value().constFind(history->notifyFrom->id); + if (k != j.value().cend()) { + notifyWaiter.value().msg = k.key(); + notifyWaiter.value().when = k.value(); + break; + } + history->getNextNotifyFrom(); + } while (history->notifyFrom); + if (!history->notifyFrom) { + notifyWaiters.erase(notifyWaiter); + notifyWhenMaps.erase(j); + continue; + } + } + } else { + break; + } + } + if (nextAlert) { + notifyWaitTimer.start(nextAlert - ms); + } + + count = NotifyWindows - count; + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + int32 ind = (*i)->index(); + if (ind < 0) continue; + --count; + (*i)->moveTo(x, y - count * (st::notifyHeight + st::notifyDeltaY)); + } +} + +void PsMainWindow::psStopHiding() { + if (cCustomNotifies()) { + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + (*i)->stopHiding(); + } + } +} + +void PsMainWindow::psStartHiding() { + if (cCustomNotifies()) { + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + (*i)->startHiding(); + } + } +} + +void PsMainWindow::psUpdateNotifies() { + if (cCustomNotifies()) { + for (PsNotifyWindows::const_iterator i = notifyWindows.cbegin(), e = notifyWindows.cend(); i != e; ++i) { + (*i)->updatePeerPhoto(); + } + } +} + +PsNotifyWindow::PsNotifyWindow(HistoryItem *item, int32 x, int32 y) : history(item->history()),// started(GetTickCount()), +close(this, st::notifyClose), alphaDuration(st::notifyFastAnim), posDuration(st::notifyFastAnim), hiding(false), _index(0), aOpacity(0), aOpacityFunc(st::notifyFastAnimFunc), aY(y + st::notifyHeight + st::notifyDeltaY) { + + int32 w = st::notifyWidth, h = st::notifyHeight; + QImage img(w * cIntRetinaFactor(), h * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + if (cRetina()) img.setDevicePixelRatio(cRetinaFactor()); + img.fill(st::notifyBG->c); + + { + QPainter p(&img); + p.setPen(st::notifyBorder->p); + p.setBrush(Qt::NoBrush); + p.drawRect(0, 0, w - 1, h - 1); + + if (history->peer->photo->loaded()) { + p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), history->peer->photo->pix(st::notifyPhotoSize)); + } else { + MTP::clearLoaderPriorities(); + peerPhoto = history->peer->photo; + peerPhoto->load(true, true); + } + + int32 itemWidth = w - st::notifyPhotoPos.x() - st::notifyPhotoSize - st::notifyTextLeft - st::notifyClosePos.x() - st::notifyClose.width; + + QRect rectForName(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyTextTop, itemWidth, st::msgNameFont->height); + if (history->peer->chat) { + p.drawPixmap(QPoint(rectForName.left() + st::dlgChatImgLeft, rectForName.top() + st::dlgChatImgTop), App::sprite(), st::dlgChatImg); + rectForName.setLeft(rectForName.left() + st::dlgChatImgSkip); + } + + QDateTime now(QDateTime::currentDateTime()), lastTime(item->date); + QDate nowDate(now.date()), lastDate(lastTime.date()); + QString dt = lastTime.toString(qsl("hh:mm")); + int32 dtWidth = st::dlgHistFont->m.width(dt); + rectForName.setWidth(rectForName.width() - dtWidth - st::dlgDateSkip); + p.setFont(st::dlgDateFont->f); + p.setPen(st::dlgDateColor->p); + p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::dlgHistFont->ascent, dt); + + const HistoryItem *textCachedFor = 0; + Text itemTextCache(itemWidth); + bool active = false; + item->drawInDialog(p, QRect(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height, itemWidth, 2 * st::dlgFont->height), active, textCachedFor, itemTextCache); + + p.setPen(st::dlgNameColor->p); + history->nameText.drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); + } + pm = QPixmap::fromImage(img); + + hideTimer.setSingleShot(true); + connect(&hideTimer, SIGNAL(timeout()), this, SLOT(hideByTimer())); + + inputTimer.setSingleShot(true); + connect(&inputTimer, SIGNAL(timeout()), this, SLOT(checkLastInput())); + + connect(&close, SIGNAL(clicked()), this, SLOT(unlinkHistory())); + close.setAcceptBoth(true); + close.move(w - st::notifyClose.width - st::notifyClosePos.x(), st::notifyClosePos.y()); + close.show(); + + aY.start(y); + setGeometry(x, aY.current(), st::notifyWidth, st::notifyHeight); + + aOpacity.start(1); + setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint); + setAttribute(Qt::WA_MacAlwaysShowToolWindow); + + show(); + + setWindowOpacity(aOpacity.current()); + + alphaDuration = posDuration = st::notifyFastAnim; + anim::start(this); + + checkLastInput(); +} + +void PsNotifyWindow::checkLastInput() { + // TODO + if (true) { + hideTimer.start(st::notifyWaitLongHide); + } else { + inputTimer.start(300); + } +} + +void PsNotifyWindow::moveTo(int32 x, int32 y, int32 index) { + if (index >= 0) { + _index = index; + } + move(x, aY.current()); + aY.start(y); + aOpacity.restart(); + posDuration = st::notifyFastAnim; + anim::start(this); +} + +void PsNotifyWindow::updatePeerPhoto() { + if (!peerPhoto->isNull() && peerPhoto->loaded()) { + QImage img(pm.toImage()); + { + QPainter p(&img); + p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), peerPhoto->pix(st::notifyPhotoSize)); + } + peerPhoto = ImagePtr(); + pm = QPixmap::fromImage(img); + update(); + } +} + +void PsNotifyWindow::unlinkHistory(History *hist) { + if (!hist || hist == history) { + animHide(st::notifyFastAnim, st::notifyFastAnimFunc); + history = 0; + App::wnd()->psShowNextNotify(); + } +} + +void PsNotifyWindow::enterEvent(QEvent */*e*/) { + if (!history) return; + if (App::wnd()) App::wnd()->psStopHiding(); +} + +void PsNotifyWindow::leaveEvent(QEvent */*e*/) { + if (!history) return; + App::wnd()->psStartHiding(); +} + +void PsNotifyWindow::startHiding() { + hideTimer.start(st::notifyWaitShortHide); +} + +void PsNotifyWindow::mousePressEvent(QMouseEvent *e) { + if (!history) return; + if (e->button() == Qt::RightButton) { + unlinkHistory(); + } else if (history) { + App::wnd()->showFromTray(); + App::wnd()->hideSettings(); + App::main()->showPeer(history->peer->id, false, true); + unlinkHistory(); + e->ignore(); + } +} + +void PsNotifyWindow::paintEvent(QPaintEvent *e) { + QPainter p(this); + p.drawPixmap(0, 0, pm); +} + +void PsNotifyWindow::animHide(float64 duration, anim::transition func) { + if (!history) return; + alphaDuration = duration; + aOpacityFunc = func; + aOpacity.start(0); + aY.restart(); + hiding = true; + anim::start(this); +} + +void PsNotifyWindow::stopHiding() { + if (!history) return; + alphaDuration = st::notifyFastAnim; + aOpacityFunc = st::notifyFastAnimFunc; + aOpacity.start(1); + aY.restart(); + hiding = false; + hideTimer.stop(); + anim::start(this); +} + +void PsNotifyWindow::hideByTimer() { + if (!history) return; + animHide(st::notifySlowHide, st::notifySlowHideFunc); +} + +bool PsNotifyWindow::animStep(float64 ms) { + float64 dtAlpha = ms / alphaDuration, dtPos = ms / posDuration; + if (dtAlpha >= 1) { + aOpacity.finish(); + if (hiding) { + deleteLater(); + } + } else { + aOpacity.update(dtAlpha, aOpacityFunc); + } + setWindowOpacity(aOpacity.current()); + if (dtPos >= 1) { + aY.finish(); + } else { + aY.update(dtPos, anim::linear); + } + move(x(), aY.current()); + update(); + return (dtAlpha < 1 || (!hiding && dtPos < 1)); +} + +PsNotifyWindow::~PsNotifyWindow() { + if (App::wnd()) App::wnd()->psShowNextNotify(this); +} + +PsApplication::PsApplication(int &argc, char **argv) : QApplication(argc, argv) { +} + +void PsApplication::psInstallEventFilter() { + delete _psEventFilter; + _psEventFilter = new _PsEventFilter(); + installNativeEventFilter(_psEventFilter); +} + +PsApplication::~PsApplication() { + delete _psEventFilter; + _psEventFilter = 0; +} + +PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : reply(0), already(0), full(0) { + updateUrl = qs(update.vurl); + moveToThread(thread); + manager.moveToThread(thread); + App::setProxySettings(manager); + + connect(thread, SIGNAL(started()), this, SLOT(start())); + initOutput(); +} + +PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const QString &url) : reply(0), already(0), full(0) { + updateUrl = url; + moveToThread(thread); + manager.moveToThread(thread); + App::setProxySettings(manager); + + connect(thread, SIGNAL(started()), this, SLOT(start())); + initOutput(); +} + +void PsUpdateDownloader::initOutput() { + QString fileName; + QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl); + if (m.hasMatch()) { + fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString()); + } + if (fileName.isEmpty()) { + fileName = qsl("tupdate-%1").arg(rand()); + } + QString dirStr = cWorkingDir() + qsl("tupdates/"); + fileName = dirStr + fileName; + QFileInfo file(fileName); + + QDir dir(dirStr); + if (dir.exists()) { + QFileInfoList all = dir.entryInfoList(QDir::Files); + for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) { + if (i->absoluteFilePath() != file.absoluteFilePath()) { + QFile::remove(i->absoluteFilePath()); + } + } + } else { + dir.mkdir(dir.absolutePath()); + } + outputFile.setFileName(fileName); + if (file.exists()) { + uint64 fullSize = file.size(); + if (fullSize < INT_MAX) { + int32 goodSize = (int32)fullSize; + if (goodSize % UpdateChunk) { + goodSize = goodSize - (goodSize % UpdateChunk); + if (goodSize) { + if (outputFile.open(QIODevice::ReadOnly)) { + QByteArray goodData = outputFile.readAll().mid(0, goodSize); + outputFile.close(); + if (outputFile.open(QIODevice::WriteOnly)) { + outputFile.write(goodData); + outputFile.close(); + + QMutexLocker lock(&mutex); + already = goodSize; + } + } + } + } else { + QMutexLocker lock(&mutex); + already = goodSize; + } + } + if (!already) { + QFile::remove(fileName); + } + } +} + +void PsUpdateDownloader::start() { + sendRequest(); +} + +void PsUpdateDownloader::sendRequest() { + QNetworkRequest req(updateUrl); + QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1); + req.setRawHeader("Range", rangeHeaderValue); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + if (reply) reply->deleteLater(); + reply = manager.get(req); + connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64))); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot())); +} + +void PsUpdateDownloader::partMetaGot() { + typedef QList Pairs; + Pairs pairs = reply->rawHeaderPairs(); + for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) { + if (QString::fromUtf8(i->first).toLower() == "content-range") { + QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second)); + if (m.hasMatch()) { + { + QMutexLocker lock(&mutex); + full = m.captured(1).toInt(); + } + emit App::app()->updateDownloading(already, full); + } + } + } +} + +int32 PsUpdateDownloader::ready() { + QMutexLocker lock(&mutex); + return already; +} + +int32 PsUpdateDownloader::size() { + QMutexLocker lock(&mutex); + return full; +} + +void PsUpdateDownloader::partFinished(qint64 got, qint64 total) { + if (!reply) return; + + QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + if (statusCode.isValid()) { + int status = statusCode.toInt(); + if (status != 200 && status != 206 && status != 416) { + LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status)); + return fatalFail(); + } + } + + if (!already && !full) { + QMutexLocker lock(&mutex); + full = total; + } + DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total)); + + if (!outputFile.isOpen()) { + if (!outputFile.open(QIODevice::Append)) { + LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName())); + return fatalFail(); + } + } + QByteArray r = reply->readAll(); + if (!r.isEmpty()) { + outputFile.write(r); + + QMutexLocker lock(&mutex); + already += r.size(); + } + if (got >= total) { + reply->deleteLater(); + reply = 0; + outputFile.close(); + unpackUpdate(); + } else { + emit App::app()->updateDownloading(already, full); + } +} + +void PsUpdateDownloader::partFailed(QNetworkReply::NetworkError e) { + if (!reply) return; + + QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + reply->deleteLater(); + reply = 0; + if (statusCode.isValid()) { + int status = statusCode.toInt(); + if (status == 416) { // Requested range not satisfiable + outputFile.close(); + unpackUpdate(); + return; + } + } + LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e)); + emit App::app()->updateFailed(); +} + +void PsUpdateDownloader::deleteDir(const QString &dir) { +// objc_deleteDir(dir); +} + +void PsUpdateDownloader::fatalFail() { + clearAll(); + emit App::app()->updateFailed(); +} + +void PsUpdateDownloader::clearAll() { + deleteDir(cWorkingDir() + qsl("tupdates")); +} + +#ifdef Q_OS_WIN +typedef DWORD VerInt; +typedef WCHAR VerChar; +#else +typedef int VerInt; +typedef wchar_t VerChar; +#endif + +void PsUpdateDownloader::unpackUpdate() { + QByteArray packed; + if (!outputFile.open(QIODevice::ReadOnly)) { + LOG(("Update Error: cant read updates file!")); + return fatalFail(); + } +#ifdef Q_OS_WIN // use Lzma SDK for win + const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header +#else + const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header +#endif + QByteArray compressed = outputFile.readAll(); + int32 compressedLen = compressed.size() - hSize; + if (compressedLen <= 0) { + LOG(("Update Error: bad compressed size: %1").arg(compressed.size())); + return fatalFail(); + } + outputFile.close(); + + QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyDirPath = cWorkingDir() + qsl("tupdates/ready"); + deleteDir(tempDirPath); + deleteDir(readyDirPath); + + QDir tempDir(tempDirPath), readyDir(readyDirPath); + if (tempDir.exists() || readyDir.exists()) { + LOG(("Update Error: cant clear tupdates/temp or tupdates/ready dir!")); + return fatalFail(); + } + + uchar sha1Buffer[20]; + bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen); + if (!goodSha1) { + LOG(("Update Error: bad SHA1 hash of update file!")); + return fatalFail(); + } + + RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast(UpdatesPublicKey), -1), 0, 0, 0); + if (!pbKey) { + LOG(("Update Error: cant read public rsa key!")); + return fatalFail(); + } + if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature + RSA_free(pbKey); + LOG(("Update Error: bad RSA signature of update file!")); + return fatalFail(); + } + RSA_free(pbKey); + + QByteArray uncompressed; + + int32 uncompressedLen; + memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen); + uncompressed.resize(uncompressedLen); + + size_t resultLen = uncompressed.size(); +#ifdef Q_OS_WIN // use Lzma SDK for win + SizeT srcLen = compressedLen; + int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE); + if (uncompressRes != SZ_OK) { + LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes)); + return fatalFail(); + } +#else + lzma_stream stream = LZMA_STREAM_INIT; + + lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED); + if (ret != LZMA_OK) { + const char *msg; + switch (ret) { + case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break; + case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break; + case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break; + default: msg = "Unknown error, possibly a bug"; break; + } + LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret)); + return fatalFail(); + } + + stream.avail_in = compressedLen; + stream.next_in = (uint8_t*)(compressed.constData() + hSize); + stream.avail_out = resultLen; + stream.next_out = (uint8_t*)uncompressed.data(); + + lzma_ret res = lzma_code(&stream, LZMA_FINISH); + if (stream.avail_in) { + LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen)); + return fatalFail(); + } else if (stream.avail_out) { + LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen)); + return fatalFail(); + } + lzma_end(&stream); + if (res != LZMA_OK && res != LZMA_STREAM_END) { + const char *msg; + switch (res) { + case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break; + case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break; + case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break; + case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break; + case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break; + default: msg = "Unknown error, possibly a bug"; break; + } + LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res)); + return fatalFail(); + } +#endif + + tempDir.mkdir(tempDir.absolutePath()); + + quint32 version; + { + QBuffer buffer(&uncompressed); + buffer.open(QIODevice::ReadOnly); + QDataStream stream(&buffer); + stream.setVersion(QDataStream::Qt_5_1); + + stream >> version; + if (stream.status() != QDataStream::Ok) { + LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status())); + return fatalFail(); + } + if (version <= AppVersion) { + LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion)); + return fatalFail(); + } + + quint32 filesCount; + stream >> filesCount; + if (stream.status() != QDataStream::Ok) { + LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status())); + return fatalFail(); + } + if (!filesCount) { + LOG(("Update Error: update is empty!")); + return fatalFail(); + } + for (uint32 i = 0; i < filesCount; ++i) { + QString relativeName; + quint32 fileSize; + QByteArray fileInnerData; + bool executable = false; + + stream >> relativeName >> fileSize >> fileInnerData; +#if defined Q_OS_MAC || defined Q_OS_LINUX + stream >> executable; +#endif + if (stream.status() != QDataStream::Ok) { + LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status())); + return fatalFail(); + } + if (fileSize != quint32(fileInnerData.size())) { + LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size())); + return fatalFail(); + } + + QFile f(tempDirPath + '/' + relativeName); + if (!QDir().mkpath(QFileInfo(f).absolutePath())) { + LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName)); + return fatalFail(); + } + if (!f.open(QIODevice::WriteOnly)) { + LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName)); + return fatalFail(); + } + if (f.write(fileInnerData) != fileSize) { + f.close(); + LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName)); + return fatalFail(); + } + f.close(); + if (executable) { + QFileDevice::Permissions p = f.permissions(); + p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther; + f.setPermissions(p); + } + } + + // create tdata/version file + tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath()); + std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString(); + + VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar)); + VerChar versionStr[32]; + memcpy(versionStr, versionString.c_str(), versionLen); + + QFile fVersion(tempDirPath + qsl("/tdata/version")); + if (!fVersion.open(QIODevice::WriteOnly)) { + LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version"))); + return fatalFail(); + } + fVersion.write((const char*)&versionNum, sizeof(VerInt)); + fVersion.write((const char*)&versionLen, sizeof(VerInt)); + fVersion.write((const char*)&versionStr[0], versionLen); + fVersion.close(); + } + + if (!tempDir.rename(tempDir.absolutePath(), readyDir.absolutePath())) { + LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'").arg(tempDir.absolutePath()).arg(readyDir.absolutePath())); + return fatalFail(); + } + deleteDir(tempDirPath); + outputFile.remove(); + + emit App::app()->updateReady(); +} + +PsUpdateDownloader::~PsUpdateDownloader() { + delete reply; + reply = 0; +} + +void psActivateProcess(uint64 pid) { +// objc_activateProgram(); +} + +QString psCurrentCountry() { + QString country;// = objc_currentCountry(); + return country.isEmpty() ? QString::fromLatin1(DefaultCountry) : country; +} + +QString psCurrentLanguage() { + QString lng;// = objc_currentLang(); + return lng.isEmpty() ? QString::fromLatin1(DefaultLanguage) : lng; +} + +QString psAppDataPath() { + return QString();//objc_appDataPath(); +} + +QString psCurrentExeDirectory(int argc, char *argv[]) { + QString first = argc ? QString::fromLocal8Bit(argv[0]) : QString(); + if (!first.isEmpty()) { + QFileInfo info(first); + if (info.exists()) { + QDir result(info.absolutePath() + qsl("/../../..")); + return result.absolutePath() + '/'; + } + } + return QString(); +} + +void psDoCleanup() { + try { + psAutoStart(false, true); + } catch (...) { + } +} + +int psCleanup() { + psDoCleanup(); + return 0; +} + +void psDoFixPrevious() { +} + +int psFixPrevious() { + psDoFixPrevious(); + return 0; +} + +bool psCheckReadyUpdate() { + QString readyPath = cWorkingDir() + qsl("tupdates/ready"); + if (!QDir(readyPath).exists()) { + return false; + } + + // check ready version + QString versionPath = readyPath + qsl("/tdata/version"); + { + QFile fVersion(versionPath); + if (!fVersion.open(QIODevice::ReadOnly)) { + LOG(("Update Error: cant read version file '%1'").arg(versionPath)); + PsUpdateDownloader::clearAll(); + return false; + } + VerInt versionNum; + if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) { + LOG(("Update Error: cant read version from file '%1'").arg(versionPath)); + PsUpdateDownloader::clearAll(); + return false; + } + fVersion.close(); + if (versionNum <= AppVersion) { + LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion)); + PsUpdateDownloader::clearAll(); + return false; + } + } + +#ifdef Q_OS_WIN + QString curUpdater = (cExeDir() + "Updater.exe"); + QFileInfo updater(cWorkingDir() + "tupdates/ready/Updater.exe"); +#elif defined Q_OS_MAC + QString curUpdater = (cExeDir() + "Telegram.app/Contents/Frameworks/Updater"); + QFileInfo updater(cWorkingDir() + "tupdates/ready/Telegram.app/Contents/Frameworks/Updater"); +#elif defined Q_OS_LINUX + QString curUpdater; + QFileInfo updater; +#endif + if (!updater.exists()) { + QFileInfo current(curUpdater); + if (!current.exists()) { + PsUpdateDownloader::clearAll(); + return false; + } + if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) { + PsUpdateDownloader::clearAll(); + return false; + } + } +#ifdef Q_OS_WIN + if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) { + PsUpdateDownloader::clearAll(); + return false; + } + if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) { + PsUpdateDownloader::clearAll(); + return false; + } +#elif defined Q_OS_MAC + QFileInfo to(curUpdater); + QDir().mkpath(to.absolutePath()); + if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) { + PsUpdateDownloader::clearAll(); + return false; + } +#endif + return true; +} + +void psPostprocessFile(const QString &name) { +} + +void psOpenFile(const QString &name, bool openWith) { + //objc_openFile(name, openWith); +} + +void psShowInFolder(const QString &name) { + //objc_showInFinder(name, QFileInfo(name).absolutePath()); +} + +void psFinish() { + //objc_finish(); +} + +void psExecUpdater() { + if (true /*!objc_execUpdater()*/) { + QString readyPath = cWorkingDir() + qsl("tupdates/ready"); + PsUpdateDownloader::deleteDir(readyPath); + } +} + +void psExecTelegram() { + //objc_execTelegram(); +} + +void psAutoStart(bool start, bool silent) { +} diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h new file mode 100644 index 000000000..e23826df3 --- /dev/null +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -0,0 +1,257 @@ +/* +This file is part of Telegram Desktop, +an unofficial desktop 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 John Preston, https://tdesktop.com +*/ +#pragma once + +inline QString psServerPrefix() { + return qsl("/tmp/"); +} +inline void psCheckLocalSocket(const QString &serverName) { + QFile address(serverName); + if (address.exists()) { + address.remove(); + } +} + + +class PsNotifyWindow : public QWidget, public Animated { + Q_OBJECT + +public: + + PsNotifyWindow(HistoryItem *item, int32 x, int32 y); + + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void mousePressEvent(QMouseEvent *e); + void paintEvent(QPaintEvent *e); + + bool animStep(float64 ms); + void animHide(float64 duration, anim::transition func); + void startHiding(); + void stopHiding(); + void moveTo(int32 x, int32 y, int32 index = -1); + + void updatePeerPhoto(); + + int32 index() const { + return history ? _index : -1; + } + + ~PsNotifyWindow(); + + public slots: + + void hideByTimer(); + void checkLastInput(); + + void unlinkHistory(History *hist = 0); + +private: + +// DWORD started; + + History *history; + IconedButton close; + QPixmap pm; + float64 alphaDuration, posDuration; + QTimer hideTimer, inputTimer; + bool hiding; + int32 _index; + anim::fvalue aOpacity; + anim::transition aOpacityFunc; + anim::ivalue aY; + ImagePtr peerPhoto; + +}; + +typedef QList PsNotifyWindows; + +class PsMainWindow : public QMainWindow { + Q_OBJECT + +public: + PsMainWindow(QWidget *parent = 0); + + int32 psResizeRowWidth() const { + return 0;//st::wndResizeAreaWidth; + } + + void psInitFrameless(); + void psInitSize(); + + void psFirstShow(); + void psInitSysMenu(); + void psUpdateSysMenu(Qt::WindowState state); + void psUpdateMargins(); + void psUpdatedPosition(); + + bool psHandleTitle(); + + void psFlash(); + void psNotifySettingGot(); + + bool psIsActive(int state = -1) const; + bool psIsOnline(int state) const; + + void psUpdateWorkmode(); + + void psRefreshTaskbarIcon(); + virtual bool minimizeToTray() { + return false; + } + + void psNotify(History *history, MsgId msgId); + void psClearNotify(History *history = 0); + void psClearNotifyFast(); + void psShowNextNotify(PsNotifyWindow *remove = 0); + void psActivateNotifies(); + void psStopHiding(); + void psStartHiding(); + void psUpdateNotifies(); + + bool psPosInited() const { + return posInited; + } + + ~PsMainWindow(); + +public slots: + + void psStateChanged(Qt::WindowState state); + void psUpdateCounter(); + void psSavePosition(Qt::WindowState state = Qt::WindowActive); + void psIdleTimeout(); + void psNotifyFire(); + +protected: + + void psNotIdle() const; + + bool posInited; + QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; + QImage icon256; + virtual void setupTrayIcon() { + } + + typedef QMap NotifyWhenMap; + typedef QMap NotifyWhenMaps; + NotifyWhenMaps notifyWhenMaps; + struct NotifyWaiter { + NotifyWaiter(MsgId msg, uint64 when) : msg(msg), when(when) { + } + MsgId msg; + uint64 when; + }; + typedef QMap NotifyWaiters; + NotifyWaiters notifyWaiters; + NotifyWaiters notifySettingWaiters; + QTimer notifyWaitTimer; + + typedef QSet NotifyWhenAlert; + typedef QMap NotifyWhenAlerts; + NotifyWhenAlerts notifyWhenAlerts; + + PsNotifyWindows notifyWindows; + + QTimer psUpdatedPositionTimer; + +private: + mutable bool psIdle; + mutable QTimer psIdleTimer; +}; + + +class PsApplication : public QApplication { + Q_OBJECT + +public: + + PsApplication(int &argc, char **argv); + void psInstallEventFilter(); + ~PsApplication(); + +signals: + + void updateChecking(); + void updateLatest(); + void updateDownloading(qint64 ready, qint64 total); + void updateReady(); + void updateFailed(); + +}; + +class PsUpdateDownloader : public QObject { + Q_OBJECT + +public: + PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update); + PsUpdateDownloader(QThread *thread, const QString &url); + + void unpackUpdate(); + + int32 ready(); + int32 size(); + + static void deleteDir(const QString &dir); + static void clearAll(); + + ~PsUpdateDownloader(); + +public slots: + + void start(); + void partMetaGot(); + void partFinished(qint64 got, qint64 total); + void partFailed(QNetworkReply::NetworkError e); + void sendRequest(); + +private: + void initOutput(); + + void fatalFail(); + + QString updateUrl; + QNetworkAccessManager manager; + QNetworkReply *reply; + int32 already, full; + QFile outputFile; + + QMutex mutex; + +}; + +void psActivateProcess(uint64 pid); +QString psLocalServerPrefix(); +QString psCurrentCountry(); +QString psCurrentLanguage(); +QString psAppDataPath(); +QString psCurrentExeDirectory(int argc, char *argv[]); +void psAutoStart(bool start, bool silent = false); + +int psCleanup(); +int psFixPrevious(); + +bool psCheckReadyUpdate(); +void psExecUpdater(); +void psExecTelegram(); + +void psPostprocessFile(const QString &name); +void psOpenFile(const QString &name, bool openWith = false); +void psShowInFolder(const QString &name); +void psFinish(); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 7fd4066fc..5600fac6a 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -68,8 +68,11 @@ QString gLangFile; bool gRetina = false; float64 gRetinaFactor = 1.; int32 gIntRetinaFactor = 1; +#ifdef Q_OS_MAC bool gCustomNotifies = false; - +#else +bool gCustomNotifies = true; +#endif uint64 gInstance = 0.; #ifdef Q_OS_WIN diff --git a/Telegram/SourceFiles/stdafx.cpp b/Telegram/SourceFiles/stdafx.cpp index d1091c7e1..89dd13ec3 100644 --- a/Telegram/SourceFiles/stdafx.cpp +++ b/Telegram/SourceFiles/stdafx.cpp @@ -23,3 +23,6 @@ Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) Q_IMPORT_PLUGIN(QWindowsAudioPlugin) Q_IMPORT_PLUGIN(AccessibleFactory) #endif +#ifdef Q_OS_LINUX +Q_IMPORT_PLUGIN(QPulseAudioPlugin) +#endif diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index 03c61d304..42bb4f8af 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -77,7 +77,7 @@ TitleWidget::TitleWidget(Window *window) connect(wnd->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(stateChanged(Qt::WindowState))); connect(App::app(), SIGNAL(updateReady()), this, SLOT(showUpdateBtn())); - if (cPlatform() == dbipMac) { + if (cPlatform() != dbipWindows) { _minimize.hide(); _maximize.hide(); _restore.hide(); @@ -127,7 +127,7 @@ TitleWidget::~TitleWidget() { void TitleWidget::resizeEvent(QResizeEvent *e) { QPoint p(width() - ((cPlatform() == dbipWindows && lastMaximized) ? 0 : st::sysBtnDelta), 0); - if (cPlatform() != dbipMac) { + if (cPlatform() == dbipWindows) { p.setX(p.x() - _close.width()); _close.move(p); diff --git a/Telegram/SourceFiles/types.cpp b/Telegram/SourceFiles/types.cpp index a08383dce..398c745f1 100644 --- a/Telegram/SourceFiles/types.cpp +++ b/Telegram/SourceFiles/types.cpp @@ -17,11 +17,10 @@ Copyright (c) 2014 John Preston, https://tdesktop.com */ #include "stdafx.h" -#ifdef Q_OS_MAC +#ifdef Q_OS_WIN +#elif defined Q_OS_MAC #include -#endif - -#ifdef Q_OS_LINUX +#else #include #endif @@ -175,8 +174,10 @@ namespace { _msStart = mach_absolute_time(); #else timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - _msStart = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec); + clock_gettime(CLOCK_MONOTONIC, &ts); + //_msFreq = 1 / 1000000.; + _msgIdCoef = float64(0xFFFF0000L) / 1000000000.; + _msStart = 1000 * uint64(ts.tv_sec) + (uint64(ts.tv_nsec) / 1000000); #endif srand((uint32)(_msStart & 0xFFFFFFFFL)); @@ -217,13 +218,13 @@ uint64 getms() { return (uint64)((msCount - _msStart) * _msFreq); #else timespec ts; - int res = clock_gettime(CLOCK_REALTIME, &ts); + int res = clock_gettime(CLOCK_MONOTONIC, &ts); if (res != 0) { LOG(("Bad clock_gettime result: %1").arg(res)); return 0; } - uint64 msCount = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec); - return (uint64)((msCount - _msStart) / 1000000); + uint64 msCount = 1000 * uint64(ts.tv_sec) + (uint64(ts.tv_nsec) / 1000000); + return (uint64)(msCount - _msStart); #endif } @@ -236,8 +237,10 @@ uint64 msgid() { uint64 msCount = mach_absolute_time(); uint64 result = _msgIdStart + (uint64)floor((msCount - _msgIdMsStart) * _msgIdCoef); #else - uint64 result = 0; - //TODO + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64 msCount = 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec); + uint64 result = _msgIdStart + (uint64)floor((msCount - _msgIdMsStart) * _msgIdCoef); #endif result &= ~0x03L; diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro index 1f9496cf2..ac47c805a 100644 --- a/Telegram/Telegram.pro +++ b/Telegram/Telegram.pro @@ -1,17 +1,19 @@ -QT += core gui widgets network multimedia +QT += core gui network multimedia widgets + +CONFIG += plugin static CONFIG(debug, debug|release) { DEFINES += _DEBUG - OBJECTS_DIR = ./../Mac/DebugIntermediate + OBJECTS_DIR = ./../Linux/DebugIntermediate MOC_DIR = ./GeneratedFiles/Debug RCC_DIR = ./GeneratedFiles - DESTDIR = ./../Mac/Debug + DESTDIR = ./../Linux/Debug } CONFIG(release, debug|release) { - OBJECTS_DIR = ./../Mac/ReleaseIntermediate + OBJECTS_DIR = ./../Linux/ReleaseIntermediate MOC_DIR = ./GeneratedFiles/Release RCC_DIR = ./GeneratedFiles - DESTDIR = ./../Mac/Release + DESTDIR = ./../Linux/Release } macx { @@ -21,6 +23,11 @@ macx { QMAKE_LFLAGS += -framework Cocoa } +linux { + SOURCES += ./SourceFiles/pspecific_linux.cpp + HEADERS += ./SourceFiles/pspecific_linux.h +} + SOURCES += \ ./SourceFiles/main.cpp \ ./SourceFiles/stdafx.cpp \ @@ -180,17 +187,19 @@ CONFIG += precompile_header PRECOMPILED_HEADER = ./SourceFiles/stdafx.h -INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.0/QtGui\ - ./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.0/QtCore\ +QMAKE_CXXFLAGS += -fno-strict-aliasing +QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter -Wno-unused-variable -Wno-switch -Wno-comment -Wno-unused-but-set-variable + +INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.3.1/QtGui\ + ./../../Libraries/QtStatic/qtbase/include/QtCore/5.3.1/QtCore\ ./../../Libraries/QtStatic/qtbase/include\ ./SourceFiles\ ./GeneratedFiles\ - ./../../Libraries/lzma/C\ - ./../../Libraries/libexif-0.6.20 - -LIBS += -lcrypto -lssl -lz + ./../../Libraries/libexif-0.6.20\ + /usr/local/ssl/include +LIBS += -L/usr/local/ssl/lib -lcrypto -lssl -lz -ldl -llzma LIBS += ./../../Libraries/libexif-0.6.20/libexif/.libs/libexif.a - +LIBS += ./../../Libraries/QtStatic/qtmultimedia/plugins/audio/libqtmedia_pulse.a RESOURCES += \ ./SourceFiles/telegram.qrc diff --git a/Telegram/_qt_5_3_1_patch/qtbase/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/Telegram/_qt_5_3_1_patch/qtbase/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp new file mode 100644 index 000000000..94c4c1d03 --- /dev/null +++ b/Telegram/_qt_5_3_1_patch/qtbase/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp @@ -0,0 +1,490 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbasicfontdatabase_p.h" + +#include +#include + +#include +#include +#include +#include +#include + +#undef QT_NO_FREETYPE +#include +#include + +#include +#include FT_TRUETYPE_TABLES_H +#include FT_ERRORS_H + +QT_BEGIN_NAMESPACE + +typedef struct { + quint16 majorVersion; + quint16 minorVersion; + quint16 numTables; + quint16 searchRange; + quint16 entrySelector; + quint16 rangeShift; +} OFFSET_TABLE; + +typedef struct { + quint32 tag; + quint32 checkSum; + quint32 offset; + quint32 length; +} TABLE_DIRECTORY; + +typedef struct { + quint16 fontSelector; + quint16 nrCount; + quint16 storageOffset; +} NAME_TABLE_HEADER; + +typedef struct { + quint16 platformID; + quint16 encodingID; + quint16 languageID; + quint16 nameID; + quint16 stringLength; + quint16 stringOffset; +} NAME_RECORD; + +void QBasicFontDatabase::populateFontDatabase() +{ + QString fontpath = fontDir(); + + if(!QFile::exists(fontpath)) { + qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", + qPrintable(fontpath)); + } + + QDir dir(fontpath); + dir.setNameFilters(QStringList() << QLatin1String("*.ttf") + << QLatin1String("*.ttc") << QLatin1String("*.pfa") + << QLatin1String("*.pfb") + << QLatin1String("*.otf")); + dir.refresh(); + for (int i = 0; i < int(dir.count()); ++i) { + const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); +// qDebug() << "looking at" << file; + addTTFile(QByteArray(), file); + } +} + +QFontEngine *QBasicFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr) +{ + FontFile *fontfile = static_cast (usrPtr); + QFontEngine::FaceId fid; + fid.filename = QFile::encodeName(fontfile->fileName); + fid.index = fontfile->indexValue; + + bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); + QFontEngineFT::GlyphFormat format = antialias? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono; + + QFontEngineFT *engine = new QFontEngineFT(fontDef); + if (!engine->init(fid, antialias, format) || engine->invalid()) { + delete engine; + engine = 0; + } + + return engine; +} + +namespace { + + class QFontEngineFTRawData: public QFontEngineFT + { + public: + QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef) + { + } + + void updateFamilyNameAndStyle() + { + fontDef.family = QString::fromLatin1(freetype->face->family_name); + + if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC) + fontDef.style = QFont::StyleItalic; + + if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD) + fontDef.weight = QFont::Bold; + } + + bool initFromData(const QByteArray &fontData) + { + FaceId faceId; + faceId.filename = ""; + faceId.index = 0; + faceId.uuid = QUuid::createUuid().toByteArray(); + + return init(faceId, true, Format_None, fontData); + } + }; + +} + +QFontEngine *QBasicFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + fontDef.hintingPreference = hintingPreference; + + QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef); + if (!fe->initFromData(fontData)) { + delete fe; + return 0; + } + + fe->updateFamilyNameAndStyle(); + + switch (hintingPreference) { + case QFont::PreferNoHinting: + fe->setDefaultHintStyle(QFontEngineFT::HintNone); + break; + case QFont::PreferFullHinting: + fe->setDefaultHintStyle(QFontEngineFT::HintFull); + break; + case QFont::PreferVerticalHinting: + fe->setDefaultHintStyle(QFontEngineFT::HintLight); + break; + default: + // Leave it as it is + break; + } + + return fe; +} + +QStringList QBasicFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + Q_UNUSED(family); + Q_UNUSED(style); + Q_UNUSED(script); + Q_UNUSED(styleHint); + return QStringList(); +} + +QStringList QBasicFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + return addTTFile(fontData,fileName.toLocal8Bit()); +} + +void QBasicFontDatabase::releaseHandle(void *handle) +{ + FontFile *file = static_cast(handle); + delete file; +} + +extern FT_Library qt_getFreetype(); + +// copied from freetype with some modifications + +#ifndef FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY +#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY FT_MAKE_TAG('i', 'g', 'p', 'f') +#endif + +#ifndef FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY +#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY FT_MAKE_TAG('i', 'g', 'p', 's') +#endif + +/* there's a Mac-specific extended implementation of FT_New_Face() */ +/* in src/base/ftmac.c */ + +#if !defined( FT_MACINTOSH ) || defined( DARWIN_NO_CARBON ) + +/* documentation is in freetype.h */ + +FT_Error __ft_New_Face(FT_Library library, const char* pathname, FT_Long face_index, FT_Face *aface) { + FT_Open_Args args; + + /* test for valid `library' and `aface' delayed to FT_Open_Face() */ + if ( !pathname ) + return FT_Err_Invalid_Argument; + + FT_Parameter params[2]; + params[0].tag = FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY; + params[0].data = 0; + params[1].tag = FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY; + params[1].data = 0; + args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; + args.pathname = (char*)pathname; + args.stream = NULL; + args.num_params = 2; + args.params = params; + + return FT_Open_Face( library, &args, face_index, aface ); +} + +#else + +FT_Error __ft_New_Face(FT_Library library, const char* pathname, FT_Long face_index, FT_Face *aface) { + return FT_New_Face(library, pathname, face_index, aface); +} + +#endif /* defined( FT_MACINTOSH ) && !defined( DARWIN_NO_CARBON ) */ + +/* documentation is in freetype.h */ + +FT_Error __ft_New_Memory_Face(FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface) { + FT_Open_Args args; + + /* test for valid `library' and `face' delayed to FT_Open_Face() */ + if ( !file_base ) + return FT_Err_Invalid_Argument; + + FT_Parameter params[2]; + params[0].tag = FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY; + params[0].data = 0; + params[1].tag = FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY; + params[1].data = 0; + args.flags = FT_OPEN_MEMORY | FT_OPEN_PARAMS; + args.memory_base = file_base; + args.memory_size = file_size; + args.stream = NULL; + args.num_params = 2; + args.params = params; + + return FT_Open_Face( library, &args, face_index, aface ); +} + +// end + +QStringList QBasicFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file, QSupportedWritingSystems *supportedWritingSystems) +{ + FT_Library library = qt_getFreetype(); + + int index = 0; + int numFaces = 0; + QStringList families; + do { + FT_Face face; + FT_Error error; + if (!fontData.isEmpty()) { + error = __ft_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face); + } else { + error = __ft_New_Face(library, file.constData(), index, &face); + } + if (error != FT_Err_Ok) { + qDebug() << "FT_New_Face failed with index" << index << ":" << hex << error; + break; + } + numFaces = face->num_faces; + + QFont::Weight weight = QFont::Normal; + + QFont::Style style = QFont::StyleNormal; + if (face->style_flags & FT_STYLE_FLAG_ITALIC) + style = QFont::StyleItalic; + + if (face->style_flags & FT_STYLE_FLAG_BOLD) + weight = QFont::Bold; + + bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); + + QSupportedWritingSystems writingSystems; + // detect symbol fonts + for (int i = 0; i < face->num_charmaps; ++i) { + FT_CharMap cm = face->charmaps[i]; + if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM + || cm->encoding == FT_ENCODING_MS_SYMBOL) { + writingSystems.setSupported(QFontDatabase::Symbol); + if (supportedWritingSystems) + supportedWritingSystems->setSupported(QFontDatabase::Symbol); + break; + } + } + + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); + if (os2) { + quint32 unicodeRange[4] = { + quint32(os2->ulUnicodeRange1), + quint32(os2->ulUnicodeRange2), + quint32(os2->ulUnicodeRange3), + quint32(os2->ulUnicodeRange4) + }; + quint32 codePageRange[2] = { + quint32(os2->ulCodePageRange1), + quint32(os2->ulCodePageRange2) + }; + + writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + if (supportedWritingSystems) + *supportedWritingSystems = writingSystems; + + if (os2->usWeightClass == 0) + ; + else if (os2->usWeightClass < 350) + weight = QFont::Light; + else if (os2->usWeightClass < 450) + weight = QFont::Normal; + else if (os2->usWeightClass < 650) + weight = QFont::DemiBold; + else if (os2->usWeightClass < 750) + weight = QFont::Bold; + else if (os2->usWeightClass < 1000) + weight = QFont::Black; + + if (os2->panose[2] >= 2) { + int w = os2->panose[2]; + if (w <= 3) + weight = QFont::Light; + else if (w <= 5) + weight = QFont::Normal; + else if (w <= 7) + weight = QFont::DemiBold; + else if (w <= 8) + weight = QFont::Bold; + else if (w <= 10) + weight = QFont::Black; + } + } + + QString family = QString::fromLatin1(face->family_name); + FontFile *fontFile = new FontFile; + fontFile->fileName = QFile::decodeName(file); + fontFile->indexValue = index; + + QFont::Stretch stretch = QFont::Unstretched; + + registerFont(family,QString::fromLatin1(face->style_name),QString(),weight,style,stretch,true,true,0,fixedPitch,writingSystems,fontFile); + + families.append(family); + + FT_Done_Face(face); + ++index; + } while (index < numFaces); + return families; +} + +QString QBasicFontDatabase::fontNameFromTTFile(const QString &filename) +{ + QFile f(filename); + QString retVal; + qint64 bytesRead; + qint64 bytesToRead; + + if (f.open(QIODevice::ReadOnly)) { + OFFSET_TABLE ttOffsetTable; + bytesToRead = sizeof(OFFSET_TABLE); + bytesRead = f.read((char*)&ttOffsetTable, bytesToRead); + if (bytesToRead != bytesRead) + return retVal; + ttOffsetTable.numTables = qFromBigEndian(ttOffsetTable.numTables); + ttOffsetTable.majorVersion = qFromBigEndian(ttOffsetTable.majorVersion); + ttOffsetTable.minorVersion = qFromBigEndian(ttOffsetTable.minorVersion); + + if (ttOffsetTable.majorVersion != 1 || ttOffsetTable.minorVersion != 0) + return retVal; + + TABLE_DIRECTORY tblDir; + bool found = false; + + for (int i = 0; i < ttOffsetTable.numTables; i++) { + bytesToRead = sizeof(TABLE_DIRECTORY); + bytesRead = f.read((char*)&tblDir, bytesToRead); + if (bytesToRead != bytesRead) + return retVal; + if (qFromBigEndian(tblDir.tag) == MAKE_TAG('n', 'a', 'm', 'e')) { + found = true; + tblDir.length = qFromBigEndian(tblDir.length); + tblDir.offset = qFromBigEndian(tblDir.offset); + break; + } + } + + if (found) { + f.seek(tblDir.offset); + NAME_TABLE_HEADER ttNTHeader; + bytesToRead = sizeof(NAME_TABLE_HEADER); + bytesRead = f.read((char*)&ttNTHeader, bytesToRead); + if (bytesToRead != bytesRead) + return retVal; + ttNTHeader.nrCount = qFromBigEndian(ttNTHeader.nrCount); + ttNTHeader.storageOffset = qFromBigEndian(ttNTHeader.storageOffset); + NAME_RECORD ttRecord; + found = false; + + for (int i = 0; i < ttNTHeader.nrCount; i++) { + bytesToRead = sizeof(NAME_RECORD); + bytesRead = f.read((char*)&ttRecord, bytesToRead); + if (bytesToRead != bytesRead) + return retVal; + ttRecord.nameID = qFromBigEndian(ttRecord.nameID); + if (ttRecord.nameID == 1) { + ttRecord.stringLength = qFromBigEndian(ttRecord.stringLength); + ttRecord.stringOffset = qFromBigEndian(ttRecord.stringOffset); + int nPos = f.pos(); + f.seek(tblDir.offset + ttRecord.stringOffset + ttNTHeader.storageOffset); + + QByteArray nameByteArray = f.read(ttRecord.stringLength); + if (!nameByteArray.isEmpty()) { + if (ttRecord.encodingID == 256 || ttRecord.encodingID == 768) { + //This is UTF-16 in big endian + int stringLength = ttRecord.stringLength / 2; + retVal.resize(stringLength); + QChar *data = retVal.data(); + const ushort *srcData = (const ushort *)nameByteArray.data(); + for (int i = 0; i < stringLength; ++i) + data[i] = qFromBigEndian(srcData[i]); + return retVal; + } else if (ttRecord.encodingID == 0) { + //This is Latin1 + retVal = QString::fromLatin1(nameByteArray); + } else { + qWarning("Could not retrieve Font name from file: %s", qPrintable(QDir::toNativeSeparators(filename))); + } + break; + } + f.seek(nPos); + } + } + } + f.close(); + } + return retVal; +} + +QT_END_NAMESPACE diff --git a/Telegram/telegram_plugin_import.cpp b/Telegram/telegram_plugin_import.cpp index 33b4b9aa5..8e9103300 100644 --- a/Telegram/telegram_plugin_import.cpp +++ b/Telegram/telegram_plugin_import.cpp @@ -1,16 +1,11 @@ // This file is autogenerated by qmake. It imports static plugin classes for // static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS. variables. #include -//Q_IMPORT_PLUGIN(AVFServicePlugin) -Q_IMPORT_PLUGIN(AVFMediaPlayerServicePlugin) -Q_IMPORT_PLUGIN(QT7ServicePlugin) Q_IMPORT_PLUGIN(AudioCaptureServicePlugin) -Q_IMPORT_PLUGIN(CoreAudioPlugin) Q_IMPORT_PLUGIN(QM3uPlaylistPlugin) Q_IMPORT_PLUGIN(AccessibleFactory) -Q_IMPORT_PLUGIN(QCoreWlanEnginePlugin) Q_IMPORT_PLUGIN(QGenericEnginePlugin) -Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin) +Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) Q_IMPORT_PLUGIN(QDDSPlugin) Q_IMPORT_PLUGIN(QICNSPlugin) Q_IMPORT_PLUGIN(QICOPlugin)